Refactor audio output
This commit is contained in:
parent
1ff2990f2e
commit
fb262cf3b1
251
src/audio.rs
Normal file
251
src/audio.rs
Normal file
@ -0,0 +1,251 @@
|
|||||||
|
use std::{
|
||||||
|
sync::mpsc::{self, Receiver, Sender, TryRecvError},
|
||||||
|
thread,
|
||||||
|
time::{Duration, Instant},
|
||||||
|
};
|
||||||
|
|
||||||
|
use log::trace;
|
||||||
|
use soloud::{AudioExt, Handle as VoiceHandle, LoadExt, Soloud, Wav};
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub enum AudioMessage {
|
||||||
|
PlaySwitchClick,
|
||||||
|
FanOn,
|
||||||
|
FanOff,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AudioMessage {
|
||||||
|
/// Returns `true` if the audio message is [`FanOff`].
|
||||||
|
///
|
||||||
|
/// [`FanOff`]: AudioMessage::FanOff
|
||||||
|
#[must_use]
|
||||||
|
pub fn is_fan_off(&self) -> bool {
|
||||||
|
matches!(self, Self::FanOff)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct AudioThread {
|
||||||
|
fan_startup: Wav,
|
||||||
|
fan_loop: Wav,
|
||||||
|
fan_shutdown: Wav,
|
||||||
|
sw_click: Wav,
|
||||||
|
sl: Soloud,
|
||||||
|
receiver: Receiver<AudioMessage>,
|
||||||
|
fan_state: FanState,
|
||||||
|
}
|
||||||
|
|
||||||
|
enum FanState {
|
||||||
|
Off,
|
||||||
|
Starting {
|
||||||
|
start_time: Instant,
|
||||||
|
sound_offset: Duration,
|
||||||
|
},
|
||||||
|
On,
|
||||||
|
Stopping {
|
||||||
|
start_time: Instant,
|
||||||
|
sound_offset: Duration,
|
||||||
|
handle: VoiceHandle,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FanState {
|
||||||
|
/// Returns `true` if the fan state is [`Off`].
|
||||||
|
///
|
||||||
|
/// [`Off`]: FanState::Off
|
||||||
|
#[must_use]
|
||||||
|
fn is_off(&self) -> bool {
|
||||||
|
matches!(self, Self::Off)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns `true` if the fan state is [`Starting`].
|
||||||
|
///
|
||||||
|
/// [`Starting`]: FanState::Starting
|
||||||
|
#[must_use]
|
||||||
|
fn is_starting(&self) -> bool {
|
||||||
|
matches!(self, Self::Starting { .. })
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns `true` if the fan state is [`On`].
|
||||||
|
///
|
||||||
|
/// [`On`]: FanState::On
|
||||||
|
#[must_use]
|
||||||
|
fn is_on(&self) -> bool {
|
||||||
|
matches!(self, Self::On)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns `true` if the fan state is [`Stopping`].
|
||||||
|
///
|
||||||
|
/// [`Stopping`]: FanState::Stopping
|
||||||
|
#[must_use]
|
||||||
|
fn is_stopping(&self) -> bool {
|
||||||
|
matches!(self, Self::Stopping { .. })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AudioThread {
|
||||||
|
pub fn init() -> Sender<AudioMessage> {
|
||||||
|
let sl = Soloud::default().unwrap();
|
||||||
|
let (tx, rx) = mpsc::channel();
|
||||||
|
|
||||||
|
thread::spawn(move || {
|
||||||
|
let fan_startup = {
|
||||||
|
let mut fan_startup = Wav::default();
|
||||||
|
fan_startup
|
||||||
|
.load("/home/pjht/projects/altair_emu/resources/fan1.wav")
|
||||||
|
.unwrap();
|
||||||
|
fan_startup
|
||||||
|
};
|
||||||
|
let fan_loop = {
|
||||||
|
let mut fan_loop = Wav::default();
|
||||||
|
fan_loop
|
||||||
|
.load("/home/pjht/projects/altair_emu/resources/fan2.wav")
|
||||||
|
.unwrap();
|
||||||
|
fan_loop
|
||||||
|
};
|
||||||
|
let fan_shutdown = {
|
||||||
|
let mut fan_shutdown = Wav::default();
|
||||||
|
fan_shutdown
|
||||||
|
.load("/home/pjht/projects/altair_emu/resources/fan3.wav")
|
||||||
|
.unwrap();
|
||||||
|
fan_shutdown
|
||||||
|
};
|
||||||
|
let sw_click = {
|
||||||
|
let mut sw_click = Wav::default();
|
||||||
|
sw_click
|
||||||
|
.load("/home/pjht/projects/altair_emu/resources/click2.wav")
|
||||||
|
.unwrap();
|
||||||
|
sw_click
|
||||||
|
};
|
||||||
|
let slf: AudioThread = Self {
|
||||||
|
fan_startup,
|
||||||
|
fan_loop,
|
||||||
|
fan_shutdown,
|
||||||
|
sw_click,
|
||||||
|
sl,
|
||||||
|
receiver: rx,
|
||||||
|
fan_state: FanState::Off,
|
||||||
|
};
|
||||||
|
slf.run();
|
||||||
|
});
|
||||||
|
tx
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run(mut self) {
|
||||||
|
loop {
|
||||||
|
let Ok(msg) = self.receiver.recv() else {
|
||||||
|
break;
|
||||||
|
};
|
||||||
|
self.handle_msg(&msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle_msg(&mut self, msg: &AudioMessage) {
|
||||||
|
self.mark_fan_off_if_stopped();
|
||||||
|
match msg {
|
||||||
|
AudioMessage::PlaySwitchClick => {
|
||||||
|
self.sl.play(&self.sw_click);
|
||||||
|
}
|
||||||
|
AudioMessage::FanOn => {
|
||||||
|
if self.fan_state.is_starting() || self.fan_state.is_on() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
trace!(target: "altair_emu::fan_thread", "Start fan");
|
||||||
|
let fan_startup_handle = if let FanState::Stopping {
|
||||||
|
start_time,
|
||||||
|
sound_offset,
|
||||||
|
handle,
|
||||||
|
} = self.fan_state
|
||||||
|
{
|
||||||
|
self.sl.stop(handle);
|
||||||
|
let fan_shutdown_elapsed =
|
||||||
|
start_time.elapsed().as_secs_f64() + sound_offset.as_secs_f64();
|
||||||
|
let fan_startup_handle = self.sl.play(&self.fan_startup);
|
||||||
|
self.sl
|
||||||
|
.seek(
|
||||||
|
fan_startup_handle,
|
||||||
|
self.fan_shutdown.length() - fan_shutdown_elapsed,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
self.fan_state = FanState::Starting {
|
||||||
|
start_time: Instant::now(),
|
||||||
|
sound_offset: Duration::from_secs_f64(
|
||||||
|
self.fan_shutdown.length() - fan_shutdown_elapsed,
|
||||||
|
),
|
||||||
|
};
|
||||||
|
fan_startup_handle
|
||||||
|
} else {
|
||||||
|
let fan_startup_handle = self.sl.play(&self.fan_startup);
|
||||||
|
self.fan_state = FanState::Starting {
|
||||||
|
start_time: Instant::now(),
|
||||||
|
sound_offset: Duration::ZERO,
|
||||||
|
};
|
||||||
|
fan_startup_handle
|
||||||
|
};
|
||||||
|
while self.sl.is_valid_voice_handle(fan_startup_handle) {
|
||||||
|
let msg = match self.receiver.try_recv() {
|
||||||
|
Ok(msg) => msg,
|
||||||
|
Err(TryRecvError::Empty) => continue,
|
||||||
|
Err(_) => return,
|
||||||
|
};
|
||||||
|
self.handle_msg(&msg);
|
||||||
|
if msg.is_fan_off() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
std::thread::sleep(std::time::Duration::from_millis(10));
|
||||||
|
}
|
||||||
|
let handle = self.sl.play(&self.fan_loop);
|
||||||
|
self.sl.set_looping(handle, true);
|
||||||
|
self.fan_state = FanState::On;
|
||||||
|
}
|
||||||
|
AudioMessage::FanOff => match self.fan_state {
|
||||||
|
FanState::Off | FanState::Stopping { .. } => return,
|
||||||
|
FanState::Starting {
|
||||||
|
start_time,
|
||||||
|
sound_offset,
|
||||||
|
} => {
|
||||||
|
self.sl.stop_all();
|
||||||
|
let fan_startup_elapsed =
|
||||||
|
start_time.elapsed().as_secs_f64() + sound_offset.as_secs_f64();
|
||||||
|
let fan_shutdown_handle = self.sl.play(&self.fan_shutdown);
|
||||||
|
self.fan_state = FanState::Stopping {
|
||||||
|
start_time: Instant::now(),
|
||||||
|
sound_offset: Duration::from_secs_f64(
|
||||||
|
self.fan_startup.length() - fan_startup_elapsed,
|
||||||
|
),
|
||||||
|
handle: fan_shutdown_handle,
|
||||||
|
};
|
||||||
|
self.sl
|
||||||
|
.seek(
|
||||||
|
fan_shutdown_handle,
|
||||||
|
self.fan_startup.length() - fan_startup_elapsed,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
FanState::On => {
|
||||||
|
self.sl.stop_all();
|
||||||
|
let fan_shutdown_handle = self.sl.play(&self.fan_shutdown);
|
||||||
|
self.fan_state = FanState::Stopping {
|
||||||
|
start_time: Instant::now(),
|
||||||
|
sound_offset: Duration::ZERO,
|
||||||
|
handle: fan_shutdown_handle,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn mark_fan_off_if_stopped(&mut self) {
|
||||||
|
if let FanState::Stopping {
|
||||||
|
start_time,
|
||||||
|
sound_offset,
|
||||||
|
..
|
||||||
|
} = self.fan_state
|
||||||
|
{
|
||||||
|
let fan_shutdown_elapsed =
|
||||||
|
start_time.elapsed().as_secs_f64() + sound_offset.as_secs_f64();
|
||||||
|
if fan_shutdown_elapsed >= self.fan_shutdown.length() {
|
||||||
|
self.fan_state = FanState::Off;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
176
src/main.rs
176
src/main.rs
@ -1,15 +1,12 @@
|
|||||||
|
mod audio;
|
||||||
mod card;
|
mod card;
|
||||||
mod cpu;
|
mod cpu;
|
||||||
mod frontpanel;
|
mod frontpanel;
|
||||||
mod ram;
|
mod ram;
|
||||||
|
|
||||||
use std::{
|
use std::{path::Path, sync::mpsc::Sender};
|
||||||
path::Path,
|
|
||||||
sync::{mpsc::Sender, Arc},
|
|
||||||
thread,
|
|
||||||
time::{Duration, Instant},
|
|
||||||
};
|
|
||||||
|
|
||||||
|
use audio::{AudioMessage, AudioThread};
|
||||||
use cpu::{MemCycle, Status, I8080};
|
use cpu::{MemCycle, Status, I8080};
|
||||||
use device_query::DeviceState;
|
use device_query::DeviceState;
|
||||||
use eframe::{
|
use eframe::{
|
||||||
@ -17,162 +14,19 @@ use eframe::{
|
|||||||
NativeOptions,
|
NativeOptions,
|
||||||
};
|
};
|
||||||
use egui_modal::Modal;
|
use egui_modal::Modal;
|
||||||
use log::{debug, trace, warn};
|
|
||||||
use parking_lot::Mutex;
|
|
||||||
use rand::RngCore;
|
use rand::RngCore;
|
||||||
use rfd::FileDialog;
|
use rfd::FileDialog;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use soloud::{audio, AudioExt, LoadExt, Soloud};
|
|
||||||
|
|
||||||
use crate::frontpanel::{switch, Frontpanel};
|
use crate::frontpanel::{switch, Frontpanel};
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
enum AudioMessage {
|
|
||||||
PlaySwitchClick,
|
|
||||||
}
|
|
||||||
|
|
||||||
fn main() -> Result<(), eframe::Error> {
|
fn main() -> Result<(), eframe::Error> {
|
||||||
env_logger::init();
|
env_logger::init();
|
||||||
let (main_audio_tx, main_audio_rx) = std::sync::mpsc::channel::<AudioMessage>();
|
let audio_tx = AudioThread::init();
|
||||||
let (fan_audio_tx, fan_audio_rx) = std::sync::mpsc::channel::<bool>();
|
|
||||||
let sl = Arc::new(Mutex::new(Soloud::default().unwrap()));
|
|
||||||
let fan_sl = Arc::clone(&sl);
|
|
||||||
thread::spawn(move || {
|
|
||||||
let sl = fan_sl;
|
|
||||||
let rx = fan_audio_rx;
|
|
||||||
let fan_startup = {
|
|
||||||
let mut fan_startup = audio::Wav::default();
|
|
||||||
fan_startup
|
|
||||||
.load("/home/pjht/projects/altair_emu/resources/fan1.wav")
|
|
||||||
.unwrap();
|
|
||||||
fan_startup
|
|
||||||
};
|
|
||||||
let fan_loop = {
|
|
||||||
let mut fan_loop = audio::Wav::default();
|
|
||||||
fan_loop
|
|
||||||
.load("/home/pjht/projects/altair_emu/resources/fan2.wav")
|
|
||||||
.unwrap();
|
|
||||||
fan_loop
|
|
||||||
};
|
|
||||||
let fan_shutdown = {
|
|
||||||
let mut fan_shutdown = audio::Wav::default();
|
|
||||||
fan_shutdown
|
|
||||||
.load("/home/pjht/projects/altair_emu/resources/fan3.wav")
|
|
||||||
.unwrap();
|
|
||||||
fan_shutdown
|
|
||||||
};
|
|
||||||
debug!(target: "altair_emu::fan_thread", "Fan startup length: {}s. Fan shutdown length: {}s", fan_startup.length(), fan_shutdown.length());
|
|
||||||
if fan_startup.length() != fan_shutdown.length() {
|
|
||||||
warn!(target: "altair_emu::fan_thread", "Fan startup sound and shutdown sounds do not have the same length! ({}s and {}s, respectively.) This may cause audio glitches.", fan_startup.length(), fan_shutdown.length());
|
|
||||||
}
|
|
||||||
let mut fan_shutdown_start: Option<(Instant, Duration)> = None;
|
|
||||||
for msg in &rx {
|
|
||||||
if msg {
|
|
||||||
trace!(target: "altair_emu::fan_thread", "Start fan");
|
|
||||||
let fan_startup_start = if let Some(fan_shutdown_start) = fan_shutdown_start.take()
|
|
||||||
{
|
|
||||||
trace!(target: "altair_emu::fan_thread", "Fan shutdown sound playing");
|
|
||||||
let sl = sl.lock();
|
|
||||||
sl.stop_all();
|
|
||||||
let fan_shutdown_elapsed = fan_shutdown_start.0.elapsed().as_secs_f64()
|
|
||||||
+ fan_shutdown_start.1.as_secs_f64();
|
|
||||||
if fan_shutdown_elapsed >= fan_shutdown.length() {
|
|
||||||
trace!(target: "altair_emu::fan_thread", "Shutdown elapsed > shutdown length, not actually playing");
|
|
||||||
let fan_startup_start = (Instant::now(), Duration::ZERO);
|
|
||||||
sl.play(&fan_startup);
|
|
||||||
fan_startup_start
|
|
||||||
} else {
|
|
||||||
trace!(target: "altair_emu::fan_thread", "Fan shutdown sound stopped, elapsed time {}s", fan_shutdown_elapsed);
|
|
||||||
let fan_startup_start = (
|
|
||||||
Instant::now(),
|
|
||||||
Duration::from_secs_f64(fan_shutdown.length() - fan_shutdown_elapsed),
|
|
||||||
);
|
|
||||||
let fan_startup_handle = sl.play(&fan_startup);
|
|
||||||
sl.seek(
|
|
||||||
fan_startup_handle,
|
|
||||||
fan_shutdown.length() - fan_shutdown_elapsed,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
drop(sl);
|
|
||||||
trace!(target: "altair_emu::fan_thread", "Fan startup playing at offset {}s", fan_shutdown.length() - fan_shutdown_elapsed);
|
|
||||||
fan_startup_start
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
trace!(target: "altair_emu::fan_thread", "Fan shutdown sound not playing");
|
|
||||||
let fan_startup_start = (Instant::now(), Duration::ZERO);
|
|
||||||
sl.lock().play(&fan_startup);
|
|
||||||
fan_startup_start
|
|
||||||
};
|
|
||||||
let mut msg = None;
|
|
||||||
while sl.lock().voice_count() > 0 {
|
|
||||||
if let Ok(r_msg) = rx.try_recv() {
|
|
||||||
trace!(target: "altair_emu::fan_thread", "Got message {} while waiting for end of startup sound", r_msg);
|
|
||||||
msg = Some(r_msg);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
std::thread::sleep(std::time::Duration::from_millis(10));
|
|
||||||
}
|
|
||||||
if let Some(msg) = msg {
|
|
||||||
if msg {
|
|
||||||
panic!();
|
|
||||||
} else {
|
|
||||||
trace!(target: "altair_emu::fan_thread", "Stop fan in startup");
|
|
||||||
let sl = sl.lock();
|
|
||||||
sl.stop_all();
|
|
||||||
let fan_startup_elapsed = fan_startup_start.0.elapsed().as_secs_f64()
|
|
||||||
+ fan_startup_start.1.as_secs_f64();
|
|
||||||
trace!(target: "altair_emu::fan_thread", "Fan startup sound stopped, elapsed time {}s", fan_startup_elapsed);
|
|
||||||
fan_shutdown_start = Some((
|
|
||||||
Instant::now(),
|
|
||||||
Duration::from_secs_f64(fan_startup.length() - fan_startup_elapsed),
|
|
||||||
));
|
|
||||||
let fan_shutdown_handle = sl.play(&fan_shutdown);
|
|
||||||
sl.seek(
|
|
||||||
fan_shutdown_handle,
|
|
||||||
fan_startup.length() - fan_startup_elapsed,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
drop(sl);
|
|
||||||
trace!(target: "altair_emu::fan_thread", "Fan shutdown playing at offset {}s", fan_startup.length() - fan_startup_elapsed);
|
|
||||||
}
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
let mut sl = sl.lock();
|
|
||||||
let handle = sl.play(&fan_loop);
|
|
||||||
sl.set_looping(handle, true);
|
|
||||||
drop(sl);
|
|
||||||
trace!(target: "altair_emu::fan_thread", "Fan loop started, start done");
|
|
||||||
} else {
|
|
||||||
trace!(target: "altair_emu::fan_thread", "Stop fan");
|
|
||||||
sl.lock().stop_all();
|
|
||||||
trace!(target: "altair_emu::fan_thread", "Fan sound stopped");
|
|
||||||
fan_shutdown_start = Some((Instant::now(), Duration::ZERO));
|
|
||||||
sl.lock().play(&fan_shutdown);
|
|
||||||
trace!(target: "altair_emu::fan_thread", "Fan shutdown sound playing, stop done");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
thread::spawn(move || {
|
|
||||||
let rx = main_audio_rx;
|
|
||||||
let head_step = {
|
|
||||||
let mut head_step = audio::Wav::default();
|
|
||||||
head_step
|
|
||||||
.load("/home/pjht/projects/altair_emu/resources/click2.wav")
|
|
||||||
.unwrap();
|
|
||||||
head_step
|
|
||||||
};
|
|
||||||
for msg in &rx {
|
|
||||||
match msg {
|
|
||||||
AudioMessage::PlaySwitchClick => {
|
|
||||||
sl.lock().play(&head_step);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
eframe::run_native(
|
eframe::run_native(
|
||||||
"Altair 8800 Emulator",
|
"Altair 8800 Emulator",
|
||||||
NativeOptions::default(),
|
NativeOptions::default(),
|
||||||
Box::new(|cc| Box::new(AltairEmulator::new(cc, main_audio_tx, fan_audio_tx))),
|
Box::new(|cc| Box::new(AltairEmulator::new(cc, audio_tx))),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -201,8 +55,7 @@ struct AltairEmulator {
|
|||||||
mouse_newdown: bool,
|
mouse_newdown: bool,
|
||||||
mouse_olddown: bool,
|
mouse_olddown: bool,
|
||||||
running: bool,
|
running: bool,
|
||||||
main_audio_tx: Sender<AudioMessage>,
|
audio_tx: Sender<AudioMessage>,
|
||||||
fan_audio_tx: Sender<bool>,
|
|
||||||
options: Options,
|
options: Options,
|
||||||
option_window: Option<OptionWindow>,
|
option_window: Option<OptionWindow>,
|
||||||
device_state: DeviceState,
|
device_state: DeviceState,
|
||||||
@ -211,11 +64,7 @@ struct AltairEmulator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl AltairEmulator {
|
impl AltairEmulator {
|
||||||
fn new(
|
fn new(cc: &eframe::CreationContext<'_>, audio_tx: Sender<AudioMessage>) -> Self {
|
||||||
cc: &eframe::CreationContext<'_>,
|
|
||||||
main_audio_tx: Sender<AudioMessage>,
|
|
||||||
fan_audio_tx: Sender<bool>,
|
|
||||||
) -> Self {
|
|
||||||
let options = if cc.storage.unwrap().get_string("options").is_none() {
|
let options = if cc.storage.unwrap().get_string("options").is_none() {
|
||||||
Options { fan_enabled: true }
|
Options { fan_enabled: true }
|
||||||
} else {
|
} else {
|
||||||
@ -272,8 +121,7 @@ impl AltairEmulator {
|
|||||||
mouse_newdown: false,
|
mouse_newdown: false,
|
||||||
mouse_olddown: false,
|
mouse_olddown: false,
|
||||||
running: false,
|
running: false,
|
||||||
main_audio_tx,
|
audio_tx,
|
||||||
fan_audio_tx,
|
|
||||||
options,
|
options,
|
||||||
option_window: None,
|
option_window: None,
|
||||||
device_state: DeviceState::new(),
|
device_state: DeviceState::new(),
|
||||||
@ -735,15 +583,15 @@ impl eframe::App for AltairEmulator {
|
|||||||
// }
|
// }
|
||||||
});
|
});
|
||||||
|
|
||||||
let old_fan_enabled = self.options.fan_enabled;
|
// let old_fan_enabled = self.options.fan_enabled;
|
||||||
if let Some(option_window) = self.option_window.as_mut() {
|
if let Some(option_window) = self.option_window.as_mut() {
|
||||||
if option_window.draw(ctx, &mut self.options) {
|
if option_window.draw(ctx, &mut self.options) {
|
||||||
self.option_window = None;
|
self.option_window = None;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (old_fan_enabled != self.options.fan_enabled) && self.power {
|
// if (old_fan_enabled != self.options.fan_enabled) && self.power {
|
||||||
self.fan_audio_tx.send(self.options.fan_enabled).unwrap();
|
// self.fan_audio_tx.send(self.options.fan_enabled).unwrap();
|
||||||
}
|
// }
|
||||||
if self.running {
|
if self.running {
|
||||||
ctx.request_repaint();
|
ctx.request_repaint();
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user