Merge pull request #121 from gwilymk/agb-sound-converter

Agb sound converter
This commit is contained in:
Gwilym Kuiper 2021-10-18 21:19:31 +01:00 committed by GitHub
commit e92383e29c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
12 changed files with 248 additions and 4 deletions

View 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

View file

@ -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
View 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"

View 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"

View 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
View file

@ -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"

View file

@ -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]

Binary file not shown.

View file

@ -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 {

View file

@ -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;

View file

@ -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