Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Create C API #6

Open
wants to merge 14 commits into
base: master
Choose a base branch
from
Open
7 changes: 7 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,10 +1,17 @@
# Generated by Cargo
# will have compiled files and executables
/target/
core/target/
capi/target/

# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries
# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html
Cargo.lock
core/Cargo.lock
capi/Cargo.lock

# Remove C headers generated by cbindgen
capi/include/*

# These are backup files generated by rustfmt
**/*.rs.bk
1 change: 0 additions & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ before_script:
- cargo --version
- sh install_rustfmt_clippy.sh
script:
- cargo build --verbose --all
- sh run_tests.sh
- sh run_sanitizers.sh
jobs:
Expand Down
31 changes: 5 additions & 26 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,26 +1,5 @@
[package]
name = "audio-mixer"
description = "Mixing audio by the input and output channel layout"
version = "0.1.2"
authors = ["Chun-Min Chang <chun.m.chang@gmail.com>"]
license = "MPL-2.0"
repository = "https://github.com/ChunMinChang/audio-mixer"
keywords = ["audio", "mixer", "mixing"]
categories = ["multimedia::audio"]
edition = "2018"

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

[badges]
travis-ci = { repository = "ChunMinChang/audio-mixer" }

[dependencies]
bitflags = "1.2"

[dev-dependencies]
criterion = "0.3"
float-cmp = "0.6"

[[bench]]
name = "benchmark"
harness = false
[workspace]
members = [
"core",
"capi"
]
24 changes: 24 additions & 0 deletions capi/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
[package]
name = "audio-mixer-capi"
description = "Mixing audio by the input and output channel layout"
version = "0.1.0"
authors = ["Chun-Min Chang <chun.m.chang@gmail.com>"]
license = "MPL-2.0"
repository = "https://github.com/ChunMinChang/audio-mixer"
keywords = ["audio", "mixer", "mixing"]
categories = ["multimedia::audio"]
edition = "2018"

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

[lib]
crate-type = ["staticlib"]

[badges]
travis-ci = { repository = "ChunMinChang/audio-mixer" }

[dependencies]
audio-mixer = { version = "0.1.2", path = "../core" }

[build-dependencies]
cbindgen = "0.13"
7 changes: 7 additions & 0 deletions capi/build.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
fn main() {
let crate_dir = std::env::var("CARGO_MANIFEST_DIR").unwrap();
println!("cargo:rerun-if-changed=src/lib.rs");
cbindgen::generate(&crate_dir)
.expect("Could not generate header")
.write_to_file("include/audio_mixer.h");
}
23 changes: 23 additions & 0 deletions capi/cbindgen.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
header = """/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#ifdef __cplusplus
extern "C" {
#endif
"""
trailer="""
#ifdef __cplusplus
} /* extern "C" */
#endif
"""
autogen_warning = """// THIS FILE IS AUTOGENERATED BY audio_mixer_capi/build.rs - DO NOT EDIT"""
include_guard = "AUDIO_MIXER_CAPI_H"
language = "C"

[enum]
rename_variants = "QualifiedScreamingSnakeCase"

[parse]
parse_deps = true
include = ["audio-mixer"]
44 changes: 44 additions & 0 deletions capi/examples/audio_mixer.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
#include "audio_mixer.h"

#include <stdio.h>

#define ARRAY_ELEMS(a) (sizeof(a) / sizeof((a)[0]))

