Compare commits
No commits in common. "2a94ac0f69ce5129be3abe790f4f6083e5801443" and "da147fe6d4775f639cfcf2c5c6c6d4d1abc90a36" have entirely different histories.
2a94ac0f69
...
da147fe6d4
|
|
@ -4,7 +4,7 @@ version = "0.1.0"
|
|||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
cpal = "0.17"
|
||||
cpal = "0.15"
|
||||
symphonia = { version = "0.5", features = ["all"] }
|
||||
rtrb = "0.3"
|
||||
midly = "0.5"
|
||||
|
|
|
|||
|
|
@ -67,12 +67,14 @@ impl AudioSystem {
|
|||
.ok_or("No output device available")?;
|
||||
|
||||
let default_output_config = output_device.default_output_config().map_err(|e| e.to_string())?;
|
||||
let sample_rate = default_output_config.sample_rate();
|
||||
let sample_rate = default_output_config.sample_rate().0;
|
||||
let channels = default_output_config.channels() as u32;
|
||||
let debug_audio = std::env::var("DAW_AUDIO_DEBUG").map_or(false, |v| v == "1");
|
||||
|
||||
eprintln!("[AUDIO] Device: {:?}, format={:?}, rate={}, channels={}",
|
||||
output_device.name().unwrap_or_default(), default_output_config.sample_format(), sample_rate, channels);
|
||||
if debug_audio {
|
||||
eprintln!("[AUDIO DEBUG] Device: {:?}", output_device.name());
|
||||
eprintln!("[AUDIO DEBUG] Default config: {:?}", default_output_config);
|
||||
eprintln!("[AUDIO DEBUG] Default buffer size: {:?}", default_output_config.buffer_size());
|
||||
}
|
||||
|
||||
// Create queues
|
||||
let (command_tx, command_rx) = rtrb::RingBuffer::new(512); // Larger buffer for MIDI + UI commands
|
||||
|
|
@ -105,23 +107,36 @@ impl AudioSystem {
|
|||
}
|
||||
}
|
||||
|
||||
// Build output stream
|
||||
let mut output_config: cpal::StreamConfig = default_output_config.into();
|
||||
// Build output stream with configurable buffer size
|
||||
let mut output_config: cpal::StreamConfig = default_output_config.clone().into();
|
||||
|
||||
// WASAPI shared mode on Windows does not support fixed buffer sizes.
|
||||
// Use the device default on Windows; honor the requested size on other platforms.
|
||||
if cfg!(target_os = "windows") {
|
||||
output_config.buffer_size = cpal::BufferSize::Default;
|
||||
} else {
|
||||
output_config.buffer_size = cpal::BufferSize::Fixed(buffer_size);
|
||||
}
|
||||
// Set the requested buffer size
|
||||
output_config.buffer_size = cpal::BufferSize::Fixed(buffer_size);
|
||||
|
||||
let mut output_buffer = vec![0.0f32; 16384];
|
||||
|
||||
if debug_audio {
|
||||
eprintln!("[AUDIO DEBUG] Output config: sr={} Hz, ch={}, buf={:?}",
|
||||
output_config.sample_rate.0, output_config.channels, output_config.buffer_size);
|
||||
if let cpal::BufferSize::Fixed(size) = output_config.buffer_size {
|
||||
let latency_ms = (size as f64 / output_config.sample_rate.0 as f64) * 1000.0;
|
||||
eprintln!("[AUDIO DEBUG] Expected latency: {:.2} ms", latency_ms);
|
||||
}
|
||||
}
|
||||
|
||||
let mut callback_log_count: u32 = 0;
|
||||
let cb_debug = debug_audio;
|
||||
let output_stream = output_device
|
||||
.build_output_stream(
|
||||
&output_config,
|
||||
move |data: &mut [f32], _: &cpal::OutputCallbackInfo| {
|
||||
if cb_debug && callback_log_count < 10 {
|
||||
let frames = data.len() / output_config.channels as usize;
|
||||
let latency_ms = (frames as f64 / output_config.sample_rate.0 as f64) * 1000.0;
|
||||
eprintln!("[AUDIO CB #{}] {} samples ({} frames, {:.2} ms)",
|
||||
callback_log_count, data.len(), frames, latency_ms);
|
||||
callback_log_count += 1;
|
||||
}
|
||||
let buf = &mut output_buffer[..data.len()];
|
||||
buf.fill(0.0);
|
||||
engine.process(buf);
|
||||
|
|
@ -130,7 +145,7 @@ impl AudioSystem {
|
|||
|err| eprintln!("Output stream error: {}", err),
|
||||
None,
|
||||
)
|
||||
.map_err(|e| format!("Failed to build output stream: {e:?}"))?;
|
||||
.map_err(|e| e.to_string())?;
|
||||
|
||||
// Get input device
|
||||
let input_device = match host.default_input_device() {
|
||||
|
|
@ -155,10 +170,13 @@ impl AudioSystem {
|
|||
}
|
||||
};
|
||||
|
||||
// Get input config - use the input device's own default config
|
||||
// Get input config matching output sample rate and channels if possible
|
||||
let input_config = match input_device.default_input_config() {
|
||||
Ok(config) => {
|
||||
let cfg: cpal::StreamConfig = config.into();
|
||||
let mut cfg: cpal::StreamConfig = config.into();
|
||||
// Try to match output sample rate and channels
|
||||
cfg.sample_rate = cpal::SampleRate(sample_rate);
|
||||
cfg.channels = channels as u16;
|
||||
cfg
|
||||
}
|
||||
Err(e) => {
|
||||
|
|
@ -175,41 +193,25 @@ impl AudioSystem {
|
|||
stream: output_stream,
|
||||
sample_rate,
|
||||
channels,
|
||||
event_rx: None,
|
||||
event_rx: None, // No event receiver when audio device unavailable
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// Build input stream that feeds into the ringbuffer
|
||||
let input_stream = match input_device
|
||||
let input_stream = input_device
|
||||
.build_input_stream(
|
||||
&input_config,
|
||||
move |data: &[f32], _: &cpal::InputCallbackInfo| {
|
||||
// Push input samples to ringbuffer for recording
|
||||
for &sample in data {
|
||||
let _ = input_tx.push(sample);
|
||||
}
|
||||
},
|
||||
|err| eprintln!("Input stream error: {}", err),
|
||||
None,
|
||||
) {
|
||||
Ok(stream) => stream,
|
||||
Err(e) => {
|
||||
eprintln!("Warning: Could not build input stream: {}, recording will be disabled", e);
|
||||
output_stream.play().map_err(|e| e.to_string())?;
|
||||
|
||||
if let Some(emitter) = event_emitter {
|
||||
Self::spawn_emitter_thread(event_rx, emitter);
|
||||
}
|
||||
|
||||
return Ok(Self {
|
||||
controller,
|
||||
stream: output_stream,
|
||||
sample_rate,
|
||||
channels,
|
||||
event_rx: None,
|
||||
});
|
||||
}
|
||||
};
|
||||
)
|
||||
.map_err(|e| e.to_string())?;
|
||||
|
||||
// Start both streams
|
||||
output_stream.play().map_err(|e| e.to_string())?;
|
||||
|
|
|
|||
|
|
@ -192,9 +192,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "alsa"
|
||||
version = "0.10.0"
|
||||
version = "0.9.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7c88dbbce13b232b26250e1e2e6ac18b6a891a646b8148285036ebce260ac5c3"
|
||||
checksum = "ed7572b7ba83a31e20d1b48970ee402d2e3e0537dcfe0a3ff4d6eb7508617d43"
|
||||
dependencies = [
|
||||
"alsa-sys",
|
||||
"bitflags 2.10.0",
|
||||
|
|
@ -226,9 +226,9 @@ dependencies = [
|
|||
"jni-sys",
|
||||
"libc",
|
||||
"log",
|
||||
"ndk",
|
||||
"ndk 0.9.0",
|
||||
"ndk-context",
|
||||
"ndk-sys",
|
||||
"ndk-sys 0.6.0+11769913",
|
||||
"num_enum",
|
||||
"thiserror 1.0.69",
|
||||
]
|
||||
|
|
@ -1294,16 +1294,22 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "coreaudio-rs"
|
||||
version = "0.13.0"
|
||||
version = "0.11.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1aae284fbaf7d27aa0e292f7677dfbe26503b0d555026f702940805a630eac17"
|
||||
checksum = "321077172d79c662f64f5071a03120748d5bb652f5231570141be24cfcd2bace"
|
||||
dependencies = [
|
||||
"bitflags 1.3.2",
|
||||
"libc",
|
||||
"objc2-audio-toolbox",
|
||||
"objc2-core-audio",
|
||||
"objc2-core-audio-types",
|
||||
"objc2-core-foundation",
|
||||
"core-foundation-sys",
|
||||
"coreaudio-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "coreaudio-sys"
|
||||
version = "0.2.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ceec7a6067e62d6f931a2baf6f3a751f4a892595bcec1461a3c94ef9949864b6"
|
||||
dependencies = [
|
||||
"bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -1328,32 +1334,25 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "cpal"
|
||||
version = "0.17.1"
|
||||
version = "0.15.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5b1f9c7312f19fc2fa12fd7acaf38de54e8320ba10d1a02dcbe21038def51ccb"
|
||||
checksum = "873dab07c8f743075e57f524c583985fbaf745602acbe916a01539364369a779"
|
||||
dependencies = [
|
||||
"alsa 0.10.0",
|
||||
"alsa 0.9.1",
|
||||
"core-foundation-sys",
|
||||
"coreaudio-rs",
|
||||
"dasp_sample",
|
||||
"jni",
|
||||
"js-sys",
|
||||
"libc",
|
||||
"mach2",
|
||||
"ndk",
|
||||
"ndk 0.8.0",
|
||||
"ndk-context",
|
||||
"num-derive",
|
||||
"num-traits",
|
||||
"objc2 0.6.3",
|
||||
"objc2-audio-toolbox",
|
||||
"objc2-avf-audio",
|
||||
"objc2-core-audio",
|
||||
"objc2-core-audio-types",
|
||||
"objc2-core-foundation",
|
||||
"objc2-foundation 0.3.2",
|
||||
"oboe",
|
||||
"wasm-bindgen",
|
||||
"wasm-bindgen-futures",
|
||||
"web-sys",
|
||||
"windows 0.61.3",
|
||||
"windows 0.54.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -3596,9 +3595,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "mach2"
|
||||
version = "0.5.0"
|
||||
version = "0.4.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6a1b95cd5421ec55b445b5ae102f5ea0e768de1f82bd3001e11f426c269c3aea"
|
||||
checksum = "d640282b302c0bb0a2a8e0233ead9035e3bed871f0b7e81fe4a1ec829765db44"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
|
@ -3814,6 +3813,20 @@ dependencies = [
|
|||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ndk"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2076a31b7010b17a38c01907c45b945e8f11495ee4dd588309718901b1f7a5b7"
|
||||
dependencies = [
|
||||
"bitflags 2.10.0",
|
||||
"jni-sys",
|
||||
"log",
|
||||
"ndk-sys 0.5.0+25.2.9519653",
|
||||
"num_enum",
|
||||
"thiserror 1.0.69",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ndk"
|
||||
version = "0.9.0"
|
||||
|
|
@ -3823,7 +3836,7 @@ dependencies = [
|
|||
"bitflags 2.10.0",
|
||||
"jni-sys",
|
||||
"log",
|
||||
"ndk-sys",
|
||||
"ndk-sys 0.6.0+11769913",
|
||||
"num_enum",
|
||||
"raw-window-handle",
|
||||
"thiserror 1.0.69",
|
||||
|
|
@ -3835,6 +3848,15 @@ version = "0.1.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "27b02d87554356db9e9a873add8782d4ea6e3e58ea071a9adb9a2e8ddb884a8b"
|
||||
|
||||
[[package]]
|
||||
name = "ndk-sys"
|
||||
version = "0.5.0+25.2.9519653"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8c196769dd60fd4f363e11d948139556a344e79d451aeb2fa2fd040738ef7691"
|
||||
dependencies = [
|
||||
"jni-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ndk-sys"
|
||||
version = "0.6.0+11769913"
|
||||
|
|
@ -4072,31 +4094,6 @@ dependencies = [
|
|||
"objc2-foundation 0.3.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "objc2-audio-toolbox"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6948501a91121d6399b79abaa33a8aa4ea7857fe019f341b8c23ad6e81b79b08"
|
||||
dependencies = [
|
||||
"bitflags 2.10.0",
|
||||
"libc",
|
||||
"objc2 0.6.3",
|
||||
"objc2-core-audio",
|
||||
"objc2-core-audio-types",
|
||||
"objc2-core-foundation",
|
||||
"objc2-foundation 0.3.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "objc2-avf-audio"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "13a380031deed8e99db00065c45937da434ca987c034e13b87e4441f9e4090be"
|
||||
dependencies = [
|
||||
"objc2 0.6.3",
|
||||
"objc2-foundation 0.3.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "objc2-cloud-kit"
|
||||
version = "0.2.2"
|
||||
|
|
@ -4121,29 +4118,6 @@ dependencies = [
|
|||
"objc2-foundation 0.2.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "objc2-core-audio"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e1eebcea8b0dbff5f7c8504f3107c68fc061a3eb44932051c8cf8a68d969c3b2"
|
||||
dependencies = [
|
||||
"dispatch2",
|
||||
"objc2 0.6.3",
|
||||
"objc2-core-audio-types",
|
||||
"objc2-core-foundation",
|
||||
"objc2-foundation 0.3.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "objc2-core-audio-types"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5a89f2ec274a0cf4a32642b2991e8b351a404d290da87bb6a9a9d8632490bd1c"
|
||||
dependencies = [
|
||||
"bitflags 2.10.0",
|
||||
"objc2 0.6.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "objc2-core-data"
|
||||
version = "0.2.2"
|
||||
|
|
@ -4163,9 +4137,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "2a180dd8642fa45cdb7dd721cd4c11b1cadd4929ce112ebd8b9f5803cc79d536"
|
||||
dependencies = [
|
||||
"bitflags 2.10.0",
|
||||
"block2 0.6.2",
|
||||
"dispatch2",
|
||||
"libc",
|
||||
"objc2 0.6.3",
|
||||
]
|
||||
|
||||
|
|
@ -4341,6 +4313,29 @@ dependencies = [
|
|||
"objc2-foundation 0.2.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "oboe"
|
||||
version = "0.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e8b61bebd49e5d43f5f8cc7ee2891c16e0f41ec7954d36bcb6c14c5e0de867fb"
|
||||
dependencies = [
|
||||
"jni",
|
||||
"ndk 0.8.0",
|
||||
"ndk-context",
|
||||
"num-derive",
|
||||
"num-traits",
|
||||
"oboe-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "oboe-sys"
|
||||
version = "0.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6c8bb09a4a2b1d668170cfe0a7d5bc103f8999fb316c98099b6a9939c9f2e79d"
|
||||
dependencies = [
|
||||
"cc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.21.3"
|
||||
|
|
@ -7029,7 +7024,7 @@ dependencies = [
|
|||
"log",
|
||||
"metal",
|
||||
"naga",
|
||||
"ndk-sys",
|
||||
"ndk-sys 0.6.0+11769913",
|
||||
"objc",
|
||||
"once_cell",
|
||||
"ordered-float",
|
||||
|
|
@ -7109,6 +7104,16 @@ dependencies = [
|
|||
"windows_x86_64_msvc 0.42.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows"
|
||||
version = "0.54.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9252e5725dbed82865af151df558e754e4a3c2c30818359eb17465f1346a1b49"
|
||||
dependencies = [
|
||||
"windows-core 0.54.0",
|
||||
"windows-targets 0.52.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows"
|
||||
version = "0.58.0"
|
||||
|
|
@ -7141,6 +7146,16 @@ dependencies = [
|
|||
"windows-core 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-core"
|
||||
version = "0.54.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "12661b9c89351d684a50a8a643ce5f608e20243b9fb84687800163429f161d65"
|
||||
dependencies = [
|
||||
"windows-result 0.1.2",
|
||||
"windows-targets 0.52.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-core"
|
||||
version = "0.58.0"
|
||||
|
|
@ -7244,6 +7259,15 @@ dependencies = [
|
|||
"windows-link 0.1.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-result"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5e383302e8ec8515204254685643de10811af0ed97ea37210dc26fb0032647f8"
|
||||
dependencies = [
|
||||
"windows-targets 0.52.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-result"
|
||||
version = "0.2.0"
|
||||
|
|
@ -7618,7 +7642,7 @@ dependencies = [
|
|||
"js-sys",
|
||||
"libc",
|
||||
"memmap2",
|
||||
"ndk",
|
||||
"ndk 0.9.0",
|
||||
"objc2 0.5.2",
|
||||
"objc2-app-kit 0.2.2",
|
||||
"objc2-foundation 0.2.2",
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ edition = "2021"
|
|||
lightningbeam-core = { path = "../lightningbeam-core" }
|
||||
daw-backend = { path = "../../daw-backend" }
|
||||
rtrb = "0.3"
|
||||
cpal = "0.17"
|
||||
cpal = "0.15"
|
||||
ffmpeg-next = { version = "8.0", features = ["static"] }
|
||||
|
||||
# UI Framework
|
||||
|
|
|
|||
|
|
@ -3,13 +3,8 @@ use std::fs;
|
|||
use std::path::PathBuf;
|
||||
|
||||
fn main() {
|
||||
let target_os = env::var("CARGO_CFG_TARGET_OS").unwrap();
|
||||
|
||||
if target_os == "windows" {
|
||||
bundle_windows_dlls();
|
||||
}
|
||||
|
||||
if target_os != "linux" {
|
||||
// Only bundle libs on Linux
|
||||
if env::var("CARGO_CFG_TARGET_OS").unwrap() != "linux" {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -84,50 +79,6 @@ fn main() {
|
|||
println!("cargo:rustc-link-arg=-Wl,-rpath,{}", lib_dir.display());
|
||||
}
|
||||
|
||||
fn bundle_windows_dlls() {
|
||||
let ffmpeg_dir = match env::var("FFMPEG_DIR") {
|
||||
Ok(dir) => PathBuf::from(dir),
|
||||
Err(_) => return,
|
||||
};
|
||||
|
||||
let bin_dir = ffmpeg_dir.join("bin");
|
||||
if !bin_dir.exists() {
|
||||
println!("cargo:warning=FFMPEG_DIR/bin not found, skipping DLL bundling");
|
||||
return;
|
||||
}
|
||||
|
||||
let out_dir = env::var("OUT_DIR").unwrap();
|
||||
let target_dir = PathBuf::from(&out_dir)
|
||||
.parent().unwrap()
|
||||
.parent().unwrap()
|
||||
.parent().unwrap()
|
||||
.to_path_buf();
|
||||
|
||||
let dlls = [
|
||||
"avcodec-62.dll",
|
||||
"avdevice-62.dll",
|
||||
"avfilter-11.dll",
|
||||
"avformat-62.dll",
|
||||
"avutil-60.dll",
|
||||
"swresample-6.dll",
|
||||
"swscale-9.dll",
|
||||
];
|
||||
|
||||
for dll in &dlls {
|
||||
let src = bin_dir.join(dll);
|
||||
let dst = target_dir.join(dll);
|
||||
if src.exists() {
|
||||
if let Err(e) = fs::copy(&src, &dst) {
|
||||
println!("cargo:warning=Failed to copy {}: {}", dll, e);
|
||||
}
|
||||
} else {
|
||||
println!("cargo:warning=FFmpeg DLL not found: {}", src.display());
|
||||
}
|
||||
}
|
||||
|
||||
println!("cargo:warning=Bundled FFmpeg DLLs to {}", target_dir.display());
|
||||
}
|
||||
|
||||
fn copy_library(lib_name: &str, search_paths: &[&str], lib_dir: &PathBuf) {
|
||||
let mut copied = false;
|
||||
|
||||
|
|
|
|||
|
|
@ -183,90 +183,64 @@ enum SplitPreviewMode {
|
|||
},
|
||||
}
|
||||
|
||||
/// Rasterize an embedded SVG and upload it as an egui texture
|
||||
fn rasterize_svg(svg_data: &[u8], name: &str, render_size: u32, ctx: &egui::Context) -> Option<egui::TextureHandle> {
|
||||
let tree = resvg::usvg::Tree::from_data(svg_data, &resvg::usvg::Options::default()).ok()?;
|
||||
let pixmap_size = tree.size().to_int_size();
|
||||
let scale_x = render_size as f32 / pixmap_size.width() as f32;
|
||||
let scale_y = render_size as f32 / pixmap_size.height() as f32;
|
||||
let scale = scale_x.min(scale_y);
|
||||
|
||||
let final_size = resvg::usvg::Size::from_wh(
|
||||
pixmap_size.width() as f32 * scale,
|
||||
pixmap_size.height() as f32 * scale,
|
||||
).unwrap_or(resvg::usvg::Size::from_wh(render_size as f32, render_size as f32).unwrap());
|
||||
|
||||
let mut pixmap = resvg::tiny_skia::Pixmap::new(
|
||||
final_size.width() as u32,
|
||||
final_size.height() as u32,
|
||||
)?;
|
||||
let transform = resvg::tiny_skia::Transform::from_scale(scale, scale);
|
||||
resvg::render(&tree, transform, &mut pixmap.as_mut());
|
||||
|
||||
let rgba_data = pixmap.data();
|
||||
let size = [pixmap.width() as usize, pixmap.height() as usize];
|
||||
let color_image = egui::ColorImage::from_rgba_unmultiplied(size, rgba_data);
|
||||
Some(ctx.load_texture(name, color_image, egui::TextureOptions::LINEAR))
|
||||
}
|
||||
|
||||
/// Embedded pane icon SVGs
|
||||
mod pane_icons {
|
||||
pub static STAGE: &[u8] = include_bytes!("../../../src/assets/stage.svg");
|
||||
pub static TIMELINE: &[u8] = include_bytes!("../../../src/assets/timeline.svg");
|
||||
pub static TOOLBAR: &[u8] = include_bytes!("../../../src/assets/toolbar.svg");
|
||||
pub static INFOPANEL: &[u8] = include_bytes!("../../../src/assets/infopanel.svg");
|
||||
pub static PIANO_ROLL: &[u8] = include_bytes!("../../../src/assets/piano-roll.svg");
|
||||
pub static PIANO: &[u8] = include_bytes!("../../../src/assets/piano.svg");
|
||||
pub static NODE_EDITOR: &[u8] = include_bytes!("../../../src/assets/node-editor.svg");
|
||||
}
|
||||
|
||||
/// Embedded tool icon SVGs
|
||||
mod tool_icons {
|
||||
pub static SELECT: &[u8] = include_bytes!("../../../src/assets/select.svg");
|
||||
pub static DRAW: &[u8] = include_bytes!("../../../src/assets/draw.svg");
|
||||
pub static TRANSFORM: &[u8] = include_bytes!("../../../src/assets/transform.svg");
|
||||
pub static RECTANGLE: &[u8] = include_bytes!("../../../src/assets/rectangle.svg");
|
||||
pub static ELLIPSE: &[u8] = include_bytes!("../../../src/assets/ellipse.svg");
|
||||
pub static PAINT_BUCKET: &[u8] = include_bytes!("../../../src/assets/paint_bucket.svg");
|
||||
pub static EYEDROPPER: &[u8] = include_bytes!("../../../src/assets/eyedropper.svg");
|
||||
pub static LINE: &[u8] = include_bytes!("../../../src/assets/line.svg");
|
||||
pub static POLYGON: &[u8] = include_bytes!("../../../src/assets/polygon.svg");
|
||||
pub static BEZIER_EDIT: &[u8] = include_bytes!("../../../src/assets/bezier_edit.svg");
|
||||
pub static TEXT: &[u8] = include_bytes!("../../../src/assets/text.svg");
|
||||
}
|
||||
|
||||
/// Embedded focus icon SVGs
|
||||
mod focus_icons {
|
||||
pub static ANIMATION: &[u8] = include_bytes!("../../../src/assets/focus-animation.svg");
|
||||
pub static MUSIC: &[u8] = include_bytes!("../../../src/assets/focus-music.svg");
|
||||
pub static VIDEO: &[u8] = include_bytes!("../../../src/assets/focus-video.svg");
|
||||
}
|
||||
|
||||
/// Icon cache for pane type icons
|
||||
struct IconCache {
|
||||
icons: HashMap<PaneType, egui::TextureHandle>,
|
||||
assets_path: std::path::PathBuf,
|
||||
}
|
||||
|
||||
impl IconCache {
|
||||
fn new() -> Self {
|
||||
let assets_path = std::path::PathBuf::from(
|
||||
std::env::var("HOME").unwrap_or_else(|_| "/home/skyler".to_string())
|
||||
).join("Dev/Lightningbeam-2/src/assets");
|
||||
|
||||
Self {
|
||||
icons: HashMap::new(),
|
||||
assets_path,
|
||||
}
|
||||
}
|
||||
|
||||
fn get_or_load(&mut self, pane_type: PaneType, ctx: &egui::Context) -> Option<&egui::TextureHandle> {
|
||||
if !self.icons.contains_key(&pane_type) {
|
||||
let svg_data = match pane_type {
|
||||
PaneType::Stage | PaneType::Outliner | PaneType::PresetBrowser | PaneType::AssetLibrary => pane_icons::STAGE,
|
||||
PaneType::Timeline => pane_icons::TIMELINE,
|
||||
PaneType::Toolbar => pane_icons::TOOLBAR,
|
||||
PaneType::Infopanel => pane_icons::INFOPANEL,
|
||||
PaneType::PianoRoll => pane_icons::PIANO_ROLL,
|
||||
PaneType::VirtualPiano => pane_icons::PIANO,
|
||||
PaneType::NodeEditor | PaneType::ShaderEditor => pane_icons::NODE_EDITOR,
|
||||
};
|
||||
if let Some(texture) = rasterize_svg(svg_data, pane_type.icon_file(), 64, ctx) {
|
||||
self.icons.insert(pane_type, texture);
|
||||
// Load SVG and rasterize using resvg
|
||||
let icon_path = self.assets_path.join(pane_type.icon_file());
|
||||
if let Ok(svg_data) = std::fs::read(&icon_path) {
|
||||
// Rasterize at reasonable size for pane icons
|
||||
let render_size = 64;
|
||||
|
||||
if let Ok(tree) = resvg::usvg::Tree::from_data(&svg_data, &resvg::usvg::Options::default()) {
|
||||
let pixmap_size = tree.size().to_int_size();
|
||||
let scale_x = render_size as f32 / pixmap_size.width() as f32;
|
||||
let scale_y = render_size as f32 / pixmap_size.height() as f32;
|
||||
let scale = scale_x.min(scale_y);
|
||||
|
||||
let final_size = resvg::usvg::Size::from_wh(
|
||||
pixmap_size.width() as f32 * scale,
|
||||
pixmap_size.height() as f32 * scale,
|
||||
).unwrap_or(resvg::usvg::Size::from_wh(render_size as f32, render_size as f32).unwrap());
|
||||
|
||||
if let Some(mut pixmap) = resvg::tiny_skia::Pixmap::new(
|
||||
final_size.width() as u32,
|
||||
final_size.height() as u32,
|
||||
) {
|
||||
let transform = resvg::tiny_skia::Transform::from_scale(scale, scale);
|
||||
resvg::render(&tree, transform, &mut pixmap.as_mut());
|
||||
|
||||
// Convert RGBA8 to egui ColorImage
|
||||
let rgba_data = pixmap.data();
|
||||
let size = [pixmap.width() as usize, pixmap.height() as usize];
|
||||
let color_image = egui::ColorImage::from_rgba_unmultiplied(size, rgba_data);
|
||||
|
||||
// Upload to GPU
|
||||
let texture = ctx.load_texture(
|
||||
pane_type.icon_file(),
|
||||
color_image,
|
||||
egui::TextureOptions::LINEAR,
|
||||
);
|
||||
self.icons.insert(pane_type, texture);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
self.icons.get(&pane_type)
|
||||
|
|
@ -276,32 +250,61 @@ impl IconCache {
|
|||
/// Icon cache for tool icons
|
||||
struct ToolIconCache {
|
||||
icons: HashMap<Tool, egui::TextureHandle>,
|
||||
assets_path: std::path::PathBuf,
|
||||
}
|
||||
|
||||
impl ToolIconCache {
|
||||
fn new() -> Self {
|
||||
let assets_path = std::path::PathBuf::from(
|
||||
std::env::var("HOME").unwrap_or_else(|_| "/home/skyler".to_string())
|
||||
).join("Dev/Lightningbeam-2/src/assets");
|
||||
|
||||
Self {
|
||||
icons: HashMap::new(),
|
||||
assets_path,
|
||||
}
|
||||
}
|
||||
|
||||
fn get_or_load(&mut self, tool: Tool, ctx: &egui::Context) -> Option<&egui::TextureHandle> {
|
||||
if !self.icons.contains_key(&tool) {
|
||||
let svg_data = match tool {
|
||||
Tool::Select => tool_icons::SELECT,
|
||||
Tool::Draw => tool_icons::DRAW,
|
||||
Tool::Transform => tool_icons::TRANSFORM,
|
||||
Tool::Rectangle => tool_icons::RECTANGLE,
|
||||
Tool::Ellipse => tool_icons::ELLIPSE,
|
||||
Tool::PaintBucket => tool_icons::PAINT_BUCKET,
|
||||
Tool::Eyedropper => tool_icons::EYEDROPPER,
|
||||
Tool::Line => tool_icons::LINE,
|
||||
Tool::Polygon => tool_icons::POLYGON,
|
||||
Tool::BezierEdit => tool_icons::BEZIER_EDIT,
|
||||
Tool::Text => tool_icons::TEXT,
|
||||
};
|
||||
if let Some(texture) = rasterize_svg(svg_data, tool.icon_file(), 180, ctx) {
|
||||
self.icons.insert(tool, texture);
|
||||
// Load SVG and rasterize at high resolution using resvg
|
||||
let icon_path = self.assets_path.join(tool.icon_file());
|
||||
if let Ok(svg_data) = std::fs::read(&icon_path) {
|
||||
// Rasterize at 3x size for crisp display (180px for 60px display)
|
||||
let render_size = 180;
|
||||
|
||||
if let Ok(tree) = resvg::usvg::Tree::from_data(&svg_data, &resvg::usvg::Options::default()) {
|
||||
let pixmap_size = tree.size().to_int_size();
|
||||
let scale_x = render_size as f32 / pixmap_size.width() as f32;
|
||||
let scale_y = render_size as f32 / pixmap_size.height() as f32;
|
||||
let scale = scale_x.min(scale_y);
|
||||
|
||||
let final_size = resvg::usvg::Size::from_wh(
|
||||
pixmap_size.width() as f32 * scale,
|
||||
pixmap_size.height() as f32 * scale,
|
||||
).unwrap_or(resvg::usvg::Size::from_wh(render_size as f32, render_size as f32).unwrap());
|
||||
|
||||
if let Some(mut pixmap) = resvg::tiny_skia::Pixmap::new(
|
||||
final_size.width() as u32,
|
||||
final_size.height() as u32,
|
||||
) {
|
||||
let transform = resvg::tiny_skia::Transform::from_scale(scale, scale);
|
||||
resvg::render(&tree, transform, &mut pixmap.as_mut());
|
||||
|
||||
// Convert RGBA8 to egui ColorImage
|
||||
let rgba_data = pixmap.data();
|
||||
let size = [pixmap.width() as usize, pixmap.height() as usize];
|
||||
let color_image = egui::ColorImage::from_rgba_unmultiplied(size, rgba_data);
|
||||
|
||||
// Upload to GPU
|
||||
let texture = ctx.load_texture(
|
||||
tool.icon_file(),
|
||||
color_image,
|
||||
egui::TextureOptions::LINEAR,
|
||||
);
|
||||
self.icons.insert(tool, texture);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
self.icons.get(&tool)
|
||||
|
|
@ -311,33 +314,74 @@ impl ToolIconCache {
|
|||
/// Icon cache for focus card icons (start screen)
|
||||
struct FocusIconCache {
|
||||
icons: HashMap<FocusIcon, egui::TextureHandle>,
|
||||
assets_path: std::path::PathBuf,
|
||||
}
|
||||
|
||||
impl FocusIconCache {
|
||||
fn new() -> Self {
|
||||
let assets_path = std::path::PathBuf::from(
|
||||
std::env::var("HOME").unwrap_or_else(|_| "/home/skyler".to_string())
|
||||
).join("Dev/Lightningbeam-2/src/assets");
|
||||
|
||||
Self {
|
||||
icons: HashMap::new(),
|
||||
assets_path,
|
||||
}
|
||||
}
|
||||
|
||||
fn get_or_load(&mut self, icon: FocusIcon, icon_color: egui::Color32, ctx: &egui::Context) -> Option<&egui::TextureHandle> {
|
||||
if !self.icons.contains_key(&icon) {
|
||||
let (svg_bytes, svg_filename) = match icon {
|
||||
FocusIcon::Animation => (focus_icons::ANIMATION, "focus-animation.svg"),
|
||||
FocusIcon::Music => (focus_icons::MUSIC, "focus-music.svg"),
|
||||
FocusIcon::Video => (focus_icons::VIDEO, "focus-video.svg"),
|
||||
// Determine which SVG file to load
|
||||
let svg_filename = match icon {
|
||||
FocusIcon::Animation => "focus-animation.svg",
|
||||
FocusIcon::Music => "focus-music.svg",
|
||||
FocusIcon::Video => "focus-video.svg",
|
||||
};
|
||||
|
||||
// Replace currentColor with the actual color
|
||||
let svg_data = String::from_utf8_lossy(svg_bytes);
|
||||
let color_hex = format!(
|
||||
"#{:02x}{:02x}{:02x}",
|
||||
icon_color.r(), icon_color.g(), icon_color.b()
|
||||
);
|
||||
let svg_with_color = svg_data.replace("currentColor", &color_hex);
|
||||
let icon_path = self.assets_path.join(svg_filename);
|
||||
if let Ok(svg_data) = std::fs::read_to_string(&icon_path) {
|
||||
// Replace currentColor with the actual color
|
||||
let color_hex = format!(
|
||||
"#{:02x}{:02x}{:02x}",
|
||||
icon_color.r(), icon_color.g(), icon_color.b()
|
||||
);
|
||||
let svg_with_color = svg_data.replace("currentColor", &color_hex);
|
||||
|
||||
if let Some(texture) = rasterize_svg(svg_with_color.as_bytes(), svg_filename, 120, ctx) {
|
||||
self.icons.insert(icon, texture);
|
||||
// Rasterize at 2x size for crisp display
|
||||
let render_size = 120;
|
||||
|
||||
if let Ok(tree) = resvg::usvg::Tree::from_data(svg_with_color.as_bytes(), &resvg::usvg::Options::default()) {
|
||||
let pixmap_size = tree.size().to_int_size();
|
||||
let scale_x = render_size as f32 / pixmap_size.width() as f32;
|
||||
let scale_y = render_size as f32 / pixmap_size.height() as f32;
|
||||
let scale = scale_x.min(scale_y);
|
||||
|
||||
let final_size = resvg::usvg::Size::from_wh(
|
||||
pixmap_size.width() as f32 * scale,
|
||||
pixmap_size.height() as f32 * scale,
|
||||
).unwrap_or(resvg::usvg::Size::from_wh(render_size as f32, render_size as f32).unwrap());
|
||||
|
||||
if let Some(mut pixmap) = resvg::tiny_skia::Pixmap::new(
|
||||
final_size.width() as u32,
|
||||
final_size.height() as u32,
|
||||
) {
|
||||
let transform = resvg::tiny_skia::Transform::from_scale(scale, scale);
|
||||
resvg::render(&tree, transform, &mut pixmap.as_mut());
|
||||
|
||||
// Convert RGBA8 to egui ColorImage
|
||||
let rgba_data = pixmap.data();
|
||||
let size = [pixmap.width() as usize, pixmap.height() as usize];
|
||||
let color_image = egui::ColorImage::from_rgba_unmultiplied(size, rgba_data);
|
||||
|
||||
// Upload to GPU
|
||||
let texture = ctx.load_texture(
|
||||
svg_filename,
|
||||
color_image,
|
||||
egui::TextureOptions::LINEAR,
|
||||
);
|
||||
self.icons.insert(icon, texture);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
self.icons.get(&icon)
|
||||
|
|
|
|||
Loading…
Reference in New Issue