~yujiri/yuki

f869726aa257367eafb253ebecacaab4f4aa3e02 — Yujiri 1 year, 10 months ago
commit
5 files changed, 311 insertions(+), 0 deletions(-)

A .gitignore
A Cargo.lock
A Cargo.toml
A README.md
A src/main.rs
A  => .gitignore +1 -0
@@ 1,1 @@
target

A  => Cargo.lock +135 -0
@@ 1,135 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3

[[package]]
name = "alsa"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5915f52fe2cf65e83924d037b6c5290b7cee097c6b5c8700746e6168a343fd6b"
dependencies = [
 "alsa-sys",
 "bitflags",
 "libc",
 "nix",
]

[[package]]
name = "alsa-sys"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "db8fee663d06c4e303404ef5f40488a53e062f89ba8bfed81f42325aafad1527"
dependencies = [
 "libc",
 "pkg-config",
]

[[package]]
name = "autocfg"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"

[[package]]
name = "bitflags"
version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"

[[package]]
name = "cc"
version = "1.0.73"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11"

[[package]]
name = "cfg-if"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"

[[package]]
name = "cmake"
version = "0.1.48"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e8ad8cef104ac57b68b89df3208164d228503abbdce70f6880ffa3d970e7443a"
dependencies = [
 "cc",
]

[[package]]
name = "fs_extra"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2022715d62ab30faffd124d40b76f4134a550a87792276512b18d63272333394"

[[package]]
name = "lazy_static"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"

[[package]]
name = "libc"
version = "0.2.122"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec647867e2bf0772e28c8bcde4f0d19a9216916e890543b5a03ed8ef27b8f259"

[[package]]
name = "memoffset"
version = "0.6.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce"
dependencies = [
 "autocfg",
]

[[package]]
name = "mygame"
version = "0.1.0"
dependencies = [
 "alsa",
 "raylib",
]

[[package]]
name = "nix"
version = "0.23.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9f866317acbd3a240710c63f065ffb1e4fd466259045ccb504130b7f668f35c6"
dependencies = [
 "bitflags",
 "cc",
 "cfg-if",
 "libc",
 "memoffset",
]

[[package]]
name = "pkg-config"
version = "0.3.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1df8c4ec4b0627e53bdf214615ad287367e482558cf84b109250b37464dc03ae"

[[package]]
name = "raylib"
version = "3.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fb567269b7ea9ae3c4a5aab4dc95e0b4d8df2a49a25e1670a3bb17bc17504606"
dependencies = [
 "cfg-if",
 "lazy_static",
 "libc",
 "raylib-sys",
]

[[package]]
name = "raylib-sys"
version = "3.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "20c97b5e251b73c52183914d4756104cab401050f244c19abe83fa05a4e86840"
dependencies = [
 "cc",
 "cmake",
 "fs_extra",
]

A  => Cargo.toml +10 -0
@@ 1,10 @@
[package]
name = "mygame"
version = "0.1.0"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
raylib = "3.7"
alsa = "*"

A  => README.md +5 -0
@@ 1,5 @@
A program that simulates a keyboard tuned to an arbitrary TET (tone equal temperament) scale using the number key row.

Uses Raylib just because I wanted to throw this together quickly and it was the easiest way I knew to handle keypresses.

