chore: initial commit
This commit is contained in:
parent
69c93a960f
commit
dd74d36bbf
7
.gitignore
vendored
7
.gitignore
vendored
@ -18,4 +18,9 @@ Cargo.lock
|
|||||||
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
|
# 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
|
# 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.
|
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
||||||
#.idea/
|
#.idea/
|
||||||
|
|
||||||
|
|
||||||
|
*.mp3
|
||||||
|
*.flac
|
||||||
|
*.wav
|
11
Cargo.toml
Normal file
11
Cargo.toml
Normal 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
96
flake.lock
generated
Normal 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
57
flake.nix
Normal 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
11
music.json
Normal 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
25
src/card.rs
Normal 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
3
src/lib.rs
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
pub mod card;
|
||||||
|
pub mod library;
|
||||||
|
pub mod portal;
|
47
src/library.rs
Normal file
47
src/library.rs
Normal 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
111
src/main.rs
Normal 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
1
src/portal/mod.rs
Normal file
@ -0,0 +1 @@
|
|||||||
|
pub mod server;
|
0
src/portal/server.rs
Normal file
0
src/portal/server.rs
Normal file
Loading…
x
Reference in New Issue
Block a user