mirror of
https://github.com/italicsjenga/agb.git
synced 2024-12-24 00:31:34 +11:00
Merge pull request #121 from gwilymk/agb-sound-converter
Agb sound converter
This commit is contained in:
commit
e92383e29c
20
.github/workflows/publish-sound-converter.yml
vendored
Normal file
20
.github/workflows/publish-sound-converter.yml
vendored
Normal file
|
@ -0,0 +1,20 @@
|
|||
name: Publish agb-sound-converter
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- agb-sound-converter/v*
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- name: Install build tools
|
||||
run: sudo apt-get update && sudo apt-get install build-essential binutils-arm-none-eabi -y
|
||||
- name: Check out repository
|
||||
uses: actions/checkout@v2
|
||||
- name: Login to crates.io
|
||||
run: cargo login ${{ secrets.CRATE_API }}
|
||||
- name: Publish agb-sound-converter
|
||||
run: cargo publish
|
||||
working-directory: ./agb-sound-converter
|
3
.github/workflows/rust.yml
vendored
3
.github/workflows/rust.yml
vendored
|
@ -38,6 +38,9 @@ jobs:
|
|||
- name: Run Clippy on agb image converter
|
||||
working-directory: agb-image-converter
|
||||
run: cargo clippy --verbose
|
||||
- name: Run Clippy on agb sound converter
|
||||
working-directory: agb-sound-converter
|
||||
run: cargo clippy --verbose
|
||||
- name: Run Clippy on agb macros
|
||||
working-directory: agb-macros
|
||||
run: cargo clippy --verbose
|
||||
|
|
61
agb-sound-converter/Cargo.lock
generated
Normal file
61
agb-sound-converter/Cargo.lock
generated
Normal file
|
@ -0,0 +1,61 @@
|
|||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "agb_sound_converter"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"hound",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"siphasher",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hound"
|
||||
version = "3.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8a164bb2ceaeff4f42542bdb847c41517c78a60f5649671b2a07312b6e117549"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.30"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "edc3358ebc67bc8b7fa0c007f945b0b18226f78437d61bec735a9eb96b61ee70"
|
||||
dependencies = [
|
||||
"unicode-xid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "38bc8cc6a5f2e3655e0899c1b848643b2562f853f114bfec7be120678e3ace05"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "siphasher"
|
||||
version = "0.3.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "533494a8f9b724d33625ab53c6c4800f7cc445895924a8ef649222dcb76e938b"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.80"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d010a1623fbd906d51d650a9916aaefc05ffa0e4053ff7fe601167f3e715d194"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-xid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-xid"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3"
|
25
agb-sound-converter/Cargo.toml
Normal file
25
agb-sound-converter/Cargo.toml
Normal file
|
@ -0,0 +1,25 @@
|
|||
[package]
|
||||
name = "agb_sound_converter"
|
||||
version = "0.1.0"
|
||||
authors = ["Gwilym Kuiper <gw@ilym.me>"]
|
||||
edition = "2018"
|
||||
license = "MPL-2.0"
|
||||
description = "Library for converting wavs for use on the Game Boy Advance"
|
||||
|
||||
[profile.dev]
|
||||
opt-level = 3
|
||||
debug = true
|
||||
|
||||
[profile.release]
|
||||
lto = true
|
||||
debug = true
|
||||
|
||||
[lib]
|
||||
proc-macro = true
|
||||
|
||||
[dependencies]
|
||||
hound = "3.4.0"
|
||||
syn = "1.0.73"
|
||||
proc-macro2 = "1.0.27"
|
||||
quote = "1.0.9"
|
||||
siphasher = "0.3.7"
|
104
agb-sound-converter/src/lib.rs
Normal file
104
agb-sound-converter/src/lib.rs
Normal file
|
@ -0,0 +1,104 @@
|
|||
#![deny(clippy::all)]
|
||||
|
||||
use proc_macro::TokenStream;
|
||||
use quote::quote;
|
||||
use siphasher::sip::SipHasher;
|
||||
use std::{
|
||||
fs,
|
||||
fs::File,
|
||||
hash::{Hash, Hasher},
|
||||
io::Write,
|
||||
path::Path,
|
||||
};
|
||||
use syn::parse_macro_input;
|
||||
|
||||
#[proc_macro]
|
||||
pub fn include_wav(input: TokenStream) -> TokenStream {
|
||||
let input = parse_macro_input!(input as syn::LitStr);
|
||||
|
||||
let filename = input.value();
|
||||
|
||||
let root = std::env::var("CARGO_MANIFEST_DIR").expect("Failed to get cargo manifest dir");
|
||||
let path = Path::new(&root).join(&*filename);
|
||||
|
||||
let include_path = path.to_string_lossy();
|
||||
|
||||
let out_file_path_include = {
|
||||
let out_dir = std::env::var("OUT_DIR").expect("Expected OUT_DIR");
|
||||
let out_filename = get_out_filename(&path);
|
||||
|
||||
let out_file_path = Path::new(&out_dir).with_file_name(&out_filename);
|
||||
|
||||
let out_file_mtime = fs::metadata(&out_file_path).and_then(|metadata| metadata.modified());
|
||||
let in_file_mtime = fs::metadata(&path).and_then(|metadata| metadata.modified());
|
||||
|
||||
let should_write = match (out_file_mtime, in_file_mtime) {
|
||||
(Ok(out_file_mtime), Ok(in_file_mtime)) => out_file_mtime <= in_file_mtime,
|
||||
_ => true,
|
||||
};
|
||||
|
||||
if should_write {
|
||||
let wav_reader = hound::WavReader::open(&path)
|
||||
.unwrap_or_else(|_| panic!("Failed to load file {}", include_path));
|
||||
|
||||
assert_eq!(
|
||||
wav_reader.spec().sample_rate,
|
||||
10512,
|
||||
"agb currently only supports sample rate of 10512Hz"
|
||||
);
|
||||
|
||||
let samples = samples_from_reader(wav_reader);
|
||||
|
||||
let mut out_file =
|
||||
File::create(&out_file_path).expect("Failed to open file for writing");
|
||||
|
||||
out_file
|
||||
.write_all(&samples.collect::<Vec<_>>())
|
||||
.expect("Failed to write to temporary file");
|
||||
}
|
||||
|
||||
out_file_path
|
||||
}
|
||||
.canonicalize()
|
||||
.expect("Failed to canonicalize");
|
||||
|
||||
let out_file_path_include = out_file_path_include.to_string_lossy();
|
||||
|
||||
let result = quote! {
|
||||
{
|
||||
const _: &[u8] = include_bytes!(#include_path);
|
||||
|
||||
include_bytes!(#out_file_path_include)
|
||||
}
|
||||
};
|
||||
|
||||
TokenStream::from(result)
|
||||
}
|
||||
|
||||
fn samples_from_reader<'a, R>(reader: hound::WavReader<R>) -> Box<dyn Iterator<Item = u8> + 'a>
|
||||
where
|
||||
R: std::io::Read + 'a,
|
||||
{
|
||||
let bitrate = reader.spec().bits_per_sample;
|
||||
let reduction = bitrate - 8;
|
||||
|
||||
match reader.spec().sample_format {
|
||||
hound::SampleFormat::Float => Box::new(
|
||||
reader
|
||||
.into_samples::<f32>()
|
||||
.map(|sample| (sample.unwrap() * (i8::MAX as f32)) as u8),
|
||||
),
|
||||
hound::SampleFormat::Int => Box::new(
|
||||
reader
|
||||
.into_samples::<i32>()
|
||||
.map(move |sample| (sample.unwrap() >> reduction) as u8),
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
fn get_out_filename(path: &Path) -> String {
|
||||
let mut hasher = SipHasher::new();
|
||||
path.hash(&mut hasher);
|
||||
|
||||
format!("{}.raw", hasher.finish())
|
||||
}
|
24
agb/Cargo.lock
generated
24
agb/Cargo.lock
generated
|
@ -14,6 +14,7 @@ version = "0.7.0"
|
|||
dependencies = [
|
||||
"agb_image_converter",
|
||||
"agb_macros",
|
||||
"agb_sound_converter",
|
||||
"bitflags",
|
||||
]
|
||||
|
||||
|
@ -39,6 +40,17 @@ dependencies = [
|
|||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "agb_sound_converter"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"hound",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"siphasher",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "1.0.1"
|
||||
|
@ -105,6 +117,12 @@ dependencies = [
|
|||
"wasi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hound"
|
||||
version = "3.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8a164bb2ceaeff4f42542bdb847c41517c78a60f5649671b2a07312b6e117549"
|
||||
|
||||
[[package]]
|
||||
name = "image"
|
||||
version = "0.23.14"
|
||||
|
@ -272,6 +290,12 @@ dependencies = [
|
|||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "siphasher"
|
||||
version = "0.3.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "533494a8f9b724d33625ab53c6c4800f7cc445895924a8ef649222dcb76e938b"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.78"
|
||||
|
|
|
@ -21,6 +21,7 @@ alloc = []
|
|||
[dependencies]
|
||||
bitflags = "1.2"
|
||||
agb_image_converter = { version = "0.6.0", path = "../agb-image-converter" }
|
||||
agb_sound_converter = { version = "0.1.0", path = "../agb-sound-converter" }
|
||||
agb_macros = { version = "0.1.0", path = "../agb-macros" }
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
|
|
BIN
agb/examples/JoshWoodward-DeadCode.wav
Normal file
BIN
agb/examples/JoshWoodward-DeadCode.wav
Normal file
Binary file not shown.
Binary file not shown.
|
@ -6,10 +6,10 @@ extern crate agb;
|
|||
use agb::input::{Button, ButtonController, Tri};
|
||||
use agb::number::Num;
|
||||
use agb::sound::mixer::SoundChannel;
|
||||
use agb::Gba;
|
||||
use agb::{include_wav, Gba};
|
||||
|
||||
// Music - "I will not let you let me down" by Josh Woodward, free download at http://joshwoodward.com
|
||||
const I_WILL_NOT_LET_YOU_LET_ME_DOWN: &[u8] = include_bytes!("i-will-not-let-you-let-me-down.raw");
|
||||
// Music - "Dead Code" by Josh Woodward, free download at http://joshwoodward.com
|
||||
const DEAD_CODE: &[u8] = include_wav!("examples/JoshWoodward-DeadCode.wav");
|
||||
|
||||
#[agb::entry]
|
||||
fn main() -> ! {
|
||||
|
@ -20,7 +20,7 @@ fn main() -> ! {
|
|||
let mut mixer = gba.mixer.mixer();
|
||||
mixer.enable();
|
||||
|
||||
let channel = SoundChannel::new(I_WILL_NOT_LET_YOU_LET_ME_DOWN);
|
||||
let channel = SoundChannel::new(DEAD_CODE);
|
||||
let channel_id = mixer.play_sound(channel).unwrap();
|
||||
|
||||
loop {
|
||||
|
|
|
@ -26,6 +26,7 @@ pub mod input;
|
|||
pub mod sound;
|
||||
|
||||
pub use agb_image_converter::include_gfx;
|
||||
pub use agb_sound_converter::include_wav;
|
||||
|
||||
pub use agb_macros::entry;
|
||||
|
||||
|
|
|
@ -35,6 +35,10 @@ case "$PROJECT" in
|
|||
DIRECTORY="agb-image-converter"
|
||||
TAGNAME="agb-image-converter/v$VERSION"
|
||||
;;
|
||||
agb-sound-converter)
|
||||
DIRECTORY="agb-sound-converter"
|
||||
TAGNAME="agb-sound-converter/v$VERSION"
|
||||
;;
|
||||
agb-macros)
|
||||
DIRECTORY="agb-macros"
|
||||
TAGNAME="agb-macros/v$VERSION"
|
||||
|
@ -84,6 +88,7 @@ fi
|
|||
# Sanity check to make sure the build works
|
||||
(cd agb && cargo test)
|
||||
(cd agb-image-converter && cargo test)
|
||||
(cd agb-sound-converter && cargo test)
|
||||
(cd agb-macros && cargo test)
|
||||
|
||||
if [ ! "$NO_COMMIT" = "--no-commit" ]; then
|
||||
|
|
Loading…
Reference in a new issue