├── .clang-format ├── .github ├── ISSUE_TEMPLATE │ └── bug_report.md ├── dependabot.yml └── workflows │ └── build.yml ├── .gitignore ├── .gitmodules ├── CHANGELOG.md ├── CONTRIBUTING.md ├── Cargo.toml ├── LICENSE ├── README.md ├── soloud-sys ├── Cargo.toml ├── README.md ├── bind.sh ├── build │ ├── android.rs │ ├── link.rs │ ├── main.rs │ └── source.rs ├── examples │ ├── sys_simple.rs │ └── sys_speech.rs ├── src │ ├── lib.rs │ └── soloud.rs └── sys │ ├── CMakeLists.txt │ ├── examples │ ├── ca.cpp │ └── example.cpp │ ├── soloud_derives.cpp │ ├── soloud_derives.h │ └── soloud_new.cpp └── soloud ├── Cargo.toml ├── README.md ├── examples ├── filter.rs ├── include_bytes.rs ├── load_mem.rs ├── recho.rs ├── simple.rs └── speech.rs └── src ├── audio ├── ay.rs ├── bus.rs ├── mod.rs ├── monotone.rs ├── noise.rs ├── openmpt.rs ├── queue.rs ├── sfxr.rs ├── speech.rs ├── tedsid.rs ├── vic.rs ├── vizsn.rs ├── wav.rs └── wavstream.rs ├── effects.rs ├── filter ├── bass.rs ├── biquad.rs ├── dc.rs ├── echo.rs ├── fft.rs ├── flanger.rs ├── freeverb.rs ├── lofi.rs ├── mod.rs ├── robotize.rs └── waveshaper.rs ├── lib.rs ├── macros ├── audio.rs ├── filter.rs ├── load.rs └── mod.rs └── prelude.rs /.clang-format: -------------------------------------------------------------------------------- 1 | --- 2 | Language: Cpp 3 | AccessModifierOffset: -2 4 | AlignAfterOpenBracket: Align 5 | AlignConsecutiveAssignments: false 6 | AlignConsecutiveDeclarations: false 7 | AlignEscapedNewlines: Right 8 | AlignOperands: true 9 | AlignTrailingComments: true 10 | AllowAllParametersOfDeclarationOnNextLine: true 11 | AllowShortBlocksOnASingleLine: false 12 | AllowShortCaseLabelsOnASingleLine: false 13 | AllowShortFunctionsOnASingleLine: false 14 | AllowShortIfStatementsOnASingleLine: false 15 | AllowShortLoopsOnASingleLine: false 16 | AlwaysBreakAfterDefinitionReturnType: None 17 | AlwaysBreakAfterReturnType: None 18 | AlwaysBreakBeforeMultilineStrings: false 19 | AlwaysBreakTemplateDeclarations: true 20 | BinPackArguments: true 21 | BinPackParameters: true 22 | BraceWrapping: 23 | AfterClass: false 24 | AfterControlStatement: false 25 | AfterEnum: false 26 | AfterFunction: false 27 | AfterNamespace: false 28 | AfterObjCDeclaration: false 29 | AfterStruct: false 30 | AfterUnion: false 31 | AfterExternBlock: false 32 | BeforeCatch: false 33 | BeforeElse: false 34 | IndentBraces: false 35 | SplitEmptyFunction: true 36 | SplitEmptyRecord: true 37 | SplitEmptyNamespace: true 38 | BreakBeforeBinaryOperators: None 39 | BreakBeforeBraces: Attach 40 | BreakBeforeInheritanceComma: false 41 | BreakBeforeTernaryOperators: true 42 | BreakConstructorInitializersBeforeComma: false 43 | BreakConstructorInitializers: BeforeColon 44 | BreakStringLiterals: true 45 | ColumnLimit: 100 46 | CommentPragmas: '^ IWYU pragma:' 47 | CompactNamespaces: false 48 | ConstructorInitializerAllOnOneLineOrOnePerLine: false 49 | ConstructorInitializerIndentWidth: 4 50 | ContinuationIndentWidth: 4 51 | Cpp11BracedListStyle: true 52 | DerivePointerAlignment: false 53 | DisableFormat: false 54 | ExperimentalAutoDetectBinPacking: false 55 | FixNamespaceComments: true 56 | IncludeBlocks: Preserve 57 | IndentCaseLabels: false 58 | IndentPPDirectives: None 59 | IndentWidth: 4 60 | IndentWrappedFunctionNames: false 61 | KeepEmptyLinesAtTheStartOfBlocks: true 62 | MacroBlockBegin: '' 63 | MacroBlockEnd: '' 64 | MaxEmptyLinesToKeep: 1 65 | NamespaceIndentation: None 66 | PenaltyBreakAssignment: 2 67 | PenaltyBreakBeforeFirstCallParameter: 19 68 | PenaltyBreakComment: 300 69 | PenaltyBreakFirstLessLess: 120 70 | PenaltyBreakString: 1000 71 | PenaltyExcessCharacter: 1000000 72 | PenaltyReturnTypeOnItsOwnLine: 60 73 | PointerAlignment: Right 74 | ReflowComments: true 75 | SortIncludes: true 76 | SortUsingDeclarations: true 77 | SpaceAfterCStyleCast: false 78 | SpaceAfterTemplateKeyword: true 79 | SpaceBeforeAssignmentOperators: true 80 | SpaceBeforeParens: ControlStatements 81 | SpaceInEmptyParentheses: false 82 | SpacesBeforeTrailingComments: 1 83 | SpacesInAngles: false 84 | SpacesInContainerLiterals: true 85 | SpacesInCStyleCastParentheses: false 86 | SpacesInParentheses: false 87 | SpacesInSquareBrackets: false 88 | Standard: Cpp11 89 | TabWidth: 8 90 | UseTab: Never 91 | ... 92 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Desktop (please complete the following information):** 24 | - OS: [e.g. iOS] 25 | - Version [e.g. 22] 26 | - Output of `cargo build -vv` 27 | 28 | **Additional context** 29 | Add any other context about the problem here. 30 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: "cargo" 4 | directory: "/soloud" 5 | schedule: 6 | interval: "daily" 7 | - package-ecosystem: "cargo" 8 | directory: "/soloud-sys" 9 | schedule: 10 | interval: "daily" 11 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Build 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | pull_request: 7 | branches: [ master ] 8 | 9 | jobs: 10 | build: 11 | 12 | runs-on: ${{ matrix.os }} 13 | strategy: 14 | matrix: 15 | os: [windows-latest, macos-latest, ubuntu-latest] 16 | 17 | steps: 18 | - uses: actions/checkout@v2 19 | - name: Update submodule 20 | run: git submodule update --init --recursive 21 | - name: Build 22 | run: cargo build --verbose 23 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | Cargo.lock 3 | temp.* 4 | .clangd 5 | *.wav 6 | *.mp3 7 | *.exe -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "soloud-sys/sys/soloud"] 2 | path = soloud-sys/sys/soloud 3 | url = https://github.com/jarikomppa/soloud 4 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## [1.1.1] - 2025-05-13 4 | - Building SoLoud with the same profile cargo uses by @Ekranos. 5 | 6 | ## [1.0.4] - 2023-08-08 7 | - Fix memory leak in Soloud initialization. 8 | 9 | ## [1.0.3] - 2023-07-24 10 | - Update Soloud submodule. 11 | 12 | ## [1.0.2] - 2022-02-17 13 | - Add scrape examples to docs.rs. 14 | 15 | ## [1.0.1] - 2022-01-16 16 | - Update soloud module. 17 | - Remove references to LoadExt::load_mem_weak(). 18 | 19 | ## [1.0.0] - 2021-10-15 20 | - Enable Android builds. 21 | - Enable passing utf-8 paths on Windows. 22 | - Remove LoadExt::load_mem_weak(). 23 | 24 | ## [0.4.4] - 2021-10-05 25 | - Remove dependence on syn & quote. 26 | - Don't depend on libc. 27 | - Update soloud and remove patch. 28 | 29 | ## [0.4.2] - 2021-09-28 30 | - Make Soloud::mix and mix_signed_16 public. 31 | - Update docs. 32 | 33 | ## [0.4.0] - 2021-05-04 34 | - Replace Cow with movable/Clonable `Vec`. 35 | - Fix expect() message in build script. 36 | - Update deps. 37 | 38 | ## [0.3.3] - 2021-01-20 39 | - Fix default features for soloud-sys. 40 | 41 | ## [0.3.2] - 2021-01-19 42 | ### Changes 43 | - [BREAKING] Wrap Handle type in a struct to avoid misuse. Thanks @toyboot4e. 44 | - [BREAKING] backend_string now returns a String instead of a &str. 45 | - [BREAKING] BiquadResonantFilterAttr::Frequence renamed to Freq as with all filter attributes. 46 | - [BREAKING] Rename unsafe LoadExt::load_mem_ex to _load_mem_ex and marked interal. 47 | - [BREAKING] Add backend parameter when initializing the soloud engine. 48 | - Fix docs. 49 | - Add missing backends to CMakeLists. 50 | - Fix builds with coreaudio framework. 51 | - Refactor boilerplate ffi handling by using a macro. Thanks @toyboot4e. 52 | - Support more backends in soloud-rs via feature flags. 53 | - Fix memory leak with LoadExt::load_mem and FromExt::from_path and add explicit methods to the LoadExt and FromExt traits which allows loading audio from memory. Thanks @toyboot4e. 54 | - load_mem_weak and from_mem_weak 55 | - load_mem_weak_unsafe and from_mem_weak_unsafe 56 | - Update deps. 57 | - Add missing docs. 58 | 59 | ## [0.2.4] - 2020-12-27 60 | ### Changes 61 | - Add derive Debug for Soloud types. Thanks @toyboot4e. 62 | - Update dependencies. 63 | 64 | ## [0.2.3] - 2020-12-02 65 | ### Changes 66 | - Use bitflags for SoloudFlag; 67 | 68 | ## [0.2.2] - 2020-11-22 69 | ### Changes 70 | - Update dependencies. 71 | - Add example using include_bytes. 72 | 73 | ## [0.2.1] - 2020-10-02 74 | ### Changes 75 | - Add a helper trait FromExt, thanks @toyboot4e 76 | - Add AsRef impl fro LoadExt::load() and FromExt::from_path trait methods. 77 | 78 | ## [0.2.0] - 2020-09-29 79 | ### Changes 80 | - Pull changes from origin. 81 | - Redirect submodule to point to origin. 82 | - Patch origin. 83 | 84 | ## [0.1.9] - 2020-09-01 85 | ### Changes 86 | - Accept CMake toolchain files for cross-compilation via SOLOUD_TOOLCHAIN env var. 87 | - Update deps. 88 | 89 | ## [0.1.8] - 2020-09-01 90 | ### Changes 91 | - Fix typo in SoloudErrorKind. 92 | - Remove unwraps in internal code. 93 | 94 | ## [0.1.7] - 2020-09-01 95 | ### Changes 96 | - Remove effects module for now. 97 | 98 | ## [0.1.6] - 2020-08-31 99 | ### Changes 100 | - Add AudioAttenuator and AudioCollider overridable methods. 101 | 102 | 103 | ## [0.1.5] - 2020-08-30 104 | ### Changes 105 | - Add Bus sound source. 106 | - Complete implementation of audio sources. 107 | 108 | ## [0.1.4] - 2020-08-28 109 | ### Changes 110 | - Make the Soloud struct an RAII type with a Drop impl. 111 | - Add a changelog. 112 | - Add CI to check builds on windows, macos and linux. 113 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | Contributions are welcome. Buf fixes, typo fixes, code improvements and examples. 4 | All Rust code should be formatted using rustfmt, and it should be compilable with Rust stable. 5 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | resolver = "2" 3 | members = [ 4 | "soloud", 5 | "soloud-sys", 6 | ] 7 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Mohammed Alyousef 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, nd/or sell 9 | copies of the Software, nd to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # soloud-rs 2 | 3 | [![Documentation](https://docs.rs/soloud/badge.svg)](https://docs.rs/soloud) 4 | [![Crates.io](https://img.shields.io/crates/v/soloud.svg)](https://crates.io/crates/soloud) 5 | [![License](https://img.shields.io/crates/l/soloud.svg)](https://github.com/MoAlyousef/soloud-rs/blob/master/LICENSE) 6 | [![Build](https://github.com/MoAlyousef/soloud-rs/workflows/Build/badge.svg)](https://github.com/MoAlyousef/soloud-rs/actions) 7 | 8 | A crossplatform Rust bindings for the soloud audio engine library. 9 | 10 | Supported formats: wav, mp3, ogg, flac. The library also comes with a speech synthesizer. 11 | 12 | - The official soloud [website](https://sol.gfxile.net/soloud/index.html) 13 | - The official soloud [repo](https://github.com/jarikomppa/soloud) 14 | 15 | ## Usage 16 | ```toml 17 | [dependencies] 18 | soloud = "1.1" 19 | ``` 20 | 21 | Or to use the git repo: 22 | ```toml 23 | [dependencies] 24 | soloud = { git = "https://github.com/moalyousef/soloud-rs" } 25 | ``` 26 | 27 | To play audio: 28 | ```rust,no_run 29 | use soloud::*; 30 | 31 | fn main() -> Result<(), Box> { 32 | let mut sl = Soloud::default()?; 33 | 34 | let mut wav = audio::Wav::default(); 35 | 36 | wav.load(&std::path::Path::new("sample.wav"))?; 37 | 38 | sl.play(&wav); // calls to play are non-blocking, so we put the thread to sleep 39 | while sl.voice_count() > 0 { 40 | std::thread::sleep(std::time::Duration::from_millis(100)); 41 | } 42 | 43 | wav.load(&std::path::Path::new("Recording.mp3"))?; 44 | 45 | sl.play(&wav); 46 | while sl.voice_count() > 0 { 47 | std::thread::sleep(std::time::Duration::from_millis(100)); 48 | } 49 | 50 | Ok(()) 51 | } 52 | ``` 53 | 54 | To play speech: 55 | ```rust,no_run 56 | use soloud::*; 57 | 58 | fn main() -> Result<(), Box> { 59 | let mut sl = Soloud::default()?; 60 | 61 | let mut speech = audio::Speech::default(); 62 | 63 | speech.set_text("Hello World")?; 64 | 65 | sl.play(&speech); 66 | while sl.active_voice_count() > 0 { 67 | std::thread::sleep(std::time::Duration::from_millis(100)); 68 | } 69 | 70 | speech.set_text("1 2 3")?; 71 | 72 | sl.play(&speech); 73 | while sl.active_voice_count() > 0 { 74 | std::thread::sleep(std::time::Duration::from_millis(100)); 75 | } 76 | 77 | speech.set_text("Can you hear me?")?; 78 | 79 | sl.play(&speech); 80 | while sl.active_voice_count() > 0 { 81 | std::thread::sleep(std::time::Duration::from_millis(100)); 82 | } 83 | 84 | Ok(()) 85 | } 86 | ``` 87 | 88 | To add a filter: 89 | ```rust,no_run 90 | use soloud::*; 91 | 92 | fn main() -> Result<(), Box> { 93 | let mut sl = Soloud::default()?; 94 | 95 | let mut wav = audio::Wav::default(); 96 | let mut filt = filter::EchoFilter::default(); 97 | filt.set_params(0.2)?; // sets the delay 98 | 99 | wav.load(&std::path::Path::new("sample.wav"))?; 100 | wav.set_filter(0, Some(&filt)); 101 | 102 | sl.play(&wav); 103 | while sl.voice_count() > 0 { 104 | std::thread::sleep(std::time::Duration::from_millis(100)); 105 | } 106 | 107 | Ok(()) 108 | } 109 | ``` 110 | 111 | The examples can be found in the soloud/examples directory. They can be run using: 112 | ```bash 113 | cargo run --example simple 114 | cargo run --example speech 115 | cargo run --example filter 116 | cargo run --example load_mem 117 | ``` 118 | You will need to have valid "sample.wav" and "Recording.mp3" audio files in the project root. Or you can change the paths to point to any supported audio file. 119 | 120 | There is also a demo gui application (using fltk) [here](https://github.com/fltk-rs/demos/tree/master/musicplayer). 121 | 122 | ## Dependencies 123 | A Rust compiler, C++ compiler, Cargo, CMake and git (all these need to be in your PATH). Ninja is recommended, but not required, and will be used if found. This crate uses the miniaudio backend by default which assumes default sound drivers are functional. 124 | 125 | ## Backends 126 | The default backend is miniaudio, however Soloud supports several backends to varying degrees. To enable support of a certain backend, alsa for example: 127 | ```toml 128 | [dependencies] 129 | soloud = { version = "1", default-features = false, features = ["alsa"] } 130 | ``` 131 | This also assumes that those libraries headers are in your include path where CMake can find them, otherwise you can set it via the command line (posix): 132 | ```bash 133 | export CXXFLAGS="-I /path/to/include" 134 | ``` 135 | or for Windows: 136 | ```bash 137 | set CXXFLAGS="-I C:\\path\\to\\include" 138 | ``` 139 | The same can be done for CFLAGS if needed. 140 | 141 | ### Supported backends: 142 | - miniaudio 143 | - oss 144 | - alsa 145 | - sdl2 146 | - sdl2-static 147 | - portaudio 148 | - openal 149 | - xaudio2 150 | - winmm 151 | - wasapi 152 | - opensles 153 | - coreaudio 154 | - jack 155 | 156 | ### Android support 157 | The ANDROID_SDK_ROOT and ANDROID_NDK_ROOT need to be set. -------------------------------------------------------------------------------- /soloud-sys/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "soloud-sys" 3 | version = "1.1.1" 4 | authors = ["MoAlyousef "] 5 | edition = "2021" 6 | build = "build/main.rs" 7 | description = "Rust bindings for the soloud audio engine" 8 | repository = "https://github.com/MoAlyousef/soloud-rs" 9 | documentation = "https://docs.rs/soloud" 10 | keywords = ["audio", "playback", "synthesizer"] 11 | categories = ["multimedia::audio"] 12 | license = "MIT" 13 | exclude = ["./examples"] 14 | readme = "README.md" 15 | links = "soloud" 16 | rust-version = "1.67" 17 | 18 | [build-dependencies] 19 | cmake = "^0.1.45" 20 | 21 | [features] 22 | use-ninja = [] 23 | miniaudio = [] 24 | sdl2 = [] 25 | sdl2-static = [] 26 | portaudio = [] 27 | openal = [] 28 | xaudio2 = [] 29 | winmm = [] 30 | wasapi = [] 31 | oss = [] 32 | alsa = [] 33 | opensles = [] 34 | coreaudio = [] 35 | jack = [] 36 | nosound = [] 37 | null = [] 38 | 39 | [package.metadata.docs.rs] 40 | features = ["miniaudio"] -------------------------------------------------------------------------------- /soloud-sys/README.md: -------------------------------------------------------------------------------- 1 | # soloud-sys 2 | 3 | Raw bindings to soloud. These are generated using bindgen on the soloud C headers. 4 | 5 | ## Usage 6 | ```toml 7 | [dependencies] 8 | soloud-sys = { version = "1", features = ["miniaudio"] } 9 | ``` 10 | 11 | Example code: 12 | ```rust,no_run 13 | use soloud_sys::soloud::*; 14 | 15 | fn main() { 16 | unsafe { 17 | let sl = Soloud_create(); 18 | Soloud_init(sl); 19 | std::thread::sleep(std::time::Duration::from_millis(100)); 20 | Soloud_setGlobalVolume(sl, 3.0); 21 | 22 | let speech = Speech_create(); 23 | 24 | let ret = Speech_setText(speech, "Hello World\0".as_ptr() as _); 25 | 26 | dbg!(ret); 27 | 28 | Soloud_play(sl, speech); 29 | while Soloud_getVoiceCount(sl) > 0 { 30 | // calls to play are non-blocking, so we put the thread to sleep 31 | std::thread::sleep(std::time::Duration::from_millis(100)); 32 | } 33 | } 34 | } 35 | ``` -------------------------------------------------------------------------------- /soloud-sys/bind.sh: -------------------------------------------------------------------------------- 1 | bindgen soloud-sys/sys/soloud_derives.h -o soloud-sys/src/soloud.rs -------------------------------------------------------------------------------- /soloud-sys/build/android.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | env, fs, 3 | path::{Path, PathBuf}, 4 | process::Command, 5 | }; 6 | 7 | pub fn build(out_dir: &Path, target_triple: &str) { 8 | println!("cargo:rerun-if-env-changed=ANDROID_SDK_ROOT"); 9 | println!("cargo:rerun-if-env-changed=ANDROID_NDK_ROOT"); 10 | 11 | let sdk = 12 | PathBuf::from(env::var("ANDROID_SDK_ROOT").expect("ANDROID_SDK_ROOT needs to be set!")); 13 | let mut ndk: Option = None; 14 | if let Ok(root) = env::var("ANDROID_NDK_ROOT") { 15 | ndk = Some(PathBuf::from(root)); 16 | } 17 | // fallback to NDK_HOME 18 | if ndk.is_none() { 19 | ndk = Some(PathBuf::from( 20 | env::var("NDK_HOME").expect("ANDROID_NDK_ROOT or NDK_HOME need to be set!"), 21 | )); 22 | } 23 | 24 | let ndk = ndk.expect("ANDROID_NDK_ROOT or NDK_HOME need to be set!"); 25 | 26 | let cmake_build_dir = out_dir.join("cmake_build").to_str().unwrap().to_string(); 27 | let mut cmd = vec![]; 28 | cmd.push(format!("-B{}", cmake_build_dir)); 29 | cmd.push("-DCMAKE_EXPORT_COMPILE_COMMANDS=ON".to_string()); 30 | cmd.push("-DCMAKE_BUILD_TYPE=Release".to_string()); 31 | cmd.push(format!( 32 | "-DCMAKE_INSTALL_PREFIX={}", 33 | out_dir.to_str().unwrap() 34 | )); 35 | cmd.push("-GNinja".to_string()); 36 | cmd.push("-DCMAKE_SYSTEM_NAME=Android".to_string()); 37 | cmd.push("-DCMAKE_SYSTEM_VERSION=21".to_string()); 38 | cmd.push("-DANDROID_PLATFORM=android-21".to_string()); 39 | cmd.push(format!("-DCMAKE_ANDROID_NDK={}", &ndk.to_str().unwrap())); 40 | cmd.push(format!("-DANDROID_NDK={}", &ndk.to_str().unwrap())); 41 | cmd.push(format!( 42 | "-DCMAKE_MAKE_PROGRAM={}", 43 | find_ninja(&sdk) 44 | .expect("Couldn't find NDK ninja!") 45 | .to_str() 46 | .unwrap() 47 | )); 48 | cmd.push(format!( 49 | "-DCMAKE_TOOLCHAIN_FILE={}", 50 | ndk.join("build") 51 | .join("cmake") 52 | .join("android.toolchain.cmake") 53 | .to_str() 54 | .unwrap() 55 | )); 56 | 57 | if cfg!(feature = "miniaudio") { 58 | cmd.push("-DWITH_MINIAUDIO=ON".to_string()); 59 | } else if cfg!(feature = "alsa") { 60 | cmd.push("-DWITH_ALSA=ON".to_string()); 61 | } else if cfg!(feature = "sdl2-static") { 62 | cmd.push("-DWITH_SDL2_STATIC=ON".to_string()); 63 | } else if cfg!(feature = "sdl2") { 64 | cmd.push("-DWITH_SDL2=ON".to_string()); 65 | } else if cfg!(feature = "openal") { 66 | cmd.push("-DWITH_OPENAL=ON".to_string()); 67 | } else if cfg!(feature = "portaudio") { 68 | cmd.push("-DWITH_PORTAUDIO=ON".to_string()); 69 | } else if cfg!(feature = "xaudio2") { 70 | cmd.push("-DWITH_XAUDIO2=ON".to_string()); 71 | } else if cfg!(feature = "winmm") { 72 | cmd.push("-DWITH_WINMM=ON".to_string()); 73 | } else if cfg!(feature = "wasapi") { 74 | cmd.push("-DWITH_WASAPI=ON".to_string()); 75 | } else if cfg!(feature = "oss") { 76 | cmd.push("-DWITH_OSS=ON".to_string()); 77 | } else if cfg!(feature = "opensles") { 78 | cmd.push("-DWITH_OPENSLES=ON".to_string()); 79 | } else if cfg!(feature = "coreaudio") { 80 | cmd.push("-DWITH_COREAUDIO=ON".to_string()); 81 | } else if cfg!(feature = "jack") { 82 | cmd.push("-DWITH_JACK=ON".to_string()); 83 | } else if cfg!(feature = "nosound") { 84 | cmd.push("-DWITH_NOSOUND=ON".to_string()); 85 | } else if cfg!(feature = "null") { 86 | cmd.push("-DWITH_NULL=ON".to_string()); 87 | } else { 88 | panic!("Unsupported backend!"); 89 | } 90 | 91 | match target_triple { 92 | "i686-linux-android" => { 93 | cmd.push("-DANDROID_ABI=x86".to_string()); 94 | cmd.push("-DCMAKE_ANDROID_ARCH_ABI=x86".to_string()); 95 | } 96 | "aarch64-linux-android" => { 97 | cmd.push("-DANDROID_ABI=arm64-v8a".to_string()); 98 | cmd.push("-DCMAKE_ANDROID_ARCH_ABI=arm64-v8a".to_string()); 99 | } 100 | "armv7-linux-androideabi" => { 101 | cmd.push("-DANDROID_ABI=armeabi-v7a".to_string()); 102 | cmd.push("-DCMAKE_ANDROID_ARCH_ABI=armeabi-v7a".to_string()); 103 | } 104 | "x86_64-linux-android" => { 105 | cmd.push("-DANDROID_ABI=x86_64".to_string()); 106 | cmd.push("-DCMAKE_ANDROID_ARCH_ABI=x86_64".to_string()); 107 | } 108 | _ => panic!("Unknown android triple"), 109 | } 110 | 111 | Command::new("cmake") 112 | .args(&cmd) 113 | .current_dir("sys") 114 | .status() 115 | .expect("CMake is needed for android builds!"); 116 | 117 | Command::new("cmake") 118 | .args(["--build", &cmake_build_dir, "--target", "install"]) 119 | .current_dir("sys") 120 | .status() 121 | .expect("CMake is needed for android builds!"); 122 | } 123 | 124 | fn find_ninja(sdk_path: &Path) -> Option { 125 | let cmk = sdk_path.join("cmake"); 126 | for subdir in fs::read_dir(cmk).unwrap() { 127 | let subdir = subdir 128 | .unwrap() // Shouldn't fail! 129 | .path() 130 | .file_name() 131 | .unwrap() 132 | .to_str() 133 | .unwrap() 134 | .to_owned(); 135 | if subdir.starts_with("3.") { 136 | return Some( 137 | sdk_path 138 | .join("cmake") 139 | .join(subdir) 140 | .join("bin") 141 | .join("ninja"), 142 | ); 143 | } 144 | } 145 | None 146 | } 147 | -------------------------------------------------------------------------------- /soloud-sys/build/link.rs: -------------------------------------------------------------------------------- 1 | use std::path::Path; 2 | 3 | pub fn link(out_dir: &Path) { 4 | if cfg!(feature = "alsa") { 5 | println!("cargo:rustc-link-lib=dylib=asound"); 6 | } else if cfg!(feature = "sdl2-static") { 7 | println!("cargo:rustc-link-lib=static=SDL2"); 8 | } else if cfg!(feature = "sdl2") { 9 | println!("cargo:rustc-link-lib=dylib=SDL2"); 10 | } else if cfg!(feature = "openal") { 11 | println!("cargo:rustc-link-lib=openal"); 12 | } else if cfg!(feature = "portaudio") { 13 | println!("cargo:rustc-link-lib=portaudio"); 14 | } else if cfg!(feature = "xaudio2") { 15 | println!("cargo:rustc-link-lib=xaudio2"); 16 | } else if cfg!(feature = "winmm") { 17 | println!("cargo:rustc-link-lib=winmm"); 18 | } else if cfg!(feature = "wasapi") { 19 | println!("cargo:rustc-link-lib=win-wasapi"); 20 | } else if cfg!(feature = "oss") { 21 | println!("cargo:rustc-link-lib=oss"); 22 | } else if cfg!(feature = "opensles") { 23 | println!("cargo:rustc-link-lib=OpenSLES"); 24 | } else if cfg!(feature = "jack") { 25 | println!("cargo:rustc-link-lib=jack"); 26 | } else { 27 | // nothing to link 28 | } 29 | 30 | println!( 31 | "cargo:rustc-link-search=native={}", 32 | out_dir.join("build").display() 33 | ); 34 | 35 | println!( 36 | "cargo:rustc-link-search=native={}", 37 | out_dir.join("lib").display() 38 | ); 39 | 40 | println!( 41 | "cargo:rustc-link-search=native={}", 42 | out_dir.join("lib").join("Release").display() 43 | ); 44 | 45 | println!("cargo:rustc-link-lib=static=soloud"); 46 | } 47 | -------------------------------------------------------------------------------- /soloud-sys/build/main.rs: -------------------------------------------------------------------------------- 1 | #![allow(clippy::needless_borrow)] 2 | use std::{env, path::PathBuf}; 3 | 4 | mod android; 5 | mod link; 6 | mod source; 7 | 8 | fn main() { 9 | println!("cargo:rerun-if-changed=build/android.rs"); 10 | println!("cargo:rerun-if-changed=build/link.rs"); 11 | println!("cargo:rerun-if-changed=build/main.rs"); 12 | println!("cargo:rerun-if-changed=build/source.rs"); 13 | 14 | let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap()); 15 | let target_triple = env::var("TARGET").unwrap(); 16 | 17 | source::build(&target_triple, &out_dir); 18 | link::link(&out_dir); 19 | } 20 | -------------------------------------------------------------------------------- /soloud-sys/build/source.rs: -------------------------------------------------------------------------------- 1 | use std::{env, path::Path}; 2 | 3 | pub fn build(target_triple: &str, out_dir: &Path) { 4 | println!("cargo:rerun-if-env-changed=CC"); 5 | println!("cargo:rerun-if-env-changed=CXX"); 6 | println!("cargo:rerun-if-env-changed=CFLAGS"); 7 | println!("cargo:rerun-if-env-changed=CXXFLAGS"); 8 | println!("cargo:rerun-if-changed=sys/CMakeLists.txt"); 9 | println!("cargo:rerun-if-changed=sys/soloud_new.cpp"); 10 | println!("cargo:rerun-if-changed=sys/soloud_derives.h"); 11 | println!("cargo:rerun-if-changed=sys/soloud_derives.cpp"); 12 | 13 | if target_triple.contains("android") { 14 | crate::android::build(out_dir, target_triple); 15 | } else { 16 | let mut dst = cmake::Config::new("sys"); 17 | 18 | if cfg!(feature = "use-ninja") || has_program("ninja") { 19 | dst.generator("Ninja"); 20 | } 21 | 22 | if cfg!(feature = "miniaudio") { 23 | dst.define("WITH_MINIAUDIO", "ON"); 24 | } else if cfg!(feature = "alsa") { 25 | dst.define("WITH_ALSA", "ON"); 26 | } else if cfg!(feature = "sdl2-static") { 27 | dst.define("WITH_SDL2_STATIC", "ON"); 28 | } else if cfg!(feature = "sdl2") { 29 | dst.define("WITH_SDL2", "ON"); 30 | } else if cfg!(feature = "openal") { 31 | dst.define("WITH_OPENAL", "ON"); 32 | } else if cfg!(feature = "portaudio") { 33 | dst.define("WITH_PORTAUDIO", "ON"); 34 | } else if cfg!(feature = "xaudio2") { 35 | dst.define("WITH_XAUDIO2", "ON"); 36 | } else if cfg!(feature = "winmm") { 37 | dst.define("WITH_WINMM", "ON"); 38 | } else if cfg!(feature = "wasapi") { 39 | dst.define("WITH_WASAPI", "ON"); 40 | } else if cfg!(feature = "oss") { 41 | dst.define("WITH_OSS", "ON"); 42 | } else if cfg!(feature = "opensles") { 43 | dst.define("WITH_OPENSLES", "ON"); 44 | } else if cfg!(feature = "coreaudio") { 45 | dst.define("WITH_COREAUDIO", "ON"); 46 | } else if cfg!(feature = "jack") { 47 | dst.define("WITH_JACK", "ON"); 48 | } else if cfg!(feature = "nosound") { 49 | dst.define("WITH_NOSOUND", "ON"); 50 | } else if cfg!(feature = "null") { 51 | dst.define("WITH_NULL", "ON"); 52 | } else { 53 | panic!("Unsupported backend!"); 54 | } 55 | 56 | if let Ok(toolchain) = env::var("SOLOUD_TOOLCHAIN") { 57 | dst.define("CMAKE_TOOLCHAIN_FILE", &toolchain); 58 | } 59 | 60 | let profile = match std::env::var("PROFILE").unwrap().as_str() { 61 | "debug" => "Debug", 62 | "release" => "Release", 63 | _ => "Release", 64 | }; 65 | 66 | let _dst = dst 67 | .profile(profile) 68 | .define("CMAKE_EXPORT_COMPILE_COMMANDS", "ON") 69 | .build(); 70 | } 71 | } 72 | 73 | fn has_program(prog: &str) -> bool { 74 | match std::process::Command::new(prog).arg("--version").output() { 75 | Ok(out) => !out.stdout.is_empty(), 76 | _ => false, 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /soloud-sys/examples/sys_simple.rs: -------------------------------------------------------------------------------- 1 | use soloud_sys::soloud::*; 2 | 3 | fn main() { 4 | unsafe { 5 | let sl = Soloud_create(); 6 | Soloud_init(sl); 7 | Soloud_setGlobalVolume(sl, 3.0); 8 | 9 | let wav = Wav_create(); 10 | 11 | let ret = Wav_load(wav, "覆.mp3\0".as_ptr() as _); 12 | 13 | dbg!(ret); 14 | 15 | Soloud_play(sl, wav); 16 | while Soloud_getVoiceCount(sl) > 0 { 17 | // calls to play are non-blocking, so we put the thread to sleep 18 | std::thread::sleep(std::time::Duration::from_millis(100)); 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /soloud-sys/examples/sys_speech.rs: -------------------------------------------------------------------------------- 1 | use soloud_sys::soloud::*; 2 | 3 | fn main() { 4 | unsafe { 5 | let sl = Soloud_create(); 6 | Soloud_init(sl); 7 | std::thread::sleep(std::time::Duration::from_millis(100)); 8 | Soloud_setGlobalVolume(sl, 3.0); 9 | 10 | let speech = Speech_create(); 11 | 12 | let ret = Speech_setText(speech, "Hello World\0".as_ptr() as _); 13 | 14 | dbg!(ret); 15 | 16 | Soloud_play(sl, speech); 17 | while Soloud_getVoiceCount(sl) > 0 { 18 | // calls to play are non-blocking, so we put the thread to sleep 19 | std::thread::sleep(std::time::Duration::from_millis(100)); 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /soloud-sys/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![allow(clippy::needless_doctest_main)] 2 | #![allow(non_camel_case_types)] 3 | #![allow(dead_code)] 4 | #![allow(non_upper_case_globals)] 5 | #![doc = include_str!("../README.md")] 6 | 7 | pub mod soloud; 8 | -------------------------------------------------------------------------------- /soloud-sys/sys/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.15) 2 | project(soloud) 3 | 4 | set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON) 5 | 6 | set(CMAKE_CXX_STANDARD 11) 7 | set(CMAKE_CXX_STANDARD_REQUIRED ON) 8 | 9 | if(MSVC) 10 | string(REGEX REPLACE "/GR" "/GR-" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}") 11 | string(REGEX REPLACE "/EHsc" "/EHs-c-" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}") 12 | else() 13 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-rtti -fno-exceptions") 14 | endif() 15 | 16 | add_compile_options("$<$:/utf-8>") 17 | add_compile_options("$<$:/utf-8>") 18 | 19 | option(WITH_MINIAUDIO "Use the miniaudio backend" OFF) 20 | option(WITH_SDL2_STATIC "Use the SDL2-static backend" OFF) 21 | option(WITH_SDL2 "Use the SDL2 backend" OFF) 22 | option(WITH_ALSA "Use the Alsa backend" OFF) 23 | option(WITH_PORTAUDIO "Use the Portaudio backend" OFF) 24 | option(WITH_COREAUDIO "Use the Coreaudio backend" OFF) 25 | option(WITH_XAUDIO2 "Use the Xaudio2 backend" OFF) 26 | option(WITH_OSS "Use the Oss backend" OFF) 27 | option(WITH_OPENSLES "Use the OpenSLES backend" OFF) 28 | option(WITH_NULL "Use the null driver backend" OFF) 29 | option(WITH_NOSOUND "Use the nosound backend" OFF) 30 | option(WITH_JACK "Use the Jack backend" OFF) 31 | option(WITH_OPENAL "Use the Openal backend" OFF) 32 | option(WITH_WINMM "Use the Winmm backend" OFF) 33 | option(WITH_WASAPI "Use the Wasapi backend" OFF) 34 | 35 | file(GLOB_RECURSE AUDIOSOURCE ${CMAKE_CURRENT_LIST_DIR}/soloud/src/audiosource/*.c*) 36 | file(GLOB_RECURSE CORE ${CMAKE_CURRENT_LIST_DIR}/soloud/src/core/*.c*) 37 | file(GLOB_RECURSE FILTER ${CMAKE_CURRENT_LIST_DIR}/soloud/src/filter/*.c*) 38 | 39 | set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_INSTALL_PREFIX}/lib) 40 | set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_INSTALL_PREFIX}/lib) 41 | set(BACKEND_DIR ${CMAKE_CURRENT_LIST_DIR}/soloud/src/backend) 42 | 43 | if (WITH_MINIAUDIO) 44 | set(BACKEND_SRCS ${BACKEND_DIR}/miniaudio/soloud_miniaudio.cpp) 45 | add_compile_definitions(WITH_MINIAUDIO) 46 | elseif (WITH_NULL) 47 | set(BACKEND_SRCS ${BACKEND_DIR}/null/soloud_null.cpp) 48 | add_compile_definitions(WITH_NULL) 49 | elseif (WITH_NOSOUND) 50 | set(BACKEND_SRCS ${BACKEND_DIR}/nosound/soloud_nosound.cpp) 51 | add_compile_definitions(WITH_NOSOUND) 52 | elseif (WITH_OPENAL) 53 | set(BACKEND_SRCS ${BACKEND_DIR}/openal/soloud_openal.cpp ${BACKEND_DIR}/openal/soloud_openal_dll.c) 54 | add_compile_definitions(WITH_OPENAL) 55 | elseif (WITH_OPENSLES) 56 | set(BACKEND_SRCS ${BACKEND_DIR}/opensles/soloud_opensles.cpp) 57 | add_compile_definitions(WITH_OPENSLES) 58 | elseif (WITH_OSS) 59 | set(BACKEND_SRCS ${BACKEND_DIR}/oss/soloud_oss.cpp) 60 | add_compile_definitions(WITH_OSS) 61 | elseif (WITH_ALSA) 62 | set(BACKEND_SRCS ${BACKEND_DIR}/alsa/soloud_alsa.cpp) 63 | add_compile_definitions(WITH_ALSA) 64 | elseif (WITH_SDL2_STATIC) 65 | set(BACKEND_SRCS ${BACKEND_DIR}/sdl2_static/soloud_sdl2_static.cpp) 66 | add_compile_definitions(WITH_SDL2_STATIC) 67 | elseif (WITH_SDL2) 68 | set(BACKEND_SRCS ${BACKEND_DIR}/sdl/soloud_sdl2.cpp ${BACKEND_DIR}/sdl/soloud_sdl2_dll.c) 69 | add_compile_definitions(WITH_SDL2) 70 | elseif (WITH_PORTAUDIO) 71 | set(BACKEND_SRCS ${BACKEND_DIR}/portaudio/soloud_portaudio.cpp ${BACKEND_DIR}/portaudio/soloud_portaudio_dll.c) 72 | add_compile_definitions(WITH_PORTAUDIO) 73 | elseif (WITH_COREAUDIO) 74 | set(BACKEND_SRCS ${BACKEND_DIR}/coreaudio/soloud_coreaudio.cpp) 75 | add_compile_definitions(WITH_COREAUDIO) 76 | elseif (WITH_JACK) 77 | set(BACKEND_SRCS ${BACKEND_DIR}/jack/soloud_jack.cpp) 78 | add_compile_definitions(WITH_JACK) 79 | elseif (WITH_XAUDIO2) 80 | set(BACKEND_SRCS ${BACKEND_DIR}/xaudio2/soloud_xaudio2.cpp) 81 | add_compile_definitions(WITH_XAUDIO2) 82 | elseif (WITH_WINMM) 83 | set(BACKEND_SRCS ${BACKEND_DIR}/winmm/soloud_winmm.cpp) 84 | add_compile_definitions(WITH_WINMM) 85 | elseif (WITH_WASAPI) 86 | set(BACKEND_SRCS ${BACKEND_DIR}/wasapi/soloud_wasapi.cpp) 87 | add_compile_definitions(WITH_WASAPI) 88 | else() 89 | message(FATAL_ERROR "Unsupported backend!") 90 | endif() 91 | 92 | set(SOLOUD_SRCS 93 | ${CMAKE_CURRENT_LIST_DIR}/soloud_new.cpp 94 | ${CMAKE_CURRENT_LIST_DIR}/soloud_derives.cpp 95 | ${BACKEND_SRCS} 96 | ${CMAKE_CURRENT_LIST_DIR}/soloud/src/c_api/soloud_c.cpp 97 | ${AUDIOSOURCE} 98 | ${CORE} 99 | ${FILTER} 100 | ) 101 | 102 | add_library(soloud STATIC ${SOLOUD_SRCS}) 103 | target_include_directories(soloud PUBLIC ${CMAKE_CURRENT_LIST_DIR}/soloud/include) 104 | 105 | install(TARGETS soloud 106 | DESTINATION ${CMAKE_INSTALL_PREFIX}/lib 107 | ) 108 | 109 | -------------------------------------------------------------------------------- /soloud-sys/sys/examples/ca.cpp: -------------------------------------------------------------------------------- 1 | // g++ -I ../soloud/include example.cpp ../../../target/debug/build/soloud-sys-69e1b085f8a6624a/out/lib/libsoloud.a 2 | 3 | #include 4 | 5 | #include "soloud.h" 6 | #include "soloud_wav.h" 7 | #include "soloud_thread.h" 8 | #include 9 | #include 10 | 11 | constexpr unsigned long BEEP_INTERVAL = 100000; //milliseconds 12 | constexpr unsigned long POLL_INTERVAL = 1000; 13 | 14 | int main() { 15 | for (int i = 0; i < 5; i++) { 16 | SoLoud::Soloud sl; 17 | SoLoud::Wav wav; 18 | sl.init(); 19 | std::ifstream in("../../../beep.mp3", ::std::ios::binary); 20 | std::vector buffer(std::istreambuf_iterator(in), {}); 21 | auto ret = wav.loadMem(buffer.data(), buffer.size(), true, false); 22 | 23 | sl.play(wav); 24 | 25 | while (sl.getVoiceCount() > 0) { 26 | SoLoud::Thread::sleep(POLL_INTERVAL); 27 | } 28 | 29 | sl.deinit(); 30 | SoLoud::Thread::sleep(BEEP_INTERVAL); 31 | } 32 | 33 | return 0; 34 | } 35 | -------------------------------------------------------------------------------- /soloud-sys/sys/examples/example.cpp: -------------------------------------------------------------------------------- 1 | // g++ -I ../soloud/include example.cpp ../../../target/debug/build/soloud-sys-69e1b085f8a6624a/out/lib/libsoloud.a 2 | 3 | #include 4 | 5 | #include "soloud.h" 6 | #include "soloud_wav.h" 7 | #include "soloud_thread.h" 8 | 9 | int main() { 10 | SoLoud::Soloud sl; 11 | SoLoud::Wav wav; 12 | sl.init(); 13 | 14 | auto ret = wav.load("覆.mp3"); 15 | printf("%d\n", ret); 16 | 17 | sl.play(wav); 18 | 19 | while (sl.getActiveVoiceCount() > 0) { 20 | SoLoud::Thread::sleep(100); 21 | } 22 | 23 | sl.deinit(); 24 | 25 | return 0; 26 | } -------------------------------------------------------------------------------- /soloud-sys/sys/soloud_derives.cpp: -------------------------------------------------------------------------------- 1 | #include "soloud.h" 2 | #include "soloud_audiosource.h" 3 | 4 | #include "soloud_derives.h" 5 | 6 | struct AudioCollider_Derived : public SoLoud::AudioCollider { 7 | void *ev_data_ = NULL; 8 | typedef float (*handler)(SoLoud::Soloud *, SoLoud::AudioSourceInstance3dData *, int, 9 | void *data); 10 | handler inner_handler = NULL; 11 | void set_handler(handler h) { 12 | inner_handler = h; 13 | } 14 | void set_handler_data(void *data) { 15 | ev_data_ = data; 16 | } 17 | virtual float collide(SoLoud::Soloud *aSoloud, 18 | SoLoud::AudioSourceInstance3dData *aAudioInstance3dData, 19 | int aUserData) override { 20 | if (ev_data_ && inner_handler) 21 | return 0; 22 | return inner_handler(aSoloud, aAudioInstance3dData, aUserData, ev_data_); 23 | } 24 | }; 25 | 26 | struct AudioAttenuator_Derived : public SoLoud::AudioAttenuator { 27 | void *ev_data_ = NULL; 28 | typedef float (*handler)(float, float, float, float, void *data); 29 | handler inner_handler = NULL; 30 | void set_handler(handler h) { 31 | inner_handler = h; 32 | } 33 | void set_handler_data(void *data) { 34 | ev_data_ = data; 35 | } 36 | virtual float attenuate(float aDistance, float aMinDistance, float aMaxDistance, 37 | float aRolloffFactor) override { 38 | if (ev_data_ && inner_handler) 39 | return 0; 40 | return inner_handler(aDistance, aMinDistance, aMaxDistance, aRolloffFactor, ev_data_); 41 | } 42 | }; 43 | 44 | AudioSourceInstance3dData *AudioSourceInstance3dData_new(AudioSource *as) { 45 | auto temp = new SoLoud::AudioSourceInstance3dData; 46 | temp->init(*(SoLoud::AudioSource *)as); 47 | return (AudioSourceInstance3dData *)temp; 48 | } 49 | 50 | void AudioSourceInstance3dData_delete(AudioSourceInstance3dData *inst) { 51 | delete (SoLoud::AudioSourceInstance3dData *)inst; 52 | } 53 | 54 | AudioCollider AudioCollider_new() { 55 | return (AudioCollider)new AudioCollider_Derived; 56 | } 57 | 58 | void AudioCollider_set_handler(AudioCollider self, 59 | float (*handler)(Soloud *, AudioSourceInstance3dData *, int, 60 | void *data), 61 | void *data) { 62 | ((AudioCollider_Derived *)self)->set_handler((AudioCollider_Derived::handler)handler); 63 | ((AudioCollider_Derived *)self)->set_handler_data(data); 64 | } 65 | 66 | void AudioCollider_delete(AudioCollider inst) { 67 | delete (AudioCollider_Derived *)inst; 68 | } 69 | 70 | AudioAttenuator AudioAttenuator_new() { 71 | return (AudioAttenuator)new AudioAttenuator_Derived; 72 | } 73 | 74 | void AudioAttenuator_set_handler(AudioAttenuator self, 75 | float (*handler)(float, float, float, float, void *data), 76 | void *data) { 77 | ((AudioAttenuator_Derived *)self)->set_handler((AudioAttenuator_Derived::handler)handler); 78 | ((AudioAttenuator_Derived *)self)->set_handler_data(data); 79 | } 80 | 81 | void AudioAttenuator_delete(AudioAttenuator inst) { 82 | delete (AudioAttenuator_Derived *)inst; 83 | } -------------------------------------------------------------------------------- /soloud-sys/sys/soloud_derives.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "soloud/include/soloud_c.h" 4 | 5 | #ifdef __cplusplus 6 | extern "C" { 7 | #endif 8 | 9 | typedef void AudioSourceInstance3dData; 10 | 11 | AudioSourceInstance3dData *AudioSourceInstance3dData_new(AudioSource *as); 12 | 13 | void AudioSourceInstance3dData_delete(AudioSourceInstance3dData *inst); 14 | 15 | AudioCollider AudioCollider_new(); 16 | 17 | void AudioCollider_set_handler(AudioCollider self, 18 | float (*handler)(Soloud *, AudioSourceInstance3dData *, int, void *), 19 | void *data); 20 | 21 | void AudioCollider_delete(AudioCollider); 22 | 23 | AudioAttenuator AudioAttenuator_new(); 24 | 25 | void AudioAttenuator_set_handler(AudioAttenuator self, 26 | float (*handler)(float, float, float, float, void *), void *data); 27 | 28 | void AudioAttenuator_delete(AudioAttenuator); 29 | 30 | #ifdef __cplusplus 31 | } 32 | #endif -------------------------------------------------------------------------------- /soloud-sys/sys/soloud_new.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | void *__gxx_personality_v0 = 0; 4 | 5 | void *operator new(size_t size) { 6 | return malloc(size); 7 | } 8 | 9 | void *operator new[](size_t size) { 10 | return malloc(size); 11 | } 12 | 13 | void operator delete(void *val) noexcept { 14 | free(val); 15 | } 16 | 17 | void operator delete[](void *val) noexcept { 18 | free(val); 19 | } 20 | 21 | extern "C" int __cxa_guard_acquire(long *g) { 22 | return !*(char *)(g); 23 | } 24 | 25 | extern "C" void __cxa_guard_release(long *g) { 26 | *(char *)g = 1; 27 | } 28 | 29 | extern "C" void __cxa_pure_virtual() { 30 | // Do nothing 31 | } 32 | 33 | extern "C" void __cxa_guard_abort(long *) { 34 | // Do nothing 35 | } 36 | 37 | extern "C" void __cxa_atexit() { 38 | // Do nothing 39 | } -------------------------------------------------------------------------------- /soloud/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "soloud" 3 | version = "1.1.1" 4 | authors = ["MoAlyousef "] 5 | edition = "2021" 6 | description = "Rust bindings for the soloud audio engine" 7 | repository = "https://github.com/MoAlyousef/soloud-rs" 8 | documentation = "https://docs.rs/soloud" 9 | keywords = ["audio", "playback", "synthesizer"] 10 | categories = ["multimedia::audio"] 11 | license = "MIT" 12 | exclude = ["./examples"] 13 | readme = "README.md" 14 | rust-version = "1.67" 15 | 16 | [dependencies] 17 | soloud-sys = { path = "../soloud-sys", version = "=1.1.1" } 18 | bitflags = "^2" 19 | paste = "1" 20 | 21 | [features] 22 | default = ["miniaudio"] 23 | use-ninja = ["soloud-sys/use-ninja"] 24 | miniaudio = ["soloud-sys/miniaudio"] 25 | sdl2 = ["soloud-sys/sdl2"] 26 | sdl2-static = ["soloud-sys/sdl2-static"] 27 | portaudio = ["soloud-sys/portaudio"] 28 | openal = ["soloud-sys/openal"] 29 | xaudio2 = ["soloud-sys/xaudio2"] 30 | winmm = ["soloud-sys/winmm"] 31 | wasapi = ["soloud-sys/wasapi"] 32 | oss = ["soloud-sys/oss"] 33 | alsa = ["soloud-sys/alsa"] 34 | opensles = ["soloud-sys/opensles"] 35 | coreaudio = ["soloud-sys/coreaudio"] 36 | jack = ["soloud-sys/jack"] 37 | nosound = ["soloud-sys/nosound"] 38 | null = ["soloud-sys/null"] 39 | -------------------------------------------------------------------------------- /soloud/README.md: -------------------------------------------------------------------------------- 1 | ../README.md -------------------------------------------------------------------------------- /soloud/examples/filter.rs: -------------------------------------------------------------------------------- 1 | use soloud::*; 2 | 3 | fn main() -> Result<(), Box> { 4 | let mut sl = Soloud::default()?; 5 | sl.set_global_volume(3.0); 6 | 7 | let mut wav = audio::Wav::default(); 8 | let mut filt = filter::EchoFilter::default(); 9 | filt.set_params(0.2)?; // Here sets the delay by default for echo filters 10 | 11 | wav.load("sample.wav")?; 12 | wav.set_filter(0, Some(&filt)); 13 | 14 | sl.play(&wav); 15 | while sl.voice_count() > 0 { 16 | std::thread::sleep(std::time::Duration::from_millis(100)); 17 | } 18 | 19 | Ok(()) 20 | } 21 | -------------------------------------------------------------------------------- /soloud/examples/include_bytes.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | eprintln!( 3 | "You have to add `sample.wav` to `soloud` directory and modify this example to run it!!" 4 | ); 5 | // use soloud::*; 6 | // let sl = Soloud::default().unwrap(); 7 | // let mut wav = audio::Wav::default(); 8 | // wav.load_mem(include_bytes!("../../sample.wav")).unwrap(); 9 | // sl.play(&wav); 10 | // while sl.voice_count() > 0 { 11 | // std::thread::sleep(std::time::Duration::from_millis(100)); 12 | // } 13 | } 14 | -------------------------------------------------------------------------------- /soloud/examples/load_mem.rs: -------------------------------------------------------------------------------- 1 | use soloud::*; 2 | 3 | fn main() -> Result<(), Box> { 4 | let sl = Soloud::default()?; 5 | 6 | let mut wav = audio::Wav::default(); 7 | 8 | let bytes = std::fs::read("sample.wav")?; 9 | 10 | wav.load_mem(&bytes)?; 11 | 12 | sl.play(&wav); 13 | while sl.voice_count() > 0 { 14 | std::thread::sleep(std::time::Duration::from_millis(100)); 15 | } 16 | 17 | Ok(()) 18 | } 19 | -------------------------------------------------------------------------------- /soloud/examples/recho.rs: -------------------------------------------------------------------------------- 1 | use soloud::*; 2 | 3 | fn main() -> Result<(), Box> { 4 | let mut sl = Soloud::default()?; 5 | sl.set_global_volume(4.0); 6 | let mut speech = audio::Speech::default(); 7 | 8 | let strings: Vec = std::env::args().collect(); 9 | 10 | if strings.len() < 2 { 11 | speech.set_text("Please provide command line arguments!")?; 12 | sl.play(&speech); 13 | while sl.active_voice_count() > 0 { 14 | std::thread::sleep(std::time::Duration::from_millis(100)); 15 | } 16 | } else { 17 | for i in strings.iter().skip(1) { 18 | speech.set_text(i)?; 19 | 20 | sl.play(&speech); 21 | while sl.active_voice_count() > 0 { 22 | std::thread::sleep(std::time::Duration::from_millis(100)); 23 | } 24 | } 25 | } 26 | 27 | Ok(()) 28 | } 29 | -------------------------------------------------------------------------------- /soloud/examples/simple.rs: -------------------------------------------------------------------------------- 1 | use soloud::*; 2 | 3 | fn main() -> Result<(), Box> { 4 | let mut sl = Soloud::default()?; 5 | sl.set_global_volume(3.0); 6 | 7 | let mut wav = audio::Wav::default(); 8 | 9 | wav.load("song.mp3")?; 10 | 11 | sl.play(&wav); 12 | while sl.voice_count() > 0 { 13 | // calls to play are non-blocking, so we put the thread to sleep 14 | std::thread::sleep(std::time::Duration::from_millis(100)); 15 | } 16 | 17 | // wav.load("Recording.mp3")?; 18 | 19 | // sl.play(&wav); 20 | // while sl.voice_count() > 0 { 21 | // std::thread::sleep(std::time::Duration::from_millis(100)); 22 | // } 23 | 24 | Ok(()) 25 | } 26 | -------------------------------------------------------------------------------- /soloud/examples/speech.rs: -------------------------------------------------------------------------------- 1 | use soloud::*; 2 | 3 | fn main() -> Result<(), Box> { 4 | let mut sl = Soloud::default()?; 5 | sl.set_global_volume(4.0); 6 | 7 | let mut speech = audio::Speech::default(); 8 | 9 | speech.set_text("Hello World")?; 10 | 11 | sl.play(&speech); 12 | while sl.active_voice_count() > 0 { 13 | std::thread::sleep(std::time::Duration::from_millis(100)); 14 | } 15 | 16 | speech.set_text("1 2 3")?; 17 | 18 | sl.play(&speech); 19 | while sl.active_voice_count() > 0 { 20 | std::thread::sleep(std::time::Duration::from_millis(100)); 21 | } 22 | 23 | speech.set_text("Can you hear me?")?; 24 | 25 | sl.play(&speech); 26 | while sl.active_voice_count() > 0 { 27 | std::thread::sleep(std::time::Duration::from_millis(100)); 28 | } 29 | 30 | Ok(()) 31 | } 32 | -------------------------------------------------------------------------------- /soloud/src/audio/ay.rs: -------------------------------------------------------------------------------- 1 | use crate::prelude::*; 2 | use soloud_sys::soloud as ffi; 3 | 4 | /// Ay audio type 5 | #[derive(Debug)] 6 | pub struct Ay { 7 | inner: *mut ffi::Ay, 8 | } 9 | 10 | crate::macros::audio::impl_audio_ext!(Ay); 11 | -------------------------------------------------------------------------------- /soloud/src/audio/bus.rs: -------------------------------------------------------------------------------- 1 | use crate::{prelude::*, Handle}; 2 | use soloud_sys::soloud as ffi; 3 | 4 | /// Bus audio type 5 | #[derive(Debug)] 6 | pub struct Bus { 7 | inner: *mut ffi::Bus, 8 | } 9 | 10 | crate::macros::audio::impl_audio_ext!(Bus); 11 | 12 | impl Bus { 13 | /// Get active voice count 14 | pub fn active_voice_count(&self) -> u32 { 15 | unsafe { ffi::Bus_getActiveVoiceCount(self.inner) } 16 | } 17 | 18 | /// Play audio 19 | pub fn play(&self, sound: &T) -> Handle { 20 | Handle(unsafe { ffi::Bus_play(self.inner, sound.inner()) }) 21 | } 22 | 23 | /// Play audio with extra args 24 | pub fn play_ex(&self, sound: &AS, volume: f32, pan: f32, paused: bool) -> Handle { 25 | Handle(unsafe { ffi::Bus_playEx(self.inner, sound.inner(), volume, pan, paused as i32) }) 26 | } 27 | 28 | /// Play clocked 29 | pub fn play_clocked(&self, sound_time: f64, sound: &AS) -> Handle { 30 | Handle(unsafe { ffi::Bus_playClocked(self.inner, sound_time, sound.inner()) }) 31 | } 32 | 33 | /// Play clocked with extra args 34 | pub fn play_clocked_ex( 35 | &self, 36 | sound_time: f64, 37 | sound: &AS, 38 | volume: f32, 39 | pan: f32, 40 | ) -> Handle { 41 | Handle(unsafe { 42 | ffi::Bus_playClockedEx(self.inner, sound_time, sound.inner(), volume, pan) 43 | }) 44 | } 45 | 46 | /// Play 3D 47 | pub fn play_3d(&self, sound: &AS, pos_x: f32, pos_y: f32, pos_z: f32) -> Handle { 48 | Handle(unsafe { ffi::Bus_play3d(self.inner, sound.inner(), pos_x, pos_y, pos_z) }) 49 | } 50 | 51 | /// Play 3D with extra args 52 | pub fn play_3d_ex( 53 | &self, 54 | sound: &AS, 55 | pos_x: f32, 56 | pos_y: f32, 57 | pos_z: f32, 58 | vel_x: f32, 59 | vel_y: f32, 60 | vel_z: f32, 61 | volume: f32, 62 | paused: bool, 63 | ) -> Handle { 64 | Handle(unsafe { 65 | ffi::Bus_play3dEx( 66 | self.inner, 67 | sound.inner(), 68 | pos_x, 69 | pos_y, 70 | pos_z, 71 | vel_x, 72 | vel_y, 73 | vel_z, 74 | volume, 75 | paused as i32, 76 | ) 77 | }) 78 | } 79 | 80 | /// Play 3D clocked 81 | pub fn play_3d_clocked( 82 | &self, 83 | sound_time: f64, 84 | sound: &AS, 85 | pos_x: f32, 86 | pos_y: f32, 87 | pos_z: f32, 88 | ) -> Handle { 89 | Handle(unsafe { 90 | ffi::Bus_play3dClocked(self.inner, sound_time, sound.inner(), pos_x, pos_y, pos_z) 91 | }) 92 | } 93 | 94 | /// Play 3D clocked with extra args 95 | pub fn play_3d_clocked_ex( 96 | &self, 97 | sound_time: f64, 98 | sound: &AS, 99 | pos_x: f32, 100 | pos_y: f32, 101 | pos_z: f32, 102 | vel_x: f32, 103 | vel_y: f32, 104 | vel_z: f32, 105 | volume: f32, 106 | ) -> Handle { 107 | Handle(unsafe { 108 | ffi::Bus_play3dClockedEx( 109 | self.inner, 110 | sound_time, 111 | sound.inner(), 112 | pos_x, 113 | pos_y, 114 | pos_z, 115 | vel_x, 116 | vel_y, 117 | vel_z, 118 | volume, 119 | ) 120 | }) 121 | } 122 | 123 | /// Enable visualizations 124 | pub fn set_visualize_enable(&self, flag: bool) { 125 | unsafe { ffi::Bus_setVisualizationEnable(self.inner, flag as i32) } 126 | } 127 | 128 | /// Calculate and get 256 floats of FFT data for visualization. Visualization has to be enabled before use 129 | pub fn calc_fft(&self) -> Vec { 130 | unsafe { 131 | let ret = ffi::Bus_calcFFT(self.inner); 132 | let ret = std::slice::from_raw_parts(ret, 256); 133 | ret.to_vec() 134 | } 135 | } 136 | 137 | /// Get 256 floats of wave data for visualization. Visualization has to be enabled before use 138 | pub fn wave(&self) -> Vec { 139 | unsafe { 140 | let ret = ffi::Bus_getWave(self.inner); 141 | let ret = std::slice::from_raw_parts(ret, 256); 142 | ret.to_vec() 143 | } 144 | } 145 | 146 | /// Get approximate volume 147 | pub fn approximate_volume(&self, channel: u32) -> f32 { 148 | unsafe { ffi::Bus_getApproximateVolume(self.inner, channel) } 149 | } 150 | 151 | /// Set bus channels 152 | pub fn set_channels(&mut self, channels: u32) -> Result<(), SoloudError> { 153 | unsafe { 154 | let ret = ffi::Bus_setChannels(self.inner, channels); 155 | if ret != 0 { 156 | Err(SoloudError::Internal(SoloudErrorKind::from_i32(ret))) 157 | } else { 158 | Ok(()) 159 | } 160 | } 161 | } 162 | 163 | /// Get bus resampler 164 | pub fn resampler(&self) -> Resampler { 165 | unsafe { std::mem::transmute(ffi::Bus_getResampler(self.inner)) } 166 | } 167 | 168 | /// Set bus resampler 169 | pub fn set_resampler(&mut self, resampler: Resampler) { 170 | unsafe { ffi::Bus_setResampler(self.inner, resampler as u32) } 171 | } 172 | } 173 | -------------------------------------------------------------------------------- /soloud/src/audio/mod.rs: -------------------------------------------------------------------------------- 1 | mod ay; 2 | mod bus; 3 | mod monotone; 4 | mod noise; 5 | mod openmpt; 6 | mod queue; 7 | mod sfxr; 8 | mod speech; 9 | mod tedsid; 10 | mod vic; 11 | mod vizsn; 12 | mod wav; 13 | mod wavstream; 14 | 15 | pub use ay::*; 16 | pub use bus::*; 17 | pub use monotone::*; 18 | pub use noise::*; 19 | pub use openmpt::*; 20 | pub use queue::*; 21 | pub use sfxr::*; 22 | pub use speech::*; 23 | pub use tedsid::*; 24 | pub use vic::*; 25 | pub use vizsn::*; 26 | pub use wav::*; 27 | pub use wavstream::*; 28 | -------------------------------------------------------------------------------- /soloud/src/audio/monotone.rs: -------------------------------------------------------------------------------- 1 | use crate::prelude::*; 2 | use soloud_sys::soloud as ffi; 3 | use std::path::Path; 4 | 5 | /// Monotone audio type 6 | #[derive(Debug)] 7 | pub struct Monotone { 8 | inner: *mut ffi::Monotone, 9 | } 10 | 11 | crate::macros::load::impl_load_ext!(Monotone); 12 | crate::macros::audio::impl_audio_ext!(Monotone); 13 | 14 | impl Monotone { 15 | /// Set monotone parameters 16 | pub fn set_params(&mut self, hardware_channel: i32) -> Result<(), SoloudError> { 17 | ffi_call!(ffi::Monotone_setParams(self.inner, hardware_channel)) 18 | } 19 | 20 | /// Set monotone parameters specifying the wave form 21 | pub fn set_params_ex( 22 | &mut self, 23 | hardware_channel: i32, 24 | wave_form: WaveForm, 25 | ) -> Result<(), SoloudError> { 26 | ffi_call!(ffi::Monotone_setParamsEx( 27 | self.inner, 28 | hardware_channel, 29 | wave_form as i32 30 | )) 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /soloud/src/audio/noise.rs: -------------------------------------------------------------------------------- 1 | use crate::prelude::*; 2 | use soloud_sys::soloud as ffi; 3 | 4 | /// Noise types 5 | #[repr(i32)] 6 | #[derive(Debug, Clone, Copy, PartialEq, PartialOrd)] 7 | pub enum NoiseType { 8 | /// White noise 9 | White = 0, 10 | /// Pink noise 11 | Pink, 12 | /// Brownish noise 13 | Brownish, 14 | /// Blueish noise 15 | Blueish, 16 | } 17 | 18 | /// Noise audio type 19 | #[derive(Debug)] 20 | pub struct Noise { 21 | inner: *mut ffi::Noise, 22 | } 23 | 24 | crate::macros::audio::impl_audio_ext!(Noise); 25 | 26 | impl Noise { 27 | /// Set noise type 28 | pub fn set_type(&mut self, typ: NoiseType) { 29 | unsafe { ffi::Noise_setType(self.inner, typ as i32) } 30 | } 31 | 32 | /// Set the octave scale 33 | pub fn set_octave_scale( 34 | &mut self, 35 | oct_0: f32, 36 | oct_1: f32, 37 | oct_2: f32, 38 | oct_3: f32, 39 | oct_4: f32, 40 | oct_5: f32, 41 | oct_6: f32, 42 | oct_7: f32, 43 | oct_8: f32, 44 | oct_9: f32, 45 | ) { 46 | unsafe { 47 | ffi::Noise_setOctaveScale( 48 | self.inner, oct_0, oct_1, oct_2, oct_3, oct_4, oct_5, oct_6, oct_7, oct_8, oct_9, 49 | ) 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /soloud/src/audio/openmpt.rs: -------------------------------------------------------------------------------- 1 | use crate::prelude::*; 2 | use soloud_sys::soloud as ffi; 3 | use std::path::Path; 4 | 5 | /// OpenMPT audio type 6 | #[derive(Debug)] 7 | pub struct Openmpt { 8 | inner: *mut ffi::Openmpt, 9 | } 10 | 11 | crate::macros::load::impl_load_ext!(Openmpt); 12 | crate::macros::audio::impl_audio_ext!(Openmpt); 13 | -------------------------------------------------------------------------------- /soloud/src/audio/queue.rs: -------------------------------------------------------------------------------- 1 | use crate::prelude::*; 2 | use soloud_sys::soloud as ffi; 3 | 4 | /// Queue audio type 5 | #[derive(Debug)] 6 | pub struct Queue { 7 | inner: *mut ffi::Queue, 8 | } 9 | 10 | crate::macros::audio::impl_audio_ext!(Queue); 11 | 12 | impl Queue { 13 | /// Play audio 14 | pub fn play(&self, sound: &T) -> Result<(), SoloudError> { 15 | ffi_call!(ffi::Queue_play(self.inner, sound.inner())) 16 | } 17 | 18 | /// Get queue count 19 | pub fn get_count(&self) -> u32 { 20 | unsafe { ffi::Queue_getQueueCount(self.inner) } 21 | } 22 | 23 | /// Check if audio source is currently playing 24 | pub fn is_currently_playing(&self, sound: &AS) -> bool { 25 | unsafe { ffi::Queue_isCurrentlyPlaying(self.inner, sound.inner()) != 0 } 26 | } 27 | 28 | /// Set params from audio source 29 | pub fn set_params_from_audio_source( 30 | &mut self, 31 | sound: &AS, 32 | ) -> Result<(), SoloudError> { 33 | ffi_call!(ffi::Queue_setParamsFromAudioSource( 34 | self.inner, 35 | sound.inner() 36 | )) 37 | } 38 | 39 | /// Set params of the queue 40 | pub fn set_params(&mut self, samplerate: f32) -> Result<(), SoloudError> { 41 | ffi_call!(ffi::Queue_setParams(self.inner, samplerate)) 42 | } 43 | 44 | /// Set params of the queue adding channels 45 | pub fn set_params_ex(&mut self, samplerate: f32, channels: u32) -> Result<(), SoloudError> { 46 | ffi_call!(ffi::Queue_setParamsEx(self.inner, samplerate, channels)) 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /soloud/src/audio/sfxr.rs: -------------------------------------------------------------------------------- 1 | use crate::prelude::*; 2 | use soloud_sys::soloud as ffi; 3 | 4 | /// Sfxr presets 5 | #[repr(i32)] 6 | #[derive(Debug, Copy, Clone, PartialEq, PartialOrd)] 7 | pub enum SfxrPreset { 8 | /// Coin preset 9 | Coin = 0, 10 | /// Laser preset 11 | Laser = 1, 12 | /// Explosion preset 13 | Explosion = 2, 14 | /// PowerUp preset 15 | PowerUp = 3, 16 | /// Hurt preset 17 | Hurt = 4, 18 | /// Jump preset 19 | Jump = 5, 20 | /// Blip preset 21 | Blip = 6, 22 | } 23 | 24 | /// Sfxr audio type 25 | #[derive(Debug)] 26 | pub struct Sfxr { 27 | inner: *mut ffi::Sfxr, 28 | } 29 | 30 | crate::macros::audio::impl_audio_ext!(Sfxr); 31 | 32 | impl Sfxr { 33 | /// Load preset 34 | pub fn load_preset(&mut self, preset: SfxrPreset, rand_seed: i32) -> Result<(), SoloudError> { 35 | ffi_call!(ffi::Sfxr_loadPreset(self.inner, preset as i32, rand_seed)) 36 | } 37 | 38 | /// Resets parameters 39 | pub fn reset_params(&mut self) { 40 | unsafe { ffi::Sfxr_resetParams(self.inner) } 41 | } 42 | 43 | /// Load parameters from a file 44 | pub fn load_params(&mut self, path: &std::path::Path) -> Result<(), SoloudError> { 45 | let path = path 46 | .to_str() 47 | .ok_or(SoloudError::Internal(SoloudErrorKind::FileLoadFailed))?; 48 | let path = std::ffi::CString::new(path)?; 49 | ffi_call!(ffi::Sfxr_loadParams(self.inner, path.as_ptr())) 50 | } 51 | 52 | /// Load parameters from memory 53 | pub fn load_params_mem(&mut self, params: &[u8]) -> Result<(), SoloudError> { 54 | ffi_call!(ffi::Sfxr_loadParamsMemEx( 55 | self.inner, 56 | params.as_ptr() as *mut _, 57 | params.len() as u32, 58 | 1, 59 | 1, 60 | )) 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /soloud/src/audio/speech.rs: -------------------------------------------------------------------------------- 1 | use crate::prelude::*; 2 | use soloud_sys::soloud as ffi; 3 | 4 | /// Klatt wave forms 5 | #[repr(i32)] 6 | #[derive(Debug, Copy, Clone, PartialOrd, PartialEq)] 7 | pub enum KlattWaveForm { 8 | /// Saw wave 9 | Saw = 0, 10 | /// Triangle wav 11 | Triangle, 12 | /// Sin wave 13 | Sin, 14 | /// Square wave 15 | Square, 16 | /// Pulse wave 17 | Pulse, 18 | /// Noise wave 19 | Noise, 20 | /// Warble wave 21 | Warble, 22 | } 23 | 24 | /// Speech audio source 25 | #[derive(Debug)] 26 | pub struct Speech { 27 | inner: *mut ffi::Speech, 28 | } 29 | 30 | crate::macros::audio::impl_audio_ext!(Speech); 31 | 32 | impl Speech { 33 | /// set speech text 34 | pub fn set_text(&mut self, txt: &str) -> Result<(), SoloudError> { 35 | let txt = std::ffi::CString::new(txt)?; 36 | ffi_call!(ffi::Speech_setText(self.inner, txt.as_ptr())) 37 | } 38 | 39 | /// Set speech params 40 | pub fn set_params(&mut self) -> Result<(), SoloudError> { 41 | ffi_call!(ffi::Speech_setParams(self.inner)) 42 | } 43 | 44 | /// Set speech params 45 | pub fn set_params_ex( 46 | &mut self, 47 | base_freq: u32, 48 | base_speed: f32, 49 | base_declination: f32, 50 | base_waveform: KlattWaveForm, 51 | ) -> Result<(), SoloudError> { 52 | ffi_call!(ffi::Speech_setParamsEx( 53 | self.inner, 54 | base_freq, 55 | base_speed, 56 | base_declination, 57 | base_waveform as i32, 58 | )) 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /soloud/src/audio/tedsid.rs: -------------------------------------------------------------------------------- 1 | use crate::prelude::*; 2 | use soloud_sys::soloud as ffi; 3 | use std::path::Path; 4 | 5 | /// TedSid audio source 6 | #[derive(Debug)] 7 | pub struct TedSid { 8 | inner: *mut ffi::TedSid, 9 | } 10 | 11 | crate::macros::load::impl_load_ext!(TedSid); 12 | crate::macros::audio::impl_audio_ext!(TedSid); 13 | -------------------------------------------------------------------------------- /soloud/src/audio/vic.rs: -------------------------------------------------------------------------------- 1 | use crate::prelude::*; 2 | use soloud_sys::soloud as ffi; 3 | 4 | /// Vic model 5 | #[repr(i32)] 6 | #[derive(Debug, Copy, Clone, PartialEq, PartialOrd)] 7 | pub enum VicModel { 8 | /// Pal model 9 | Pal = 0, 10 | /// Ntsc model 11 | Ntsc, 12 | } 13 | 14 | /// Vic register 15 | #[repr(u8)] 16 | #[derive(Debug, Copy, Clone, PartialEq, PartialOrd)] 17 | pub enum VicRegister { 18 | /// Bass register 19 | Bass = 0, 20 | /// Alto register 21 | Alto, 22 | /// Soprano register 23 | Soprano, 24 | /// Noise register 25 | Noise, 26 | /// Max regs register 27 | MaxRegs, 28 | } 29 | 30 | /// Vic-20 emulator audio type 31 | #[derive(Debug)] 32 | pub struct Vic { 33 | inner: *mut ffi::Vic, 34 | } 35 | 36 | crate::macros::audio::impl_audio_ext!(Vic); 37 | 38 | impl Vic { 39 | /// Get the VicModel of the Vic object 40 | pub fn model(&self) -> VicModel { 41 | unsafe { std::mem::transmute(ffi::Vic_getModel(self.inner)) } 42 | } 43 | 44 | /// Set the VicModel of the Vic object 45 | pub fn set_model(&mut self, model: VicModel) { 46 | unsafe { ffi::Vic_setModel(self.inner, model as i32) } 47 | } 48 | 49 | /// Get the VicRegister of the Vic object 50 | pub fn register(&self, reg: i32) -> VicRegister { 51 | unsafe { std::mem::transmute(ffi::Vic_getRegister(self.inner, reg)) } 52 | } 53 | 54 | /// Set the VicRegister of the Vic object 55 | pub fn set_register(&mut self, val: u8, reg: VicRegister) { 56 | unsafe { ffi::Vic_setRegister(self.inner, val as i32, reg as u8) } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /soloud/src/audio/vizsn.rs: -------------------------------------------------------------------------------- 1 | use crate::prelude::*; 2 | use soloud_sys::soloud as ffi; 3 | 4 | /// Vizsn audio type 5 | #[derive(Debug)] 6 | pub struct Vizsn { 7 | inner: *mut ffi::Vizsn, 8 | } 9 | 10 | crate::macros::audio::impl_audio_ext!(Vizsn); 11 | 12 | impl Vizsn { 13 | /// set speech text 14 | pub fn set_text(&mut self, txt: &str) -> Result<(), SoloudError> { 15 | unsafe { 16 | let txt = std::ffi::CString::new(txt)?; 17 | ffi::Vizsn_setText(self.inner, txt.as_ptr() as *mut _); 18 | Ok(()) 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /soloud/src/audio/wav.rs: -------------------------------------------------------------------------------- 1 | use crate::prelude::*; 2 | use soloud_sys::soloud as ffi; 3 | use std::path::Path; 4 | 5 | /// Wav audio type 6 | #[derive(Debug)] 7 | pub struct Wav { 8 | inner: *mut ffi::Wav, 9 | } 10 | 11 | crate::macros::load::impl_load_ext!(Wav); 12 | crate::macros::audio::impl_audio_ext!(Wav); 13 | 14 | impl Wav { 15 | /// Load raw wav data of precise bits 16 | /// # Safety 17 | /// The data must be valid 18 | pub unsafe fn load_raw_wav_8(&mut self, data: &[u8]) -> Result<(), SoloudError> { 19 | ffi_call!(ffi::Wav_loadRawWave8( 20 | self.inner, 21 | data.as_ptr() as *mut _, 22 | data.len() as u32 23 | )) 24 | } 25 | 26 | /// Load raw wav data of precise bits 27 | /// # Safety 28 | /// The data must be valid 29 | pub unsafe fn load_raw_wav_8_ex( 30 | &mut self, 31 | data: &[u8], 32 | samplerate: f32, 33 | channels: u32, 34 | ) -> Result<(), SoloudError> { 35 | ffi_call!(ffi::Wav_loadRawWave8Ex( 36 | self.inner, 37 | data.as_ptr() as *mut _, 38 | data.len() as u32, 39 | samplerate, 40 | channels, 41 | )) 42 | } 43 | 44 | /// Load raw wav data of precise bits 45 | /// # Safety 46 | /// The data must be valid 47 | pub unsafe fn load_raw_wav_16(&mut self, data: &[i16]) -> Result<(), SoloudError> { 48 | ffi_call!(ffi::Wav_loadRawWave16( 49 | self.inner, 50 | data.as_ptr() as *mut _, 51 | data.len() as u32 52 | )) 53 | } 54 | 55 | /// Load raw wav data of precise bits 56 | /// # Safety 57 | /// The data must be valid 58 | pub unsafe fn load_raw_wav_16_ex( 59 | &mut self, 60 | data: &[i16], 61 | samplerate: f32, 62 | channels: u32, 63 | ) -> Result<(), SoloudError> { 64 | ffi_call!(ffi::Wav_loadRawWave16Ex( 65 | self.inner, 66 | data.as_ptr() as *mut _, 67 | data.len() as u32, 68 | samplerate, 69 | channels, 70 | )) 71 | } 72 | 73 | /// Load raw wav data of precise bits 74 | /// # Safety 75 | /// The data must be valid 76 | pub unsafe fn load_raw_wav(&mut self, data: &[f32]) -> Result<(), SoloudError> { 77 | ffi_call!(ffi::Wav_loadRawWave( 78 | self.inner, 79 | data.as_ptr() as *mut _, 80 | data.len() as u32 81 | )) 82 | } 83 | 84 | /// Load raw wav data of precise bits 85 | /// # Safety 86 | /// The data must be valid 87 | pub unsafe fn load_raw_wav_ex( 88 | &mut self, 89 | data: &[f32], 90 | samplerate: f32, 91 | channels: u32, 92 | copy: bool, 93 | take_ownership: bool, 94 | ) -> Result<(), SoloudError> { 95 | ffi_call!(ffi::Wav_loadRawWaveEx( 96 | self.inner, 97 | data.as_ptr() as *mut _, 98 | data.len() as u32, 99 | samplerate, 100 | channels, 101 | copy as i32, 102 | take_ownership as i32, 103 | )) 104 | } 105 | 106 | /// Get the length of the Wav object 107 | pub fn length(&self) -> f64 { 108 | unsafe { ffi::Wav_getLength(self.inner) } 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /soloud/src/audio/wavstream.rs: -------------------------------------------------------------------------------- 1 | use crate::prelude::*; 2 | use soloud_sys::soloud as ffi; 3 | use std::path::Path; 4 | 5 | /// Wavstream audio type 6 | #[derive(Debug)] 7 | pub struct WavStream { 8 | inner: *mut ffi::WavStream, 9 | } 10 | 11 | crate::macros::load::impl_load_ext!(WavStream); 12 | crate::macros::audio::impl_audio_ext!(WavStream); 13 | 14 | impl WavStream { 15 | /// Get the length of the WavStream object 16 | pub fn length(&self) -> f64 { 17 | unsafe { ffi::WavStream_getLength(self.inner) } 18 | } 19 | 20 | /// Load a file to memory 21 | pub fn load_to_mem(&mut self, path: &std::path::Path) -> Result<(), SoloudError> { 22 | let path = path 23 | .to_str() 24 | .ok_or(SoloudError::Internal(SoloudErrorKind::FileLoadFailed))?; 25 | let path = std::ffi::CString::new(path)?; 26 | ffi_call!(ffi::WavStream_loadToMem(self.inner, path.as_ptr())) 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /soloud/src/effects.rs: -------------------------------------------------------------------------------- 1 | use soloud_sys::soloud as ffi; 2 | use std::os::raw::*; 3 | 4 | #[derive(Debug)] 5 | pub struct AudioSourceInstance3dData { 6 | inner: *mut ffi::AudioSourceInstance3dData, 7 | } 8 | 9 | impl AudioSourceInstance3dData { 10 | /// Instantiante a new AudioSourceInstance3dData 11 | pub fn new(engine: &crate::Soloud) -> Self { 12 | unsafe { 13 | let ptr = ffi::AudioSourceInstance3dData_new(engine.inner()); 14 | assert!(!ptr.is_null()); 15 | AudioSourceInstance3dData { inner: ptr } 16 | } 17 | } 18 | 19 | unsafe fn from_ptr(ptr: *mut c_void) -> Self { 20 | assert!(!ptr.is_null()); 21 | AudioSourceInstance3dData { inner: ptr } 22 | } 23 | } 24 | 25 | impl Drop for AudioSourceInstance3dData { 26 | fn drop(&mut self) { 27 | unsafe { ffi::AudioSourceInstance3dData_delete(self.inner) } 28 | } 29 | } 30 | 31 | #[derive(Debug)] 32 | /// Audio Collider struct 33 | pub struct AudioCollider { 34 | inner: ffi::AudioCollider, 35 | } 36 | 37 | impl AudioCollider { 38 | /// Instantiate a new AudioCollider 39 | pub fn default() -> Self { 40 | unsafe { 41 | let ptr = ffi::AudioCollider_new(); 42 | assert!(!ptr.is_null()); 43 | AudioCollider { inner: ptr } 44 | } 45 | } 46 | 47 | /// Override the collide method 48 | /// collide(aSoloud, aAudioInstance3dData, aUserData) 49 | pub fn collide( 50 | &mut self, 51 | cb: Box f32>, 52 | ) { 53 | unsafe { 54 | unsafe extern "C" fn shim( 55 | arg1: *mut *mut c_void, 56 | arg2: *mut c_void, 57 | arg3: c_int, 58 | data: *mut c_void, 59 | ) -> f32 { 60 | let a: *mut Box f32> = 61 | data as *mut Box< 62 | dyn FnMut(&crate::Soloud, &AudioSourceInstance3dData, i32) -> f32, 63 | >; 64 | let f: &mut (dyn FnMut(&crate::Soloud, &AudioSourceInstance3dData, i32) -> f32) = 65 | &mut **a; 66 | let res = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| { 67 | f( 68 | &crate::Soloud::from_ptr(*arg1), 69 | &AudioSourceInstance3dData::from_ptr(arg2), 70 | arg3, 71 | ) 72 | })); 73 | if let Ok(res) = res { 74 | res 75 | } else { 76 | 0.0 77 | } 78 | } 79 | let a: *mut Box f32> = 80 | Box::into_raw(Box::new(cb)); 81 | let data: *mut c_void = a as *mut c_void; 82 | let callback: Option< 83 | unsafe extern "C" fn( 84 | arg1: *mut *mut c_void, 85 | arg2: *mut c_void, 86 | arg3: c_int, 87 | arg4: *mut c_void, 88 | ) -> f32, 89 | > = Some(shim); 90 | ffi::AudioCollider_set_handler(self.inner, callback, data); 91 | } 92 | } 93 | 94 | /// Gets the inner ptr of the audio collider 95 | /// # Safety 96 | /// The inner pointer should be modified with care! 97 | pub unsafe fn inner(&self) -> ffi::AudioCollider { 98 | self.inner 99 | } 100 | } 101 | 102 | impl Drop for AudioCollider { 103 | fn drop(&mut self) { 104 | unsafe { ffi::AudioCollider_delete(self.inner) } 105 | } 106 | } 107 | 108 | /// Audio Attenuator struct 109 | #[derive(Debug)] 110 | pub struct AudioAttenuator { 111 | inner: ffi::AudioAttenuator, 112 | } 113 | 114 | impl AudioAttenuator { 115 | /// Instantiate a new AudioAttenuator 116 | pub fn default() -> Self { 117 | unsafe { 118 | let ptr = ffi::AudioAttenuator_new(); 119 | assert!(!ptr.is_null()); 120 | AudioAttenuator { inner: ptr } 121 | } 122 | } 123 | 124 | /// Override the attenuate method 125 | /// attenuate(aDistance, aMinDistance, aMaxDistance, aRolloffFactor) 126 | pub fn attenuate(&mut self, cb: Box f32>) { 127 | assert!(!self.inner.is_null()); 128 | unsafe { 129 | unsafe extern "C" fn shim( 130 | arg1: f32, 131 | arg2: f32, 132 | arg3: f32, 133 | arg4: f32, 134 | data: *mut c_void, 135 | ) -> f32 { 136 | let a: *mut Box f32> = 137 | data as *mut Box f32>; 138 | let f: &mut (dyn FnMut(f32, f32, f32, f32) -> f32) = &mut **a; 139 | let res = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| { 140 | f(arg1, arg2, arg3, arg4) 141 | })); 142 | if let Ok(res) = res { 143 | res 144 | } else { 145 | 0.0 146 | } 147 | } 148 | let a: *mut Box f32> = Box::into_raw(Box::new(cb)); 149 | let data: *mut c_void = a as *mut c_void; 150 | let callback: Option< 151 | unsafe extern "C" fn( 152 | arg1: f32, 153 | arg2: f32, 154 | arg3: f32, 155 | arg4: f32, 156 | data: *mut c_void, 157 | ) -> f32, 158 | > = Some(shim); 159 | ffi::AudioAttenuator_set_handler(self.inner, callback, data); 160 | } 161 | } 162 | 163 | /// Gets the inner ptr of the audio attenuator 164 | /// # Safety 165 | /// The inner pointer should be modified with care! 166 | pub unsafe fn inner(&self) -> ffi::AudioAttenuator { 167 | self.inner 168 | } 169 | } 170 | 171 | impl Drop for AudioAttenuator { 172 | fn drop(&mut self) { 173 | unsafe { ffi::AudioAttenuator_delete(self.inner) } 174 | } 175 | } 176 | -------------------------------------------------------------------------------- /soloud/src/filter/bass.rs: -------------------------------------------------------------------------------- 1 | use super::ParamType; 2 | use crate::prelude::*; 3 | 4 | /// Bass boost filter attribute 5 | #[repr(u32)] 6 | #[derive(Copy, Clone, Debug, PartialOrd, PartialEq)] 7 | pub enum BassBoostFilterAttr { 8 | /// Wet attribute 9 | Wet = 0, 10 | /// Boost attribute 11 | Boost = 1, 12 | } 13 | 14 | /// Bass boost filter 15 | #[derive(Debug)] 16 | pub struct BassboostFilter { 17 | inner: *mut soloud_sys::soloud::BassboostFilter, 18 | } 19 | 20 | crate::macros::filter::impl_filter_ext!(BassboostFilter); 21 | crate::macros::filter::impl_filter_type!(BassBoostFilterAttr); 22 | 23 | impl BassboostFilter { 24 | /// Set filter params 25 | pub fn set_params(&mut self, delay: f32) -> Result<(), SoloudError> { 26 | ffi_call!(soloud_sys::soloud::BassboostFilter_setParams( 27 | self.inner, delay 28 | )) 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /soloud/src/filter/biquad.rs: -------------------------------------------------------------------------------- 1 | use super::ParamType; 2 | use crate::prelude::*; 3 | 4 | /// Biquad resonant filter attributes 5 | #[repr(u32)] 6 | #[derive(Copy, Clone, Debug, PartialOrd, PartialEq)] 7 | pub enum BiquadResonantFilterAttr { 8 | /// Wet attribute 9 | Wet = 0, 10 | /// Type attribute 11 | Type, 12 | /// Frequency attribute 13 | Freq, 14 | /// Resoncance attribute 15 | Resonance, 16 | } 17 | 18 | /// Biquad resonant filter types 19 | #[repr(i32)] 20 | #[derive(Copy, Clone, Debug, PartialOrd, PartialEq)] 21 | pub enum BiquadResonantFilterType { 22 | /// Lowpass biquad resonant filter 23 | LowPass, 24 | /// Highpass biquad resonant filter 25 | HighPass, 26 | /// Bandpass biquad resonant filter 27 | BandPass, 28 | } 29 | 30 | /// Biquad resonant filter 31 | #[derive(Debug)] 32 | pub struct BiquadResonantFilter { 33 | inner: *mut soloud_sys::soloud::BiquadResonantFilter, 34 | } 35 | 36 | crate::macros::filter::impl_filter_ext!(BiquadResonantFilter); 37 | crate::macros::filter::impl_filter_type!(BiquadResonantFilterType); 38 | crate::macros::filter::impl_filter_type!(BiquadResonantFilterAttr); 39 | 40 | impl BiquadResonantFilter { 41 | /// Set filter params 42 | pub fn set_params( 43 | &mut self, 44 | filter_type: BiquadResonantFilterType, 45 | frequency: f32, 46 | resonance: f32, 47 | ) -> Result<(), SoloudError> { 48 | ffi_call!(soloud_sys::soloud::BiquadResonantFilter_setParams( 49 | self.inner, 50 | filter_type as i32, 51 | frequency, 52 | resonance, 53 | )) 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /soloud/src/filter/dc.rs: -------------------------------------------------------------------------------- 1 | use super::ParamType; 2 | use crate::prelude::*; 3 | 4 | /// DC removal filter 5 | #[derive(Debug)] 6 | pub struct DCRemovalFilter { 7 | inner: *mut soloud_sys::soloud::DCRemovalFilter, 8 | } 9 | 10 | crate::macros::filter::impl_filter_ext!(DCRemovalFilter); 11 | 12 | impl DCRemovalFilter { 13 | /// Set filter params 14 | pub fn set_params(&mut self) -> Result<(), SoloudError> { 15 | ffi_call!(soloud_sys::soloud::DCRemovalFilter_setParams(self.inner)) 16 | } 17 | 18 | /// Set filter params with extra args 19 | pub fn set_params_ex(&mut self, delay: f32) -> Result<(), SoloudError> { 20 | ffi_call!(soloud_sys::soloud::DCRemovalFilter_setParamsEx( 21 | self.inner, delay 22 | )) 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /soloud/src/filter/echo.rs: -------------------------------------------------------------------------------- 1 | use super::ParamType; 2 | use crate::prelude::*; 3 | 4 | /// Echo filter attributes 5 | #[repr(u32)] 6 | #[derive(Copy, Clone, Debug, PartialOrd, PartialEq)] 7 | pub enum EchoFilterAttr { 8 | /// Wet attribute 9 | Wet = 0, 10 | /// Delay attribute 11 | Delay, 12 | /// Decay attribute 13 | Decay, 14 | /// Filter attribute 15 | Filter, 16 | } 17 | 18 | /// Echo filter 19 | #[derive(Debug)] 20 | pub struct EchoFilter { 21 | inner: *mut soloud_sys::soloud::EchoFilter, 22 | } 23 | 24 | crate::macros::filter::impl_filter_ext!(EchoFilter); 25 | crate::macros::filter::impl_filter_type!(EchoFilterAttr); 26 | 27 | impl EchoFilter { 28 | /// Set filter params 29 | pub fn set_params(&mut self, delay: f32) -> Result<(), SoloudError> { 30 | ffi_call!(soloud_sys::soloud::EchoFilter_setParams(self.inner, delay)) 31 | } 32 | 33 | /// Set filter params with extra args 34 | pub fn set_params_ex( 35 | &mut self, 36 | delay: f32, 37 | decay: f32, 38 | filter: f32, 39 | ) -> Result<(), SoloudError> { 40 | ffi_call!(soloud_sys::soloud::EchoFilter_setParamsEx( 41 | self.inner, delay, decay, filter 42 | )) 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /soloud/src/filter/fft.rs: -------------------------------------------------------------------------------- 1 | use super::ParamType; 2 | use crate::prelude::*; 3 | 4 | /// FFT Filter 5 | #[derive(Debug)] 6 | pub struct FFTFilter { 7 | inner: *mut soloud_sys::soloud::FFTFilter, 8 | } 9 | 10 | crate::macros::filter::impl_filter_ext!(FFTFilter); 11 | -------------------------------------------------------------------------------- /soloud/src/filter/flanger.rs: -------------------------------------------------------------------------------- 1 | use super::ParamType; 2 | use crate::prelude::*; 3 | 4 | /// Flanger filter attributes 5 | #[repr(u32)] 6 | #[derive(Copy, Clone, Debug, PartialOrd, PartialEq)] 7 | pub enum FlangerFilterAttr { 8 | /// Wet attribute 9 | Wet = 0, 10 | /// Delay attribute 11 | Delay = 1, 12 | /// Frequency attribute 13 | Freq = 2, 14 | } 15 | 16 | /// Flanger filter 17 | #[derive(Debug)] 18 | pub struct FlangerFilter { 19 | inner: *mut soloud_sys::soloud::FlangerFilter, 20 | } 21 | 22 | crate::macros::filter::impl_filter_ext!(FlangerFilter); 23 | crate::macros::filter::impl_filter_type!(FlangerFilterAttr); 24 | 25 | impl FlangerFilter { 26 | /// Set filter params 27 | pub fn set_params(&mut self, delay: f32, freq: f32) -> Result<(), SoloudError> { 28 | ffi_call!(soloud_sys::soloud::FlangerFilter_setParams( 29 | self.inner, delay, freq 30 | )) 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /soloud/src/filter/freeverb.rs: -------------------------------------------------------------------------------- 1 | use super::ParamType; 2 | use crate::prelude::*; 3 | 4 | /// Freeverb filter 5 | #[derive(Debug)] 6 | pub struct FreeverbFilter { 7 | inner: *mut soloud_sys::soloud::FreeverbFilter, 8 | } 9 | 10 | crate::macros::filter::impl_filter_ext!(FreeverbFilter); 11 | 12 | impl FreeverbFilter { 13 | /// Set filter params 14 | pub fn set_params( 15 | &mut self, 16 | mode: f32, 17 | room_size: f32, 18 | damp: f32, 19 | width: f32, 20 | ) -> Result<(), SoloudError> { 21 | ffi_call!(soloud_sys::soloud::FreeverbFilter_setParams( 22 | self.inner, mode, room_size, damp, width, 23 | )) 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /soloud/src/filter/lofi.rs: -------------------------------------------------------------------------------- 1 | use super::ParamType; 2 | use crate::prelude::*; 3 | 4 | /// Lofi filter attributes 5 | #[repr(u32)] 6 | #[derive(Copy, Clone, Debug, PartialOrd, PartialEq)] 7 | pub enum LofiFilterAttr { 8 | /// Wet attribute 9 | Wet = 0, 10 | /// Samplerate attribute 11 | Samplerate = 1, 12 | /// Bitdepth attribute 13 | BitDepth = 2, 14 | } 15 | 16 | /// Lofi filter 17 | #[derive(Debug)] 18 | pub struct LofiFilter { 19 | inner: *mut soloud_sys::soloud::LofiFilter, 20 | } 21 | 22 | crate::macros::filter::impl_filter_ext!(LofiFilter); 23 | crate::macros::filter::impl_filter_type!(LofiFilterAttr); 24 | 25 | impl LofiFilter { 26 | /// Set filter params 27 | pub fn set_params(&mut self, samplerate: f32, bit_depth: f32) -> Result<(), SoloudError> { 28 | ffi_call!(soloud_sys::soloud::LofiFilter_setParams( 29 | self.inner, samplerate, bit_depth 30 | )) 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /soloud/src/filter/mod.rs: -------------------------------------------------------------------------------- 1 | mod bass; 2 | mod biquad; 3 | mod dc; 4 | mod echo; 5 | mod fft; 6 | mod flanger; 7 | mod freeverb; 8 | mod lofi; 9 | mod robotize; 10 | mod waveshaper; 11 | 12 | pub use bass::*; 13 | pub use biquad::*; 14 | pub use dc::*; 15 | pub use echo::*; 16 | pub use fft::*; 17 | pub use flanger::*; 18 | pub use freeverb::*; 19 | pub use lofi::*; 20 | pub use robotize::*; 21 | pub use waveshaper::*; 22 | 23 | /// The associated parameter types 24 | #[repr(u32)] 25 | #[derive(Copy, Clone, Debug, PartialEq, PartialOrd)] 26 | pub enum ParamType { 27 | /// Floating-point parameter type 28 | Float = 0, 29 | /// Integral parameter type 30 | Int, 31 | /// Boolean parameter type 32 | Bool, 33 | } 34 | -------------------------------------------------------------------------------- /soloud/src/filter/robotize.rs: -------------------------------------------------------------------------------- 1 | use super::ParamType; 2 | use crate::prelude::*; 3 | 4 | /// Robotize filter attributes 5 | #[repr(u32)] 6 | #[derive(Copy, Clone, Debug, PartialOrd, PartialEq)] 7 | pub enum RobotizeFilterAttr { 8 | /// Wet attribute 9 | Wet = 0, 10 | /// Frequency attribute 11 | Freq = 1, 12 | /// Wave attribute 13 | Wave = 2, 14 | } 15 | 16 | /// Robotize filter 17 | #[derive(Debug)] 18 | pub struct RobotizeFilter { 19 | inner: *mut soloud_sys::soloud::RobotizeFilter, 20 | } 21 | 22 | crate::macros::filter::impl_filter_ext!(RobotizeFilter); 23 | crate::macros::filter::impl_filter_type!(RobotizeFilterAttr); 24 | 25 | impl RobotizeFilter { 26 | /// Set filter params 27 | pub fn set_params(&mut self, freq: f32, wave_form: WaveForm) { 28 | unsafe { soloud_sys::soloud::RobotizeFilter_setParams(self.inner, freq, wave_form as i32) } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /soloud/src/filter/waveshaper.rs: -------------------------------------------------------------------------------- 1 | use super::ParamType; 2 | use crate::prelude::*; 3 | 4 | /// Wave shaper filter attributes 5 | #[repr(u32)] 6 | #[derive(Copy, Clone, Debug, PartialOrd, PartialEq)] 7 | pub enum WaveShaperFilterAttr { 8 | /// "Wet" attribute 9 | Wet = 0, 10 | /// "Amount" attribute 11 | Amount = 1, 12 | } 13 | 14 | /// Wrapper around the wave shaper filter 15 | #[derive(Debug)] 16 | pub struct WaveShaperFilter { 17 | inner: *mut soloud_sys::soloud::WaveShaperFilter, 18 | } 19 | 20 | crate::macros::filter::impl_filter_ext!(WaveShaperFilter); 21 | crate::macros::filter::impl_filter_type!(WaveShaperFilterAttr); 22 | 23 | impl WaveShaperFilter { 24 | /// Set filter params 25 | pub fn set_params(&mut self, amount: f32) -> Result<(), SoloudError> { 26 | ffi_call!(soloud_sys::soloud::WaveShaperFilter_setParams( 27 | self.inner, amount 28 | )) 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /soloud/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![allow(unused_unsafe)] 2 | #![allow(non_upper_case_globals)] 3 | #![warn(missing_docs)] 4 | #![allow(clippy::too_many_arguments)] 5 | #![allow(clippy::needless_doctest_main)] 6 | #![doc = include_str!("../README.md")] 7 | 8 | /// FFI function call with error handling 9 | /// 10 | /// NOTE: It has to be defined _before_ declaring sub modules. 11 | macro_rules! ffi_call { 12 | ($s:expr) => { 13 | match unsafe { $s } { 14 | 0 => Ok(()), 15 | code => Err(SoloudError::Internal(SoloudErrorKind::from_i32(code))), 16 | } 17 | }; 18 | } 19 | 20 | /// Audio module containing all audio types 21 | pub mod audio; 22 | /// Filter module containing all filter types and attributes 23 | pub mod filter; 24 | /// Internal macros 25 | mod macros; 26 | /// Prelude module containing all traits and error codes 27 | pub mod prelude; 28 | 29 | pub use audio::*; 30 | pub use filter::*; 31 | pub use prelude::*; 32 | 33 | use soloud_sys::soloud as ffi; 34 | 35 | /// A wrapper around the raw handle of the audio object which can be used to adjust the parameters 36 | /// of the sound while it's playing 37 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] 38 | pub struct Handle(u32); 39 | 40 | impl Handle { 41 | /// Primary bus 42 | pub const PRIMARY: Self = Self(0); 43 | 44 | /// Create a handle from a raw value 45 | /// # Safety 46 | /// The value must be a valid handle 47 | pub unsafe fn from_raw(raw: u32) -> Self { 48 | Self(raw) 49 | } 50 | 51 | /// Retrieve the raw value of the handle 52 | pub fn raw(&self) -> u32 { 53 | self.0 54 | } 55 | } 56 | 57 | /// Backends supported by the Soloud crate 58 | #[repr(u32)] 59 | #[derive(Debug, Clone, Copy)] 60 | pub enum Backend { 61 | /// Autoselection by Soloud 62 | Auto = 0, 63 | /// Sdl2 64 | Sdl2 = 2, 65 | /// Portaudio 66 | Portaudio = 3, 67 | /// Winmm 68 | Winmm = 4, 69 | /// Xaudio2 70 | Xaudio2 = 5, 71 | /// Wasapi 72 | Wasapi = 6, 73 | /// Alsa 74 | Alsa = 7, 75 | /// Jack 76 | Jack = 8, 77 | /// Oss 78 | Oss = 9, 79 | /// OpenAL 80 | OpenAL = 10, 81 | /// Coreaudio 82 | CoreAudio = 11, 83 | /// OpenSLES 84 | OpenSLES = 12, 85 | /// Miniaudio 86 | Miniaudio = 14, 87 | /// Nosound 88 | Nosound = 15, 89 | /// Null driver 90 | Null = 16, 91 | } 92 | 93 | /// Wrapper around the Soloud native object 94 | #[derive(Debug)] 95 | pub struct Soloud { 96 | inner: *mut ffi::Soloud, 97 | } 98 | 99 | unsafe impl Send for Soloud {} 100 | 101 | unsafe impl Sync for Soloud {} 102 | 103 | impl Soloud { 104 | /// Creates an uninitialized instance of a Soloud engine 105 | /// # Safety 106 | /// Creates an uninitialized instance of Soloud 107 | pub unsafe fn default_uninit() -> std::mem::MaybeUninit { 108 | unsafe { 109 | let ptr = ffi::Soloud_create(); 110 | assert!(!ptr.is_null()); 111 | std::mem::MaybeUninit::new(Soloud { inner: ptr }) 112 | } 113 | } 114 | 115 | /// initialize an uninitialized instance of Soloud 116 | /// # Safety 117 | /// initializes an uninitialized instance of Soloud 118 | pub unsafe fn init(&mut self) -> Result<(), SoloudError> { 119 | assert!(!self.inner.is_null()); 120 | ffi_call!(ffi::Soloud_init(self.inner)) 121 | } 122 | 123 | /// Creates a default initialized instance of soloud 124 | #[allow(clippy::should_implement_trait)] 125 | pub fn default() -> Result { 126 | unsafe { 127 | let mut temp = Soloud::default_uninit().assume_init(); 128 | temp.init()?; 129 | Ok(temp) 130 | } 131 | } 132 | 133 | /// initialize an uninitialized instance of Soloud with extra args 134 | pub fn init_ex( 135 | &mut self, 136 | flags: SoloudFlag, 137 | backend: Backend, 138 | samplerate: u32, 139 | buf_size: u32, 140 | channels: u32, 141 | ) -> Result<(), SoloudError> { 142 | assert!(!self.inner.is_null()); 143 | ffi_call!(ffi::Soloud_initEx( 144 | self.inner, 145 | flags.bits() as u32, 146 | backend as u32, 147 | samplerate, 148 | buf_size, 149 | channels, 150 | )) 151 | } 152 | 153 | /// Creates a default initialized instance of soloud 154 | pub fn new( 155 | flags: SoloudFlag, 156 | backend: Backend, 157 | samplerate: u32, 158 | buf_size: u32, 159 | channels: u32, 160 | ) -> Result { 161 | let mut temp = unsafe { Soloud::default_uninit().assume_init() }; 162 | temp.init_ex(flags, backend, samplerate, buf_size, channels)?; 163 | Ok(temp) 164 | } 165 | 166 | /// Gets the current version of the Soloud library 167 | pub fn version(&self) -> u32 { 168 | assert!(!self.inner.is_null()); 169 | unsafe { ffi::Soloud_getVersion(self.inner) } 170 | } 171 | 172 | /// Gets the backend id 173 | pub fn backend_id(&self) -> u32 { 174 | unsafe { ffi::Soloud_getBackendId(self.inner) } 175 | } 176 | 177 | /// Gets the backend name 178 | pub fn backend_string(&self) -> String { 179 | assert!(!self.inner.is_null()); 180 | unsafe { 181 | let ptr = ffi::Soloud_getBackendString(self.inner); 182 | assert!(!ptr.is_null()); 183 | std::ffi::CStr::from_ptr(ptr).to_string_lossy().to_string() 184 | } 185 | } 186 | 187 | /// Get the backend channels 188 | pub fn backend_channels(&self) -> u32 { 189 | assert!(!self.inner.is_null()); 190 | unsafe { ffi::Soloud_getBackendChannels(self.inner) } 191 | } 192 | 193 | /// Get the backend samplerate 194 | pub fn backend_samplerate(&self) -> u32 { 195 | assert!(!self.inner.is_null()); 196 | unsafe { ffi::Soloud_getBackendSamplerate(self.inner) } 197 | } 198 | 199 | /// Get the backend buffer size 200 | pub fn backend_buffer_size(&self) -> u32 { 201 | assert!(!self.inner.is_null()); 202 | unsafe { ffi::Soloud_getBackendBufferSize(self.inner) } 203 | } 204 | 205 | /// Set speaker position 206 | pub fn set_speaker_position( 207 | &mut self, 208 | channel: u32, 209 | x: f32, 210 | y: f32, 211 | z: f32, 212 | ) -> Result<(), SoloudError> { 213 | assert!(!self.inner.is_null()); 214 | ffi_call!(ffi::Soloud_setSpeakerPosition(self.inner, channel, x, y, z)) 215 | } 216 | 217 | /// Get the speaker position 218 | pub fn speaker_position(&self, channel: u32) -> Result<(f32, f32, f32), SoloudError> { 219 | assert!(!self.inner.is_null()); 220 | let mut x = 0.0; 221 | let mut y = 0.0; 222 | let mut z = 0.0; 223 | ffi_call!(ffi::Soloud_getSpeakerPosition( 224 | self.inner, channel, &mut x, &mut y, &mut z 225 | ))?; 226 | Ok((x, y, z)) 227 | } 228 | 229 | /// Play audio with extra args 230 | pub fn play_ex( 231 | &self, 232 | sound: &AS, 233 | volume: f32, 234 | pan: f32, 235 | paused: bool, 236 | bus: Handle, 237 | ) -> Handle { 238 | assert!(!self.inner.is_null()); 239 | Handle(unsafe { 240 | ffi::Soloud_playEx(self.inner, sound.inner(), volume, pan, paused as i32, bus.0) 241 | }) 242 | } 243 | 244 | /// Play clocked 245 | pub fn play_clocked(&self, sound_time: f64, sound: &AS) -> Handle { 246 | assert!(!self.inner.is_null()); 247 | Handle(unsafe { ffi::Soloud_playClocked(self.inner, sound_time, sound.inner()) }) 248 | } 249 | 250 | /// Play clocked with extra args 251 | pub fn play_clocked_ex( 252 | &self, 253 | sound_time: f64, 254 | sound: &AS, 255 | volume: f32, 256 | pan: f32, 257 | bus: Handle, 258 | ) -> Handle { 259 | assert!(!self.inner.is_null()); 260 | Handle(unsafe { 261 | ffi::Soloud_playClockedEx(self.inner, sound_time, sound.inner(), volume, pan, bus.0) 262 | }) 263 | } 264 | 265 | /// Play 3D 266 | pub fn play_3d(&self, sound: &AS, pos_x: f32, pos_y: f32, pos_z: f32) -> Handle { 267 | assert!(!self.inner.is_null()); 268 | Handle(unsafe { ffi::Soloud_play3d(self.inner, sound.inner(), pos_x, pos_y, pos_z) }) 269 | } 270 | 271 | /// Play 3D with extra args 272 | pub fn play_3d_ex( 273 | &self, 274 | sound: &AS, 275 | pos_x: f32, 276 | pos_y: f32, 277 | pos_z: f32, 278 | vel_x: f32, 279 | vel_y: f32, 280 | vel_z: f32, 281 | volume: f32, 282 | paused: bool, 283 | bus: Handle, 284 | ) -> Handle { 285 | assert!(!self.inner.is_null()); 286 | Handle(unsafe { 287 | ffi::Soloud_play3dEx( 288 | self.inner, 289 | sound.inner(), 290 | pos_x, 291 | pos_y, 292 | pos_z, 293 | vel_x, 294 | vel_y, 295 | vel_z, 296 | volume, 297 | paused as i32, 298 | bus.0, 299 | ) 300 | }) 301 | } 302 | 303 | /// Play 3D clocked 304 | pub fn play_3d_clocked( 305 | &self, 306 | sound_time: f64, 307 | sound: &AS, 308 | pos_x: f32, 309 | pos_y: f32, 310 | pos_z: f32, 311 | ) -> Handle { 312 | assert!(!self.inner.is_null()); 313 | Handle(unsafe { 314 | ffi::Soloud_play3dClocked(self.inner, sound_time, sound.inner(), pos_x, pos_y, pos_z) 315 | }) 316 | } 317 | 318 | /// Play 3D clocked with extra args 319 | pub fn play_3d_clocked_ex( 320 | &self, 321 | sound_time: f64, 322 | sound: &AS, 323 | pos_x: f32, 324 | pos_y: f32, 325 | pos_z: f32, 326 | vel_x: f32, 327 | vel_y: f32, 328 | vel_z: f32, 329 | volume: f32, 330 | bus: Handle, 331 | ) -> Handle { 332 | assert!(!self.inner.is_null()); 333 | Handle(unsafe { 334 | ffi::Soloud_play3dClockedEx( 335 | self.inner, 336 | sound_time, 337 | sound.inner(), 338 | pos_x, 339 | pos_y, 340 | pos_z, 341 | vel_x, 342 | vel_y, 343 | vel_z, 344 | volume, 345 | bus.0, 346 | ) 347 | }) 348 | } 349 | 350 | /// Play in the background 351 | pub fn play_background(&self, sound: &AS) -> Handle { 352 | assert!(!self.inner.is_null()); 353 | Handle(unsafe { ffi::Soloud_playBackground(self.inner, sound.inner()) }) 354 | } 355 | 356 | /// Play in the background with extra args 357 | pub fn play_background_ex( 358 | &self, 359 | sound: &AS, 360 | volume: f32, 361 | paused: bool, 362 | bus: Handle, 363 | ) -> Handle { 364 | assert!(!self.inner.is_null()); 365 | Handle(unsafe { 366 | ffi::Soloud_playBackgroundEx(self.inner, sound.inner(), volume, paused as i32, bus.0) 367 | }) 368 | } 369 | 370 | /// Seek in seconds 371 | pub fn seek(&self, voice_handle: Handle, seconds: f64) -> Result<(), SoloudError> { 372 | assert!(!self.inner.is_null()); 373 | ffi_call!(ffi::Soloud_seek(self.inner, voice_handle.0, seconds)) 374 | } 375 | 376 | /// Stop audio by handle 377 | pub fn stop(&self, voice_handle: Handle) { 378 | assert!(!self.inner.is_null()); 379 | unsafe { ffi::Soloud_stop(self.inner, voice_handle.0) } 380 | } 381 | 382 | /// Stop all audio 383 | pub fn stop_all(&self) { 384 | assert!(!self.inner.is_null()); 385 | unsafe { ffi::Soloud_stopAll(self.inner) } 386 | } 387 | 388 | /// Deinitialize the soloud engine 389 | /// # Safety 390 | /// Deinitializing will require correct reinitialization 391 | pub unsafe fn deinit(&mut self) { 392 | assert!(!self.inner.is_null()); 393 | unsafe { 394 | ffi::Soloud_deinit(self.inner); 395 | } 396 | } 397 | 398 | /// Play audio, returns a handle identifying the played audio 399 | pub fn play(&self, sound: &T) -> Handle { 400 | assert!(!self.inner.is_null()); 401 | Handle(unsafe { ffi::Soloud_play(self.inner, sound.inner()) }) 402 | } 403 | 404 | /// Get active voice count 405 | pub fn active_voice_count(&self) -> u32 { 406 | assert!(!self.inner.is_null()); 407 | unsafe { ffi::Soloud_getActiveVoiceCount(self.inner) } 408 | } 409 | 410 | /// Get voice count 411 | pub fn voice_count(&self) -> u32 { 412 | assert!(!self.inner.is_null()); 413 | unsafe { ffi::Soloud_getVoiceCount(self.inner) } 414 | } 415 | 416 | /// Set global volume 417 | pub fn set_global_volume(&mut self, val: f32) { 418 | assert!(!self.inner.is_null()); 419 | unsafe { ffi::Soloud_setGlobalVolume(self.inner, val) } 420 | } 421 | 422 | /// Stop audio source 423 | pub fn stop_audio_source(&self, sound: &AS) { 424 | assert!(!self.inner.is_null()); 425 | unsafe { ffi::Soloud_stopAudioSource(self.inner, sound.inner()) } 426 | } 427 | 428 | /// Count audio source 429 | pub fn count_audio_source(&self, sound: &AS) -> i32 { 430 | assert!(!self.inner.is_null()); 431 | unsafe { ffi::Soloud_countAudioSource(self.inner, sound.inner()) } 432 | } 433 | 434 | /// Get stream time 435 | pub fn stream_time(&self, voice_handle: Handle) -> f64 { 436 | assert!(!self.inner.is_null()); 437 | unsafe { ffi::Soloud_getStreamTime(self.inner, voice_handle.0) } 438 | } 439 | 440 | /// Get stream position 441 | pub fn stream_position(&self, voice_handle: Handle) -> f64 { 442 | assert!(!self.inner.is_null()); 443 | unsafe { ffi::Soloud_getStreamPosition(self.inner, voice_handle.0) } 444 | } 445 | 446 | /// Pause audio 447 | pub fn pause(&self, voice_handle: Handle) -> bool { 448 | assert!(!self.inner.is_null()); 449 | unsafe { ffi::Soloud_getPause(self.inner, voice_handle.0) != 0 } 450 | } 451 | 452 | /// Get audio volume 453 | pub fn volume(&self, voice_handle: Handle) -> f32 { 454 | assert!(!self.inner.is_null()); 455 | unsafe { ffi::Soloud_getVolume(self.inner, voice_handle.0) } 456 | } 457 | 458 | /// Get overall volume 459 | pub fn overall_volume(&self, voice_handle: Handle) -> f32 { 460 | assert!(!self.inner.is_null()); 461 | unsafe { ffi::Soloud_getOverallVolume(self.inner, voice_handle.0) } 462 | } 463 | 464 | /// Get pan value 465 | pub fn pan(&self, voice_handle: Handle) -> f32 { 466 | assert!(!self.inner.is_null()); 467 | unsafe { ffi::Soloud_getPan(self.inner, voice_handle.0) } 468 | } 469 | 470 | /// Get samplerate of audio 471 | pub fn samplerate(&self, voice_handle: Handle) -> f32 { 472 | assert!(!self.inner.is_null()); 473 | unsafe { ffi::Soloud_getSamplerate(self.inner, voice_handle.0) } 474 | } 475 | 476 | /// Return whether protect voice is set 477 | pub fn protect_voice(&self, voice_handle: Handle) -> bool { 478 | assert!(!self.inner.is_null()); 479 | unsafe { ffi::Soloud_getProtectVoice(self.inner, voice_handle.0) != 0 } 480 | } 481 | 482 | /// Check whether a handle is a valid voice handle 483 | pub fn is_valid_voice_handle(&self, voice_handle: Handle) -> bool { 484 | assert!(!self.inner.is_null()); 485 | unsafe { ffi::Soloud_isValidVoiceHandle(self.inner, voice_handle.0) != 0 } 486 | } 487 | 488 | /// Get relative play speed 489 | pub fn relative_play_speed(&self, voice_handle: Handle) -> f32 { 490 | assert!(!self.inner.is_null()); 491 | unsafe { ffi::Soloud_getRelativePlaySpeed(self.inner, voice_handle.0) } 492 | } 493 | 494 | /// Get post clip scaler 495 | pub fn post_clip_scaler(&self) -> f32 { 496 | assert!(!self.inner.is_null()); 497 | unsafe { ffi::Soloud_getPostClipScaler(self.inner) } 498 | } 499 | 500 | /// Get main resampler 501 | pub fn main_resampler(&self) -> Resampler { 502 | assert!(!self.inner.is_null()); 503 | unsafe { std::mem::transmute(ffi::Soloud_getMainResampler(self.inner)) } 504 | } 505 | 506 | /// Get global volume 507 | pub fn global_volume(&self) -> f32 { 508 | assert!(!self.inner.is_null()); 509 | unsafe { ffi::Soloud_getGlobalVolume(self.inner) } 510 | } 511 | 512 | /// Get max active voice count 513 | pub fn max_active_voice_count(&self) -> u32 { 514 | assert!(!self.inner.is_null()); 515 | unsafe { ffi::Soloud_getMaxActiveVoiceCount(self.inner) } 516 | } 517 | 518 | /// Return whether an audio is looping 519 | pub fn looping(&self, voice_handle: Handle) -> bool { 520 | assert!(!self.inner.is_null()); 521 | unsafe { ffi::Soloud_getLooping(self.inner, voice_handle.0) != 0 } 522 | } 523 | 524 | /// Check whether an audio auto stops 525 | pub fn auto_stop(&self, voice_handle: Handle) -> bool { 526 | assert!(!self.inner.is_null()); 527 | unsafe { ffi::Soloud_getAutoStop(self.inner, voice_handle.0) != 0 } 528 | } 529 | 530 | /// Get loop point 531 | pub fn loop_point(&self, voice_handle: Handle) -> f64 { 532 | assert!(!self.inner.is_null()); 533 | unsafe { ffi::Soloud_getLoopPoint(self.inner, voice_handle.0) } 534 | } 535 | 536 | /// Set loop point 537 | pub fn set_loop_point(&mut self, voice_handle: Handle, loop_point: f64) { 538 | assert!(!self.inner.is_null()); 539 | unsafe { ffi::Soloud_setLoopPoint(self.inner, voice_handle.0, loop_point) } 540 | } 541 | 542 | /// Set whether audio is looping 543 | pub fn set_looping(&mut self, voice_handle: Handle, flag: bool) { 544 | assert!(!self.inner.is_null()); 545 | unsafe { ffi::Soloud_setLooping(self.inner, voice_handle.0, flag as i32) } 546 | } 547 | 548 | /// Set auto stop 549 | pub fn set_auto_stop(&mut self, voice_handle: Handle, flag: bool) { 550 | assert!(!self.inner.is_null()); 551 | unsafe { ffi::Soloud_setAutoStop(self.inner, voice_handle.0, flag as i32) } 552 | } 553 | 554 | /// Set max active voice count 555 | pub fn set_max_active_voice_count(&mut self, count: u32) -> Result<(), SoloudError> { 556 | assert!(!self.inner.is_null()); 557 | ffi_call!(ffi::Soloud_setMaxActiveVoiceCount(self.inner, count)) 558 | } 559 | 560 | /// Set inaudible behaviour 561 | pub fn set_inaudible_behavior(&mut self, voice_handle: Handle, must_tick: bool, kill: bool) { 562 | assert!(!self.inner.is_null()); 563 | unsafe { 564 | ffi::Soloud_setInaudibleBehavior( 565 | self.inner, 566 | voice_handle.0, 567 | must_tick as i32, 568 | kill as i32, 569 | ) 570 | } 571 | } 572 | 573 | /// Set post clip scaler 574 | pub fn set_post_clip_scaler(&mut self, scaler: f32) { 575 | assert!(!self.inner.is_null()); 576 | unsafe { ffi::Soloud_setPostClipScaler(self.inner, scaler) } 577 | } 578 | 579 | /// Set main resampler 580 | pub fn set_main_resampler(&mut self, resampler: Resampler) { 581 | assert!(!self.inner.is_null()); 582 | unsafe { ffi::Soloud_setMainResampler(self.inner, resampler as u32) } 583 | } 584 | 585 | /// Set whether a handle pauses 586 | pub fn set_pause(&mut self, voice_handle: Handle, flag: bool) { 587 | assert!(!self.inner.is_null()); 588 | unsafe { ffi::Soloud_setPause(self.inner, voice_handle.0, flag as i32) } 589 | } 590 | 591 | /// Set pause for all handles 592 | pub fn set_pause_all(&mut self, flag: bool) { 593 | assert!(!self.inner.is_null()); 594 | unsafe { ffi::Soloud_setPauseAll(self.inner, flag as i32) } 595 | } 596 | 597 | /// Set relative play speed 598 | pub fn set_relative_play_speed( 599 | &mut self, 600 | voice_handle: Handle, 601 | speed: f32, 602 | ) -> Result<(), SoloudError> { 603 | assert!(!self.inner.is_null()); 604 | ffi_call!(ffi::Soloud_setRelativePlaySpeed( 605 | self.inner, 606 | voice_handle.0, 607 | speed 608 | )) 609 | } 610 | 611 | /// Set whether an audio source has protect voice 612 | pub fn set_protect_voice(&mut self, voice_handle: Handle, flag: bool) { 613 | assert!(!self.inner.is_null()); 614 | unsafe { ffi::Soloud_setProtectVoice(self.inner, voice_handle.0, flag as i32) } 615 | } 616 | 617 | /// Set samplerate 618 | pub fn set_samplerate(&mut self, voice_handle: Handle, samplerate: f32) { 619 | assert!(!self.inner.is_null()); 620 | unsafe { ffi::Soloud_setSamplerate(self.inner, voice_handle.0, samplerate) } 621 | } 622 | 623 | /// Set pan 624 | pub fn set_pan(&mut self, voice_handle: Handle, pan: f32) { 625 | assert!(!self.inner.is_null()); 626 | unsafe { ffi::Soloud_setPan(self.inner, voice_handle.0, pan) } 627 | } 628 | 629 | /// Set pan absolute 630 | pub fn set_pan_absolute(&mut self, voice_handle: Handle, lvolume: f32, rvolume: f32) { 631 | assert!(!self.inner.is_null()); 632 | unsafe { ffi::Soloud_setPanAbsolute(self.inner, voice_handle.0, lvolume, rvolume) } 633 | } 634 | 635 | /// Set channel volume 636 | pub fn set_channel_volume(&mut self, voice_handle: Handle, channel: u32, volume: f32) { 637 | assert!(!self.inner.is_null()); 638 | unsafe { ffi::Soloud_setChannelVolume(self.inner, voice_handle.0, channel, volume) } 639 | } 640 | 641 | /// Set volume by handle 642 | pub fn set_volume(&mut self, voice_handle: Handle, volume: f32) { 643 | assert!(!self.inner.is_null()); 644 | unsafe { ffi::Soloud_setVolume(self.inner, voice_handle.0, volume) } 645 | } 646 | 647 | /// Set delay samples 648 | pub fn set_delay_samples(&mut self, voice_handle: Handle, samples: u32) { 649 | assert!(!self.inner.is_null()); 650 | unsafe { ffi::Soloud_setDelaySamples(self.inner, voice_handle.0, samples) } 651 | } 652 | 653 | /// Set up volume fader 654 | pub fn fade_volume(&self, voice_handle: Handle, to: f32, time: f64) { 655 | assert!(!self.inner.is_null()); 656 | unsafe { ffi::Soloud_fadeVolume(self.inner, voice_handle.0, to, time) } 657 | } 658 | 659 | /// Set up panning fader 660 | pub fn fade_pan(&self, voice_handle: Handle, to: f32, time: f64) { 661 | assert!(!self.inner.is_null()); 662 | unsafe { ffi::Soloud_fadePan(self.inner, voice_handle.0, to, time) } 663 | } 664 | 665 | /// Set fader relative play speed 666 | pub fn fade_relative_play_speed(&self, voice_handle: Handle, to: f32, time: f64) { 667 | assert!(!self.inner.is_null()); 668 | unsafe { ffi::Soloud_fadeRelativePlaySpeed(self.inner, voice_handle.0, to, time) } 669 | } 670 | 671 | /// Set fader global volume 672 | pub fn fade_global_volume(&self, to: f32, time: f64) { 673 | assert!(!self.inner.is_null()); 674 | unsafe { ffi::Soloud_fadeGlobalVolume(self.inner, to, time) } 675 | } 676 | 677 | /// Schedule a pause 678 | pub fn schedule_pause(&self, voice_handle: Handle, time: f64) { 679 | assert!(!self.inner.is_null()); 680 | unsafe { ffi::Soloud_schedulePause(self.inner, voice_handle.0, time) } 681 | } 682 | 683 | /// Schedule a stop 684 | pub fn schedule_stop(&self, voice_handle: Handle, time: f64) { 685 | assert!(!self.inner.is_null()); 686 | unsafe { ffi::Soloud_scheduleStop(self.inner, voice_handle.0, time) } 687 | } 688 | 689 | /// Set up volume oscillator 690 | pub fn oscillate_volume(&self, voice_handle: Handle, from: f32, to: f32, time: f64) { 691 | assert!(!self.inner.is_null()); 692 | unsafe { ffi::Soloud_oscillateVolume(self.inner, voice_handle.0, from, to, time) } 693 | } 694 | 695 | /// Set up panning oscillator 696 | pub fn oscillate_pan(&self, voice_handle: Handle, from: f32, to: f32, time: f64) { 697 | assert!(!self.inner.is_null()); 698 | unsafe { ffi::Soloud_oscillatePan(self.inner, voice_handle.0, from, to, time) } 699 | } 700 | 701 | /// Oscillator relative play speed 702 | pub fn oscillate_relative_play_speed( 703 | &self, 704 | voice_handle: Handle, 705 | from: f32, 706 | to: f32, 707 | time: f64, 708 | ) { 709 | assert!(!self.inner.is_null()); 710 | unsafe { 711 | ffi::Soloud_oscillateRelativePlaySpeed(self.inner, voice_handle.0, from, to, time) 712 | } 713 | } 714 | 715 | /// Get oscillator global volume 716 | pub fn oscillate_global_volume(&self, from: f32, to: f32, time: f64) { 717 | assert!(!self.inner.is_null()); 718 | unsafe { ffi::Soloud_oscillateGlobalVolume(self.inner, from, to, time) } 719 | } 720 | 721 | /// Enable visualizations 722 | pub fn set_visualize_enable(&self, flag: bool) { 723 | assert!(!self.inner.is_null()); 724 | unsafe { ffi::Soloud_setVisualizationEnable(self.inner, flag as i32) } 725 | } 726 | 727 | /// Calculate and get 256 floats of FFT data for visualization. Visualization has to be enabled before use 728 | pub fn calc_fft(&self) -> Vec { 729 | assert!(!self.inner.is_null()); 730 | unsafe { 731 | let ret = ffi::Soloud_calcFFT(self.inner); 732 | let ret = std::slice::from_raw_parts(ret, 256); 733 | ret.to_vec() 734 | } 735 | } 736 | 737 | /// Get 256 floats of wave data for visualization. Visualization has to be enabled before use 738 | pub fn wave(&self) -> Vec { 739 | assert!(!self.inner.is_null()); 740 | unsafe { 741 | let ret = ffi::Soloud_getWave(self.inner); 742 | let ret = std::slice::from_raw_parts(ret, 256); 743 | ret.to_vec() 744 | } 745 | } 746 | 747 | /// Get approximate volume 748 | pub fn approximate_volume(&self, channel: u32) -> f32 { 749 | assert!(!self.inner.is_null()); 750 | unsafe { ffi::Soloud_getApproximateVolume(self.inner, channel) } 751 | } 752 | 753 | /// Get loop count 754 | pub fn loop_count(&self, voice_handle: Handle) -> u32 { 755 | assert!(!self.inner.is_null()); 756 | unsafe { ffi::Soloud_getLoopCount(self.inner, voice_handle.0) } 757 | } 758 | 759 | /// get info by key 760 | pub fn info(&self, voice_handle: Handle, key: u32) -> f32 { 761 | assert!(!self.inner.is_null()); 762 | unsafe { ffi::Soloud_getInfo(self.inner, voice_handle.0, key) } 763 | } 764 | 765 | /// Create a voice group 766 | pub fn create_voice_group(&self) -> Handle { 767 | assert!(!self.inner.is_null()); 768 | Handle(unsafe { ffi::Soloud_createVoiceGroup(self.inner) }) 769 | } 770 | 771 | /// Destroy a voice group 772 | pub fn destroy_voice_group(&self, voice_group_handle: Handle) -> Result<(), SoloudError> { 773 | assert!(!self.inner.is_null()); 774 | ffi_call!(ffi::Soloud_destroyVoiceGroup( 775 | self.inner, 776 | voice_group_handle.0 777 | )) 778 | } 779 | 780 | /// Add a voice handle to a voice group 781 | pub fn add_voice_to_group( 782 | &self, 783 | voice_group_handle: Handle, 784 | voice_handle: Handle, 785 | ) -> Result<(), SoloudError> { 786 | assert!(!self.inner.is_null()); 787 | ffi_call!(ffi::Soloud_addVoiceToGroup( 788 | self.inner, 789 | voice_group_handle.0, 790 | voice_handle.0 791 | )) 792 | } 793 | 794 | /// Check whether a handle is of a voice group 795 | pub fn is_voice_group(&self, voice_group_handle: Handle) -> bool { 796 | assert!(!self.inner.is_null()); 797 | unsafe { ffi::Soloud_isVoiceGroup(self.inner, voice_group_handle.0) != 0 } 798 | } 799 | 800 | /// Check whether a voice group is empty 801 | pub fn is_voice_group_empty(&self, voice_group_handle: Handle) -> bool { 802 | assert!(!self.inner.is_null()); 803 | unsafe { ffi::Soloud_isVoiceGroupEmpty(self.inner, voice_group_handle.0) != 0 } 804 | } 805 | 806 | /// Update 3D audio 807 | pub fn update_3d_audio(&self) { 808 | assert!(!self.inner.is_null()); 809 | unsafe { ffi::Soloud_update3dAudio(self.inner) } 810 | } 811 | 812 | /// Set 3D sound speed 813 | pub fn set_3d_sound_speed(&self, speed: f32) -> Result<(), SoloudError> { 814 | assert!(!self.inner.is_null()); 815 | ffi_call!(ffi::Soloud_set3dSoundSpeed(self.inner, speed)) 816 | } 817 | 818 | /// Get 3d sound speed 819 | pub fn get_3d_sound_speed(&self) -> f32 { 820 | assert!(!self.inner.is_null()); 821 | unsafe { ffi::Soloud_get3dSoundSpeed(self.inner) } 822 | } 823 | 824 | /// Set 3D listener parameters 825 | pub fn set_3d_listener_params( 826 | &mut self, 827 | pos_x: f32, 828 | pos_y: f32, 829 | pos_z: f32, 830 | at_x: f32, 831 | at_y: f32, 832 | at_z: f32, 833 | up_x: f32, 834 | up_y: f32, 835 | up_z: f32, 836 | ) { 837 | assert!(!self.inner.is_null()); 838 | unsafe { 839 | ffi::Soloud_set3dListenerParameters( 840 | self.inner, pos_x, pos_y, pos_z, at_x, at_y, at_z, up_x, up_y, up_z, 841 | ) 842 | } 843 | } 844 | 845 | /// Set 3D listerner parameters with extra args 846 | pub fn set_3d_listener_params_ex( 847 | &mut self, 848 | pos_x: f32, 849 | pos_y: f32, 850 | pos_z: f32, 851 | at_x: f32, 852 | at_y: f32, 853 | at_z: f32, 854 | up_x: f32, 855 | up_y: f32, 856 | up_z: f32, 857 | velocity_x: f32, 858 | velocity_y: f32, 859 | velocity_z: f32, 860 | ) { 861 | assert!(!self.inner.is_null()); 862 | unsafe { 863 | ffi::Soloud_set3dListenerParametersEx( 864 | self.inner, pos_x, pos_y, pos_z, at_x, at_y, at_z, up_x, up_y, up_z, velocity_x, 865 | velocity_y, velocity_z, 866 | ) 867 | } 868 | } 869 | 870 | /// Set 3D listener position 871 | pub fn set_3d_listener_position(&mut self, pos_x: f32, pos_y: f32, pos_z: f32) { 872 | assert!(!self.inner.is_null()); 873 | unsafe { ffi::Soloud_set3dListenerPosition(self.inner, pos_x, pos_y, pos_z) } 874 | } 875 | 876 | /// Set 3D listener position with extra params 877 | pub fn set_3d_listener_at(&mut self, at_x: f32, at_y: f32, at_z: f32) { 878 | assert!(!self.inner.is_null()); 879 | unsafe { ffi::Soloud_set3dListenerAt(self.inner, at_x, at_y, at_z) } 880 | } 881 | 882 | /// Set up 3D listener 883 | pub fn set_3d_listener_up(&mut self, up_x: f32, up_y: f32, up_z: f32) { 884 | assert!(!self.inner.is_null()); 885 | unsafe { ffi::Soloud_set3dListenerUp(self.inner, up_x, up_y, up_z) } 886 | } 887 | 888 | /// Set 3D listener velocity 889 | pub fn set_3d_listener_velocity(&mut self, velocity_x: f32, velocity_y: f32, velocity_z: f32) { 890 | assert!(!self.inner.is_null()); 891 | unsafe { ffi::Soloud_set3dListenerVelocity(self.inner, velocity_x, velocity_y, velocity_z) } 892 | } 893 | 894 | /// Set 3D source parameters 895 | pub fn set_3d_source_params( 896 | &mut self, 897 | voice_handle: Handle, 898 | pos_x: f32, 899 | pos_y: f32, 900 | pos_z: f32, 901 | ) { 902 | assert!(!self.inner.is_null()); 903 | unsafe { 904 | ffi::Soloud_set3dSourceParameters(self.inner, voice_handle.0, pos_x, pos_y, pos_z) 905 | } 906 | } 907 | 908 | /// Set 3D source parameters 909 | pub fn set_3d_source_params_ex( 910 | &mut self, 911 | voice_handle: Handle, 912 | pos_x: f32, 913 | pos_y: f32, 914 | pos_z: f32, 915 | velocity_x: f32, 916 | velocity_y: f32, 917 | velocity_z: f32, 918 | ) { 919 | assert!(!self.inner.is_null()); 920 | unsafe { 921 | ffi::Soloud_set3dSourceParametersEx( 922 | self.inner, 923 | voice_handle.0, 924 | pos_x, 925 | pos_y, 926 | pos_z, 927 | velocity_x, 928 | velocity_y, 929 | velocity_z, 930 | ) 931 | } 932 | } 933 | 934 | /// Set 3D source position 935 | pub fn set_3d_source_position( 936 | &mut self, 937 | voice_handle: Handle, 938 | pos_x: f32, 939 | pos_y: f32, 940 | pos_z: f32, 941 | ) { 942 | assert!(!self.inner.is_null()); 943 | unsafe { ffi::Soloud_set3dSourcePosition(self.inner, voice_handle.0, pos_x, pos_y, pos_z) } 944 | } 945 | 946 | /// Set 3D source velocity 947 | pub fn set_3d_source_velocity( 948 | &mut self, 949 | voice_handle: Handle, 950 | velocity_x: f32, 951 | velocity_y: f32, 952 | velocity_z: f32, 953 | ) { 954 | assert!(!self.inner.is_null()); 955 | unsafe { 956 | ffi::Soloud_set3dSourceVelocity( 957 | self.inner, 958 | voice_handle.0, 959 | velocity_x, 960 | velocity_y, 961 | velocity_z, 962 | ) 963 | } 964 | } 965 | 966 | /// Set 3D source min and max distances 967 | pub fn set_3d_source_minmax_distance( 968 | &mut self, 969 | voice_handle: Handle, 970 | min_distance: f32, 971 | max_distance: f32, 972 | ) { 973 | assert!(!self.inner.is_null()); 974 | unsafe { 975 | ffi::Soloud_set3dSourceMinMaxDistance( 976 | self.inner, 977 | voice_handle.0, 978 | min_distance, 979 | max_distance, 980 | ) 981 | } 982 | } 983 | 984 | /// Set 3D source attenuation 985 | pub fn set_3d_source_attenuation( 986 | &mut self, 987 | voice_handle: Handle, 988 | model: AttenuationModel, 989 | rolloff_factor: f32, 990 | ) { 991 | assert!(!self.inner.is_null()); 992 | unsafe { 993 | ffi::Soloud_set3dSourceAttenuation( 994 | self.inner, 995 | voice_handle.0, 996 | model as u32, 997 | rolloff_factor, 998 | ) 999 | } 1000 | } 1001 | 1002 | /// Set 3D source doppler factor 1003 | pub fn set_3d_source_doppler_factor(&mut self, voice_handle: Handle, doppler_factor: f32) { 1004 | assert!(!self.inner.is_null()); 1005 | unsafe { ffi::Soloud_set3dSourceDopplerFactor(self.inner, voice_handle.0, doppler_factor) } 1006 | } 1007 | 1008 | /// The back-end can call the mix function to request a number of stereo 1009 | /// samples from SoLoud. The samples will be in float format, and the 1010 | /// back-end is responsible for converting them to the desired output 1011 | /// format. 1012 | pub fn mix(&mut self, buffer: &mut [f32]) { 1013 | assert!(!self.inner.is_null()); 1014 | unsafe { ffi::Soloud_mix(self.inner, buffer.as_mut_ptr(), buffer.len() as u32) } 1015 | } 1016 | 1017 | /// Since so many back-ends prefer 16 bit signed data instead of float 1018 | /// data, SoLoud also provides a mix call that outputs signed 16 bit data. 1019 | pub fn mix_signed_16(&mut self, buffer: &mut [i16]) { 1020 | assert!(!self.inner.is_null()); 1021 | unsafe { ffi::Soloud_mixSigned16(self.inner, buffer.as_mut_ptr(), buffer.len() as u32) } 1022 | } 1023 | 1024 | /// Set filter parameters 1025 | pub fn set_filter_param( 1026 | &mut self, 1027 | voice_handle: Handle, 1028 | filter_id: u32, 1029 | attr: impl FilterAttr, 1030 | val: f32, 1031 | ) { 1032 | assert!(!self.inner.is_null()); 1033 | unsafe { 1034 | ffi::Soloud_setFilterParameter( 1035 | self.inner, 1036 | voice_handle.0, 1037 | filter_id, 1038 | attr.to_u32(), 1039 | val, 1040 | ) 1041 | } 1042 | } 1043 | 1044 | /// Get filter parameter by filter id 1045 | pub fn filter_param( 1046 | &mut self, 1047 | voice_handle: Handle, 1048 | filter_id: u32, 1049 | attr: impl FilterAttr, 1050 | ) -> f32 { 1051 | assert!(!self.inner.is_null()); 1052 | unsafe { 1053 | ffi::Soloud_getFilterParameter(self.inner, voice_handle.0, filter_id, attr.to_u32()) 1054 | } 1055 | } 1056 | 1057 | /// Get fader filter params 1058 | pub fn fade_filter_param( 1059 | &mut self, 1060 | voice_handle: Handle, 1061 | filter_id: u32, 1062 | attr: impl FilterAttr, 1063 | to: f32, 1064 | time: f64, 1065 | ) { 1066 | assert!(!self.inner.is_null()); 1067 | unsafe { 1068 | ffi::Soloud_fadeFilterParameter( 1069 | self.inner, 1070 | voice_handle.0, 1071 | filter_id, 1072 | attr.to_u32(), 1073 | to, 1074 | time, 1075 | ) 1076 | } 1077 | } 1078 | 1079 | /// Get oscillator filter params 1080 | pub fn oscillate_filter_param( 1081 | &mut self, 1082 | voice_handle: Handle, 1083 | filter_id: u32, 1084 | attr: impl FilterAttr, 1085 | from: f32, 1086 | to: f32, 1087 | time: f64, 1088 | ) { 1089 | assert!(!self.inner.is_null()); 1090 | unsafe { 1091 | ffi::Soloud_oscillateFilterParameter( 1092 | self.inner, 1093 | voice_handle.0, 1094 | filter_id, 1095 | attr.to_u32(), 1096 | from, 1097 | to, 1098 | time, 1099 | ) 1100 | } 1101 | } 1102 | 1103 | /// Set a global filter 1104 | pub fn set_global_filter(&mut self, filter_id: u32, filter: impl FilterExt) { 1105 | assert!(!self.inner.is_null()); 1106 | unsafe { ffi::Soloud_setGlobalFilter(self.inner, filter_id, filter.inner()) } 1107 | } 1108 | 1109 | /// Get the inner pointer of the Soloud engine 1110 | /// # Safety 1111 | /// The pointer must remain valid 1112 | pub unsafe fn inner(&self) -> *mut ffi::Soloud { 1113 | self.inner 1114 | } 1115 | 1116 | // unsafe fn from_ptr(ptr: *mut std::os::raw::c_void) -> Soloud { 1117 | // assert!(!ptr.is_null()); 1118 | // Soloud { 1119 | // inner: ptr as *mut ffi::Soloud, 1120 | // } 1121 | // } 1122 | 1123 | /// Deletes the Soloud instance 1124 | /// # Safety 1125 | /// Calling this before any wav instance are dropped will cause memory violation 1126 | pub unsafe fn delete(mut this: Soloud) { 1127 | unsafe { 1128 | this.deinit(); 1129 | ffi::Soloud_destroy(this.inner); 1130 | } 1131 | } 1132 | } 1133 | -------------------------------------------------------------------------------- /soloud/src/macros/audio.rs: -------------------------------------------------------------------------------- 1 | macro_rules! impl_audio_ext { 2 | ($name: ident) => { 3 | paste::paste! { 4 | unsafe impl AudioExt for $name { 5 | fn default() -> Self { 6 | let ptr = unsafe { ffi::[<$name _create>]() }; 7 | assert!(!ptr.is_null()); 8 | $name { inner: ptr } 9 | } 10 | 11 | fn set_volume(&mut self, volume: f32) { 12 | 13 | unsafe { 14 | soloud_sys::soloud::[<$name _setVolume>](self.inner, volume) 15 | } 16 | } 17 | 18 | fn set_looping(&mut self, flag: bool) { 19 | 20 | unsafe { 21 | soloud_sys::soloud::[<$name _setLooping>](self.inner, flag as i32) 22 | } 23 | } 24 | 25 | fn set_auto_stop(&mut self, flag: bool) { 26 | 27 | unsafe { 28 | soloud_sys::soloud::[<$name _setAutoStop>](self.inner, flag as i32) 29 | } 30 | } 31 | 32 | fn set_3d_min_max_distance(&mut self, min_distance: f32, max_distance: f32) { 33 | 34 | unsafe { 35 | soloud_sys::soloud::[<$name _set3dMinMaxDistance>](self.inner, min_distance, max_distance) 36 | } 37 | } 38 | 39 | fn set_3d_attenuation(&mut self, model: AttenuationModel, rolloff_factor: f32) { 40 | 41 | unsafe { 42 | soloud_sys::soloud::[<$name _set3dAttenuation>](self.inner, model as u32, rolloff_factor) 43 | } 44 | } 45 | 46 | fn set_3d_doppler_factor(&mut self, doppler_factor: f32) { 47 | 48 | unsafe { 49 | soloud_sys::soloud::[<$name _set3dDopplerFactor>](self.inner, doppler_factor) 50 | } 51 | } 52 | 53 | fn set_3d_listener_relative(&mut self, flag: bool) { 54 | 55 | unsafe { 56 | soloud_sys::soloud::[<$name _set3dListenerRelative>](self.inner, flag as i32) 57 | } 58 | } 59 | 60 | fn set_3d_distance_delay(&mut self, distance_delay: i32) { 61 | 62 | unsafe { 63 | soloud_sys::soloud::[<$name _set3dDistanceDelay>](self.inner, distance_delay) 64 | } 65 | } 66 | 67 | // fn set_3d_collider(&mut self, collider: Option<&AudioCollider>) { 68 | // let mut collider = match collider { 69 | // Some(v) => unsafe { v.inner() }, 70 | // None => std::ptr::null_mut(), 71 | // }; 72 | 73 | // unsafe { 74 | // soloud_sys::soloud::[<$name _set3dCollider>](self.inner, &mut collider as *mut *mut _) 75 | // } 76 | // } 77 | 78 | // fn set_3d_attenuator(&mut self, attenuator: Option<&AudioAttenuator>) { 79 | // let mut attenuator = match attenuator { 80 | // Some(v) => unsafe { v.inner() }, 81 | // None => std::ptr::null_mut(), 82 | // }; 83 | 84 | // unsafe { 85 | // soloud_sys::soloud::[<$name _set3dAttenuator>](self.inner, &mut attenuator as *mut *mut _) 86 | // } 87 | // } 88 | 89 | fn set_inaudible_behavior(&mut self, must_tick: bool, kill: bool) { 90 | 91 | unsafe { 92 | soloud_sys::soloud::[<$name _setInaudibleBehavior>](self.inner, must_tick as i32, kill as i32) 93 | } 94 | } 95 | 96 | fn set_loop_point(&mut self, loop_point: f64) { 97 | 98 | unsafe { 99 | soloud_sys::soloud::[<$name _setLoopPoint>](self.inner, loop_point) 100 | } 101 | } 102 | 103 | fn loop_point(&self) -> f64 { 104 | 105 | unsafe { 106 | soloud_sys::soloud::[<$name _getLoopPoint>](self.inner) 107 | } 108 | } 109 | 110 | fn set_filter(&mut self, filter_id: u32, filter: Option<&F>) { 111 | let filter = match filter { 112 | Some(v) => unsafe { v.inner() }, 113 | None => std::ptr::null_mut(), 114 | }; 115 | 116 | unsafe { 117 | soloud_sys::soloud::[<$name _setFilter>](self.inner, filter_id, filter) 118 | } 119 | } 120 | 121 | fn stop(&mut self) { 122 | 123 | unsafe { 124 | soloud_sys::soloud::[<$name _stop>](self.inner) 125 | } 126 | } 127 | 128 | unsafe fn inner(&self) -> *mut *mut std::os::raw::c_void { 129 | self.inner 130 | } 131 | } 132 | 133 | unsafe impl Send for $name {} 134 | unsafe impl Sync for $name {} 135 | 136 | impl Drop for $name { 137 | fn drop(&mut self) { 138 | unsafe { 139 | soloud_sys::soloud::[<$name _destroy>](self.inner); 140 | self.inner = std::ptr::null_mut(); 141 | } 142 | } 143 | } 144 | } 145 | }; 146 | } 147 | 148 | pub(crate) use impl_audio_ext; 149 | -------------------------------------------------------------------------------- /soloud/src/macros/filter.rs: -------------------------------------------------------------------------------- 1 | macro_rules! impl_filter_ext { 2 | ($name: ident) => { 3 | paste::paste! { 4 | unsafe impl FilterExt for $name { 5 | unsafe fn inner(&self) -> *mut *mut std::os::raw::c_void { 6 | self.inner 7 | } 8 | 9 | fn default() -> Self { 10 | unsafe { 11 | let ptr = soloud_sys::soloud::[<$name _create>](); 12 | assert!(!ptr.is_null()); 13 | $name { 14 | inner: ptr, 15 | } 16 | } 17 | } 18 | 19 | fn param_count(&mut self) -> i32 { 20 | 21 | unsafe { 22 | soloud_sys::soloud::[<$name _getParamCount>](self.inner) 23 | } 24 | } 25 | 26 | 27 | fn param_name(&mut self, param_idx: u32) -> Option { 28 | 29 | unsafe { 30 | let ptr = soloud_sys::soloud::[<$name _getParamName>](self.inner, param_idx); 31 | if ptr.is_null() { 32 | None 33 | } else { 34 | Some(std::ffi::CStr::from_ptr(ptr).to_string_lossy().to_string()) 35 | } 36 | } 37 | } 38 | 39 | 40 | fn param_type(&mut self, param_idx: u32) -> ParamType { 41 | 42 | unsafe { 43 | std::mem::transmute(soloud_sys::soloud::[<$name _getParamType>](self.inner, param_idx)) 44 | } 45 | } 46 | 47 | 48 | fn param_max(&mut self, param_idx: u32) -> f32 { 49 | 50 | unsafe { 51 | soloud_sys::soloud::[<$name _getParamMax>](self.inner, param_idx) 52 | } 53 | } 54 | 55 | 56 | fn param_min(&mut self, param_idx: u32) -> f32 { 57 | 58 | unsafe { 59 | soloud_sys::soloud::[<$name _getParamMin>](self.inner, param_idx) 60 | } 61 | } 62 | } 63 | 64 | impl Drop for $name { 65 | fn drop(&mut self) { 66 | unsafe { soloud_sys::soloud::[<$name _destroy>](self.inner) } 67 | } 68 | } 69 | } 70 | }; 71 | } 72 | 73 | macro_rules! impl_filter_type { 74 | ($name: ident) => { 75 | impl FilterAttr for $name { 76 | fn to_u32(self) -> u32 { 77 | self as u32 78 | } 79 | } 80 | }; 81 | } 82 | 83 | pub(crate) use impl_filter_ext; 84 | pub(crate) use impl_filter_type; 85 | -------------------------------------------------------------------------------- /soloud/src/macros/load.rs: -------------------------------------------------------------------------------- 1 | macro_rules! impl_load_ext { 2 | ($name: ident) => { 3 | paste::paste! { 4 | unsafe impl LoadExt for $name { 5 | fn load>(&mut self, path: P) -> Result<(), SoloudError> { 6 | let c = path.as_ref().to_str().ok_or(SoloudError::Internal(SoloudErrorKind::FileLoadFailed))?; 7 | let val = if cfg!(target_os = "windows") { 8 | c.chars().all(|c| c.is_ascii_lowercase()) 9 | } else { 10 | true 11 | }; 12 | if !val { 13 | let bytes = std::fs::read(path)?; 14 | self.load_mem(&bytes)?; 15 | Ok(()) 16 | } else { 17 | unsafe { 18 | let path = std::ffi::CString::new(path.as_ref().to_str().ok_or(SoloudError::Internal(SoloudErrorKind::FileLoadFailed))?)?; 19 | let ret = soloud_sys::soloud::[<$name _load>](self.inner, path.as_ref().as_ptr()); 20 | if ret != 0 { 21 | Err(SoloudError::Internal(SoloudErrorKind::from_i32(ret))) 22 | } else { 23 | Ok(()) 24 | } 25 | } 26 | } 27 | } 28 | 29 | unsafe fn _load_mem_ex(&mut self, data: &[u8], copy: bool, take_ownership: bool) -> Result<(), SoloudError> { 30 | unsafe { 31 | let ret = soloud_sys::soloud::[<$name _loadMemEx>](self.inner, data.as_ptr(), data.len() as u32, copy as i32, take_ownership as i32); 32 | if ret != 0 { 33 | Err(SoloudError::Internal(SoloudErrorKind::from_i32(ret))) 34 | } else { 35 | Ok(()) 36 | } 37 | } 38 | } 39 | } 40 | } 41 | }; 42 | } 43 | 44 | pub(crate) use impl_load_ext; 45 | -------------------------------------------------------------------------------- /soloud/src/macros/mod.rs: -------------------------------------------------------------------------------- 1 | pub(crate) mod audio; 2 | pub(crate) mod filter; 3 | pub(crate) mod load; 4 | -------------------------------------------------------------------------------- /soloud/src/prelude.rs: -------------------------------------------------------------------------------- 1 | // pub use crate::effects::*; 2 | use std::convert::From; 3 | use std::os::raw::*; 4 | use std::path::Path; 5 | use std::{fmt, io}; 6 | 7 | /// Soloud error types 8 | #[derive(Debug)] 9 | pub enum SoloudError { 10 | /// i/o error 11 | IoError(io::Error), 12 | /// Mull value error in the read memory 13 | NullError(std::ffi::NulError), 14 | /// Internal soloud error 15 | Internal(SoloudErrorKind), 16 | /// Unknown error 17 | Unknown(String), 18 | } 19 | 20 | /// Error kinds enum for SoloudError 21 | #[repr(i32)] 22 | #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] 23 | pub enum SoloudErrorKind { 24 | /// Some parameter is invalid 25 | InvalidParameter = 1, 26 | /// File not found 27 | FileNotFound = 2, 28 | /// File found, but could not be loaded 29 | FileLoadFailed = 3, 30 | /// DLL not found, or wrong DLL 31 | DllNotFound = 4, 32 | /// Out of memory 33 | OutOfMemory = 5, 34 | /// Feature not implemented 35 | NotImplemented = 6, 36 | /// Other error 37 | UnknownError = 7, 38 | } 39 | 40 | impl SoloudErrorKind { 41 | /// Get a Soloud error from the returned i32 error code 42 | pub fn from_i32(val: i32) -> SoloudErrorKind { 43 | unsafe { std::mem::transmute(val) } 44 | } 45 | } 46 | 47 | impl std::error::Error for SoloudError { 48 | /// Get the source of the Soloud Error 49 | fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { 50 | match self { 51 | SoloudError::IoError(err) => Some(err), 52 | SoloudError::NullError(err) => Some(err), 53 | _ => None, 54 | } 55 | } 56 | } 57 | 58 | impl fmt::Display for SoloudError { 59 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 60 | match *self { 61 | SoloudError::IoError(ref err) => err.fmt(f), 62 | SoloudError::NullError(ref err) => err.fmt(f), 63 | SoloudError::Internal(ref err) => write!(f, "An internal error occured {:?}", err), 64 | SoloudError::Unknown(ref err) => write!(f, "An unknown error occurred {:?}", err), 65 | } 66 | } 67 | } 68 | 69 | impl From for SoloudError { 70 | fn from(err: io::Error) -> SoloudError { 71 | SoloudError::IoError(err) 72 | } 73 | } 74 | 75 | impl From for SoloudError { 76 | fn from(err: std::ffi::NulError) -> SoloudError { 77 | SoloudError::NullError(err) 78 | } 79 | } 80 | 81 | bitflags::bitflags! { 82 | /// Soloud flags 83 | pub struct SoloudFlag: i32 { 84 | /// Clip roundoff 85 | const ClipRoundoff = 1; 86 | /// Enable visualization 87 | const EnableVisualization = 2; 88 | /// Left handed 3D 89 | const LeftHanded3D = 4; 90 | /// No Fpu register change 91 | const NoFpuRegisterChange = 8; 92 | } 93 | } 94 | 95 | /// Waveform types 96 | #[repr(i32)] 97 | #[derive(Debug, Copy, Clone, PartialOrd, PartialEq)] 98 | pub enum WaveForm { 99 | /// Square waveform 100 | Square = 0, 101 | /// Saw waveform 102 | Saw, 103 | /// Sin waveform 104 | Sin, 105 | /// Triangle waveform 106 | Triangle, 107 | /// Bounce waveform 108 | Bounce, 109 | /// Jaws waveform 110 | Jaws, 111 | /// Humps waveform 112 | Humps, 113 | /// Fsquare waveform 114 | FSquare, 115 | /// Fsaw waveform 116 | FSaw, 117 | } 118 | 119 | /// Resampler types 120 | #[repr(i32)] 121 | #[derive(Debug, Copy, Clone, PartialOrd, PartialEq)] 122 | pub enum Resampler { 123 | /// Point resampler 124 | Point, 125 | /// Linear resampler 126 | Linear, 127 | /// Catmullrom resampler 128 | Catmullrom, 129 | } 130 | 131 | /// Attenuation models 132 | #[repr(u32)] 133 | #[derive(Debug, Copy, Clone, PartialOrd, PartialEq)] 134 | pub enum AttenuationModel { 135 | /// No attenuation 136 | NoAttenuation = 0, 137 | /// Inverse distance attenuation model 138 | InverseDistance = 1, 139 | /// Linear distance attenuation model 140 | LinearDistance = 2, 141 | /// Exponential distance attenuation model 142 | ExponentialDistance = 3, 143 | } 144 | 145 | /// Methods shared by all audio sources 146 | /// # Safety 147 | /// These traits aren't meant to be implemented manually, and are subject to possible change 148 | pub unsafe trait AudioExt { 149 | /// Creates a default initialized object 150 | fn default() -> Self; 151 | /// Sets the volume 152 | fn set_volume(&mut self, volume: f32); 153 | /// Set whether the audio is looping 154 | fn set_looping(&mut self, flag: bool); 155 | /// Set auto stop 156 | fn set_auto_stop(&mut self, flag: bool); 157 | /// Set 3D min and max distances 158 | fn set_3d_min_max_distance(&mut self, min_distance: f32, max_distance: f32); 159 | /// Set 3D attenuation 160 | fn set_3d_attenuation(&mut self, model: AttenuationModel, rolloff_factor: f32); 161 | /// Set 3D doppler factor 162 | fn set_3d_doppler_factor(&mut self, doppler_factor: f32); 163 | /// Set 3D listener relative 164 | fn set_3d_listener_relative(&mut self, flag: bool); 165 | /// Set 3D distance delay 166 | fn set_3d_distance_delay(&mut self, delay: i32); 167 | // /// Set 3D collider 168 | // fn set_3d_collider(&mut self, collider: Option<&AudioCollider>); 169 | // /// Set 3D attenuator 170 | // fn set_3d_attenuator(&mut self, attenuator: Option<&AudioAttenuator>); 171 | /// Set inaudible behavior 172 | fn set_inaudible_behavior(&mut self, must_tick: bool, kill: bool); 173 | /// Set a loop point 174 | fn set_loop_point(&mut self, loop_point: f64); 175 | /// Get the loop point 176 | fn loop_point(&self) -> f64; 177 | /// Set a filter, the filter_id is assigned by the developer and becomes the id for that filter, 178 | /// and to cancel pass None as a filter to the already assigned id 179 | fn set_filter(&mut self, filter_id: u32, filter: Option<&F>); 180 | /// Stop 181 | fn stop(&mut self); 182 | /// Get the inner pointer 183 | /// # Safety 184 | /// The inner pointer should be modified with care! 185 | unsafe fn inner(&self) -> *mut *mut c_void; 186 | } 187 | 188 | /// Methods for initializing sound sources from memory 189 | /// # Safety 190 | /// These traits aren't meant to be implemented manually, and are subject to possible change 191 | pub unsafe trait LoadExt { 192 | /// Load audio from a file 193 | fn load>(&mut self, path: P) -> Result<(), SoloudError>; 194 | /// Load audio from memory. 195 | fn load_mem(&mut self, data: &[u8]) -> Result<(), SoloudError> { 196 | unsafe { self._load_mem_ex(data, false, false) } 197 | } 198 | #[doc(hidden)] 199 | /// (Internal) load audio from memory with options to copy and/or take ownership 200 | /// # Safety 201 | /// The audio source should not be invalidated 202 | unsafe fn _load_mem_ex( 203 | &mut self, 204 | data: &[u8], 205 | copy: bool, 206 | take_ownership: bool, 207 | ) -> Result<(), SoloudError>; 208 | } 209 | 210 | /// Filter creation and setting methods 211 | /// # Safety 212 | /// These traits aren't meant to be implemented manually, and are subject to possible change 213 | pub unsafe trait FilterExt { 214 | /// Creates a default initialized object 215 | fn default() -> Self; 216 | /// Get the param count 217 | fn param_count(&mut self) -> i32; 218 | /// Get the param name by index 219 | fn param_name(&mut self, param_idx: u32) -> Option; 220 | /// Get the param type by index 221 | fn param_type(&mut self, param_idx: u32) -> crate::filter::ParamType; 222 | /// Get the maximum value of a parameter 223 | fn param_max(&mut self, param_idx: u32) -> f32; 224 | /// Get the minimum value of a parameter 225 | fn param_min(&mut self, param_idx: u32) -> f32; 226 | /// Get the inner pointer 227 | /// # Safety 228 | /// The inner pointer should be modified with care! 229 | unsafe fn inner(&self) -> *mut *mut c_void; 230 | } 231 | 232 | /// A trait applicable to all filter attributes 233 | pub trait FilterAttr { 234 | /// Convert a filter attribute to a u32 235 | fn to_u32(self) -> u32; 236 | } 237 | 238 | /// A trait defining methods of initializing audio sources from a path or memory 239 | /// # Safety 240 | /// These traits aren't meant to be implemented manually, and are subject to possible change 241 | pub unsafe trait FromExt: Sized { 242 | /// Loads an audio source from path 243 | fn from_path>(p: P) -> Result; 244 | /// Loads an audio source from memory. Prefer [`LoadExt::load_mem`] when possible like when 245 | /// using `'static &[u8]` 246 | fn from_mem(data: &[u8]) -> Result; 247 | } 248 | 249 | unsafe impl FromExt for T { 250 | fn from_path>(path: P) -> Result { 251 | let mut x = Self::default(); 252 | x.load(path.as_ref())?; 253 | Ok(x) 254 | } 255 | 256 | fn from_mem(data: &[u8]) -> Result { 257 | let mut x = Self::default(); 258 | x.load_mem(data)?; 259 | Ok(x) 260 | } 261 | } 262 | --------------------------------------------------------------------------------