mbillingr / ambisonic Goto Github PK
View Code? Open in Web Editor NEWCompose and play 3D audio in pure Rust
Home Page: https://crates.io/crates/ambisonic
License: Apache License 2.0
Compose and play 3D audio in pure Rust
Home Page: https://crates.io/crates/ambisonic
License: Apache License 2.0
Distance calculation is wrong in Bweights::from_position
for any z != 0:
let dist = (pos[0] * pos[0] + pos[1] * pos[1] + pos[1] * pos[2]).sqrt()
^ should be 2
Hello there, thanks for making this project! I went about trying to figure out how to an mp3 I loaded using the rodio library, and wanted to share what I found and discuss with you what you think. I hope that is ok!
First my use case is for a game I am making, whenever an NPC is moving I want to play a walking/running/slithering/buzzing/etc... sound at their 3d location. Before finding your library, I got this working using rodio, but my plan was to have your library help me with the 3d part of it. Without the 3d part the game doesn't feel as immersive.
My code is setup to hold a buffer of the data so I can create a sink when I want to play it, or pause it when the NPC stops moving.
I tried copy/pasting the code from the ambisonic readme and swapping the source to the source containing my mp3 file, but I got an error about a type-mismatch:
To work around this I figured out this adaptor code:
/// Since rodio requires everything to be Send + Sync, the data is wrapped with Arc so only one copy
/// of the data is in memory at a time per-sound.
#[derive(Default)]
pub struct Sound(Arc<Vec<u8>>);
impl AsRef<[u8]> for Sound {
fn as_ref(&self) -> &[u8] {
&self.0
}
}
impl Sound {
pub fn cursor(self: &Self) -> Cursor<Sound> {
Cursor::new(Sound(self.0.clone()))
}
}
This wrapper type wraps my custom buffer:
struct Wrapper(ambisonic::rodio::Decoder<Cursor<Sound>>);
impl Iterator for Wrapper {
type Item = f32;
#[inline]
fn next(&mut self) -> Option<f32> {
self.0.next().map(|v| v as f32) // v is type i16, is mapping the i16 value to f32 valid?
}
}
impl ambisonic::rodio::Source for Wrapper {
#[inline]
fn current_frame_len(&self) -> Option<usize> {
self.0.current_frame_len()
}
#[inline]
fn channels(&self) -> u16 {
self.0.channels()
}
#[inline]
fn sample_rate(&self) -> u32 {
self.0.sample_rate()
}
#[inline]
fn total_duration(&self) -> Option<Duration> {
self.0.total_duration()
}
}
Also I needed to wrap the source:
let source = ambisonic::rodio::Decoder::new(cursor).unwrap();
let source = Wrapper(source); // wrap
let mut sound = scene.play_at(source, pos);
This Wrapper
gets the code to compile and executing it works, the mp3 sound plays left-to-right in my headphones just like the sine wave code from the readme.
Iterator
for the type passed into play_at()
because play_at() expects the input value to implement it for f32. Can ambisonic be changed to not care, or is f32 important here? Is mapping the i16 value to f32 valid?I'm super impressed that with such a small change ambisonic was able to work with the mp3 file, congrats on building something so cool (:
ambisonic = "0.4.1"
use ambisonic::{AmbisonicBuilder, HrtfConfig, PlaybackConfiguration};
pub fn test() {
let mut position = [50.0, -2.0, 0.0];
let velocity = [-0.1, 0.0, 0.0];
let config = PlaybackConfiguration::Hrtf(HrtfConfig::default());
let scene = AmbisonicBuilder::default().with_config(config).build();
let source = ambisonic::rodio::source::SineWave::new(440);
let mut sound = scene.play_at(source, position);
sound.set_velocity(velocity);
for i in 0..1000 {
position[0] += velocity[0];
position[1] += velocity[1];
position[2] += velocity[2];
sound.adjust_position(position);
std::thread::sleep(std::time::Duration::from_millis(10));
}
}
I even put the headphones on backwards to make sure I wasn't just imagining it. Did a blind test with another person as well. Sounds positioned on the right sound centered.
Currently the play_at
function expects sources with sample type f32
because that is what's internally used.
Converting sources from other types requires annoying boilerplate #5.
It should be possible to make the function generic to accept any type that can be safely cast to f32
.
I made a video demonstrating how Ambisonic is able to work in a 3D game I'd like to share with you. I thought maybe it could be useful for promoting the library with a real-world example.
Feel free to close.
:)
Since there haven't been any updates for two years and due to a lack of an alternative in the area of spacial audio, I'd like to know if there is any interest in development left.
I tried to run the examples, which use HRTF, and I always get this error:
ALSA lib pcm.c:8545:(snd_pcm_recover) underrun occurred
Not sure, if that's a bug in ambisonic or if I have to change my system settings.
maybe ambisonic predates this functionality in rodio?
https://docs.rs/rodio/latest/rodio/source/struct.Spatial.html
Hello!
I'm trying to adopt this for my game engine, and it's hard to understand how the positioning is working.
First of all, I cannot hear anything when a sound source is at 0, 0, 0
. But I can hear when it is at 0, 0.0001, 0
and sort of. Why I cannot hear sounds of origin? Is there documentations about this?
Second, which dimension the X, Y and Z represents? Maybe, X for left-to-right, Y for down-to-up, Z for back-to-front?
I'm sorry for bothering you, thanks!
The current distance-attenuation is just an ad-hoc 1 / d^2
formula with a cap for small distances.
Of course, attenuation depends on the medium the sound travels in and on the scene geometry (compare an open space vs. inside a narrow pipe). Some simplifications will be required.
Furthermore, it may be necessary to introduce a proper loudness model (power, sound pressure level, etc) in order to clarify what attenuation actually means.
TODO
Hi again, since you requested feedback I wanted to create another issue for this question. Is it possible to pause a stream? I don't see this in the SoundController:
pub fn stop(&self);
pub fn set_doppler_factor(&mut self, factor: f32);
pub fn set_velocity(&mut self, vel: [f32; 3]);
pub fn adjust_position(&mut self, pos: [f32; 3]);
pub fn set_position(&mut self, pos: [f32; 3]);
In rodio stop() drops all the bytes from the stream, and playing again after a stream isn't possible it seems. Pausing a stream does work for me using rodio. It is possible to allow me to pause the sound using the SoundController and then play it again in ambisonic?
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.