Giter Site home page Giter Site logo

ritiek / piano-rs Goto Github PK

View Code? Open in Web Editor NEW
218.0 4.0 23.0 3.95 MB

A multiplayer piano using UDP sockets that can be played using computer keyboard, in the terminal

License: MIT License

Rust 100.00%
music piano terminal cli notes network udp-sockets multiplayer

piano-rs's People

Contributors

alex-pk avatar andy5995 avatar dependabot[bot] avatar flappybug avatar jsomedon avatar ritiek avatar stuarth avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

piano-rs's Issues

Support playback from MIDI format

It might be possible to use rimd crate to parse .mid files and play them in piano-rs.

Basic rimd usage that could be handy to us:

extern crate rimd;

use rimd::SMF;
use std::path::Path;

let midi = SMF::from_file(Path::new("/path/to/example.mid")).unwrap();
for track in &midi.tracks {
    for k in &track.events {
        println!("{:?}", k);
    }
}

allow custom key bindings via a config file

piano-rs/src/game/notes.rs

Lines 94 to 165 in 2997bee

pub fn key_to_base_note(mut key: KeyEvent, sequence: i8) -> Option<String> {
let mut offset: i8 = 0;
let keys = ['z', 's', 'x', 'c', 'f', 'v', 'g', 'b', 'n',
'j', 'm', 'k', '1', ',', 'q', 'l', '2', '.',
'w', '/', 'e', '\'', '4', 'r', '5', 't', 'y',
'7', 'u', '8', 'i', '9', 'o', 'p', '[', ']', 'a'];
let base_sounds = ["a", "as", "b", "c", "cs", "d", "ds", "e", "f",
"fs", "g", "gs", "gs", "a", "a", "as", "as", "b",
"b", "c", "c", "cs", "cs", "d", "ds", "e", "f",
"fs", "g", "gs", "a", "as", "b", "c", "d", "e", "gs"];
let factors = [-1, -1, -1, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 2, 2, 2, -1];
let special = ['!', '@', '$', '%', '&', '*', '(', '"', '<',
'>', '?', '{', '}'];
let special_matches = ['1', '2', '4', '5', '7', '8', '9', '\'', ',',
'.', '/', '[', ']'];
// Handle terminal control characters
if key == KeyEvent::Enter {
// Ctrl+m sends Enter in terminal
key = KeyEvent::Ctrl('m');
} else if key == KeyEvent::Tab {
// Ctrl+i sends Tab in terminal
key = KeyEvent::Ctrl('i');
}
// Translate Ctrl+<character> to <character>
if let KeyEvent::Ctrl(c) = key {
key = KeyEvent::Char(c);
offset -= 1;
}
// Increment `offset` if key was shift prefixed (Shift+<character>)
let note: Option<String> = if let KeyEvent::Char(mut c) = key {
if c.is_uppercase() {
c = c.to_ascii_lowercase();
offset += 1;
} else if special.contains(&c) {
let j = special.iter()
.position(|&key| key == c)
.unwrap();
c = special_matches[j];
offset += 1;
}
if let Some(i) = keys.iter().position(|&key| key == c) {
let factor = factors[i];
let base_note = format!("{}{}",
base_sounds[i].to_string(),
offset + factor + sequence
);
Some(base_note)
} else {
None
}
} else {
None
};
note
}

  • toml - much more human-friendly config format, I mean not such easy to break like yaml
  • OnceLock for once init of key bind map; may not need to be thread-safe?
  • FxHashMap for keymapping

related issue: #23

Leave piano mark for specific duration

Instead of clearing the previous red mark (indicating the piano key pressed) on every consecutive key press, it would be nice to have every key show the red mark for a specific duration (maybe 2 secs would be enough) without clearing the previous the mark.

This feature will probably involve multi-threading. RustBox must be wrapped in a Mutex to pass between threads.

Compilation failed: Fix by installing pkg-config

error: failed to run custom build command for `alsa-sys v0.1.2`

Caused by:
  process didn't exit successfully: `/home/ubuntu/piano-rs/target/release/build/alsa-sys-d70e4f27cbae452a/build-script-build` (exit status: 101)
  --- stderr
  thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value: "Failed to run `\"pkg-config\" \"--libs\" \"--cflags\" \"alsa\"`: No such file or directory (os error 2)"', /home/ubuntu/.cargo/registry/src/github.com-1ecc6299db9ec823/alsa-sys-0.1.2/build.rs:4:38
  note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
