Merge remote-tracking branch 'origin/master'

This commit is contained in:
Niko 2025-02-24 12:11:01 -07:00
commit 745336dd6d
No known key found for this signature in database
13 changed files with 364 additions and 178 deletions

View file

@ -73,4 +73,4 @@ jobs:
toolchain: ${{ matrix.rust-version }} toolchain: ${{ matrix.rust-version }}
- name: Check build - name: Check build
run: cargo build -F whisper-cpp-log,whisper-cpp-tracing --verbose --examples run: cargo build -F log_backend,tracing_backend --verbose --examples

View file

@ -32,8 +32,14 @@ hipblas = ["whisper-rs-sys/hipblas", "_gpu"]
openblas = ["whisper-rs-sys/openblas"] openblas = ["whisper-rs-sys/openblas"]
metal = ["whisper-rs-sys/metal", "_gpu"] metal = ["whisper-rs-sys/metal", "_gpu"]
vulkan = ["whisper-rs-sys/vulkan", "_gpu"] vulkan = ["whisper-rs-sys/vulkan", "_gpu"]
openmp = ["whisper-rs-sys/openmp"]
_gpu = [] _gpu = []
test-with-tiny-model = [] test-with-tiny-model = []
whisper-cpp-log = ["dep:log"]
whisper-cpp-tracing = ["dep:tracing"] # Bring logs into Rust via the log crate. *Warning*: not mutually exclusive with tracing_backend,
openmp = ["whisper-rs-sys/openmp"] # will result in duplicate logs if both are enabled and one consumes logs from the other.
log_backend = ["dep:log"]
# Bring logs into Rust via the tracing crate. *Warning*: not mutually exclusive with log_backend,
# will result in duplicate logs if both are enabled and one consumes logs from the other.
tracing_backend = ["dep:tracing"]

76
src/common_logging.rs Normal file
View file

@ -0,0 +1,76 @@
macro_rules! generic_error {
($($expr:tt)*) => {
#[cfg(feature = "log_backend")]
log::error!($($expr)*);
#[cfg(feature = "tracing_backend")]
tracing::error!($($expr)*);
};
}
macro_rules! generic_warn {
($($expr:tt)*) => {
#[cfg(feature = "log_backend")]
log::warn!($($expr)*);
#[cfg(feature = "tracing_backend")]
tracing::warn!($($expr)*);
}
}
macro_rules! generic_info {
($($expr:tt)*) => {
#[cfg(feature = "log_backend")]
log::info!($($expr)*);
#[cfg(feature = "tracing_backend")]
tracing::info!($($expr)*);
}
}
macro_rules! generic_debug {
($($expr:tt)*) => {
#[cfg(feature = "log_backend")]
log::debug!($($expr)*);
#[cfg(feature = "tracing_backend")]
tracing::debug!($($expr)*);
}
}
macro_rules! generic_trace {
($($expr:tt)*) => {
#[cfg(feature = "log_backend")]
log::trace!($($expr)*);
#[cfg(feature = "tracing_backend")]
tracing::trace!($($expr)*);
}
}
use whisper_rs_sys::ggml_log_level;
pub(crate) use {generic_debug, generic_error, generic_info, generic_trace, generic_warn};
// Unsigned integer type on most platforms is 32 bit, niche platforms that whisper.cpp
// likely doesn't even support would use 16 bit and would still fit
#[cfg_attr(any(not(windows), target_env = "gnu"), repr(u32))]
// Of course Windows thinks it's a special little shit and
// picks a signed integer for an unsigned type
#[cfg_attr(all(windows, not(target_env = "gnu")), repr(i32))]
pub enum GGMLLogLevel {
None = whisper_rs_sys::ggml_log_level_GGML_LOG_LEVEL_NONE,
Info = whisper_rs_sys::ggml_log_level_GGML_LOG_LEVEL_INFO,
Warn = whisper_rs_sys::ggml_log_level_GGML_LOG_LEVEL_WARN,
Error = whisper_rs_sys::ggml_log_level_GGML_LOG_LEVEL_ERROR,
Debug = whisper_rs_sys::ggml_log_level_GGML_LOG_LEVEL_DEBUG,
Cont = whisper_rs_sys::ggml_log_level_GGML_LOG_LEVEL_CONT,
Unknown(ggml_log_level),
}
impl From<ggml_log_level> for GGMLLogLevel {
fn from(level: ggml_log_level) -> Self {
match level {
whisper_rs_sys::ggml_log_level_GGML_LOG_LEVEL_NONE => GGMLLogLevel::None,
whisper_rs_sys::ggml_log_level_GGML_LOG_LEVEL_INFO => GGMLLogLevel::Info,
whisper_rs_sys::ggml_log_level_GGML_LOG_LEVEL_WARN => GGMLLogLevel::Warn,
whisper_rs_sys::ggml_log_level_GGML_LOG_LEVEL_ERROR => GGMLLogLevel::Error,
whisper_rs_sys::ggml_log_level_GGML_LOG_LEVEL_DEBUG => GGMLLogLevel::Debug,
whisper_rs_sys::ggml_log_level_GGML_LOG_LEVEL_CONT => GGMLLogLevel::Cont,
other => GGMLLogLevel::Unknown(other),
}
}
}

