Code Monkey home page Code Monkey logo

Comments (8)

mrDIMAS avatar mrDIMAS commented on May 11, 2024

Hi! You locking mutex for 5 seconds and mixer thread just does not have enough time to mix everything and send data to sound device. Here is your loop:

loop {
	println!("Looping");
	let mut context = context.lock().unwrap();
	let source = context.source_mut(source_handle);
	source.play();
	// This introduces audio artifacts after a while.
	// Use `from_secs(5)` and these go away for me.
	thread::sleep(Duration::from_millis(5000));
}

and here is correct version:

loop {
	println!("Looping");
	// This scope ensures that mutex will be released before calling thread::sleep.
	// Alternatively you can std::mem::drop(context) after source.play(); which will
	// also release the lock.
	{
		let mut context = context.lock().unwrap();
		let source = context.source_mut(source_handle);
		source.play();
	}
	// This introduces audio artifacts after a while.
	// Use `from_secs(5)` and these go away for me.
	thread::sleep(Duration::from_millis(5000));
}

So, general rule is to lock mutex as short time as possible, otherwise mixer thread will just wait and sound will be incorrect.

from fyrox.

ndarilek avatar ndarilek commented on May 11, 2024

Oh, duh. Sorry for the silly mistake.

Now, with this code, I'm able to trigger the odd delays. I would expect this footstep sound to play rhythmically. But, on my system, it doesn't. There are odd delays every few footsteps, and the pattern doesn't repeat. To be clear, I don't expect it to cleanly loop. I just expect it to sound rhythmic, but instead it is more staggered.

Not sure if play is meant to restart the source, which is why I'm stopping it.

Am I doing something wrong here as well? Thanks.

use std::{thread, time::Duration};

use rg3d_sound::{
    buffer::{DataSource, SoundBuffer},
    context::Context,
    pool::Handle,
    source::{generic::GenericSourceBuilder, SoundSource},
};

fn main() {
    // Initialize new sound context with default output device.
    let context = Context::new().unwrap();

    // Load sound buffer.
    let footstep_buffer =
        SoundBuffer::new_generic(DataSource::from_file("footstep.wav").unwrap()).unwrap();

    // Create generic source (without spatial effects) using that buffer.
    let source = GenericSourceBuilder::new(footstep_buffer)
        .with_looping(false)
        .build_source()
        .unwrap();

    // Each sound sound must be added to context, context takes ownership on source
    // and returns pool handle to it by which it can be accessed later on if needed.
    let source_handle: Handle<SoundSource> = context.lock().unwrap().add_source(source);
    // This plays the sound way more than once every 5 seconds.
    loop {
        {
            let mut context = context.lock().unwrap();
            let source = context.source_mut(source_handle);
            source.stop().unwrap();
            source.play();
        }
        thread::sleep(Duration::from_millis(500));
    }
}

from fyrox.

mrDIMAS avatar mrDIMAS commented on May 11, 2024

Here is the picture that should clarify what is going on under the hood:

rect833

So there are two threads - one is user thread, and second is mixer thread. Mixer thread mix samples into audio buffer and sends data to output device, the time that send procedure could take is undefined, it depends on the driver, OS, and other stuff. So perfect looping could be achived only by setting .with_looping(true) on a sound source. By doing this, looping happens in mixer thread and it is able to do perfect loop. Please note that backend implemented using ring buffer and by phrase "mixing another portion" on the picture I mean that we mixing a portion of samples for the next iteration, not the current one.

from fyrox.

ndarilek avatar ndarilek commented on May 11, 2024

from fyrox.

mrDIMAS avatar mrDIMAS commented on May 11, 2024

Oh, sorry. What I'm doing in my shooter is that I'm creating new source per footstep. Also the engine can automatically handle lifetime of a source for such temporary sound sources. So you need to create a source with .with_play_once(true) and just play it and forget about it - it will die automatically when it ends. Creating a source is cheap, however you should ensure that amount of simultaneously playing sources is relatively low (soft cap here is about 64 sources), otherwise mixer probably will take large amount of time and sound will stutter.

Other way of implementing this is to use multiple sources with different buffers - each buffer should have slightly different version of footstep to sound more naturally. In this case you should stop currently playing source, and then play a random one.

I'm not a sound designer and don't know all the tricks that will result in natural sound, just relying on what I hear.

from fyrox.

ndarilek avatar ndarilek commented on May 11, 2024

Got it, so seems this is working as designed. Any chance you might eventually support more responsive stopping/restarting of sources? Or should this be closed?

Having to create new sources vs. controlling an existing one would seem to make some types of problems more difficult. It's also a bit more verbose, since instead of just reusing this footstep source, I'd have to create a new one each time.

Thanks for your help.

from fyrox.

mrDIMAS avatar mrDIMAS commented on May 11, 2024

I can't imagine a situation in game where such precision is needed, in case of footsteps small variation is good actually, in other cases such small delay is ok too for me 🤔 .

from fyrox.

ndarilek avatar ndarilek commented on May 11, 2024

from fyrox.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.