mirror of
https://github.com/italicsjenga/agb.git
synced 2024-12-25 09:11: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
|
- name: Run Clippy on agb image converter
|
||||||
working-directory: agb-image-converter
|
working-directory: agb-image-converter
|
||||||
run: cargo clippy --verbose
|
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
|
- name: Run Clippy on agb macros
|
||||||
working-directory: agb-macros
|
working-directory: agb-macros
|
||||||
run: cargo clippy --verbose
|
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 = [
|
dependencies = [
|
||||||
"agb_image_converter",
|
"agb_image_converter",
|
||||||
"agb_macros",
|
"agb_macros",
|
||||||
|
"agb_sound_converter",
|
||||||
"bitflags",
|
"bitflags",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -39,6 +40,17 @@ dependencies = [
|
||||||
"syn",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "agb_sound_converter"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"hound",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"siphasher",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "autocfg"
|
name = "autocfg"
|
||||||
version = "1.0.1"
|
version = "1.0.1"
|
||||||
|
@ -105,6 +117,12 @@ dependencies = [
|
||||||
"wasi",
|
"wasi",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hound"
|
||||||
|
version = "3.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8a164bb2ceaeff4f42542bdb847c41517c78a60f5649671b2a07312b6e117549"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "image"
|
name = "image"
|
||||||
version = "0.23.14"
|
version = "0.23.14"
|
||||||
|
@ -272,6 +290,12 @@ dependencies = [
|
||||||
"syn",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "siphasher"
|
||||||
|
version = "0.3.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "533494a8f9b724d33625ab53c6c4800f7cc445895924a8ef649222dcb76e938b"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "syn"
|
name = "syn"
|
||||||
version = "1.0.78"
|
version = "1.0.78"
|
||||||
|
|
|
@ -21,6 +21,7 @@ alloc = []
|
||||||
[dependencies]
|
[dependencies]
|
||||||
bitflags = "1.2"
|
bitflags = "1.2"
|
||||||
agb_image_converter = { version = "0.6.0", path = "../agb-image-converter" }
|
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" }
|
agb_macros = { version = "0.1.0", path = "../agb-macros" }
|
||||||
|
|
||||||
[package.metadata.docs.rs]
|
[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::input::{Button, ButtonController, Tri};
|
||||||
use agb::number::Num;
|
use agb::number::Num;
|
||||||
use agb::sound::mixer::SoundChannel;
|
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
|
// Music - "Dead Code" 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");
|
const DEAD_CODE: &[u8] = include_wav!("examples/JoshWoodward-DeadCode.wav");
|
||||||
|
|
||||||
#[agb::entry]
|
#[agb::entry]
|
||||||
fn main() -> ! {
|
fn main() -> ! {
|
||||||
|
@ -20,7 +20,7 @@ fn main() -> ! {
|
||||||
let mut mixer = gba.mixer.mixer();
|
let mut mixer = gba.mixer.mixer();
|
||||||
mixer.enable();
|
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();
|
let channel_id = mixer.play_sound(channel).unwrap();
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
|
|
|
@ -26,6 +26,7 @@ pub mod input;
|
||||||
pub mod sound;
|
pub mod sound;
|
||||||
|
|
||||||
pub use agb_image_converter::include_gfx;
|
pub use agb_image_converter::include_gfx;
|
||||||
|
pub use agb_sound_converter::include_wav;
|
||||||
|
|
||||||
pub use agb_macros::entry;
|
pub use agb_macros::entry;
|
||||||
|
|
||||||
|
|
|
@ -35,6 +35,10 @@ case "$PROJECT" in
|
||||||
DIRECTORY="agb-image-converter"
|
DIRECTORY="agb-image-converter"
|
||||||
TAGNAME="agb-image-converter/v$VERSION"
|
TAGNAME="agb-image-converter/v$VERSION"
|
||||||
;;
|
;;
|
||||||
|
agb-sound-converter)
|
||||||
|
DIRECTORY="agb-sound-converter"
|
||||||
|
TAGNAME="agb-sound-converter/v$VERSION"
|
||||||
|
;;
|
||||||
agb-macros)
|
agb-macros)
|
||||||
DIRECTORY="agb-macros"
|
DIRECTORY="agb-macros"
|
||||||
TAGNAME="agb-macros/v$VERSION"
|
TAGNAME="agb-macros/v$VERSION"
|
||||||
|
@ -84,6 +88,7 @@ fi
|
||||||
# Sanity check to make sure the build works
|
# Sanity check to make sure the build works
|
||||||
(cd agb && cargo test)
|
(cd agb && cargo test)
|
||||||
(cd agb-image-converter && cargo test)
|
(cd agb-image-converter && cargo test)
|
||||||
|
(cd agb-sound-converter && cargo test)
|
||||||
(cd agb-macros && cargo test)
|
(cd agb-macros && cargo test)
|
||||||
|
|
||||||
if [ ! "$NO_COMMIT" = "--no-commit" ]; then
|
if [ ! "$NO_COMMIT" = "--no-commit" ]; then
|
||||||
|
|
Loading…
Reference in a new issue