Named after the [Spem word for music](https://yujiri.xyz/spem/search?word=yuki).

A  => src/main.rs +160 -0
@@ 1,160 @@
#![feature(decl_macro)]
use raylib::prelude::*;
use alsa::{Direction, ValueOr};
use alsa::pcm::{PCM, HwParams, Format, Access};

fn main() {
	let (mut rl, thread) = raylib::init().title("yuki").build();
	// Open default playback device
	let pcm = PCM::new("default", Direction::Playback, false).unwrap();
	set_hw_params(&pcm);
	let io = pcm.io_i16().unwrap();

	let octave: f32 = 2.0;
	let semitone: f32 = octave.powf(1.0/19.0);

	rl.set_target_fps(100);
	let mut wave1_index = 0;
	let mut wave2_index = 0;
	let mut wave3_index = 0;
	let mut wave4_index = 0;
	let mut wave5_index = 0;
	let mut wave6_index = 0;
	let mut wave7_index = 0;
	let mut wave8_index = 0;
	let mut wave9_index = 0;
	let mut wave10_index = 0;
	let mut wave11_index = 0;
	let mut wave12_index = 0;
	let mut wave13_index = 0;
	while !rl.window_should_close() {
		let mut waves = vec![];
		if rl.is_key_down(KeyboardKey::KEY_ONE) {
			let wave = make_sine_wave(440.0*semitone.powi(0), wave1_index);
			waves.push(wave);
			wave1_index += wave.len();
		} else {
			wave1_index = 0;
		}
		if rl.is_key_down(KeyboardKey::KEY_TWO) {
			let wave = make_sine_wave(440.0*semitone.powi(1), wave2_index);
			waves.push(wave);
			wave2_index += wave.len();
		} else {
			wave2_index = 0;
		}
		if rl.is_key_down(KeyboardKey::KEY_THREE) {
			let wave = make_sine_wave(440.0*semitone.powi(2), wave3_index);
			waves.push(wave);
			wave3_index += wave.len();
		} else {
			wave3_index = 0;
		}
		if rl.is_key_down(KeyboardKey::KEY_FOUR) {
			let wave = make_sine_wave(440.0*semitone.powi(3), wave4_index);
			waves.push(wave);
			wave4_index += wave.len();
		} else {
			wave4_index = 0;
		}
		if rl.is_key_down(KeyboardKey::KEY_FIVE) {
			let wave = make_sine_wave(440.0*semitone.powi(4), wave5_index);
			waves.push(wave);
			wave5_index += wave.len();
		} else {
			wave5_index = 0;
		}
		if rl.is_key_down(KeyboardKey::KEY_SIX) {
			let wave = make_sine_wave(440.0*semitone.powi(5), wave6_index);
			waves.push(wave);
			wave6_index += wave.len();
		} else {
			wave6_index = 0;
		}
		if rl.is_key_down(KeyboardKey::KEY_SEVEN) {
			let wave = make_sine_wave(440.0*semitone.powi(6), wave7_index);
			waves.push(wave);
			wave7_index += wave.len();
		} else {
			wave7_index = 0;
		}
		if rl.is_key_down(KeyboardKey::KEY_EIGHT) {
			let wave = make_sine_wave(440.0*semitone.powi(7), wave8_index);
			waves.push(wave);
			wave8_index += wave.len();
		} else {
			wave8_index = 0;
		}
		if rl.is_key_down(KeyboardKey::KEY_NINE) {
			let wave = make_sine_wave(440.0*semitone.powi(8), wave9_index);
			waves.push(wave);
			wave9_index += wave.len();
		} else {
			wave9_index = 0;
		}
		if rl.is_key_down(KeyboardKey::KEY_ZERO) {
			let wave = make_sine_wave(440.0*semitone.powi(9), wave10_index);
			waves.push(wave);
			wave10_index += wave.len();
		} else {
			wave10_index = 0;
		}
		if rl.is_key_down(KeyboardKey::KEY_MINUS) {
			let wave = make_sine_wave(440.0*semitone.powi(10), wave11_index);
			waves.push(wave);
			wave11_index += wave.len();
		} else {
			wave11_index = 0;
		}
		if rl.is_key_down(KeyboardKey::KEY_EQUAL) {
			let wave = make_sine_wave(440.0*semitone.powi(11), wave12_index);
			waves.push(wave);
			wave12_index += wave.len();
		} else {
			wave12_index = 0;
		}
		if rl.is_key_down(KeyboardKey::KEY_BACKSPACE) {
			let wave = make_sine_wave(440.0*semitone.powi(12), wave13_index);
			waves.push(wave);
			wave13_index += wave.len();
		} else {
			wave13_index = 0;
		}
		let mut final_wave = [0i16; SAMPLE_WIDTH];
		for (i, a) in final_wave.iter_mut().enumerate() {
			*a = waves.iter().map(|w| w[i]).sum()
		}
		assert_eq!(io.writei(&final_wave).unwrap(), SAMPLE_WIDTH);		
		let mut d = rl.begin_drawing(&thread);
		d.clear_background(Color::WHITE);
	}
}

fn set_hw_params(pcm: &PCM) {
	// Set hardware parameters: 44100 Hz / Mono / 16 bit
	let hwp = HwParams::any(pcm).unwrap();
	hwp.set_channels(1).unwrap();
	hwp.set_rate(44100, ValueOr::Nearest).unwrap();
	hwp.set_format(Format::s16()).unwrap();
	hwp.set_access(Access::RWInterleaved).unwrap();
	pcm.hw_params(&hwp).unwrap();

	// Make sure we don't start the stream too early
	let hwp = pcm.hw_params_current().unwrap();
	let swp = pcm.sw_params_current().unwrap();
	swp.set_start_threshold(hwp.get_buffer_size().unwrap()).unwrap();
	pcm.sw_params(&swp).unwrap();
}

const SAMPLE_WIDTH: usize = 441;

fn make_sine_wave(freq: f32, prev_x: usize) -> [i16; SAMPLE_WIDTH] {
	use std::f32::consts::PI;
	let wavelen = 64.0 / (freq/440.0);
	let mut buf = [0i16; SAMPLE_WIDTH];
	for (i, point) in buf.iter_mut().enumerate() {
		let x = (prev_x+i) as f32;
		*point = ((x * PI / wavelen).sin() * 8192.0) as i16;
	}
	buf
}