generated from GarandPLG/rust-flake-template
Add audio subsystem with mute and volume controls
Update flake.lock dependencies to latest revisions
This commit is contained in:
Generated
+12
-12
@@ -8,11 +8,11 @@
|
|||||||
"rust-analyzer-src": "rust-analyzer-src"
|
"rust-analyzer-src": "rust-analyzer-src"
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1762929886,
|
"lastModified": 1775462255,
|
||||||
"narHash": "sha256-TQZ3Ugb1FoHpTSc8KLrzN4njIZU4FemAMHyS4M3mt6s=",
|
"narHash": "sha256-YRzdvh6nvMebcgO2nDpr8dqVwKHpp1BBRUHeMxX9UAY=",
|
||||||
"owner": "nix-community",
|
"owner": "nix-community",
|
||||||
"repo": "fenix",
|
"repo": "fenix",
|
||||||
"rev": "6998514dce2c365142a0a119a95ef95d89b84086",
|
"rev": "f90343f1ed330243d4bbdbce51acbd93b776a797",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -31,11 +31,11 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1752689277,
|
"lastModified": 1769799857,
|
||||||
"narHash": "sha256-uldUBFkZe/E7qbvxa3mH1ItrWZyT6w1dBKJQF/3ZSsc=",
|
"narHash": "sha256-88IFXZ7Sa1vxbz5pty0Io5qEaMQMMUPMonLa3Ls/ss4=",
|
||||||
"owner": "nix-community",
|
"owner": "nix-community",
|
||||||
"repo": "naersk",
|
"repo": "naersk",
|
||||||
"rev": "0e72363d0938b0208d6c646d10649164c43f4d64",
|
"rev": "9d4ed44d8b8cecdceb1d6fd76e74123d90ae6339",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -46,11 +46,11 @@
|
|||||||
},
|
},
|
||||||
"nixpkgs": {
|
"nixpkgs": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1762844143,
|
"lastModified": 1775036866,
|
||||||
"narHash": "sha256-SlybxLZ1/e4T2lb1czEtWVzDCVSTvk9WLwGhmxFmBxI=",
|
"narHash": "sha256-ZojAnPuCdy657PbTq5V0Y+AHKhZAIwSIT2cb8UgAz/U=",
|
||||||
"owner": "nixos",
|
"owner": "nixos",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"rev": "9da7f1cf7f8a6e2a7cb3001b048546c92a8258b4",
|
"rev": "6201e203d09599479a3b3450ed24fa81537ebc4e",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -70,11 +70,11 @@
|
|||||||
"rust-analyzer-src": {
|
"rust-analyzer-src": {
|
||||||
"flake": false,
|
"flake": false,
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1762860488,
|
"lastModified": 1775429583,
|
||||||
"narHash": "sha256-rMfWMCOo/pPefM2We0iMBLi2kLBAnYoB9thi4qS7uk4=",
|
"narHash": "sha256-bFC/p7Ywyd9QIr9DbU3Q75c7AcaCm9wVmEvcI3702cY=",
|
||||||
"owner": "rust-lang",
|
"owner": "rust-lang",
|
||||||
"repo": "rust-analyzer",
|
"repo": "rust-analyzer",
|
||||||
"rev": "2efc80078029894eec0699f62ec8d5c1a56af763",
|
"rev": "38fb8f92ac15853d7fa9fb47fc2d81fdd5cd6c7e",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
|
|||||||
@@ -48,7 +48,10 @@
|
|||||||
};
|
};
|
||||||
|
|
||||||
devShells.${system}.default = pkgs.mkShell {
|
devShells.${system}.default = pkgs.mkShell {
|
||||||
env.RUST_SRC_PATH = "${pkgs.rust.packages.stable.rustPlatform.rustLibSrc}";
|
env = {
|
||||||
|
RUST_SRC_PATH = "${pkgs.rust.packages.stable.rustPlatform.rustLibSrc}";
|
||||||
|
LD_LIBRARY_PATH = pkgs.lib.makeLibraryPath (with pkgs; [alsa-lib]);
|
||||||
|
};
|
||||||
buildInputs = with pkgs; [
|
buildInputs = with pkgs; [
|
||||||
rustToolchain
|
rustToolchain
|
||||||
alsa-lib
|
alsa-lib
|
||||||
|
|||||||
Binary file not shown.
+9
-3
@@ -1,11 +1,15 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
app::{GameStates, handle_keybindings, threads::AppEvent, view::View},
|
app::{
|
||||||
|
GameStates, handle_keybindings,
|
||||||
|
threads::{AppEvent, AudioCmd},
|
||||||
|
view::View,
|
||||||
|
},
|
||||||
cli::Cli,
|
cli::Cli,
|
||||||
};
|
};
|
||||||
use ratatui::{DefaultTerminal, Frame, crossterm::event::KeyEvent, layout::Rect};
|
use ratatui::{DefaultTerminal, Frame, crossterm::event::KeyEvent, layout::Rect};
|
||||||
use std::{
|
use std::{
|
||||||
io::Result,
|
io::Result,
|
||||||
sync::mpsc::{Receiver, RecvTimeoutError},
|
sync::mpsc::{Receiver, RecvTimeoutError, Sender},
|
||||||
time::Duration,
|
time::Duration,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -15,16 +19,18 @@ pub struct App {
|
|||||||
pub window_area: Rect,
|
pub window_area: Rect,
|
||||||
pub args: Cli,
|
pub args: Cli,
|
||||||
pub states: Option<GameStates>,
|
pub states: Option<GameStates>,
|
||||||
|
pub audio_tx: Sender<AudioCmd>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl App {
|
impl App {
|
||||||
pub fn new(args: Cli) -> Self {
|
pub fn new(args: Cli, audio_tx: Sender<AudioCmd>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
exit: false,
|
exit: false,
|
||||||
view: args.view,
|
view: args.view,
|
||||||
window_area: Rect::default(),
|
window_area: Rect::default(),
|
||||||
args: args,
|
args: args,
|
||||||
states: None,
|
states: None,
|
||||||
|
audio_tx,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
use crate::app::{
|
use crate::app::{
|
||||||
App, View,
|
App, View,
|
||||||
keybindings::{Action, event_to_action},
|
keybindings::{Action, event_to_action},
|
||||||
|
threads::AudioCmd,
|
||||||
};
|
};
|
||||||
use ratatui::crossterm::event::KeyEvent;
|
use ratatui::crossterm::event::KeyEvent;
|
||||||
|
|
||||||
@@ -8,6 +9,9 @@ pub fn common_keybindings(app: &mut App, action: Action) {
|
|||||||
match action {
|
match action {
|
||||||
Action::Quit | Action::Quit2 => app.exit = true,
|
Action::Quit | Action::Quit2 => app.exit = true,
|
||||||
Action::Esc => app.view = View::MainMenu,
|
Action::Esc => app.view = View::MainMenu,
|
||||||
|
Action::Mute => if let Err(_) = app.audio_tx.send(AudioCmd::Mute) {},
|
||||||
|
Action::VolumeUp => if let Err(_) = app.audio_tx.send(AudioCmd::VolumeUp) {},
|
||||||
|
Action::VolumeDown => if let Err(_) = app.audio_tx.send(AudioCmd::VolumeDown) {},
|
||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -41,6 +41,12 @@ pub enum Action {
|
|||||||
ZoomIn,
|
ZoomIn,
|
||||||
/// Zoom the view out.
|
/// Zoom the view out.
|
||||||
ZoomOut,
|
ZoomOut,
|
||||||
|
/// Mute music.
|
||||||
|
Mute,
|
||||||
|
/// Volume up.
|
||||||
|
VolumeUp,
|
||||||
|
/// Volume down.
|
||||||
|
VolumeDown,
|
||||||
/// Matches any character key; the inner `char` is the actual key pressed.
|
/// Matches any character key; the inner `char` is the actual key pressed.
|
||||||
WildCard(char),
|
WildCard(char),
|
||||||
}
|
}
|
||||||
@@ -61,6 +67,8 @@ pub enum Group {
|
|||||||
Input,
|
Input,
|
||||||
/// Zoom related bindings.
|
/// Zoom related bindings.
|
||||||
Zoom,
|
Zoom,
|
||||||
|
/// Music related bindings.
|
||||||
|
Music,
|
||||||
/// Quit related bindings.
|
/// Quit related bindings.
|
||||||
Quit,
|
Quit,
|
||||||
}
|
}
|
||||||
@@ -236,6 +244,33 @@ pub static KEYBINDINGS: &[KeyBinding] = &[
|
|||||||
symbol: ".",
|
symbol: ".",
|
||||||
description: "Zoom out",
|
description: "Zoom out",
|
||||||
},
|
},
|
||||||
|
KeyBinding {
|
||||||
|
action: Action::Mute,
|
||||||
|
code: KeyCode::Char('m'),
|
||||||
|
kind: KeyEventKind::Press,
|
||||||
|
modifiers: KeyModifiers::NONE,
|
||||||
|
group: Group::Music,
|
||||||
|
symbol: "m",
|
||||||
|
description: "Mute music",
|
||||||
|
},
|
||||||
|
KeyBinding {
|
||||||
|
action: Action::VolumeUp,
|
||||||
|
code: KeyCode::Char('b'),
|
||||||
|
kind: KeyEventKind::Press,
|
||||||
|
modifiers: KeyModifiers::NONE,
|
||||||
|
group: Group::Music,
|
||||||
|
symbol: "b",
|
||||||
|
description: "Increase music volume",
|
||||||
|
},
|
||||||
|
KeyBinding {
|
||||||
|
action: Action::VolumeDown,
|
||||||
|
code: KeyCode::Char('n'),
|
||||||
|
kind: KeyEventKind::Press,
|
||||||
|
modifiers: KeyModifiers::NONE,
|
||||||
|
group: Group::Music,
|
||||||
|
symbol: "n",
|
||||||
|
description: "Decrease music volume",
|
||||||
|
},
|
||||||
KeyBinding {
|
KeyBinding {
|
||||||
action: Action::WildCard('_'),
|
action: Action::WildCard('_'),
|
||||||
code: KeyCode::Char('_'),
|
code: KeyCode::Char('_'),
|
||||||
|
|||||||
@@ -0,0 +1,80 @@
|
|||||||
|
use rodio::{Decoder, DeviceSinkBuilder, MixerDeviceSink, Player, Source, source::Amplify};
|
||||||
|
use std::{fs::File, io::BufReader, sync::mpsc::Receiver};
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum AudioCmd {
|
||||||
|
Mute,
|
||||||
|
VolumeUp,
|
||||||
|
VolumeDown,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum SoundrackParts {
|
||||||
|
Calm,
|
||||||
|
Buildup,
|
||||||
|
Assult,
|
||||||
|
Outro,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn load_audio(part: SoundrackParts) -> Amplify<Decoder<BufReader<File>>> {
|
||||||
|
let file: File = match part {
|
||||||
|
SoundrackParts::Calm => File::open("soundtrack/default/test.ogg").expect("open audio file"),
|
||||||
|
SoundrackParts::Buildup => {
|
||||||
|
File::open("soundtrack/default/test.ogg").expect("open audio file")
|
||||||
|
}
|
||||||
|
SoundrackParts::Assult => {
|
||||||
|
File::open("soundtrack/default/test.ogg").expect("open audio file")
|
||||||
|
}
|
||||||
|
SoundrackParts::Outro => {
|
||||||
|
File::open("soundtrack/default/test.ogg").expect("open audio file")
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Decoder::try_from(file)
|
||||||
|
.expect("decoder issue")
|
||||||
|
.amplify(0.20)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn handle_audio(rx: Receiver<AudioCmd>, mute: bool) {
|
||||||
|
let mut handle: MixerDeviceSink =
|
||||||
|
DeviceSinkBuilder::open_default_sink().expect("open default audio stream");
|
||||||
|
|
||||||
|
handle.log_on_drop(false);
|
||||||
|
|
||||||
|
let player: Player = Player::connect_new(&handle.mixer());
|
||||||
|
|
||||||
|
let mut volume: f32 = player.volume();
|
||||||
|
|
||||||
|
if mute {
|
||||||
|
player.set_volume(0.0);
|
||||||
|
} else {
|
||||||
|
player.set_volume(volume);
|
||||||
|
}
|
||||||
|
|
||||||
|
player.append(load_audio(SoundrackParts::Calm));
|
||||||
|
|
||||||
|
loop {
|
||||||
|
if player.empty() {
|
||||||
|
player.append(load_audio(SoundrackParts::Calm));
|
||||||
|
}
|
||||||
|
|
||||||
|
for cmd in rx.try_iter() {
|
||||||
|
match cmd {
|
||||||
|
AudioCmd::Mute => {
|
||||||
|
if player.volume() == 0.0 {
|
||||||
|
player.set_volume(volume);
|
||||||
|
} else {
|
||||||
|
player.set_volume(0.0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
AudioCmd::VolumeUp => {
|
||||||
|
volume = (volume + 0.1).min(1.0);
|
||||||
|
player.set_volume(volume);
|
||||||
|
}
|
||||||
|
AudioCmd::VolumeDown => {
|
||||||
|
volume = (volume - 0.1).max(0.0);
|
||||||
|
player.set_volume(volume);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,5 +1,7 @@
|
|||||||
|
pub mod audio;
|
||||||
pub mod events;
|
pub mod events;
|
||||||
pub mod handle_events;
|
pub mod handle_events;
|
||||||
|
|
||||||
|
pub use audio::{AudioCmd, SoundrackParts, handle_audio};
|
||||||
pub use events::AppEvent;
|
pub use events::AppEvent;
|
||||||
pub use handle_events::handle_events;
|
pub use handle_events::handle_events;
|
||||||
|
|||||||
@@ -17,6 +17,9 @@ const ACTIONS: &[Action] = &[
|
|||||||
Action::Up,
|
Action::Up,
|
||||||
Action::Down,
|
Action::Down,
|
||||||
Action::Space,
|
Action::Space,
|
||||||
|
Action::VolumeUp,
|
||||||
|
Action::VolumeDown,
|
||||||
|
Action::Mute,
|
||||||
Action::Quit,
|
Action::Quit,
|
||||||
Action::Quit2,
|
Action::Quit2,
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -23,6 +23,9 @@ const ACTIONS: &[Action] = &[
|
|||||||
Action::ScrollRight,
|
Action::ScrollRight,
|
||||||
Action::ZoomIn,
|
Action::ZoomIn,
|
||||||
Action::ZoomOut,
|
Action::ZoomOut,
|
||||||
|
Action::VolumeUp,
|
||||||
|
Action::VolumeDown,
|
||||||
|
Action::Mute,
|
||||||
Action::Quit,
|
Action::Quit,
|
||||||
Action::Quit2,
|
Action::Quit2,
|
||||||
Action::Esc,
|
Action::Esc,
|
||||||
|
|||||||
@@ -140,6 +140,14 @@ pub struct Cli {
|
|||||||
default_value_t = false
|
default_value_t = false
|
||||||
)]
|
)]
|
||||||
pub log: bool,
|
pub log: bool,
|
||||||
|
|
||||||
|
/// Mute soundtrack (default: disabled).
|
||||||
|
#[arg(
|
||||||
|
long,
|
||||||
|
help = "Mute soundtrack (default: disabled)",
|
||||||
|
default_value_t = false
|
||||||
|
)]
|
||||||
|
pub mute: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parses the command line arguments and returns a fully populated `Cli`
|
/// Parses the command line arguments and returns a fully populated `Cli`
|
||||||
|
|||||||
+26
-7
@@ -2,12 +2,15 @@ use ratatui::{Terminal, prelude::CrosstermBackend};
|
|||||||
use std::{
|
use std::{
|
||||||
io::{Result, Stdout},
|
io::{Result, Stdout},
|
||||||
sync::mpsc::channel,
|
sync::mpsc::channel,
|
||||||
thread::{self, JoinHandle},
|
thread::{
|
||||||
|
self,
|
||||||
|
// JoinHandle
|
||||||
|
},
|
||||||
};
|
};
|
||||||
use war_in_tunnels::{
|
use war_in_tunnels::{
|
||||||
app::{
|
app::{
|
||||||
App,
|
App,
|
||||||
threads::{AppEvent, handle_events},
|
threads::{AppEvent, AudioCmd, handle_audio, handle_events},
|
||||||
},
|
},
|
||||||
cli::{Cli, get_args},
|
cli::{Cli, get_args},
|
||||||
logs::init_logger,
|
logs::init_logger,
|
||||||
@@ -24,19 +27,35 @@ fn main() -> Result<()> {
|
|||||||
init_logger();
|
init_logger();
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut terminal: Terminal<CrosstermBackend<Stdout>> = ratatui::init();
|
|
||||||
let mut app: App = App::new(args);
|
|
||||||
|
|
||||||
let (app_event_tx, app_event_rx) = channel::<AppEvent>();
|
let (app_event_tx, app_event_rx) = channel::<AppEvent>();
|
||||||
|
|
||||||
let app_event_thread: JoinHandle<()> = thread::spawn(move || {
|
// let app_event_thread: JoinHandle<()> = thread::spawn(move || {
|
||||||
|
// handle_events(app_event_tx);
|
||||||
|
// });
|
||||||
|
|
||||||
|
thread::spawn(move || {
|
||||||
handle_events(app_event_tx);
|
handle_events(app_event_tx);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
let (audio_tx, audio_rx) = channel::<AudioCmd>();
|
||||||
|
|
||||||
|
// let audio_event_thread: JoinHandle<()> = thread::spawn(move || {
|
||||||
|
// handle_audio(audio_rx);
|
||||||
|
// });
|
||||||
|
|
||||||
|
thread::spawn(move || {
|
||||||
|
handle_audio(audio_rx, args.mute);
|
||||||
|
});
|
||||||
|
|
||||||
|
let mut terminal: Terminal<CrosstermBackend<Stdout>> = ratatui::init();
|
||||||
|
let mut app: App = App::new(args, audio_tx);
|
||||||
|
|
||||||
let app_result: Result<()> = app.run(&mut terminal, app_event_rx);
|
let app_result: Result<()> = app.run(&mut terminal, app_event_rx);
|
||||||
|
|
||||||
ratatui::restore();
|
ratatui::restore();
|
||||||
|
|
||||||
let _ = app_event_thread.join();
|
// let _ = app_event_thread.;
|
||||||
|
// let _ = audio_event_thread.join();
|
||||||
|
|
||||||
app_result
|
app_result
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user