Fix build on Windows
This commit is contained in:
parent
ffe7799b6a
commit
6c10112a16
|
|
@ -4,7 +4,7 @@ version = "0.1.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
cpal = "0.15"
|
cpal = "0.17"
|
||||||
symphonia = { version = "0.5", features = ["all"] }
|
symphonia = { version = "0.5", features = ["all"] }
|
||||||
rtrb = "0.3"
|
rtrb = "0.3"
|
||||||
midly = "0.5"
|
midly = "0.5"
|
||||||
|
|
|
||||||
|
|
@ -67,14 +67,12 @@ impl AudioSystem {
|
||||||
.ok_or("No output device available")?;
|
.ok_or("No output device available")?;
|
||||||
|
|
||||||
let default_output_config = output_device.default_output_config().map_err(|e| e.to_string())?;
|
let default_output_config = output_device.default_output_config().map_err(|e| e.to_string())?;
|
||||||
let sample_rate = default_output_config.sample_rate().0;
|
let sample_rate = default_output_config.sample_rate();
|
||||||
let channels = default_output_config.channels() as u32;
|
let channels = default_output_config.channels() as u32;
|
||||||
let debug_audio = std::env::var("DAW_AUDIO_DEBUG").map_or(false, |v| v == "1");
|
let debug_audio = std::env::var("DAW_AUDIO_DEBUG").map_or(false, |v| v == "1");
|
||||||
if debug_audio {
|
|
||||||
eprintln!("[AUDIO DEBUG] Device: {:?}", output_device.name());
|
eprintln!("[AUDIO] Device: {:?}, format={:?}, rate={}, channels={}",
|
||||||
eprintln!("[AUDIO DEBUG] Default config: {:?}", default_output_config);
|
output_device.name().unwrap_or_default(), default_output_config.sample_format(), sample_rate, channels);
|
||||||
eprintln!("[AUDIO DEBUG] Default buffer size: {:?}", default_output_config.buffer_size());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create queues
|
// Create queues
|
||||||
let (command_tx, command_rx) = rtrb::RingBuffer::new(512); // Larger buffer for MIDI + UI commands
|
let (command_tx, command_rx) = rtrb::RingBuffer::new(512); // Larger buffer for MIDI + UI commands
|
||||||
|
|
@ -107,36 +105,23 @@ impl AudioSystem {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build output stream with configurable buffer size
|
// Build output stream
|
||||||
let mut output_config: cpal::StreamConfig = default_output_config.clone().into();
|
let mut output_config: cpal::StreamConfig = default_output_config.into();
|
||||||
|
|
||||||
// Set the requested buffer size
|
// WASAPI shared mode on Windows does not support fixed buffer sizes.
|
||||||
output_config.buffer_size = cpal::BufferSize::Fixed(buffer_size);
|
// 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);
|
||||||
|
}
|
||||||
|
|
||||||
let mut output_buffer = vec![0.0f32; 16384];
|
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
|
let output_stream = output_device
|
||||||
.build_output_stream(
|
.build_output_stream(
|
||||||
&output_config,
|
&output_config,
|
||||||
move |data: &mut [f32], _: &cpal::OutputCallbackInfo| {
|
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()];
|
let buf = &mut output_buffer[..data.len()];
|
||||||
buf.fill(0.0);
|
buf.fill(0.0);
|
||||||
engine.process(buf);
|
engine.process(buf);
|
||||||
|
|
@ -145,7 +130,7 @@ impl AudioSystem {
|
||||||
|err| eprintln!("Output stream error: {}", err),
|
|err| eprintln!("Output stream error: {}", err),
|
||||||
None,
|
None,
|
||||||
)
|
)
|
||||||
.map_err(|e| e.to_string())?;
|
.map_err(|e| format!("Failed to build output stream: {e:?}"))?;
|
||||||
|
|
||||||
// Get input device
|
// Get input device
|
||||||
let input_device = match host.default_input_device() {
|
let input_device = match host.default_input_device() {
|
||||||
|
|
@ -170,13 +155,10 @@ impl AudioSystem {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Get input config matching output sample rate and channels if possible
|
// Get input config - use the input device's own default config
|
||||||
let input_config = match input_device.default_input_config() {
|
let input_config = match input_device.default_input_config() {
|
||||||
Ok(config) => {
|
Ok(config) => {
|
||||||
let mut cfg: cpal::StreamConfig = config.into();
|
let 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
|
cfg
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
|
|
@ -193,25 +175,41 @@ impl AudioSystem {
|
||||||
stream: output_stream,
|
stream: output_stream,
|
||||||
sample_rate,
|
sample_rate,
|
||||||
channels,
|
channels,
|
||||||
event_rx: None, // No event receiver when audio device unavailable
|
event_rx: None,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Build input stream that feeds into the ringbuffer
|
// Build input stream that feeds into the ringbuffer
|
||||||
let input_stream = input_device
|
let input_stream = match input_device
|
||||||
.build_input_stream(
|
.build_input_stream(
|
||||||
&input_config,
|
&input_config,
|
||||||
move |data: &[f32], _: &cpal::InputCallbackInfo| {
|
move |data: &[f32], _: &cpal::InputCallbackInfo| {
|
||||||
// Push input samples to ringbuffer for recording
|
|
||||||
for &sample in data {
|
for &sample in data {
|
||||||
let _ = input_tx.push(sample);
|
let _ = input_tx.push(sample);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|err| eprintln!("Input stream error: {}", err),
|
|err| eprintln!("Input stream error: {}", err),
|
||||||
None,
|
None,
|
||||||
)
|
) {
|
||||||
.map_err(|e| e.to_string())?;
|
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,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// Start both streams
|
// Start both streams
|
||||||
output_stream.play().map_err(|e| e.to_string())?;
|
output_stream.play().map_err(|e| e.to_string())?;
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,10 @@ name = "accesskit"
|
||||||
version = "0.21.1"
|
version = "0.21.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "cf203f9d3bd8f29f98833d1fbef628df18f759248a547e7e01cfbf63cda36a99"
|
checksum = "cf203f9d3bd8f29f98833d1fbef628df18f759248a547e7e01cfbf63cda36a99"
|
||||||
|
dependencies = [
|
||||||
|
"enumn",
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "accesskit_atspi_common"
|
name = "accesskit_atspi_common"
|
||||||
|
|
@ -145,6 +149,7 @@ dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"getrandom 0.3.4",
|
"getrandom 0.3.4",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
|
"serde",
|
||||||
"version_check",
|
"version_check",
|
||||||
"zerocopy",
|
"zerocopy",
|
||||||
]
|
]
|
||||||
|
|
@ -187,9 +192,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "alsa"
|
name = "alsa"
|
||||||
version = "0.9.1"
|
version = "0.10.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ed7572b7ba83a31e20d1b48970ee402d2e3e0537dcfe0a3ff4d6eb7508617d43"
|
checksum = "7c88dbbce13b232b26250e1e2e6ac18b6a891a646b8148285036ebce260ac5c3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"alsa-sys",
|
"alsa-sys",
|
||||||
"bitflags 2.10.0",
|
"bitflags 2.10.0",
|
||||||
|
|
@ -221,9 +226,9 @@ dependencies = [
|
||||||
"jni-sys",
|
"jni-sys",
|
||||||
"libc",
|
"libc",
|
||||||
"log",
|
"log",
|
||||||
"ndk 0.9.0",
|
"ndk",
|
||||||
"ndk-context",
|
"ndk-context",
|
||||||
"ndk-sys 0.6.0+11769913",
|
"ndk-sys",
|
||||||
"num_enum",
|
"num_enum",
|
||||||
"thiserror 1.0.69",
|
"thiserror 1.0.69",
|
||||||
]
|
]
|
||||||
|
|
@ -1289,22 +1294,16 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "coreaudio-rs"
|
name = "coreaudio-rs"
|
||||||
version = "0.11.3"
|
version = "0.13.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "321077172d79c662f64f5071a03120748d5bb652f5231570141be24cfcd2bace"
|
checksum = "1aae284fbaf7d27aa0e292f7677dfbe26503b0d555026f702940805a630eac17"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 1.3.2",
|
"bitflags 1.3.2",
|
||||||
"core-foundation-sys",
|
"libc",
|
||||||
"coreaudio-sys",
|
"objc2-audio-toolbox",
|
||||||
]
|
"objc2-core-audio",
|
||||||
|
"objc2-core-audio-types",
|
||||||
[[package]]
|
"objc2-core-foundation",
|
||||||
name = "coreaudio-sys"
|
|
||||||
version = "0.2.17"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "ceec7a6067e62d6f931a2baf6f3a751f4a892595bcec1461a3c94ef9949864b6"
|
|
||||||
dependencies = [
|
|
||||||
"bindgen",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
@ -1329,25 +1328,32 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cpal"
|
name = "cpal"
|
||||||
version = "0.15.3"
|
version = "0.17.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "873dab07c8f743075e57f524c583985fbaf745602acbe916a01539364369a779"
|
checksum = "5b1f9c7312f19fc2fa12fd7acaf38de54e8320ba10d1a02dcbe21038def51ccb"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"alsa 0.9.1",
|
"alsa 0.10.0",
|
||||||
"core-foundation-sys",
|
|
||||||
"coreaudio-rs",
|
"coreaudio-rs",
|
||||||
"dasp_sample",
|
"dasp_sample",
|
||||||
"jni",
|
"jni",
|
||||||
"js-sys",
|
"js-sys",
|
||||||
"libc",
|
"libc",
|
||||||
"mach2",
|
"mach2",
|
||||||
"ndk 0.8.0",
|
"ndk",
|
||||||
"ndk-context",
|
"ndk-context",
|
||||||
"oboe",
|
"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",
|
||||||
"wasm-bindgen",
|
"wasm-bindgen",
|
||||||
"wasm-bindgen-futures",
|
"wasm-bindgen-futures",
|
||||||
"web-sys",
|
"web-sys",
|
||||||
"windows 0.54.0",
|
"windows 0.61.3",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
@ -1798,6 +1804,7 @@ checksum = "71ddb8ac7643d1dba1bb02110e804406dd459a838efcb14011ced10556711a8e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bytemuck",
|
"bytemuck",
|
||||||
"emath",
|
"emath",
|
||||||
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
@ -1851,6 +1858,8 @@ dependencies = [
|
||||||
"log",
|
"log",
|
||||||
"nohash-hasher",
|
"nohash-hasher",
|
||||||
"profiling",
|
"profiling",
|
||||||
|
"ron",
|
||||||
|
"serde",
|
||||||
"smallvec",
|
"smallvec",
|
||||||
"unicode-segmentation",
|
"unicode-segmentation",
|
||||||
]
|
]
|
||||||
|
|
@ -1943,9 +1952,9 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "egui_node_graph2"
|
name = "egui_node_graph2"
|
||||||
version = "0.7.0"
|
version = "0.7.0"
|
||||||
source = "git+https://github.com/PVDoriginal/egui_node_graph2#a25a90822d8f9c956e729f3907aad98f59fa46bc"
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"egui",
|
"egui",
|
||||||
|
"serde",
|
||||||
"slotmap",
|
"slotmap",
|
||||||
"smallvec",
|
"smallvec",
|
||||||
"thiserror 1.0.69",
|
"thiserror 1.0.69",
|
||||||
|
|
@ -1964,6 +1973,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "491bdf728bf25ddd9ad60d4cf1c48588fa82c013a2440b91aa7fc43e34a07c32"
|
checksum = "491bdf728bf25ddd9ad60d4cf1c48588fa82c013a2440b91aa7fc43e34a07c32"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bytemuck",
|
"bytemuck",
|
||||||
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
@ -2022,6 +2032,17 @@ dependencies = [
|
||||||
"syn 2.0.110",
|
"syn 2.0.110",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "enumn"
|
||||||
|
version = "0.1.14"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2f9ed6b3789237c8a0c1c505af1c7eb2c560df6186f01b098c3a1064ea532f38"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn 2.0.110",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "epaint"
|
name = "epaint"
|
||||||
version = "0.33.3"
|
version = "0.33.3"
|
||||||
|
|
@ -2038,6 +2059,7 @@ dependencies = [
|
||||||
"nohash-hasher",
|
"nohash-hasher",
|
||||||
"parking_lot",
|
"parking_lot",
|
||||||
"profiling",
|
"profiling",
|
||||||
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
@ -3411,6 +3433,7 @@ dependencies = [
|
||||||
name = "lightningbeam-core"
|
name = "lightningbeam-core"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"arboard",
|
||||||
"base64 0.21.7",
|
"base64 0.21.7",
|
||||||
"bytemuck",
|
"bytemuck",
|
||||||
"chrono",
|
"chrono",
|
||||||
|
|
@ -3593,9 +3616,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "mach2"
|
name = "mach2"
|
||||||
version = "0.4.3"
|
version = "0.5.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d640282b302c0bb0a2a8e0233ead9035e3bed871f0b7e81fe4a1ec829765db44"
|
checksum = "6a1b95cd5421ec55b445b5ae102f5ea0e768de1f82bd3001e11f426c269c3aea"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
@ -3811,20 +3834,6 @@ dependencies = [
|
||||||
"unicode-ident",
|
"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]]
|
[[package]]
|
||||||
name = "ndk"
|
name = "ndk"
|
||||||
version = "0.9.0"
|
version = "0.9.0"
|
||||||
|
|
@ -3834,7 +3843,7 @@ dependencies = [
|
||||||
"bitflags 2.10.0",
|
"bitflags 2.10.0",
|
||||||
"jni-sys",
|
"jni-sys",
|
||||||
"log",
|
"log",
|
||||||
"ndk-sys 0.6.0+11769913",
|
"ndk-sys",
|
||||||
"num_enum",
|
"num_enum",
|
||||||
"raw-window-handle",
|
"raw-window-handle",
|
||||||
"thiserror 1.0.69",
|
"thiserror 1.0.69",
|
||||||
|
|
@ -3846,15 +3855,6 @@ version = "0.1.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "27b02d87554356db9e9a873add8782d4ea6e3e58ea071a9adb9a2e8ddb884a8b"
|
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]]
|
[[package]]
|
||||||
name = "ndk-sys"
|
name = "ndk-sys"
|
||||||
version = "0.6.0+11769913"
|
version = "0.6.0+11769913"
|
||||||
|
|
@ -4092,6 +4092,31 @@ dependencies = [
|
||||||
"objc2-foundation 0.3.2",
|
"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]]
|
[[package]]
|
||||||
name = "objc2-cloud-kit"
|
name = "objc2-cloud-kit"
|
||||||
version = "0.2.2"
|
version = "0.2.2"
|
||||||
|
|
@ -4116,6 +4141,29 @@ dependencies = [
|
||||||
"objc2-foundation 0.2.2",
|
"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]]
|
[[package]]
|
||||||
name = "objc2-core-data"
|
name = "objc2-core-data"
|
||||||
version = "0.2.2"
|
version = "0.2.2"
|
||||||
|
|
@ -4135,7 +4183,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "2a180dd8642fa45cdb7dd721cd4c11b1cadd4929ce112ebd8b9f5803cc79d536"
|
checksum = "2a180dd8642fa45cdb7dd721cd4c11b1cadd4929ce112ebd8b9f5803cc79d536"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.10.0",
|
"bitflags 2.10.0",
|
||||||
|
"block2 0.6.2",
|
||||||
"dispatch2",
|
"dispatch2",
|
||||||
|
"libc",
|
||||||
"objc2 0.6.3",
|
"objc2 0.6.3",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
@ -4311,29 +4361,6 @@ dependencies = [
|
||||||
"objc2-foundation 0.2.2",
|
"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]]
|
[[package]]
|
||||||
name = "once_cell"
|
name = "once_cell"
|
||||||
version = "1.21.3"
|
version = "1.21.3"
|
||||||
|
|
@ -5293,6 +5320,19 @@ dependencies = [
|
||||||
"syn 1.0.109",
|
"syn 1.0.109",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ron"
|
||||||
|
version = "0.11.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "db09040cc89e461f1a265139777a2bde7f8d8c67c4936f700c63ce3e2904d468"
|
||||||
|
dependencies = [
|
||||||
|
"base64 0.22.1",
|
||||||
|
"bitflags 2.10.0",
|
||||||
|
"serde",
|
||||||
|
"serde_derive",
|
||||||
|
"unicode-ident",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "roxmltree"
|
name = "roxmltree"
|
||||||
version = "0.20.0"
|
version = "0.20.0"
|
||||||
|
|
@ -5629,6 +5669,7 @@ version = "1.0.7"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "dbff4acf519f630b3a3ddcfaea6c06b42174d9a44bc70c620e9ed1649d58b82a"
|
checksum = "dbff4acf519f630b3a3ddcfaea6c06b42174d9a44bc70c620e9ed1649d58b82a"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"serde",
|
||||||
"version_check",
|
"version_check",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
@ -7008,7 +7049,7 @@ dependencies = [
|
||||||
"log",
|
"log",
|
||||||
"metal",
|
"metal",
|
||||||
"naga",
|
"naga",
|
||||||
"ndk-sys 0.6.0+11769913",
|
"ndk-sys",
|
||||||
"objc",
|
"objc",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"ordered-float",
|
"ordered-float",
|
||||||
|
|
@ -7088,16 +7129,6 @@ dependencies = [
|
||||||
"windows_x86_64_msvc 0.42.2",
|
"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]]
|
[[package]]
|
||||||
name = "windows"
|
name = "windows"
|
||||||
version = "0.58.0"
|
version = "0.58.0"
|
||||||
|
|
@ -7130,16 +7161,6 @@ dependencies = [
|
||||||
"windows-core 0.61.2",
|
"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]]
|
[[package]]
|
||||||
name = "windows-core"
|
name = "windows-core"
|
||||||
version = "0.58.0"
|
version = "0.58.0"
|
||||||
|
|
@ -7243,15 +7264,6 @@ dependencies = [
|
||||||
"windows-link 0.1.3",
|
"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]]
|
[[package]]
|
||||||
name = "windows-result"
|
name = "windows-result"
|
||||||
version = "0.2.0"
|
version = "0.2.0"
|
||||||
|
|
@ -7626,7 +7638,7 @@ dependencies = [
|
||||||
"js-sys",
|
"js-sys",
|
||||||
"libc",
|
"libc",
|
||||||
"memmap2",
|
"memmap2",
|
||||||
"ndk 0.9.0",
|
"ndk",
|
||||||
"objc2 0.5.2",
|
"objc2 0.5.2",
|
||||||
"objc2-app-kit 0.2.2",
|
"objc2-app-kit 0.2.2",
|
||||||
"objc2-foundation 0.2.2",
|
"objc2-foundation 0.2.2",
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ edition = "2021"
|
||||||
lightningbeam-core = { path = "../lightningbeam-core" }
|
lightningbeam-core = { path = "../lightningbeam-core" }
|
||||||
daw-backend = { path = "../../daw-backend" }
|
daw-backend = { path = "../../daw-backend" }
|
||||||
rtrb = "0.3"
|
rtrb = "0.3"
|
||||||
cpal = "0.15"
|
cpal = "0.17"
|
||||||
ffmpeg-next = { version = "8.0", features = ["static"] }
|
ffmpeg-next = { version = "8.0", features = ["static"] }
|
||||||
|
|
||||||
# UI Framework
|
# UI Framework
|
||||||
|
|
|
||||||
|
|
@ -3,8 +3,13 @@ use std::fs;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
// Only bundle libs on Linux
|
let target_os = env::var("CARGO_CFG_TARGET_OS").unwrap();
|
||||||
if env::var("CARGO_CFG_TARGET_OS").unwrap() != "linux" {
|
|
||||||
|
if target_os == "windows" {
|
||||||
|
bundle_windows_dlls();
|
||||||
|
}
|
||||||
|
|
||||||
|
if target_os != "linux" {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -79,6 +84,50 @@ fn main() {
|
||||||
println!("cargo:rustc-link-arg=-Wl,-rpath,{}", lib_dir.display());
|
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) {
|
fn copy_library(lib_name: &str, search_paths: &[&str], lib_dir: &PathBuf) {
|
||||||
let mut copied = false;
|
let mut copied = false;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -183,64 +183,90 @@ 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
|
/// Icon cache for pane type icons
|
||||||
struct IconCache {
|
struct IconCache {
|
||||||
icons: HashMap<PaneType, egui::TextureHandle>,
|
icons: HashMap<PaneType, egui::TextureHandle>,
|
||||||
assets_path: std::path::PathBuf,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl IconCache {
|
impl IconCache {
|
||||||
fn new() -> Self {
|
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 {
|
Self {
|
||||||
icons: HashMap::new(),
|
icons: HashMap::new(),
|
||||||
assets_path,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_or_load(&mut self, pane_type: PaneType, ctx: &egui::Context) -> Option<&egui::TextureHandle> {
|
fn get_or_load(&mut self, pane_type: PaneType, ctx: &egui::Context) -> Option<&egui::TextureHandle> {
|
||||||
if !self.icons.contains_key(&pane_type) {
|
if !self.icons.contains_key(&pane_type) {
|
||||||
// Load SVG and rasterize using resvg
|
let svg_data = match pane_type {
|
||||||
let icon_path = self.assets_path.join(pane_type.icon_file());
|
PaneType::Stage | PaneType::Outliner | PaneType::PresetBrowser | PaneType::AssetLibrary => pane_icons::STAGE,
|
||||||
if let Ok(svg_data) = std::fs::read(&icon_path) {
|
PaneType::Timeline => pane_icons::TIMELINE,
|
||||||
// Rasterize at reasonable size for pane icons
|
PaneType::Toolbar => pane_icons::TOOLBAR,
|
||||||
let render_size = 64;
|
PaneType::Infopanel => pane_icons::INFOPANEL,
|
||||||
|
PaneType::PianoRoll => pane_icons::PIANO_ROLL,
|
||||||
if let Ok(tree) = resvg::usvg::Tree::from_data(&svg_data, &resvg::usvg::Options::default()) {
|
PaneType::VirtualPiano => pane_icons::PIANO,
|
||||||
let pixmap_size = tree.size().to_int_size();
|
PaneType::NodeEditor | PaneType::ShaderEditor => pane_icons::NODE_EDITOR,
|
||||||
let scale_x = render_size as f32 / pixmap_size.width() as f32;
|
};
|
||||||
let scale_y = render_size as f32 / pixmap_size.height() as f32;
|
if let Some(texture) = rasterize_svg(svg_data, pane_type.icon_file(), 64, ctx) {
|
||||||
let scale = scale_x.min(scale_y);
|
self.icons.insert(pane_type, texture);
|
||||||
|
|
||||||
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)
|
self.icons.get(&pane_type)
|
||||||
|
|
@ -250,61 +276,32 @@ impl IconCache {
|
||||||
/// Icon cache for tool icons
|
/// Icon cache for tool icons
|
||||||
struct ToolIconCache {
|
struct ToolIconCache {
|
||||||
icons: HashMap<Tool, egui::TextureHandle>,
|
icons: HashMap<Tool, egui::TextureHandle>,
|
||||||
assets_path: std::path::PathBuf,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ToolIconCache {
|
impl ToolIconCache {
|
||||||
fn new() -> Self {
|
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 {
|
Self {
|
||||||
icons: HashMap::new(),
|
icons: HashMap::new(),
|
||||||
assets_path,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_or_load(&mut self, tool: Tool, ctx: &egui::Context) -> Option<&egui::TextureHandle> {
|
fn get_or_load(&mut self, tool: Tool, ctx: &egui::Context) -> Option<&egui::TextureHandle> {
|
||||||
if !self.icons.contains_key(&tool) {
|
if !self.icons.contains_key(&tool) {
|
||||||
// Load SVG and rasterize at high resolution using resvg
|
let svg_data = match tool {
|
||||||
let icon_path = self.assets_path.join(tool.icon_file());
|
Tool::Select => tool_icons::SELECT,
|
||||||
if let Ok(svg_data) = std::fs::read(&icon_path) {
|
Tool::Draw => tool_icons::DRAW,
|
||||||
// Rasterize at 3x size for crisp display (180px for 60px display)
|
Tool::Transform => tool_icons::TRANSFORM,
|
||||||
let render_size = 180;
|
Tool::Rectangle => tool_icons::RECTANGLE,
|
||||||
|
Tool::Ellipse => tool_icons::ELLIPSE,
|
||||||
if let Ok(tree) = resvg::usvg::Tree::from_data(&svg_data, &resvg::usvg::Options::default()) {
|
Tool::PaintBucket => tool_icons::PAINT_BUCKET,
|
||||||
let pixmap_size = tree.size().to_int_size();
|
Tool::Eyedropper => tool_icons::EYEDROPPER,
|
||||||
let scale_x = render_size as f32 / pixmap_size.width() as f32;
|
Tool::Line => tool_icons::LINE,
|
||||||
let scale_y = render_size as f32 / pixmap_size.height() as f32;
|
Tool::Polygon => tool_icons::POLYGON,
|
||||||
let scale = scale_x.min(scale_y);
|
Tool::BezierEdit => tool_icons::BEZIER_EDIT,
|
||||||
|
Tool::Text => tool_icons::TEXT,
|
||||||
let final_size = resvg::usvg::Size::from_wh(
|
};
|
||||||
pixmap_size.width() as f32 * scale,
|
if let Some(texture) = rasterize_svg(svg_data, tool.icon_file(), 180, ctx) {
|
||||||
pixmap_size.height() as f32 * scale,
|
self.icons.insert(tool, texture);
|
||||||
).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)
|
self.icons.get(&tool)
|
||||||
|
|
@ -314,74 +311,33 @@ impl ToolIconCache {
|
||||||
/// Icon cache for focus card icons (start screen)
|
/// Icon cache for focus card icons (start screen)
|
||||||
struct FocusIconCache {
|
struct FocusIconCache {
|
||||||
icons: HashMap<FocusIcon, egui::TextureHandle>,
|
icons: HashMap<FocusIcon, egui::TextureHandle>,
|
||||||
assets_path: std::path::PathBuf,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FocusIconCache {
|
impl FocusIconCache {
|
||||||
fn new() -> Self {
|
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 {
|
Self {
|
||||||
icons: HashMap::new(),
|
icons: HashMap::new(),
|
||||||
assets_path,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_or_load(&mut self, icon: FocusIcon, icon_color: egui::Color32, ctx: &egui::Context) -> Option<&egui::TextureHandle> {
|
fn get_or_load(&mut self, icon: FocusIcon, icon_color: egui::Color32, ctx: &egui::Context) -> Option<&egui::TextureHandle> {
|
||||||
if !self.icons.contains_key(&icon) {
|
if !self.icons.contains_key(&icon) {
|
||||||
// Determine which SVG file to load
|
let (svg_bytes, svg_filename) = match icon {
|
||||||
let svg_filename = match icon {
|
FocusIcon::Animation => (focus_icons::ANIMATION, "focus-animation.svg"),
|
||||||
FocusIcon::Animation => "focus-animation.svg",
|
FocusIcon::Music => (focus_icons::MUSIC, "focus-music.svg"),
|
||||||
FocusIcon::Music => "focus-music.svg",
|
FocusIcon::Video => (focus_icons::VIDEO, "focus-video.svg"),
|
||||||
FocusIcon::Video => "focus-video.svg",
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let icon_path = self.assets_path.join(svg_filename);
|
// Replace currentColor with the actual color
|
||||||
if let Ok(svg_data) = std::fs::read_to_string(&icon_path) {
|
let svg_data = String::from_utf8_lossy(svg_bytes);
|
||||||
// Replace currentColor with the actual color
|
let color_hex = format!(
|
||||||
let color_hex = format!(
|
"#{:02x}{:02x}{:02x}",
|
||||||
"#{:02x}{:02x}{:02x}",
|
icon_color.r(), icon_color.g(), icon_color.b()
|
||||||
icon_color.r(), icon_color.g(), icon_color.b()
|
);
|
||||||
);
|
let svg_with_color = svg_data.replace("currentColor", &color_hex);
|
||||||
let svg_with_color = svg_data.replace("currentColor", &color_hex);
|
|
||||||
|
|
||||||
// Rasterize at 2x size for crisp display
|
if let Some(texture) = rasterize_svg(svg_with_color.as_bytes(), svg_filename, 120, ctx) {
|
||||||
let render_size = 120;
|
self.icons.insert(icon, texture);
|
||||||
|
|
||||||
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)
|
self.icons.get(&icon)
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue