chore: initial commit

This commit is contained in:
itsscb 2024-10-21 23:20:15 +02:00
parent 69c93a960f
commit dd74d36bbf
11 changed files with 368 additions and 1 deletions

7
.gitignore vendored
View File

@ -18,4 +18,9 @@ Cargo.lock
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
# and can be added to the global gitignore or merged into this file. For a more nuclear
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
#.idea/
#.idea/
*.mp3
*.flac
*.wav

11
Cargo.toml Normal file
View File

@ -0,0 +1,11 @@
[package]
name = "marlinbox-rs"
version = "0.1.0"
edition = "2021"
[dependencies]
rand = "0.8.5"
rodio = "0.19.0"
rusb = "0.9.4"
serde = { version = "1.0.210", features = ["derive", "rc"] }
serde_json = "1.0.132"

96
flake.lock generated Normal file
View File

@ -0,0 +1,96 @@
{
"nodes": {
"flake-utils": {
"inputs": {
"systems": "systems"
},
"locked": {
"lastModified": 1726560853,
"narHash": "sha256-X6rJYSESBVr3hBoH0WbKE5KvhPU5bloyZ2L4K60/fPQ=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "c1dfcf08411b08f6b8615f7d8971a2bfa81d5e8a",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
}
},
"nixpkgs": {
"locked": {
"lastModified": 1729265718,
"narHash": "sha256-4HQI+6LsO3kpWTYuVGIzhJs1cetFcwT7quWCk/6rqeo=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "ccc0c2126893dd20963580b6478d1a10a4512185",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixpkgs-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs_2": {
"locked": {
"lastModified": 1728538411,
"narHash": "sha256-f0SBJz1eZ2yOuKUr5CA9BHULGXVSn6miBuUWdTyhUhU=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "b69de56fac8c2b6f8fd27f2eca01dcda8e0a4221",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixpkgs-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"root": {
"inputs": {
"flake-utils": "flake-utils",
"nixpkgs": "nixpkgs",
"rust-overlay": "rust-overlay"
}
},
"rust-overlay": {
"inputs": {
"nixpkgs": "nixpkgs_2"
},
"locked": {
"lastModified": 1729477859,
"narHash": "sha256-r0VyeJxy4O4CgTB/PNtfQft9fPfN1VuGvnZiCxDArvg=",
"owner": "oxalica",
"repo": "rust-overlay",
"rev": "ada8266712449c4c0e6ee6fcbc442b3c217c79e1",
"type": "github"
},
"original": {
"owner": "oxalica",
"repo": "rust-overlay",
"type": "github"
}
},
"systems": {
"locked": {
"lastModified": 1681028828,
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
"owner": "nix-systems",
"repo": "default",
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
"type": "github"
},
"original": {
"owner": "nix-systems",
"repo": "default",
"type": "github"
}
}
},
"root": "root",
"version": 7
}

57
flake.nix Normal file
View File

@ -0,0 +1,57 @@
{
description = "Example Rust development environment for Zero to Nix";
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
rust-overlay.url = "github:oxalica/rust-overlay";
flake-utils.url = "github:numtide/flake-utils";
};
outputs = { self, nixpkgs, rust-overlay, flake-utils, ... }:
flake-utils.lib.eachDefaultSystem (system:
let
overlays = [ (import rust-overlay) ];
pkgs = import nixpkgs {
inherit system overlays;
};
rustToolchain = pkgs.rust-bin.stable.latest.default.override {
extensions = [ "rust-src" "rust-analyzer" "clippy" "rustfmt" ];
targets = [ "x86_64-unknown-linux-gnu" "wasm32-unknown-unknown" ];
};
in
{
devShells.default = pkgs.mkShell {
buildInputs = with pkgs; [
rustToolchain
trunk
clippy
# tailwindcss
# dioxus-cli
# wasm-bindgen-cli
# cargo-shuttle
cargo-edit
cargo-binstall
bacon
alsa-lib.dev
openssl
pkg-config
];
shellHook = ''
export PATH=${rustToolchain}/bin:$PATH
export RUSTC_VERSION=$(rustc --version)
export RUST_SRC_PATH="${rustToolchain}/lib/rustlib/src/rust/library"
export OPENSSL_DIR="${pkgs.openssl.dev}"
export OPENSSL_LIB_DIR="${pkgs.openssl.out}/lib"
export OPENSSL_INCLUDE_DIR="${pkgs.openssl.dev}/include"
'';
packages = pkgs.lib.optionals pkgs.stdenv.isDarwin (with pkgs; [ libiconv ]);
};
}
);
}

11
music.json Normal file
View File

@ -0,0 +1,11 @@
{
"music": {
"27271E24211E1E26": {
"Play": "02 - Disturbed - Immortalized.mp3"
},
"27271E242121221F": "Shuffle",
"27271E2420222321": {
"Play": "01-13 33 RPM.flac"
}
}
}

25
src/card.rs Normal file
View File

@ -0,0 +1,25 @@
use std::sync::Arc;
use serde::{Deserialize, Serialize};
#[derive(Debug, Serialize, Deserialize, Clone)]
pub enum Card {
Play(Arc<str>),
Pause,
Resume,
Next,
Previous,
Shuffle,
}
impl From<&Card> for Option<Card> {
fn from(card: &Card) -> Self {
Some(card.clone())
}
}
impl<T: AsRef<str>> From<T> for Card {
fn from(music_file: T) -> Self {
Self::Play(Arc::from(music_file.as_ref()))
}
}

3
src/lib.rs Normal file
View File

@ -0,0 +1,3 @@
pub mod card;
pub mod library;
pub mod portal;

47
src/library.rs Normal file
View File

@ -0,0 +1,47 @@
use std::{collections::HashMap, sync::Arc};
use rand::seq::SliceRandom;
use serde::{Deserialize, Serialize};
use crate::card::Card;
#[derive(Debug, Serialize, Deserialize)]
pub struct Library {
music: HashMap<Arc<str>, Option<Card>>,
}
impl Library {
#[must_use]
pub fn new() -> Self {
Self {
music: HashMap::new(),
}
}
pub fn add(&mut self, card_id: &str) {
self.music.insert(card_id.into(), None);
}
pub fn update(&mut self, card_id: &str, music_file: Option<Card>) {
self.music
.insert(card_id.into(), music_file.map(Into::into));
}
#[must_use]
pub fn get(&self, card_id: &str) -> Option<&Card> {
self.music.get::<str>(card_id).unwrap_or(&None).as_ref()
}
#[must_use]
pub fn get_random(&self) -> Option<&Card> {
let play_cards: Vec<&Card> = self.music.values().flatten().collect();
play_cards.choose(&mut rand::thread_rng()).copied()
}
}
impl Default for Library {
fn default() -> Self {
Self::new()
}
}

111
src/main.rs Normal file
View File

@ -0,0 +1,111 @@
use marlinbox_rs::{card::Card, library::Library};
use rodio::{Decoder, OutputStream, Sink};
use rusb::{Context, DeviceHandle, UsbContext};
use std::{fs::File, io::BufReader, time::Duration};
const VID: u16 = 0xffff; // Replace with your device's Vendor ID
const PID: u16 = 0x0035; // Replace with your device's Product ID
fn extract_card_id(buf: &[u8]) -> Option<String> {
let significant_indices = [2, 18, 34, 50, 66, 82, 98, 114];
let extracted: Vec<u8> = significant_indices
.iter()
.filter_map(|&i| buf.get(i).copied())
.collect();
if extracted.len() == 8 && extracted.iter().all(|&b| b != 0) {
Some(extracted.iter().fold(String::new(), |mut acc, &b| {
acc.push_str(&format!("{b:02X}"));
acc
}))
} else {
None
}
}
fn main() -> Result<(), Box<dyn std::error::Error>> {
let (_stream, stream_handle) = OutputStream::try_default().unwrap();
let sink = Sink::try_new(&stream_handle).unwrap();
let music: Library = serde_json::from_reader(File::open("music.json")?)?;
// let mut music = Library::new();
// music.update(
// "27271E24211E1E26",
// Some(Card::Play("02 - Disturbed - Immortalized.mp3".into())),
// );
// music.update(
// "27271E242121221F",
// Some(Card::Play(
// "11 - Disturbed - The Sound Of Silence.mp3".into(),
// )),
// );
// music.update("27271E2420222321", Card::Shuffle.into());
// let music_json = serde_json::to_string_pretty(&music)?;
// fs::write("music.json", music_json)?;
let context = Context::new()?;
let handle: DeviceHandle<Context> = context
.open_device_with_vid_pid(VID, PID)
.ok_or("Device not found")?;
#[cfg(target_os = "linux")]
{
if handle.kernel_driver_active(0)? {
handle.detach_kernel_driver(0)?;
}
}
handle.claim_interface(0)?;
println!("Starting to read RFID cards...");
let mut buf = [0u8; 120];
// let mut last_id: Option<String> = None;
loop {
match handle.read_interrupt(0x81, &mut buf, Duration::from_millis(1000)) {
Ok(len) => {
if len == 120 {
if let Some(card_id) = extract_card_id(&buf) {
// if last_id.as_ref() != Some(&card_id) {
println!("Card ID: {card_id}");
if let Some(music_file) = music.get(card_id.as_ref()) {
println!("Playing: {music_file:?}");
match music_file {
Card::Play(music_file) => {
sink.stop();
let file =
BufReader::new(File::open(music_file.as_ref()).unwrap());
let source = Decoder::new(file).unwrap();
sink.append(source);
}
Card::Pause => sink.pause(),
Card::Resume => sink.play(),
Card::Next | Card::Previous => sink.stop(),
Card::Shuffle => {
if let Some(Card::Play(music_file)) = music.get_random() {
sink.stop();
let file = BufReader::new(
File::open(music_file.as_ref()).unwrap(),
);
let source = Decoder::new(file).unwrap();
sink.append(source);
}
}
}
} else {
println!("No music file found for this card");
}
}
}
}
Err(rusb::Error::Timeout) => {
// last_id = None;
}
Err(e) => eprintln!("Error: {e:?}"),
}
}
}

1
src/portal/mod.rs Normal file
View File

@ -0,0 +1 @@
pub mod server;

0
src/portal/server.rs Normal file
View File