73
src/ggml_logging_hook.rs Normal file
View file

@ -0,0 +1,73 @@
use crate::common_logging::{
generic_debug, generic_error, generic_info, generic_trace, generic_warn, GGMLLogLevel,
};
use core::ffi::{c_char, c_void};
use std::borrow::Cow;
use std::ffi::CStr;
use std::sync::Once;
use whisper_rs_sys::ggml_log_level;
static GGML_LOG_TRAMPOLINE_INSTALL: Once = Once::new();
pub(crate) fn install_ggml_logging_hook() {
GGML_LOG_TRAMPOLINE_INSTALL.call_once(|| unsafe {
whisper_rs_sys::ggml_log_set(Some(ggml_logging_trampoline), std::ptr::null_mut())
});
}
unsafe extern "C" fn ggml_logging_trampoline(
level: ggml_log_level,
text: *const c_char,
_: *mut c_void, // user_data
) {
if text.is_null() {
generic_error!("ggml_logging_trampoline: text is nullptr");
}
let level = GGMLLogLevel::from(level);
// SAFETY: we must trust ggml that it will not pass us a string that does not satisfy
// from_ptr's requirements.
let log_str = unsafe { CStr::from_ptr(text) }.to_string_lossy();
ggml_logging_trampoline_safe(level, log_str)
}
// this code essentially compiles down to a noop if neither feature is enabled
#[cfg_attr(
not(any(feature = "log_backend", feature = "tracing_backend")),
allow(unused_variables)
)]
fn ggml_logging_trampoline_safe(level: GGMLLogLevel, text: Cow<str>) {
match level {
GGMLLogLevel::None => {
// no clue what to do here, trace it?
generic_trace!("{}", text.trim());
}
GGMLLogLevel::Info => {
generic_info!("{}", text.trim());
}
GGMLLogLevel::Warn => {
generic_warn!("{}", text.trim());
}
GGMLLogLevel::Error => {
generic_error!("{}", text.trim());
}
GGMLLogLevel::Debug => {
generic_debug!("{}", text.trim());
}
GGMLLogLevel::Cont => {
// this means continue previous log
// storing state to do this is a massive pain so it's just a lot easier to not
// plus as far as i can tell it's not actually *used* anywhere
// ggml splits at 128 chars and doesn't actually change the kind of log
// so technically this is unused
generic_trace!("{}", text.trim());
}
GGMLLogLevel::Unknown(level) => {
generic_warn!(
"ggml_logging_trampoline: unknown log level {}: message: {}",
level,
text.trim()
);
}
}
}

View file