int main() {
// The input and output channels are mapped to the test in lib.rs so it's
// easier to check the result.
const Channel input_channels[] = {CHANNEL_FRONT_LEFT, CHANNEL_SILENCE,
CHANNEL_FRONT_RIGHT, CHANNEL_FRONT_CENTER,
CHANNEL_BACK_LEFT, CHANNEL_SIDE_RIGHT,
CHANNEL_LOW_FREQUENCY, CHANNEL_SIDE_LEFT,
CHANNEL_BACK_CENTER, CHANNEL_BACK_RIGHT};
const Channel output_channels[] = {CHANNEL_SILENCE, CHANNEL_FRONT_RIGHT,
CHANNEL_FRONT_LEFT};

Channels input_layout = {(const Channel*)&input_channels,
(uintptr_t)ARRAY_ELEMS(input_channels)};
Channels output_layout = {(const Channel*)&output_channels,
(uintptr_t)ARRAY_ELEMS(output_channels)};

float input_buffers[ARRAY_ELEMS(input_channels)] = {1, 2, 3, 4, 5,
6, 7, 8, 9, 10};

float output_buffers[ARRAY_ELEMS(output_channels)] = {0};

Handle handle = mixer_create(FORMAT_F32, input_layout, output_layout);

mixer_mix(handle, &input_buffers, (uintptr_t)ARRAY_ELEMS(input_channels),
&output_buffers, (uintptr_t)ARRAY_ELEMS(output_channels));

for (size_t i = 0; i < ARRAY_ELEMS(input_buffers); i++) {
printf("ch[%zu] = %f\n", i, input_buffers[i]);
}
printf("is mixed to\n");
for (size_t i = 0; i < ARRAY_ELEMS(output_buffers); i++) {
printf("ch[%zu] = %f\n", i, output_buffers[i]);
}

mixer_destroy(handle);

return 0;
}
56 changes: 56 additions & 0 deletions capi/run_sanitizers.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
# The option `Z` is only accepted on the nightly compiler
# so changing to nightly toolchain by `rustup default nightly` is required.

# See: https://github.com/rust-lang/rust/issues/39699 for more sanitizer support.

toolchain=$(rustup default)
echo "\nUse Rust toolchain: $toolchain"

NIGHTLY_PREFIX="nightly"
if [ ! -z "${toolchain##$NIGHTLY_PREFIX*}" ]; then
echo "The sanitizer is only available on Rust Nightly only. Skip."
exit
fi

# Display verbose backtrace for debugging if backtrace is unset
if [ -z "${RUST_BACKTRACE}" ]; then
export RUST_BACKTRACE=1
fi
echo "RUST_BACKTRACE is set to ${RUST_BACKTRACE}\n"

# `cbindgen` cannot work with sanitizers, so we need to avoid building crate
# with `cbindgen` when enabling sanitizers. We can generate the library and
# the header that will be used during the test in advance, and then disable
# the build script, which uses `cbindgen`, to run the tests with sanitizers.

# Generating the library and the header that will be used during the test is
# necessary, before running `test_build_ffi` with sanitizers. If no FFI library
# is built, `cargo build --lib` will be executed in `test_build_ffi` to
# generate the FFI library we need. However, when `RUSTFLAGS="-Z sanitizer=*"`
# is set when building the FFI library, then the library will need symbols
# in `__*san_` pattern and we will need to set those `*San` symbol via `-l`
# when running `test_build_ffi`. Otherwise the linking will fail.
# To avoid those linking settings, the simplest way is to build the FFI
# library in advance, and running test with pre-built FFI library.
echo "Build C API library and header\n------------------------------"
cargo build --lib

# Disable the build script. Avoding building crate with cbindgen when
# enabling sanitizers
ORIGINAL_MANIFEST="Cargo_ori.toml"
TEST_MANIFEST="Cargo.toml"
mv $TEST_MANIFEST $ORIGINAL_MANIFEST
# Delete lines that contain a `criterion` from Cargo.toml.
sed '/package/a\
build = false
' $ORIGINAL_MANIFEST > $TEST_MANIFEST

sanitizers=("address" "leak" "memory" "thread")
for san in "${sanitizers[@]}"
do
San="$(tr '[:lower:]' '[:upper:]' <<< ${san:0:1})${san:1}"
echo "\n\nRun ${San}Sanitizer\n------------------------------"
RUSTFLAGS="-Z sanitizer=${san}" cargo test
done

mv $ORIGINAL_MANIFEST $TEST_MANIFEST
Loading