|
5 | 5 | //! Supports up to 4 distinct speakers with voice cloning from .wav files. |
6 | 6 |
|
7 | 7 | use anyhow::Result; |
8 | | -use candle_core::{DType, Device, IndexOp, Module, Tensor, D}; |
| 8 | +use candle_core::{DType, Device, IndexOp, Module, Shape, Tensor, D}; |
9 | 9 | use candle_nn::VarBuilder; |
10 | 10 | use log::info; |
11 | 11 |
|
| 12 | +/// Backend that loads safetensors to CPU, casts to target dtype, then moves to device. |
| 13 | +/// Avoids BF16 CUDA kernel issues on GPUs older than Ampere (SM < 8.0). |
| 14 | +struct CpuCastBackend { |
| 15 | + inner: candle_core::safetensors::MmapedSafetensors, |
| 16 | + target_device: Device, |
| 17 | +} |
| 18 | + |
| 19 | +impl candle_nn::var_builder::SimpleBackend for CpuCastBackend { |
| 20 | + fn get( |
| 21 | + &self, |
| 22 | + s: Shape, |
| 23 | + name: &str, |
| 24 | + _h: candle_nn::Init, |
| 25 | + dtype: DType, |
| 26 | + _dev: &Device, |
| 27 | + ) -> candle_core::Result<Tensor> { |
| 28 | + let tensor = self.inner.load(name, &Device::Cpu)? |
| 29 | + .to_dtype(dtype)? |
| 30 | + .to_device(&self.target_device)?; |
| 31 | + if tensor.shape() != &s { |
| 32 | + Err(candle_core::Error::UnexpectedShape { |
| 33 | + msg: format!("shape mismatch for {name}"), |
| 34 | + expected: s, |
| 35 | + got: tensor.shape().clone(), |
| 36 | + }.bt())? |
| 37 | + } |
| 38 | + Ok(tensor) |
| 39 | + } |
| 40 | + |
| 41 | + fn get_unchecked(&self, name: &str, dtype: DType, _dev: &Device) -> candle_core::Result<Tensor> { |
| 42 | + self.inner.load(name, &Device::Cpu)? |
| 43 | + .to_dtype(dtype)? |
| 44 | + .to_device(&self.target_device) |
| 45 | + } |
| 46 | + |
| 47 | + fn contains_tensor(&self, name: &str) -> bool { |
| 48 | + self.inner.get(name).is_ok() |
| 49 | + } |
| 50 | +} |
| 51 | + |
12 | 52 | use super::acoustic_connector::AcousticConnector; |
13 | 53 | use super::config_1_5b::*; |
14 | 54 | use super::ddpm::DpmSolverPP; |
@@ -69,10 +109,15 @@ impl VibeVoice1_5B { |
69 | 109 | let common_cfg = config.into_config(); |
70 | 110 |
|
71 | 111 | info!("Loading VibeVoice-1.5B..."); |
72 | | - let dtype = DType::BF16; |
| 112 | + let dtype = DType::F16; |
73 | 113 |
|
74 | 114 | let vb = unsafe { |
75 | | - VarBuilder::from_mmaped_safetensors(weight_paths, dtype, device)? |
| 115 | + // Load via CPU to handle BF16→F16 cast (BF16 CUDA kernels need SM 8.0+). |
| 116 | + // MmapedSafetensors loads BF16 on CPU, casts to F16, then moves to device. |
| 117 | + let tensors = candle_core::safetensors::MmapedSafetensors::multi(weight_paths)?; |
| 118 | + let backend: Box<dyn candle_nn::var_builder::SimpleBackend> = |
| 119 | + Box::new(CpuCastBackend { inner: tensors, target_device: device.clone() }); |
| 120 | + VarBuilder::from_backend(backend, dtype, device.clone()) |
76 | 121 | }; |
77 | 122 |
|
78 | 123 | // Single LM (28 layers) |
|
0 commit comments