feat: add card pairing, manager toggle and confirmation sounds

This commit is contained in:
itsscb 2024-10-29 22:36:09 +01:00
parent c390975a8d
commit 56f03fdb25

View File

@ -4,11 +4,15 @@ use std::{
sync::{Arc, Mutex},
};
use crossbeam_channel::Receiver;
use crossbeam_channel::{Receiver, Sender};
use rodio::{Decoder, OutputStream, Sink};
use tracing::{debug, error, info};
use wifi_rs::{prelude::*, WiFi};
use crate::{card::Card, error::Error, library::Library};
use crate::{card::Card, error::Error, library::Library, manager};
static SUCCESS_SOUND: &str = "sounds/positive_confirmation.wav";
static FAILURE_SOUND: &str = "sounds/negative_confirmation.wav";
fn toggle_hotspot(enable: bool) -> Result<(), Error> {
let config = Config {
@ -26,6 +30,42 @@ fn toggle_hotspot(enable: bool) -> Result<(), Error> {
Ok(())
}
fn play_card(card: &Card, sink: &Sink) {
match card {
Card::Play(music_file) => {
play_sound(sink, music_file);
}
Card::Pause => sink.pause(),
Card::Resume => sink.play(),
Card::Next | Card::Previous => sink.stop(),
Card::ToggleHotspot | Card::Shuffle => {}
// TODO: Volume management. Currently the volume is set independetly from the OS which leads to a horrible quality decrease.
Card::VolumeUp => sink.set_volume(sink.volume() + 1.0),
Card::VolumeDown => sink.set_volume(sink.volume() - 1.0),
}
}
fn play_sound(sink: &Sink, file_path: &str) {
let file = match File::open(file_path) {
Ok(file) => file,
Err(err) => {
error!("Failed to open file {file_path}: {err}");
return;
}
};
let file = BufReader::new(file);
let source = match Decoder::new(file) {
Ok(source) => source,
Err(err) => {
error!("Failed to decode file {file_path}: {err}");
return;
}
};
sink.stop();
sink.append(source);
}
/// Runs the service.
///
/// # Arguments
@ -36,57 +76,111 @@ fn toggle_hotspot(enable: bool) -> Result<(), Error> {
/// # Errors
///
/// Returns an error if there is an issue running the service.
pub fn run(rx: &Receiver<Arc<str>>, library: &Arc<Mutex<Library>>) -> Result<(), Error> {
pub fn run(
library: &Arc<Mutex<Library>>,
tx_manager_shutdown: Sender<()>,
rx_manager_shutdown: Receiver<()>,
rx: &Receiver<Arc<str>>,
) -> Result<(), Error> {
let tx_manager_shutdown = Arc::from(tx_manager_shutdown);
let rx_manager_shutdown = Arc::from(rx_manager_shutdown);
let (_stream, stream_handle) = OutputStream::try_default().map_err(Error::from)?;
let sink = Sink::try_new(&stream_handle)?;
let (tx_pairing, rx_pairing): (Sender<()>, Receiver<()>) = crossbeam_channel::bounded(1);
let tx_pairing = Arc::from(tx_pairing);
let mut pairing_cards: Vec<Arc<str>> = vec![];
let mut hotspot_enabled = false;
let mut is_pairing = false;
loop {
match rx.recv() {
match rx_pairing.try_recv() {
Ok(()) => {
is_pairing = !is_pairing;
if is_pairing {
info!("Pairing mode enabled");
pairing_cards.clear();
}
}
Err(e) => match e {
crossbeam_channel::TryRecvError::Empty => {}
crossbeam_channel::TryRecvError::Disconnected => unreachable!(),
},
}
match rx.try_recv() {
Ok(card_id) => {
println!("Card ID: {card_id}");
debug!("Card ID: {card_id}");
let library_lock = library.lock()?;
let mut library_lock = library.lock()?;
let card = library_lock.get(&card_id);
if let Some(music_file) = card {
println!("Playing: {music_file:?}");
info!("Playing: {music_file:?}");
match music_file {
Card::Play(music_file) => {
sink.stop();
let file = BufReader::new(File::open(music_file.as_ref())?);
let source = Decoder::new(file)?;
sink.append(source);
}
Card::Pause => sink.pause(),
Card::Resume => sink.play(),
Card::Next | Card::Previous => sink.stop(),
Card::Shuffle => {
let card = library_lock.get_random();
if let Some(Card::Play(music_file)) = card {
sink.stop();
let file = BufReader::new(File::open(music_file.as_ref())?);
let source = Decoder::new(file)?;
sink.append(source);
if let Some(card) = card {
play_card(card, &sink);
}
}
Card::ToggleHotspot => {
toggle_hotspot(!hotspot_enabled)?;
let msg = if hotspot_enabled { "disable" } else { "enable" };
match toggle_hotspot(!hotspot_enabled) {
Ok(()) => info!("hotspot {msg}d"),
Err(err) => error!("Failed to {msg} hotspot: {err}"),
}
if hotspot_enabled {
match tx_manager_shutdown.send(()) {
Ok(()) => info!("Sent shutdown message"),
Err(err) => error!("Failed to send shutdown message: {err}"),
}
} else {
let tx_pairing_clone = tx_pairing.clone();
let rx_manager_shutdown_clone = rx_manager_shutdown.clone();
std::thread::spawn(move || {
match manager::serve(
"0.0.0.0:8080",
tx_pairing_clone,
rx_manager_shutdown_clone,
) {
Ok(()) => info!("Manager started"),
Err(err) => error!("Failed to start manager: {err}"),
}
});
}
hotspot_enabled = !hotspot_enabled;
}
// TODO: Volume management. Currently the volume is set independetly from the OS which leads to a horrible quality decrease.
Card::VolumeUp => sink.set_volume(sink.volume() + 1.0),
Card::VolumeDown => sink.set_volume(sink.volume() - 1.0),
card => play_card(card, &sink),
}
} else if is_pairing {
info!("Read card: {card_id}");
pairing_cards.push(card_id);
if let Some(most_common_card) = pairing_cards
.iter()
.find(|&card| pairing_cards.iter().filter(|&c| *c == *card).count() >= 3)
{
library_lock.add(most_common_card);
if let Err(err) = library_lock.save_to_file("music.json") {
library_lock.remove(most_common_card);
error!("Failed to save library: {err}");
play_sound(&sink, FAILURE_SOUND);
} else {
play_sound(&sink, SUCCESS_SOUND);
info!("Added card to library: {most_common_card}");
}
is_pairing = !is_pairing;
}
} else {
println!("No music file found for this card");
info!("Unknown card");
}
drop(library_lock);
}
Err(e) => match e {
crossbeam_channel::RecvError => break,
crossbeam_channel::TryRecvError::Empty => continue,
crossbeam_channel::TryRecvError::Disconnected => break,
},
}
}