warning: build failed, waiting for other jobs to finish...
error: build failed

Fixed it by installing:

sudo apt install pkg-config

error: failed to run custom build command for `termbox-sys v0.2.10

Got this error during build:

error: failed to run custom build command for `termbox-sys v0.2.10`
process didn't exit successfully: `/home/shong/Developer/piano-rs/target/release/build/termbox-sys-564ea512af8dd516/build-script-build` (exit code: 101)
--- stdout
running: "git" "clone" "https://github.com/nsf/termbox" ".termbox"
waf configure: setting CFLAGS to: `-m64 -fPIC`
running: "./waf" "configure" "--prefix=/"
Traceback (most recent call last):
  File "/home/shong/.cargo/registry/src/github.com-1ecc6299db9ec823/termbox-sys-0.2.10/.termbox/.waf3-1.8.0-5c05be2d0e42d5c2c81403948a2d295b/waflib/Node.py", line 285, in ant_iter
    raise StopIteration
StopIteration

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "/home/shong/.cargo/registry/src/github.com-1ecc6299db9ec823/termbox-sys-0.2.10/.termbox/.waf3-1.8.0-5c05be2d0e42d5c2c81403948a2d295b/waflib/Scripting.py", line 98, in waf_entry_point
    run_commands()
  File "/home/shong/.cargo/registry/src/github.com-1ecc6299db9ec823/termbox-sys-0.2.10/.termbox/.waf3-1.8.0-5c05be2d0e42d5c2c81403948a2d295b/waflib/Scripting.py", line 155, in run_commands
    parse_options()
  File "/home/shong/.cargo/registry/src/github.com-1ecc6299db9ec823/termbox-sys-0.2.10/.termbox/.waf3-1.8.0-5c05be2d0e42d5c2c81403948a2d295b/waflib/Scripting.py", line 128, in parse_options
    Context.create_context('options').execute()
  File "/home/shong/.cargo/registry/src/github.com-1ecc6299db9ec823/termbox-sys-0.2.10/.termbox/.waf3-1.8.0-5c05be2d0e42d5c2c81403948a2d295b/waflib/Options.py", line 138, in execute
    super(OptionsContext,self).execute()
  File "/home/shong/.cargo/registry/src/github.com-1ecc6299db9ec823/termbox-sys-0.2.10/.termbox/.waf3-1.8.0-5c05be2d0e42d5c2c81403948a2d295b/waflib/Context.py", line 92, in execute
    self.recurse([os.path.dirname(g_module.root_path)])
  File "/home/shong/.cargo/registry/src/github.com-1ecc6299db9ec823/termbox-sys-0.2.10/.termbox/.waf3-1.8.0-5c05be2d0e42d5c2c81403948a2d295b/waflib/Context.py", line 133, in recurse
    user_function(self)
  File "/home/shong/.cargo/registry/src/github.com-1ecc6299db9ec823/termbox-sys-0.2.10/.termbox/wscript", line 11, in options
    opt.load('compiler_c')
  File "/home/shong/.cargo/registry/src/github.com-1ecc6299db9ec823/termbox-sys-0.2.10/.termbox/.waf3-1.8.0-5c05be2d0e42d5c2c81403948a2d295b/waflib/Context.py", line 89, in load
    fun(self)
  File "/home/shong/.cargo/registry/src/github.com-1ecc6299db9ec823/termbox-sys-0.2.10/.termbox/.waf3-1.8.0-5c05be2d0e42d5c2c81403948a2d295b/waflib/Tools/compiler_c.py", line 36, in options
    opt.load_special_tools('c_*.py',ban=['c_dumbpreproc.py'])
  File "/home/shong/.cargo/registry/src/github.com-1ecc6299db9ec823/termbox-sys-0.2.10/.termbox/.waf3-1.8.0-5c05be2d0e42d5c2c81403948a2d295b/waflib/Context.py", line 296, in load_special_tools
    lst=self.root.find_node(waf_dir).find_node('waflib/extras').ant_glob(var)
  File "/home/shong/.cargo/registry/src/github.com-1ecc6299db9ec823/termbox-sys-0.2.10/.termbox/.waf3-1.8.0-5c05be2d0e42d5c2c81403948a2d295b/waflib/Node.py", line 334, in ant_glob
    ret=[x for x in self.ant_iter(accept=accept,pats=[to_pat(incl),to_pat(excl)],maxdepth=kw.get('maxdepth',25),dir=dir,src=src,remove=kw.get('remove',True))]
  File "/home/shong/.cargo/registry/src/github.com-1ecc6299db9ec823/termbox-sys-0.2.10/.termbox/.waf3-1.8.0-5c05be2d0e42d5c2c81403948a2d295b/waflib/Node.py", line 334, in <listcomp>
    ret=[x for x in self.ant_iter(accept=accept,pats=[to_pat(incl),to_pat(excl)],maxdepth=kw.get('maxdepth',25),dir=dir,src=src,remove=kw.get('remove',True))]
RuntimeError: generator raised StopIteration

--- stderr
Cloning into '.termbox'...
thread 'main' panicked at 'assertion failed: cmd.stdout(Stdio::inherit()).stderr(Stdio::inherit()).status().unwrap().success()', /home/shong/.cargo/registry/src/github.com-1ecc6299db9ec823/termbox-sys-0.2.10/build.rs:87:5
note: Run with `RUST_BACKTRACE=1` for a backtrace.

warning: build failed, waiting for other jobs to finish...
error: build failed

Display alphabet on the keys

Having an option to display corresponding alphabet on the keys will make it easier for new comers to understand how the piano is mapped.

Multiplayer support?

It would no doubt be very interesting to play piano simultaneously with other people on the LAN. I am not sure at the moment how to proceed with this, any security issues that may arise, etc.

At the moment, an alternative can be to use a terminal multiplexer like screen or tmux to share sessions between users.

Adjust piano volume internally

Would be useful if there is something else the user wants to listen to while playing the piano. I think + and - would do.

panicked at 'called Result::unwrap() on an Err

I cloned from git, built with cargo --build.

  • OS: Debian 10
  • Rust version: rustc 1.47.0 (18bf6b4f0 2020-10-07)

I got a short error message each time I ran the program (reproduced 3 times) after pressing keys at random, one at a time. So I ran it using RUST_BACKTRACE=1 and got some extra detail.

thread '' panicked at 'called Result::unwrap() on an Err value: Custom("invalid value: integer 4169265872, expected variant index 0 <= i < 5")', src/network/receiver.rs:25:69
stack backtrace:
0: rust_begin_unwind
at /rustc/18bf6b4f01a6feaf7259ba7cdae58031af1b7b39/library/std/src/panicking.rs:475
1: core::panicking::panic_fmt
at /rustc/18bf6b4f01a6feaf7259ba7cdae58031af1b7b39/library/core/src/panicking.rs:85
2: core::option::expect_none_failed
at /rustc/18bf6b4f01a6feaf7259ba7cdae58031af1b7b39/library/core/src/option.rs:1221
3: core::result::Result<T,E>::unwrap
at /rustc/18bf6b4f01a6feaf7259ba7cdae58031af1b7b39/library/core/src/result.rs:973
4: piano_rs::network::receiver::Receiver::poll_event
at ./src/network/receiver.rs:25
5: piano_rs::handle_network_receive_event
at ./src/main.rs:37
6: piano_rs::main::{{closure}}
at ./src/main.rs:135
note: Some details are omitted, run with RUST_BACKTRACE=full for a verbose backtrace.

Support more key combos

Some key combos like shift+<number> to call special characters (^, &, *, etc.) don't work currently. It would be nice to support them as well.

No sound

Hie,
So with the last release, I can "press piano keys" with my keyboard (before, the application would close immedialty if I press any key). But the problem is that I don't have sound at all.
Do I need pulseaudio to be installed, or is alsa enough (i don't have pulseaudio installed).

Thanks!

Play from file and keyboard simultaneously

At the moment when playing notes from a file, it ignores any input from the keyboard. It would be nice to play both from file and keyboard at the same time, useful for pushing our own notes into .yml piano solo.

This should be possible with threads.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.