@ -1,26 +1,21 @@
#![allow(clippy::uninlined_format_args)] #![allow(clippy::uninlined_format_args)]
#![cfg_attr(test, feature(test))] #![cfg_attr(test, feature(test))]
mod common_logging;
mod error; mod error;
mod ggml_logging_hook;
mod standalone; mod standalone;
mod utilities; mod utilities;
mod whisper_ctx; mod whisper_ctx;
mod whisper_ctx_wrapper; mod whisper_ctx_wrapper;
mod whisper_grammar; mod whisper_grammar;
mod whisper_logging_hook;
mod whisper_params; mod whisper_params;
mod whisper_state; mod whisper_state;
#[cfg(feature = "whisper-cpp-log")]
mod whisper_sys_log;
#[cfg(feature = "whisper-cpp-tracing")]
mod whisper_sys_tracing;
#[cfg(any(feature = "whisper-cpp-log", feature = "whisper-cpp-tracing"))]
static LOG_TRAMPOLINE_INSTALL: Once = Once::new();
pub use common_logging::GGMLLogLevel;
pub use error::WhisperError; pub use error::WhisperError;
pub use standalone::*; pub use standalone::*;
#[cfg(any(feature = "whisper-cpp-log", feature = "whisper-cpp-tracing"))]
use std::sync::Once;
pub use utilities::*; pub use utilities::*;
pub use whisper_ctx::DtwMode; pub use whisper_ctx::DtwMode;
pub use whisper_ctx::DtwModelPreset; pub use whisper_ctx::DtwModelPreset;
@ -33,10 +28,6 @@ pub use whisper_params::{FullParams, SamplingStrategy, SegmentCallbackData};
#[cfg(feature = "raw-api")] #[cfg(feature = "raw-api")]
pub use whisper_rs_sys; pub use whisper_rs_sys;
pub use whisper_state::WhisperState; pub use whisper_state::WhisperState;
#[cfg(feature = "whisper-cpp-log")]
pub use whisper_sys_log::install_whisper_log_trampoline;
#[cfg(feature = "whisper-cpp-tracing")]
pub use whisper_sys_tracing::install_whisper_tracing_trampoline;
pub type WhisperSysContext = whisper_rs_sys::whisper_context; pub type WhisperSysContext = whisper_rs_sys::whisper_context;
pub type WhisperSysState = whisper_rs_sys::whisper_state; pub type WhisperSysState = whisper_rs_sys::whisper_state;
@ -53,3 +44,25 @@ pub type DtwAhead = whisper_rs_sys::whisper_ahead;
/// The version of whisper.cpp that whisper-rs was linked with. /// The version of whisper.cpp that whisper-rs was linked with.
pub static WHISPER_CPP_VERSION: &str = env!("WHISPER_CPP_VERSION"); pub static WHISPER_CPP_VERSION: &str = env!("WHISPER_CPP_VERSION");
/// Redirect all whisper.cpp and GGML logs to logging hooks installed by whisper-rs.
///
/// This will stop most logs from being output to stdout/stderr and will bring them into
/// `log` or `tracing`, if the `log_backend` or `tracing_backend` features, respectively,
/// are enabled. If neither is enabled, this will essentially disable logging, as they won't
/// be output anywhere.
///
/// Note whisper.cpp and GGML do not reliably follow Rust logging conventions.
/// Use your logging crate's configuration to control how these logs will be output.
/// whisper-rs does not currently output any logs, but this may change in the future.
/// You should configure by module path and use `whisper_rs::ggml_logging_hook`,
/// and/or `whisper_rs::whisper_logging_hook`, to avoid possibly ignoring useful
/// `whisper-rs` logs in the future.
///
/// Safe to call multiple times. Only has an effect the first time.
/// (note this means installing your own logging handlers with unsafe functions after this call
/// is permanent and cannot be undone)
pub fn install_logging_hooks() {
crate::whisper_logging_hook::install_whisper_logging_hook();
crate::ggml_logging_hook::install_ggml_logging_hook();
}

View file

@ -105,8 +105,6 @@ pub struct SystemInfo {
pub avx2: bool, pub avx2: bool,
pub fma: bool, pub fma: bool,
pub f16c: bool, pub f16c: bool,
pub blas: bool,
pub cuda: bool,
} }
impl Default for SystemInfo { impl Default for SystemInfo {
@ -117,8 +115,6 @@ impl Default for SystemInfo {
avx2: whisper_rs_sys::ggml_cpu_has_avx2() != 0, avx2: whisper_rs_sys::ggml_cpu_has_avx2() != 0,
fma: whisper_rs_sys::ggml_cpu_has_fma() != 0, fma: whisper_rs_sys::ggml_cpu_has_fma() != 0,
f16c: whisper_rs_sys::ggml_cpu_has_f16c() != 0, f16c: whisper_rs_sys::ggml_cpu_has_f16c() != 0,
blas: whisper_rs_sys::ggml_cpu_has_blas() != 0,
cuda: whisper_rs_sys::ggml_cpu_has_cuda() != 0,
} }
} }
} }

View file

@ -0,0 +1,73 @@
use crate::common_logging::{
generic_debug, generic_error, generic_info, generic_trace, generic_warn, GGMLLogLevel,
};
use core::ffi::{c_char, c_void};
use std::borrow::Cow;
use std::ffi::CStr;
use std::sync::Once;
use whisper_rs_sys::ggml_log_level;
static WHISPER_LOG_TRAMPOLINE_INSTALL: Once = Once::new();
pub(crate) fn install_whisper_logging_hook() {
WHISPER_LOG_TRAMPOLINE_INSTALL.call_once(|| unsafe {
whisper_rs_sys::whisper_log_set(Some(whisper_logging_trampoline), std::ptr::null_mut())
});
}
unsafe extern "C" fn whisper_logging_trampoline(
level: ggml_log_level,
text: *const c_char,
_: *mut c_void, // user_data
) {
if text.is_null() {
generic_error!("whisper_logging_trampoline: text is nullptr");
}
let level = GGMLLogLevel::from(level);
// SAFETY: we must trust whisper.cpp that it will not pass us a string that does not satisfy
// from_ptr's requirements.
let log_str = unsafe { CStr::from_ptr(text) }.to_string_lossy();
whisper_logging_trampoline_safe(level, log_str)
}
// this code essentially compiles down to a noop if neither feature is enabled
#[cfg_attr(
not(any(feature = "log_backend", feature = "tracing_backend")),
allow(unused_variables)
)]
fn whisper_logging_trampoline_safe(level: GGMLLogLevel, text: Cow<str>) {
match level {
GGMLLogLevel::None => {
// no clue what to do here, trace it?
generic_trace!("{}", text.trim());
}
GGMLLogLevel::Info => {
generic_info!("{}", text.trim());
}
GGMLLogLevel::Warn => {
generic_warn!("{}", text.trim());
}
GGMLLogLevel::Error => {
generic_error!("{}", text.trim());
}
GGMLLogLevel::Debug => {
generic_debug!("{}", text.trim());
}
GGMLLogLevel::Cont => {
// this means continue previous log
// storing state to do this is a massive pain so it's just a lot easier to not
// plus as far as i can tell it's not actually *used* anywhere
// whisper splits at 1024 chars and doesn't actually change the kind
// so technically this is unused
generic_trace!("{}", text.trim());
}
GGMLLogLevel::Unknown(level) => {
generic_warn!(
"whisper_logging_trampoline: unknown log level {}: message: {}",
level,
text.trim()
);
}
}
}

View file

@ -304,8 +304,8 @@ impl<'a, 'b> FullParams<'a, 'b> {
/// for more information. /// for more information.
/// ///
/// Defaults to false. /// Defaults to false.
pub fn set_suppress_non_speech_tokens(&mut self, suppress_non_speech_tokens: bool) { pub fn set_suppress_nst(&mut self, suppress_nst: bool) {
self.fp.suppress_non_speech_tokens = suppress_non_speech_tokens; self.fp.suppress_nst = suppress_nst;
} }
/// Set initial decoding temperature. /// Set initial decoding temperature.

View file

@ -346,25 +346,43 @@ impl WhisperState {
Ok(unsafe { whisper_rs_sys::whisper_full_get_segment_t1_from_state(self.ptr, segment) }) Ok(unsafe { whisper_rs_sys::whisper_full_get_segment_t1_from_state(self.ptr, segment) })
} }
fn full_get_segment_raw(&self, segment: c_int) -> Result<&CStr, WhisperError> {
let ret =
unsafe { whisper_rs_sys::whisper_full_get_segment_text_from_state(self.ptr, segment) };
if ret.is_null() {
return Err(WhisperError::NullPointer);
}
unsafe { Ok(CStr::from_ptr(ret)) }
}
/// Get the raw bytes of the specified segment.
///
/// # Arguments
/// * segment: Segment index.
///
/// # Returns
/// `Ok(Vec<u8>)` on success, with the returned bytes or
/// `Err(WhisperError::NullPointer)` on failure (this is the only possible error)
///
/// # C++ equivalent
/// `const char * whisper_full_get_segment_text(struct whisper_context * ctx, int i_segment)`
pub fn full_get_segment_bytes(&self, segment: c_int) -> Result<Vec<u8>, WhisperError> {
Ok(self.full_get_segment_raw(segment)?.to_bytes().to_vec())
}
/// Get the text of the specified segment. /// Get the text of the specified segment.
/// ///
/// # Arguments /// # Arguments
/// * segment: Segment index. /// * segment: Segment index.
/// ///
/// # Returns /// # Returns
/// Ok(String) on success, Err(WhisperError) on failure. /// `Ok(String)` on success, with the UTF-8 validated string, or
/// `Err(WhisperError)` on failure (either `NullPointer` or `InvalidUtf8`)
/// ///
/// # C++ equivalent /// # C++ equivalent
/// `const char * whisper_full_get_segment_text(struct whisper_context * ctx, int i_segment)` /// `const char * whisper_full_get_segment_text(struct whisper_context * ctx, int i_segment)`
pub fn full_get_segment_text(&self, segment: c_int) -> Result<String, WhisperError> { pub fn full_get_segment_text(&self, segment: c_int) -> Result<String, WhisperError> {
let ret = Ok(self.full_get_segment_raw(segment)?.to_str()?.to_string())
unsafe { whisper_rs_sys::whisper_full_get_segment_text_from_state(self.ptr, segment) };
if ret.is_null() {
return Err(WhisperError::NullPointer);
}
let c_str = unsafe { CStr::from_ptr(ret) };
let r_str = c_str.to_str()?;
Ok(r_str.to_string())
} }
/// Get the text of the specified segment. /// Get the text of the specified segment.
@ -376,38 +394,16 @@ impl WhisperState {
/// * segment: Segment index. /// * segment: Segment index.
/// ///
/// # Returns /// # Returns
/// Ok(String) on success, Err(WhisperError) on failure. /// `Ok(String)` on success, or
/// `Err(WhisperError::NullPointer)` on failure (this is the only possible error)
/// ///
/// # C++ equivalent /// # C++ equivalent
/// `const char * whisper_full_get_segment_text(struct whisper_context * ctx, int i_segment)` /// `const char * whisper_full_get_segment_text(struct whisper_context * ctx, int i_segment)`
pub fn full_get_segment_text_lossy(&self, segment: c_int) -> Result<String, WhisperError> { pub fn full_get_segment_text_lossy(&self, segment: c_int) -> Result<String, WhisperError> {
let ret = Ok(self
unsafe { whisper_rs_sys::whisper_full_get_segment_text_from_state(self.ptr, segment) }; .full_get_segment_raw(segment)?
if ret.is_null() { .to_string_lossy()
return Err(WhisperError::NullPointer); .to_string())
}
let c_str = unsafe { CStr::from_ptr(ret) };
Ok(c_str.to_string_lossy().to_string())
}
/// Get the bytes of the specified segment.
///
/// # Arguments
/// * segment: Segment index.
///
/// # Returns
/// `Ok(Vec<u8>)` on success, `Err(WhisperError)` on failure.
///
/// # C++ equivalent
/// `const char * whisper_full_get_segment_text(struct whisper_context * ctx, int i_segment)`
pub fn full_get_segment_bytes(&self, segment: c_int) -> Result<Vec<u8>, WhisperError> {
let ret =
unsafe { whisper_rs_sys::whisper_full_get_segment_text_from_state(self.ptr, segment) };
if ret.is_null() {
return Err(WhisperError::NullPointer);
}
let c_str = unsafe { CStr::from_ptr(ret) };
Ok(c_str.to_bytes().to_vec())
} }
/// Get number of tokens in the specified segment. /// Get number of tokens in the specified segment.
@ -425,22 +421,7 @@ impl WhisperState {
Ok(unsafe { whisper_rs_sys::whisper_full_n_tokens_from_state(self.ptr, segment) }) Ok(unsafe { whisper_rs_sys::whisper_full_n_tokens_from_state(self.ptr, segment) })
} }
/// Get the token text of the specified token in the specified segment. fn full_get_token_raw(&self, segment: c_int, token: c_int) -> Result<&CStr, WhisperError> {
///
/// # Arguments
/// * segment: Segment index.
/// * token: Token index.
///
/// # Returns
/// Ok(String) on success, Err(WhisperError) on failure.
///
/// # C++ equivalent
/// `const char * whisper_full_get_token_text(struct whisper_context * ctx, int i_segment, int i_token)`
pub fn full_get_token_text(
&self,
segment: c_int,
token: c_int,
) -> Result<String, WhisperError> {
let ret = unsafe { let ret = unsafe {
whisper_rs_sys::whisper_full_get_token_text_from_state( whisper_rs_sys::whisper_full_get_token_text_from_state(
self.ctx.ctx, self.ctx.ctx,
@ -452,9 +433,53 @@ impl WhisperState {
if ret.is_null() { if ret.is_null() {
return Err(WhisperError::NullPointer); return Err(WhisperError::NullPointer);
} }
let c_str = unsafe { CStr::from_ptr(ret) }; unsafe { Ok(CStr::from_ptr(ret)) }
let r_str = c_str.to_str()?; }
Ok(r_str.to_string())
/// Get the raw token bytes of the specified token in the specified segment.
///
/// Useful if you're using a language for which whisper is known to split tokens
/// away from UTF-8 character boundaries.
///
/// # Arguments
/// * segment: Segment index.
/// * token: Token index.
///
/// # Returns
/// `Ok(Vec<u8>)` on success, with the returned bytes or
/// `Err(WhisperError::NullPointer)` on failure (this is the only possible error)
///
/// # C++ equivalent
/// `const char * whisper_full_get_token_text(struct whisper_context * ctx, int i_segment, int i_token)`
pub fn full_get_token_bytes(
&self,
segment: c_int,
token: c_int,
) -> Result<Vec<u8>, WhisperError> {
Ok(self.full_get_token_raw(segment, token)?.to_bytes().to_vec())
}
/// Get the token text of the specified token in the specified segment.
///
/// # Arguments
/// * segment: Segment index.
/// * token: Token index.
///
/// # Returns
/// `Ok(String)` on success, with the UTF-8 validated string, or
/// `Err(WhisperError)` on failure (either `NullPointer` or `InvalidUtf8`)
///
/// # C++ equivalent
/// `const char * whisper_full_get_token_text(struct whisper_context * ctx, int i_segment, int i_token)`
pub fn full_get_token_text(
&self,
segment: c_int,
token: c_int,
) -> Result<String, WhisperError> {
Ok(self
.full_get_token_raw(segment, token)?
.to_str()?
.to_string())
} }
/// Get the token text of the specified token in the specified segment. /// Get the token text of the specified token in the specified segment.
@ -467,7 +492,8 @@ impl WhisperState {
/// * token: Token index. /// * token: Token index.
/// ///
/// # Returns /// # Returns
/// Ok(String) on success, Err(WhisperError) on failure. /// `Ok(String)` on success, or
/// `Err(WhisperError::NullPointer)` on failure (this is the only possible error)
/// ///
/// # C++ equivalent /// # C++ equivalent
/// `const char * whisper_full_get_token_text(struct whisper_context * ctx, int i_segment, int i_token)` /// `const char * whisper_full_get_token_text(struct whisper_context * ctx, int i_segment, int i_token)`
@ -476,19 +502,10 @@ impl WhisperState {
segment: c_int, segment: c_int,
token: c_int, token: c_int,
) -> Result<String, WhisperError> { ) -> Result<String, WhisperError> {
let ret = unsafe { Ok(self
whisper_rs_sys::whisper_full_get_token_text_from_state( .full_get_token_raw(segment, token)?
self.ctx.ctx, .to_string_lossy()
self.ptr, .to_string())
segment,
token,
)
};
if ret.is_null() {
return Err(WhisperError::NullPointer);
}
let c_str = unsafe { CStr::from_ptr(ret) };
Ok(c_str.to_string_lossy().to_string())
} }
/// Get the token ID of the specified token in the specified segment. /// Get the token ID of the specified token in the specified segment.

View file

@ -1,42 +0,0 @@
use log::{debug, error, info, warn};
use whisper_rs_sys::ggml_log_level;
unsafe extern "C" fn whisper_cpp_log_trampoline(
level: ggml_log_level,
text: *const std::os::raw::c_char,
_: *mut std::os::raw::c_void, // user_data
) {
if text.is_null() {
error!("whisper_cpp_log_trampoline: text is nullptr");
}
// SAFETY: we must trust whisper.cpp that it will not pass us a string that does not satisfy
// from_ptr's requirements.
let log_str = unsafe { std::ffi::CStr::from_ptr(text) }.to_string_lossy();
// whisper.cpp gives newlines at the end of its log messages, so we trim them
let trimmed = log_str.trim();
match level {
whisper_rs_sys::ggml_log_level_GGML_LOG_LEVEL_DEBUG => debug!("{}", trimmed),
whisper_rs_sys::ggml_log_level_GGML_LOG_LEVEL_INFO => info!("{}", trimmed),
whisper_rs_sys::ggml_log_level_GGML_LOG_LEVEL_WARN => warn!("{}", trimmed),
whisper_rs_sys::ggml_log_level_GGML_LOG_LEVEL_ERROR => error!("{}", trimmed),
_ => {
warn!(
"whisper_cpp_log_trampoline: unknown log level {}: message: {}",
level, trimmed
)
}
}
}
/// Shortcut utility to redirect all whisper.cpp logging to the `log` crate.
///
/// Filter for logs from the `whisper-rs` crate to see all log output from whisper.cpp.
///
/// You should only call this once (subsequent calls have no ill effect).
pub fn install_whisper_log_trampoline() {
crate::LOG_TRAMPOLINE_INSTALL.call_once(|| unsafe {
whisper_rs_sys::whisper_log_set(Some(whisper_cpp_log_trampoline), std::ptr::null_mut());
});
}

View file

@ -1,42 +0,0 @@
use tracing::{debug, error, info, warn};
use whisper_rs_sys::ggml_log_level;
unsafe extern "C" fn whisper_cpp_tracing_trampoline(
level: ggml_log_level,
text: *const std::os::raw::c_char,
_: *mut std::os::raw::c_void, // user_data
) {
if text.is_null() {
error!("whisper_cpp_tracing_trampoline: text is nullptr");
}
// SAFETY: we must trust whisper.cpp that it will not pass us a string that does not satisfy
// from_ptr's requirements.
let log_str = unsafe { std::ffi::CStr::from_ptr(text) }.to_string_lossy();
// whisper.cpp gives newlines at the end of its log messages, so we trim them
let trimmed = log_str.trim();
match level {
whisper_rs_sys::ggml_log_level_GGML_LOG_LEVEL_DEBUG => debug!("{}", trimmed),
whisper_rs_sys::ggml_log_level_GGML_LOG_LEVEL_INFO => info!("{}", trimmed),
whisper_rs_sys::ggml_log_level_GGML_LOG_LEVEL_WARN => warn!("{}", trimmed),
whisper_rs_sys::ggml_log_level_GGML_LOG_LEVEL_ERROR => error!("{}", trimmed),
_ => {
warn!(
"whisper_cpp_tracing_trampoline: unknown log level {}: message: {}",
level, trimmed
)
}
}
}
/// Shortcut utility to redirect all whisper.cpp logging to the `tracing` crate.
///
/// Filter for logs from the `whisper-rs` crate to see all log output from whisper.cpp.
///
/// You should only call this once (subsequent calls have no effect).
pub fn install_whisper_tracing_trampoline() {
crate::LOG_TRAMPOLINE_INSTALL.call_once(|| unsafe {
whisper_rs_sys::whisper_log_set(Some(whisper_cpp_tracing_trampoline), std::ptr::null_mut());
});
}

View file

@ -253,6 +253,22 @@ fn main() {
println!("cargo:rustc-link-search=native={}", destination.display()); println!("cargo:rustc-link-search=native={}", destination.display());
println!("cargo:rustc-link-lib=static=whisper"); println!("cargo:rustc-link-lib=static=whisper");
println!("cargo:rustc-link-lib=static=ggml"); println!("cargo:rustc-link-lib=static=ggml");
println!("cargo:rustc-link-lib=static=ggml-base");
println!("cargo:rustc-link-lib=static=ggml-cpu");
if cfg!(target_os = "macos") {
println!("cargo:rustc-link-lib=static=ggml-blas");
}
if cfg!(feature = "vulkan") {
println!("cargo:rustc-link-lib=static=ggml-vulkan");
}
if cfg!(feature = "metal") {
println!("cargo:rustc-link-lib=static=ggml-metal");
}
if cfg!(feature = "cuda") {
println!("cargo:rustc-link-lib=static=ggml-cuda");
}
println!( println!(
"cargo:WHISPER_CPP_VERSION={}", "cargo:WHISPER_CPP_VERSION={}",

@ -1 +1 @@
Subproject commit 0377596b77a3602e36430320cbe45f8c305ef04a Subproject commit 8a9ad7844d6e2a10cddf4b92de4089d7ac2b14a9