Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

After upgrading to 0.10, audio quickly cuts out #116

Open
PWhiddy opened this issue Jan 13, 2025 · 13 comments
Open

After upgrading to 0.10, audio quickly cuts out #116

PWhiddy opened this issue Jan 13, 2025 · 13 comments

Comments

@PWhiddy
Copy link

PWhiddy commented Jan 13, 2025

On 0.9.4, I was able to trigger thousands of short samples per second without issue. For some reason when upgrading to 0.10.1, sounds play for a very brief fraction of a second on startup and then cut out, regardless of how many samples are playing. I made only minor tweaks to satisfy the new api changes. This is what my code looks like:

impl SimAudio {
    pub fn new() -> Self {
        let (tx, rx): (
            Sender<SoundEventsMessage>,
            Receiver<SoundEventsMessage>,
        ) = flume::unbounded();

        let mut audio_manager = AudioManager::<DefaultBackend>::new(AudioManagerSettings {
            main_track_builder: MainTrackBuilder::new().sound_capacity(MAX_SOUNDS),
            capacities: Capacities {
                sub_track_capacity: 128,
                send_track_capacity: 16,
                listener_capacity: 8,
                clock_capacity: 8,
                modulator_capacity: 16,
            },
            ..Default::default()
        }).expect("failed to create kira audio manager");

        let mut clock_handle = audio_manager
            .add_clock(ClockSpeed::TicksPerMinute(60.0))
            .expect("failed to create clock");
        clock_handle.start();

        let reverb_builder = ReverbBuilder::new();
        let mut sub_track = audio_manager
            .add_sub_track(TrackBuilder::new()
                .with_effect(reverb_builder)
                .sound_capacity(MAX_SOUNDS)
            ).expect("failed to create track");

        let samplers = all_audio_data()
            .into_iter()
            .map(|sample_group| {
                MultiSampler::new(
                    vec![sample_group], // here's where multiple groups could be created?
                )
            })
            .collect::<Vec<MultiSampler>>();

        thread::spawn(move || {
            let mut rng = WyRand::new_seed(42);

            for message in rx {
                let event_count = f32::powf(message.events.len() as f32, 0.5);
                let mut short_sound_counts = [0u16; MAX_SHORT_SOUND_TYPES];
                for event in message.events {
                    short_sound_counts[event.id as usize] += 1;
                }
                for (id, count) in short_sound_counts.into_iter().enumerate() {
                    for _i in 0..count {
                        let input_sample = samplers[id]
                            .get_sample(1, &mut rng)
                            .volume(0.25 / event_count)
                            .start_time(
                                // TODO we should be able to use 1x instead of 8x - investigate
                                // are our time units wrong?
                                clock_handle.time()
                                    + (rng.generate::<f32>()
                                        * message.time_interval
                                        * 8.0)
                                        as f64,
                            );
                        let result = sub_track.play(input_sample);
                        if result.is_err() {
                            log::error!("error playing sound: {:?}", result);
                        }
                    }
                }
            }
        });

        Self { message_sender: tx }
    }

No errors are logged.

@PWhiddy
Copy link
Author

PWhiddy commented Jan 13, 2025

Hm, if I bypass the subtrack and play directly to the audio manager's main track I don't experience that issue. However for some reason though the above code no longer respects the volume being set on the sample. All samples seem to be triggering at 100% volume, even if its set to 0.0.

@PWhiddy
Copy link
Author

PWhiddy commented Jan 13, 2025

Ah okay, reading the release notes I see that volume units have changed from amplitude to decibels, which explains the second thing. Not sure I understand the first issue fully still but I have a workaround for now.

@PWhiddy
Copy link
Author

PWhiddy commented Jan 13, 2025

One more thing I'm experiencing here - in 0.10 when I'm triggering lots of overlapping samples (with essentially the code above), I hear a high pitched buzzing at a particular frequency. My guess is that it could have something to do with the buffered sampling, but I'm not sure yet. I may resolve this one myself again, but posting here in case you know any obvious reasons :)

@PWhiddy
Copy link
Author

PWhiddy commented Jan 13, 2025

I confirmed the buzzing is 344 hz which is 44100 / 128 (default buffer size), so that tracks with aliasing around the buffer hmm

@PWhiddy
Copy link
Author

PWhiddy commented Jan 13, 2025

Okay, reducing the internal buffer size from 128 to 16 seemed to help.

There does seem to be a bit of a tradeoff between buffer size and artifacts.

I'm not sure if the artifacts are caused by the reduced precision in clock (as you mention in the release notes) or just by the buffering in the first place. It could have to do with the way I'm adding random sample offsets to avoid artifacts from triggering many samples at once every 1/60th of a second. If you happen to know of a better setup for that I would be curious to know :)

@tesselode
Copy link
Owner

I have to ask: why are you playing thousands of sounds per second?

@PWhiddy
Copy link
Author

PWhiddy commented Jan 15, 2025

I am building a large scale ecosystem simulation that has hundreds of thousands of little critters 🙂

quick_show_sm

Lots of sounds can be triggered at this scale. One plan I had for optimization is baking multiple samples together to trigger all at once. Kira has been working great so far (thanks for open sourcing it!), and I'm surprised how many samples I can play just in the naive way. Curious to know how you might approach this particular scenario.

@tesselode
Copy link
Owner

Can I try the sim out? Hard to comment on anything without hearing it for myself.

@PWhiddy
Copy link
Author

PWhiddy commented Jan 15, 2025

I sent you an invite to the repo!
The project hasn't been announced publicly yet so please don't share for now :)
Should be straightforward to get running, but a lot of the controls / UI listed in the readme needs to be updated.
Feel free to email me if you have any questions or comments out of the scope of this thread!

@tesselode
Copy link
Owner

OK, I think the artifacts were in fact because the clocks are less precise now. I could imagine playing a lot of sounds at regular intervals creating new harmonics.

@PWhiddy
Copy link
Author

PWhiddy commented Jan 15, 2025

I was suspecting that could be an issue. But also I'd think that the random offsets I'm adding which are larger than the clock precision would mitigate that. Perhaps the offsets are only a little larger and so its only partially mitigated.

@tesselode
Copy link
Owner

The start times are quantized to the internal buffer size in general.

@PWhiddy
Copy link
Author

PWhiddy commented Jan 17, 2025

Ah that makes sense! I can probably get by using a smaller buffer size, but I wonder how hard it would to allow samples to start at an arbitrary time, perhaps by offsetting the index into the sample buffer? Not that familiar with the Kira internals yet.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants