Compare commits
15
Commits
16613fb982
...
v0.11.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
acd7a27c5b | ||
|
|
faf258bbda | ||
|
|
1b1fa3ced0 | ||
|
|
9dfc942dfc | ||
|
|
d269082c8b | ||
|
|
640078e9b9 | ||
|
|
4187b6683e | ||
|
|
ab86c7abd2 | ||
|
|
25b08d26af | ||
|
|
3068084b5f | ||
|
|
18dccb8a64 | ||
|
|
6325a90b4f | ||
|
|
be1133adf7 | ||
|
|
ac9d3c51d6 | ||
|
|
8cf546167f |
Generated
+1290
-1330
File diff suppressed because it is too large
Load Diff
+15
-14
@@ -4,27 +4,28 @@ description = "An emulator for the Virtual Boy."
|
|||||||
repository = "https://git.virtual-boy.com/PVB/lemur"
|
repository = "https://git.virtual-boy.com/PVB/lemur"
|
||||||
publish = false
|
publish = false
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
version = "0.9.2"
|
version = "0.11.0"
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
anyhow = "1"
|
anyhow = "1"
|
||||||
atoi = "2"
|
atoi = "2"
|
||||||
|
audioadapter-buffers = "2"
|
||||||
atomic = "0.6"
|
atomic = "0.6"
|
||||||
bitflags = { version = "2", features = ["serde"] }
|
bitflags = { version = "2", features = ["serde"] }
|
||||||
bytemuck = { version = "1", features = ["derive"] }
|
bytemuck = { version = "1", features = ["derive"] }
|
||||||
clap = { version = "4", features = ["derive"] }
|
clap = { version = "4", features = ["derive"] }
|
||||||
cpal = { git = "https://github.com/sidit77/cpal.git", rev = "66ed6be" }
|
cpal = { git = "https://github.com/sidit77/cpal.git", rev = "66ed6be" }
|
||||||
directories = "6"
|
directories = "6"
|
||||||
egui = { version = "0.32", features = ["serde"] }
|
egui = { version = "0.33", features = ["serde"] }
|
||||||
egui_extras = { version = "0.32", features = ["image"] }
|
egui_extras = { version = "0.33", features = ["image"] }
|
||||||
egui-notify = "0.20"
|
egui-notify = "0.21"
|
||||||
egui-winit = "0.32"
|
egui-winit = "0.33"
|
||||||
egui-wgpu = { version = "0.32", features = ["winit"] }
|
egui-wgpu = { version = "0.33", features = ["winit"] }
|
||||||
fxprof-processed-profile = "0.8"
|
fxprof-processed-profile = "0.8"
|
||||||
fixed = { version = "1.28", features = ["num-traits"] }
|
fixed = { version = "1.28", features = ["num-traits"] }
|
||||||
gilrs = { version = "0.11", features = ["serde-serialize"] }
|
gilrs = { version = "0.11", features = ["serde-serialize"] }
|
||||||
gimli = "0.32"
|
gimli = "0.33"
|
||||||
hex = "0.4"
|
hex = "0.4"
|
||||||
image = { version = "0.25", default-features = false, features = ["png"] }
|
image = { version = "0.25", default-features = false, features = ["png"] }
|
||||||
itertools = "0.14"
|
itertools = "0.14"
|
||||||
@@ -32,25 +33,25 @@ normpath = "1"
|
|||||||
notify = "8"
|
notify = "8"
|
||||||
num-derive = "0.4"
|
num-derive = "0.4"
|
||||||
num-traits = "0.2"
|
num-traits = "0.2"
|
||||||
object = "0.37"
|
object = "0.38"
|
||||||
oneshot = "0.1"
|
oneshot = "0.1"
|
||||||
pollster = "0.4"
|
pollster = "0.4"
|
||||||
rand = "0.9"
|
rand = "0.10"
|
||||||
rfd = { version = "0.15", default-features = false, features = ["xdg-portal", "async-std"]}
|
rfd = "0.17"
|
||||||
rtrb = "0.3"
|
rtrb = "0.3"
|
||||||
rubato = "0.16"
|
rubato = "1"
|
||||||
serde = { version = "1", features = ["derive"] }
|
serde = { version = "1", features = ["derive"] }
|
||||||
serde_json = "1"
|
serde_json = "1"
|
||||||
thread-priority = "2"
|
thread-priority = "3"
|
||||||
tokio = { version = "1", features = ["io-util", "macros", "net", "rt", "sync", "time"] }
|
tokio = { version = "1", features = ["io-util", "macros", "net", "rt", "sync", "time"] }
|
||||||
tracing = { version = "0.1", features = ["release_max_level_info"] }
|
tracing = { version = "0.1", features = ["release_max_level_info"] }
|
||||||
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
|
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
|
||||||
wgpu = "25"
|
wgpu = "27"
|
||||||
wholesym = "0.8"
|
wholesym = "0.8"
|
||||||
winit = { version = "0.30", features = ["serde"] }
|
winit = { version = "0.30", features = ["serde"] }
|
||||||
|
|
||||||
[target.'cfg(windows)'.dependencies]
|
[target.'cfg(windows)'.dependencies]
|
||||||
windows = { version = "0.61", features = ["Win32_System_Threading"] }
|
windows = { version = "0.62", features = ["Win32_System_Threading"] }
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
cc = "1"
|
cc = "1"
|
||||||
|
|||||||
+17
-13
@@ -9,31 +9,35 @@ ADD "https://github.com/joseluisq/macosx-sdks/releases/download/14.5/MacOSX14.5.
|
|||||||
RUN apt-get update && \
|
RUN apt-get update && \
|
||||||
apt-get install -y ca-certificates
|
apt-get install -y ca-certificates
|
||||||
COPY llvm.sources /etc/apt/sources.list.d/llvm.sources
|
COPY llvm.sources /etc/apt/sources.list.d/llvm.sources
|
||||||
|
COPY install-llvm.sh .
|
||||||
RUN apt-get update && \
|
RUN apt-get update && \
|
||||||
apt-get install -y bash bzip2 clang-20 git lld-20 llvm-20 make patch xz-utils && \
|
./install-llvm.sh && \
|
||||||
ln -s $(which clang-20) /usr/bin/clang && \
|
apt-get install -y bash bzip2 git make patch xz-utils && \
|
||||||
ln -s $(which clang++-20) /usr/bin/clang++ && \
|
ln -s $(which clang-21) /usr/bin/clang && \
|
||||||
ln -s $(which ld64.lld-20) /usr/bin/ld64.lld && \
|
ln -s $(which clang++-21) /usr/bin/clang++ && \
|
||||||
|
ln -s $(which ld64.lld-21) /usr/bin/ld64.lld && \
|
||||||
SDK_VERSION=14.5 UNATTENDED=1 ENABLE_COMPILER_RT_INSTALL=1 TARGET_DIR=/osxcross ./build.sh
|
SDK_VERSION=14.5 UNATTENDED=1 ENABLE_COMPILER_RT_INSTALL=1 TARGET_DIR=/osxcross ./build.sh
|
||||||
|
|
||||||
FROM rust:1.90-bookworm
|
FROM rust:1.93-bookworm
|
||||||
ADD --chmod=644 "https://apt.llvm.org/llvm-snapshot.gpg.key" /etc/apt/trusted.gpg.d/apt.llvm.org.asc
|
ADD --chmod=644 "https://apt.llvm.org/llvm-snapshot.gpg.key" /etc/apt/trusted.gpg.d/apt.llvm.org.asc
|
||||||
COPY llvm.sources /etc/apt/sources.list.d/llvm.sources
|
COPY llvm.sources /etc/apt/sources.list.d/llvm.sources
|
||||||
|
COPY install-llvm.sh .
|
||||||
RUN rustup target add x86_64-pc-windows-msvc && \
|
RUN rustup target add x86_64-pc-windows-msvc && \
|
||||||
rustup target add x86_64-apple-darwin && \
|
rustup target add x86_64-apple-darwin && \
|
||||||
rustup target add aarch64-apple-darwin && \
|
rustup target add aarch64-apple-darwin && \
|
||||||
apt-get update && \
|
apt-get update && \
|
||||||
apt-get install -y clang-20 lld-20 libc6-dev libasound2-dev libudev-dev genisoimage mingw-w64 && \
|
./install-llvm.sh && \
|
||||||
|
apt-get install -y libc6-dev libasound2-dev libudev-dev genisoimage mingw-w64 && \
|
||||||
cargo install cargo-bundle xwin && \
|
cargo install cargo-bundle xwin && \
|
||||||
xwin --accept-license splat --output xwin && \
|
xwin --accept-license splat --output xwin && \
|
||||||
rm -rf .xwin-cache && \
|
rm -rf .xwin-cache && \
|
||||||
ln -s $(which clang-20) /usr/bin/clang && \
|
ln -s $(which clang-21) /usr/bin/clang && \
|
||||||
ln -s $(which clang++-20) /usr/bin/clang++
|
ln -s $(which clang++-21) /usr/bin/clang++
|
||||||
COPY --from=osxcross /osxcross /osxcross
|
COPY --from=osxcross /osxcross /osxcross
|
||||||
|
|
||||||
ENV PATH="/osxcross/bin:$PATH" \
|
ENV PATH="/osxcross/bin:$PATH" \
|
||||||
LD_LIBRARY_PATH="/osxcross/lib" \
|
LD_LIBRARY_PATH="/osxcross/lib" \
|
||||||
CC="clang-20" CXX="clang++-20" AR="llvm-ar-20" \
|
CC="clang-21" CXX="clang++-21" AR="llvm-ar-21" \
|
||||||
CC_x86_64-apple-darwin="o64-clang" \
|
CC_x86_64-apple-darwin="o64-clang" \
|
||||||
CXX_x86_64-apple-darwin="o64-clang++" \
|
CXX_x86_64-apple-darwin="o64-clang++" \
|
||||||
CC_aarch64-apple-darwin="oa64-clang" \
|
CC_aarch64-apple-darwin="oa64-clang" \
|
||||||
@@ -43,11 +47,11 @@ ENV PATH="/osxcross/bin:$PATH" \
|
|||||||
SHROOMS_CFLAGS_x86_64-pc-windows-msvc="-flto" \
|
SHROOMS_CFLAGS_x86_64-pc-windows-msvc="-flto" \
|
||||||
CFLAGS_x86_64-pc-windows-msvc="-I/xwin/crt/include -I/xwin/sdk/include/ucrt -I/xwin/sdk/include/um -I/xwin/sdk/include/shared" \
|
CFLAGS_x86_64-pc-windows-msvc="-I/xwin/crt/include -I/xwin/sdk/include/ucrt -I/xwin/sdk/include/um -I/xwin/sdk/include/shared" \
|
||||||
CARGO_TARGET_X86_64_PC_WINDOWS_MSVC_RUSTFLAGS="-Lnative=/xwin/crt/lib/x86_64 -Lnative=/xwin/sdk/lib/um/x86_64 -Lnative=/xwin/sdk/lib/ucrt/x86_64 -Clinker-plugin-lto -Clink-arg=-fuse-ld=lld" \
|
CARGO_TARGET_X86_64_PC_WINDOWS_MSVC_RUSTFLAGS="-Lnative=/xwin/crt/lib/x86_64 -Lnative=/xwin/sdk/lib/um/x86_64 -Lnative=/xwin/sdk/lib/ucrt/x86_64 -Clinker-plugin-lto -Clink-arg=-fuse-ld=lld" \
|
||||||
CARGO_TARGET_X86_64_PC_WINDOWS_MSVC_LINKER="lld-link-20" \
|
CARGO_TARGET_X86_64_PC_WINDOWS_MSVC_LINKER="lld-link-21" \
|
||||||
CARGO_TARGET_X86_64_APPLE_DARWIN_LINKER="o64-clang" \
|
CARGO_TARGET_X86_64_APPLE_DARWIN_LINKER="o64-clang" \
|
||||||
CARGO_TARGET_X86_64_APPLE_DARWIN_AR="llvm-ar-20" \
|
CARGO_TARGET_X86_64_APPLE_DARWIN_AR="llvm-ar-21" \
|
||||||
CARGO_TARGET_AARCH64_APPLE_DARWIN_LINKER="oa64-clang" \
|
CARGO_TARGET_AARCH64_APPLE_DARWIN_LINKER="oa64-clang" \
|
||||||
CARGO_TARGET_AARCH64_APPLE_DARWIN_AR="llvm-ar-20" \
|
CARGO_TARGET_AARCH64_APPLE_DARWIN_AR="llvm-ar-21" \
|
||||||
RC_PATH="llvm-rc-20" \
|
RC_PATH="llvm-rc-21" \
|
||||||
MACOSX_DEPLOYMENT_TARGET="14.5"
|
MACOSX_DEPLOYMENT_TARGET="14.5"
|
||||||
ENTRYPOINT ["bash"]
|
ENTRYPOINT ["bash"]
|
||||||
|
|||||||
Executable
+19
@@ -0,0 +1,19 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
# Hopefully temporary script to manually install working llvm packages.
|
||||||
|
# The apt index for these is broken in bookworm, and upgrading to trixie
|
||||||
|
# would make them depend on too new of a libc version.
|
||||||
|
|
||||||
|
# https://apt.llvm.org/bookworm/pool/main/l/llvm-toolchain-21/clang-21_21.1.5~%2B%2B20251023083151%2B45afac62e373-1~exp1~20251023083333.51_amd64.deb
|
||||||
|
PACKAGES=('clang-21' 'clang-tools-21' 'libclang-common-21-dev' 'libclang-cpp21' 'libclang-rt-21-dev' 'libclang1-21' 'libllvm21' 'lld-21' 'llvm-21' 'llvm-21-dev' 'llvm-21-linker-tools' 'llvm-21-runtime' 'llvm-21-tools')
|
||||||
|
FILES=()
|
||||||
|
URL='https://apt.llvm.org/bookworm/pool/main/l/llvm-toolchain-21'
|
||||||
|
VERSION='21.1.5~%2B%2B20251023083151%2B45afac62e373-1~exp1~20251023083333.51_amd64.deb'
|
||||||
|
|
||||||
|
apt-get install -y curl python3
|
||||||
|
for package in "${PACKAGES[@]}"; do
|
||||||
|
curl -O -L "$URL/${package}_$VERSION"
|
||||||
|
FILES+=("./${package}_$VERSION")
|
||||||
|
done
|
||||||
|
|
||||||
|
apt-get install -y "${FILES[@]}"
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
Types: deb deb-src
|
Types: deb deb-src
|
||||||
URIs: http://apt.llvm.org/bookworm/
|
URIs: http://apt.llvm.org/bookworm/
|
||||||
Suites: llvm-toolchain-bookworm-20
|
Suites: llvm-toolchain-bookworm-21
|
||||||
Components: main
|
Components: main
|
||||||
|
|||||||
+17
-8
@@ -74,7 +74,7 @@ impl Application {
|
|||||||
) -> Self {
|
) -> Self {
|
||||||
let wgpu = WgpuState::new();
|
let wgpu = WgpuState::new();
|
||||||
let icon = load_icon().ok().map(Arc::new);
|
let icon = load_icon().ok().map(Arc::new);
|
||||||
let mappings = MappingProvider::new(persistence.clone());
|
let mappings = MappingProvider::new(persistence.clone(), args.player2_controller);
|
||||||
let shortcuts = ShortcutProvider::new(persistence.clone());
|
let shortcuts = ShortcutProvider::new(persistence.clone());
|
||||||
let controllers = ControllerManager::new(client.clone(), &mappings);
|
let controllers = ControllerManager::new(client.clone(), &mappings);
|
||||||
let memory = Arc::new(MemoryClient::new(client.clone()));
|
let memory = Arc::new(MemoryClient::new(client.clone()));
|
||||||
@@ -84,7 +84,7 @@ impl Application {
|
|||||||
let proxy = proxy.clone();
|
let proxy = proxy.clone();
|
||||||
thread::spawn(|| process_gamepad_input(mappings, proxy));
|
thread::spawn(|| process_gamepad_input(mappings, proxy));
|
||||||
}
|
}
|
||||||
Self {
|
let app = Self {
|
||||||
icon,
|
icon,
|
||||||
wgpu,
|
wgpu,
|
||||||
client,
|
client,
|
||||||
@@ -106,7 +106,16 @@ impl Application {
|
|||||||
init_framebuffers: args.frame_buffers,
|
init_framebuffers: args.frame_buffers,
|
||||||
init_registers: args.registers,
|
init_registers: args.registers,
|
||||||
init_terminal: args.terminal,
|
init_terminal: args.terminal,
|
||||||
|
};
|
||||||
|
if args.player2 {
|
||||||
|
app.client
|
||||||
|
.send_command(EmulatorCommand::StartSecondSim(args.rom.clone()));
|
||||||
|
|
||||||
|
app.proxy
|
||||||
|
.send_event(UserEvent::OpenPlayer2)
|
||||||
|
.expect("Failed to open Player 2 window");
|
||||||
}
|
}
|
||||||
|
app
|
||||||
}
|
}
|
||||||
|
|
||||||
fn open(&mut self, event_loop: &ActiveEventLoop, window: Box<dyn AppWindow>) {
|
fn open(&mut self, event_loop: &ActiveEventLoop, window: Box<dyn AppWindow>) {
|
||||||
@@ -382,7 +391,8 @@ impl WgpuState {
|
|||||||
});
|
});
|
||||||
|
|
||||||
let adapter = pollster::block_on(instance.request_adapter(&wgpu::RequestAdapterOptions {
|
let adapter = pollster::block_on(instance.request_adapter(&wgpu::RequestAdapterOptions {
|
||||||
power_preference: wgpu::PowerPreference::HighPerformance,
|
power_preference:
|
||||||
|
wgpu::PowerPreference::from_env().unwrap_or(wgpu::PowerPreference::LowPower),
|
||||||
compatible_surface: None,
|
compatible_surface: None,
|
||||||
force_fallback_adapter: false,
|
force_fallback_adapter: false,
|
||||||
}))
|
}))
|
||||||
@@ -439,7 +449,7 @@ impl Viewport {
|
|||||||
egui_extras::install_image_loaders(&ctx);
|
egui_extras::install_image_loaders(&ctx);
|
||||||
|
|
||||||
let wgpu_config = egui_wgpu::WgpuConfiguration {
|
let wgpu_config = egui_wgpu::WgpuConfiguration {
|
||||||
present_mode: wgpu::PresentMode::AutoNoVsync,
|
present_mode: wgpu::PresentMode::AutoVsync,
|
||||||
wgpu_setup: egui_wgpu::WgpuSetup::Existing(egui_wgpu::WgpuSetupExisting {
|
wgpu_setup: egui_wgpu::WgpuSetup::Existing(egui_wgpu::WgpuSetupExisting {
|
||||||
instance: wgpu.instance.clone(),
|
instance: wgpu.instance.clone(),
|
||||||
adapter: wgpu.adapter.clone(),
|
adapter: wgpu.adapter.clone(),
|
||||||
@@ -449,13 +459,12 @@ impl Viewport {
|
|||||||
..egui_wgpu::WgpuConfiguration::default()
|
..egui_wgpu::WgpuConfiguration::default()
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let options = egui_wgpu::RendererOptions::default();
|
||||||
let mut painter = pollster::block_on(egui_wgpu::winit::Painter::new(
|
let mut painter = pollster::block_on(egui_wgpu::winit::Painter::new(
|
||||||
ctx.clone(),
|
ctx.clone(),
|
||||||
wgpu_config,
|
wgpu_config,
|
||||||
1,
|
|
||||||
None,
|
|
||||||
false,
|
false,
|
||||||
true,
|
options,
|
||||||
));
|
));
|
||||||
|
|
||||||
let mut info = ViewportInfo::default();
|
let mut info = ViewportInfo::default();
|
||||||
@@ -554,7 +563,7 @@ impl Viewport {
|
|||||||
&mut self.info,
|
&mut self.info,
|
||||||
std::mem::take(&mut self.commands),
|
std::mem::take(&mut self.commands),
|
||||||
&self.window,
|
&self.window,
|
||||||
&mut HashSet::default(),
|
&mut vec![],
|
||||||
);
|
);
|
||||||
|
|
||||||
if self.info.close_requested() {
|
if self.info.close_requested() {
|
||||||
|
|||||||
+38
-33
@@ -1,17 +1,17 @@
|
|||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
use anyhow::{Result, bail};
|
use anyhow::{Result, bail};
|
||||||
|
use audioadapter_buffers::direct::InterleavedSlice;
|
||||||
use cpal::traits::{DeviceTrait, HostTrait, StreamTrait};
|
use cpal::traits::{DeviceTrait, HostTrait, StreamTrait};
|
||||||
use itertools::Itertools;
|
use rubato::Resampler;
|
||||||
use rubato::{FastFixedOut, Resampler};
|
|
||||||
use tracing::error;
|
use tracing::error;
|
||||||
|
|
||||||
pub struct Audio {
|
pub struct Audio {
|
||||||
#[allow(unused)]
|
#[allow(unused)]
|
||||||
stream: cpal::Stream,
|
stream: cpal::Stream,
|
||||||
sampler: FastFixedOut<f32>,
|
sampler: rubato::Async<f32>,
|
||||||
input_buffer: Vec<Vec<f32>>,
|
input_buffer: Vec<f32>,
|
||||||
output_buffer: Vec<Vec<f32>>,
|
output_buffer: Vec<f32>,
|
||||||
sample_sink: rtrb::Producer<f32>,
|
sample_sink: rtrb::Producer<f32>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -32,17 +32,18 @@ impl Audio {
|
|||||||
let mut config = config.with_max_sample_rate().config();
|
let mut config = config.with_max_sample_rate().config();
|
||||||
let resample_ratio = config.sample_rate.0 as f64 / VB_FREQUENCY as f64;
|
let resample_ratio = config.sample_rate.0 as f64 / VB_FREQUENCY as f64;
|
||||||
let chunk_size = (834.0 * resample_ratio) as usize;
|
let chunk_size = (834.0 * resample_ratio) as usize;
|
||||||
let sampler = FastFixedOut::new(
|
let sampler = rubato::Async::new_poly(
|
||||||
resample_ratio,
|
resample_ratio,
|
||||||
64.0,
|
64.0,
|
||||||
rubato::PolynomialDegree::Cubic,
|
rubato::PolynomialDegree::Cubic,
|
||||||
chunk_size,
|
chunk_size,
|
||||||
2,
|
2,
|
||||||
|
rubato::FixedAsync::Output,
|
||||||
)?;
|
)?;
|
||||||
config.buffer_size = cpal::BufferSize::Fixed(sampler.output_frames_max() as u32);
|
config.buffer_size = cpal::BufferSize::Fixed(sampler.output_frames_max() as u32);
|
||||||
|
|
||||||
let input_buffer = sampler.input_buffer_allocate(true);
|
let input_buffer = Vec::with_capacity(sampler.nbr_channels() * sampler.input_frames_max());
|
||||||
let output_buffer = sampler.output_buffer_allocate(true);
|
let output_buffer = vec![0.0; sampler.nbr_channels() * sampler.output_frames_max()];
|
||||||
let (sample_sink, mut sample_source) =
|
let (sample_sink, mut sample_source) =
|
||||||
rtrb::RingBuffer::new(sampler.output_frames_max() * 4);
|
rtrb::RingBuffer::new(sampler.output_frames_max() * 4);
|
||||||
|
|
||||||
@@ -78,34 +79,38 @@ impl Audio {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn update(&mut self, samples: &[f32]) {
|
pub fn update(&mut self, mut samples: &[f32]) {
|
||||||
for sample in samples.chunks_exact(2) {
|
while self.input_buffer.len() + samples.len() >= self.sampler.input_frames_next() * 2 {
|
||||||
for (channel, value) in self.input_buffer.iter_mut().zip(sample) {
|
let samples_needed =
|
||||||
channel.push(*value);
|
(self.sampler.input_frames_next() * 2).saturating_sub(self.input_buffer.len());
|
||||||
}
|
let (current_samples, future_samples) = samples.split_at(samples_needed);
|
||||||
if self.input_buffer[0].len() >= self.sampler.input_frames_next() {
|
self.input_buffer.extend_from_slice(current_samples);
|
||||||
let (_, output_samples) = self
|
samples = future_samples;
|
||||||
.sampler
|
|
||||||
.process_into_buffer(&self.input_buffer, &mut self.output_buffer, None)
|
let buffer_in =
|
||||||
|
InterleavedSlice::new(&self.input_buffer, 2, self.sampler.input_frames_next())
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
let mut buffer_out = InterleavedSlice::new_mut(
|
||||||
let chunk = match self.sample_sink.write_chunk_uninit(output_samples * 2) {
|
&mut self.output_buffer,
|
||||||
Ok(c) => c,
|
2,
|
||||||
Err(rtrb::chunks::ChunkError::TooFewSlots(n)) => {
|
self.sampler.output_frames_next(),
|
||||||
self.sample_sink.write_chunk_uninit(n).unwrap()
|
)
|
||||||
}
|
.unwrap();
|
||||||
};
|
let (_, output_samples) = self
|
||||||
let interleaved = self.output_buffer[0]
|
.sampler
|
||||||
.iter()
|
.process_into_buffer(&buffer_in, &mut buffer_out, None)
|
||||||
.interleave(self.output_buffer[1].iter())
|
.unwrap();
|
||||||
.cloned();
|
let chunk = match self.sample_sink.write_chunk_uninit(output_samples * 2) {
|
||||||
chunk.fill_from_iter(interleaved);
|
Ok(c) => c,
|
||||||
|
Err(rtrb::chunks::ChunkError::TooFewSlots(n)) => {
|
||||||
for channel in &mut self.input_buffer {
|
self.sample_sink.write_chunk_uninit(n).unwrap()
|
||||||
channel.clear();
|
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
chunk.fill_from_iter(self.output_buffer[..output_samples * 2].iter().copied());
|
||||||
|
|
||||||
|
self.input_buffer.clear();
|
||||||
}
|
}
|
||||||
|
self.input_buffer.extend_from_slice(samples);
|
||||||
|
|
||||||
while self.sample_sink.slots() < self.sampler.output_frames_max() * 2 {
|
while self.sample_sink.slots() < self.sampler.output_frames_max() * 2 {
|
||||||
std::thread::sleep(Duration::from_micros(500));
|
std::thread::sleep(Duration::from_micros(500));
|
||||||
|
|||||||
@@ -40,6 +40,12 @@ pub struct CliArgs {
|
|||||||
/// Watch ROM files for changes, automatically reload
|
/// Watch ROM files for changes, automatically reload
|
||||||
#[arg(short, long)]
|
#[arg(short, long)]
|
||||||
pub watch: bool,
|
pub watch: bool,
|
||||||
|
/// Automatically open Player 2 for multiplayer
|
||||||
|
#[arg(long)]
|
||||||
|
pub player2: bool,
|
||||||
|
/// Map the first connected controller to Player 2
|
||||||
|
#[arg(long)]
|
||||||
|
pub player2_controller: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const COLOR_PRESETS: [[Color32; 2]; 3] = [
|
pub const COLOR_PRESETS: [[Color32; 2]; 3] = [
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use notify::Watcher;
|
use notify::Watcher;
|
||||||
use rand::Rng;
|
use rand::RngExt;
|
||||||
use std::{
|
use std::{
|
||||||
fs::{self, File},
|
fs::{self, File},
|
||||||
io::{Read, Seek as _, SeekFrom, Write as _},
|
io::{Read, Seek as _, SeekFrom, Write as _},
|
||||||
|
|||||||
@@ -245,9 +245,12 @@ impl ParseContext<'_> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_inline(ctx: &mut ParseContext, node: gimli::EntriesTreeNode<Reader>) -> Result<()> {
|
fn parse_inline<'a>(
|
||||||
|
ctx: &mut ParseContext<'a>,
|
||||||
|
node: gimli::EntriesTreeNode<'a, '_, Reader<'a>>,
|
||||||
|
) -> Result<()> {
|
||||||
if node.entry().tag() == gimli::DW_TAG_inlined_subroutine
|
if node.entry().tag() == gimli::DW_TAG_inlined_subroutine
|
||||||
&& let Some(attr) = node.entry().attr_value(gimli::DW_AT_abstract_origin)?
|
&& let Some(attr) = node.entry().attr_value(gimli::DW_AT_abstract_origin)
|
||||||
&& let Some(name) = ctx.name_attr(attr)?
|
&& let Some(name) = ctx.name_attr(attr)?
|
||||||
{
|
{
|
||||||
let name = Arc::new(name);
|
let name = Arc::new(name);
|
||||||
|
|||||||
+10
-3
@@ -272,13 +272,14 @@ impl Mappings for InputMapping {
|
|||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct MappingProvider {
|
pub struct MappingProvider {
|
||||||
persistence: Persistence,
|
persistence: Persistence,
|
||||||
|
first_gamepad_is_p2: bool,
|
||||||
device_mappings: Arc<RwLock<HashMap<DeviceId, Arc<RwLock<GamepadMapping>>>>>,
|
device_mappings: Arc<RwLock<HashMap<DeviceId, Arc<RwLock<GamepadMapping>>>>>,
|
||||||
sim_mappings: HashMap<SimId, Arc<RwLock<InputMapping>>>,
|
sim_mappings: HashMap<SimId, Arc<RwLock<InputMapping>>>,
|
||||||
gamepad_info: Arc<RwLock<HashMap<GamepadId, GamepadInfo>>>,
|
gamepad_info: Arc<RwLock<HashMap<GamepadId, GamepadInfo>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MappingProvider {
|
impl MappingProvider {
|
||||||
pub fn new(persistence: Persistence) -> Self {
|
pub fn new(persistence: Persistence, first_gamepad_is_p2: bool) -> Self {
|
||||||
let mut sim_mappings = HashMap::new();
|
let mut sim_mappings = HashMap::new();
|
||||||
let mut device_mappings = HashMap::new();
|
let mut device_mappings = HashMap::new();
|
||||||
|
|
||||||
@@ -307,6 +308,7 @@ impl MappingProvider {
|
|||||||
device_mappings: Arc::new(RwLock::new(device_mappings)),
|
device_mappings: Arc::new(RwLock::new(device_mappings)),
|
||||||
gamepad_info: Arc::new(RwLock::new(HashMap::new())),
|
gamepad_info: Arc::new(RwLock::new(HashMap::new())),
|
||||||
sim_mappings,
|
sim_mappings,
|
||||||
|
first_gamepad_is_p2,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -338,7 +340,12 @@ impl MappingProvider {
|
|||||||
.clone();
|
.clone();
|
||||||
drop(lock);
|
drop(lock);
|
||||||
let mut lock = self.gamepad_info.write().unwrap();
|
let mut lock = self.gamepad_info.write().unwrap();
|
||||||
let bound_to = SimId::values()
|
let players = if self.first_gamepad_is_p2 {
|
||||||
|
vec![SimId::Player2, SimId::Player1]
|
||||||
|
} else {
|
||||||
|
vec![SimId::Player1, SimId::Player2]
|
||||||
|
};
|
||||||
|
let bound_to = players
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.find(|sim_id| lock.values().all(|info| info.bound_to != Some(*sim_id)));
|
.find(|sim_id| lock.values().all(|info| info.bound_to != Some(*sim_id)));
|
||||||
if let Entry::Vacant(entry) = lock.entry(gamepad.id()) {
|
if let Entry::Vacant(entry) = lock.entry(gamepad.id()) {
|
||||||
@@ -683,7 +690,7 @@ impl ShortcutProvider {
|
|||||||
else {
|
else {
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
if shortcut.logical_key != *key || !shortcut.modifiers.contains(*modifiers) {
|
if *key != shortcut.logical_key || !modifiers.contains(shortcut.modifiers) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if matches!(command, Command::FastForward(_)) {
|
if matches!(command, Command::FastForward(_)) {
|
||||||
|
|||||||
Reference in New Issue
Block a user