#Better Audio
1 messages · Page 7 of 1
No that would all still remain as-is. You can just choose some size like 256 and only ever use that.
Or better yet -- put the size in a config struct so people can tune it if they want.
256 is a decent default because the frame count is unlikely to be smaller (and if it is it probably won't be latency sensitive anyway).
OOOOOH and then I let the processor only process once it has reached 256 frames?
Okay I think I got it
alright, I'll work on removing the mentions of AudioSettings which hardcode the sample rate and frame size
And if I understood you correctly, there's currently nothing I can do to get it to run, right?
Because of the output size
well you totally could, you just can't slot it into a sampler pool yet
if you add the effects after the pool, you can freely connect them however you like.
how do I do that 👀
or am I better off just waiting a bit until there's an API upstream?
commands
.spawn(SamplerPool(Steamy))
.chain_node(AmbisonicProcessor::default())
.chain_node_with(AmbisonicDecoderNode::default(), &[(0, 0), (1, 1), ..., (8, 8)]);
commands.spawn((
Steamy,
SamplePlayer::new(server.load("wow.wav")),
));
This will apply the same exact effect to all sample players in Steamy, but ya know it would at least work.
I should have enough time in the morning to add the proper feature for this to work within the sampler pool.
thanks 
Hmm
I need the Simulator in the ECS
But to construct it, I need the sample rate and frame size
which I just put into an AmbisonicsNodeConfig
which would be nice
but now I need that exact information earlier 
Do you need the simulator before constructing these nodes?
hmmmm
I suppose not, no
But I need one simulator globally
not one per node
so uuh
do I just pick the config of the first node? 
Right, my point is that they can be decoupled with no problem. If the frame sizes need to match, then I'd just hardcode it for the library as a const. To get the sample rate, you can just ask what it is from the audio context when constructing the simulator.
how do I get the sample rate in the ECS?
two ways, hold on
You can just ask it via the resource:
fn system(mut context: ResMut<AudioContext>) {
let sample_rate = context.with(|c| c.stream_info().unwrap().sample_rate());
// ...
}
But this is annoying (the stream might not be active, you won't catch restarts, etc.).
The better way would be to observe the StreamStartEvent https://docs.rs/bevy_seedling/latest/bevy_seedling/context/struct.StreamStartEvent.html and StreamRestartEvent https://docs.rs/bevy_seedling/latest/bevy_seedling/context/struct.StreamRestartEvent.html.
They'll tell you exactly what the sample rate is the moment it initializes (or changes).
The sample rate can change when you change devices.
Like if you unplug headphones or whatever.
Ooooh I see
so I should rebuild my simulator each time I change the sample rate, right?
yeah if you can't change the sample rate with a method
good point, let me check
nope, no chance
but yeah rebuilding sounds fine
oof
when creating a simulator, all sources need to be added
so rebuilding it in practice means migrating it
@cold isle is there some method you could add so change a simulator's sampling rate at runtime?
little issue with this: there are no sane defaults for the processors
I mean, I could just wrap all params in Options
or hmm
I guess I can rearrange
well in that example you could provide actual values
i was just using some shorthand
But to generate those, I need the sample rate
Which I don't have on startup
(assuming you meant to put that snippet on startup)
nothings stopping you from spawning it in an observer 
fair enough
(StreamStartEvent will only trigger once, so that should be a good thing to listen to.)
Also it’s basically always triggered in PostStartup, so it’s basically still at startup.
ah neat
eek
please tell me I don't need to implement Component for the processor
I assume you meant to use the nodes there and not the processors, right?
No dice:
2025-10-06T00:58:51.032601Z ERROR bevy_seedling::edge::connect: failed to connect audio node to target: Input port idx 1 is out of range on node NodeID(Index { slot: 42, generation: Generation(1) }) with ChannelCount(1) input ports
oooh is this because I'm actually now reading an ogg which is stereo?
Doing this doesn't insert AmbisonicProcessor into the entity holding the pool, so I'm unsure how to retrieve them. The query I wrote doesn't match as a return :/
Anyhoot
I addressed all your feedback 🙂
See the updated PR
The one thing I didn't do is react to changed sample rates
Because that would be very very annoying, and I hope we can get a function to just set that instead
The demo runs, but because the query never matches any nodes, nothing plays.
Oh hey I just figured I can hack it by setting the settings on all nodes
hmm now audionimbus tells me that the input must have exactly one channel
I set it to two because I thought maybe seedling is expecting me to accept two channels from the ogg
Alright, changed it back to use a single input channel and used this thingy to make seedling accept my setup:
commands
.spawn(SamplerPool(AudionimbusPool))
.chain_node_with(ambisonic_node, &[(0, 0), (1, 0)])
.chain_node_with(
ambisonic_decode_node,
&[
(0, 0),
(1, 1),
(2, 2),
(3, 3),
(4, 4),
(5, 5),
(6, 6),
(7, 7),
(8, 8),
],
);
And now it successfully sounds like a buzzing fly 😄
huzzah!
given that it sounds like a weird fly no matter how far away the camera is, I think it's safe to say I fucked something up
pushed that as well
Enough for today 
If you want to try it on your machine, you'll need to also clone https://github.com/janhohenheim/audionimbus/tree/no-alloc, including the submodule
never mind, couldn't resist playing around some more 😄
It now plays sound!
It's clearly still a bit borked
but it's recognizable 
Interestingly it sounds more or less correct when the camera flies out of the room
Though it never becomes more silent with distance
Oh, another thing:
if proc_info.in_silence_mask.all_channels_silent(inputs.len()) {
// All inputs are silent.
return ProcessStatus::ClearAllOutputs;
}
would this not kill remaining reverb after an input ends?
oh did i not actually send that feedback
yes
it would
yes sorry
Yeah, it shouldn’t — the pool already has a node when you spawn it (a volume node). When you chain a node, it spawns a new one and connects the first entity (in this case the pool) to it.
You can supply arbitrary bundles to chain_node, so you could give it a marker if you want.
Not on GitHub, but you wrote plenty on Discord for me to work with 
Can i get the connected nodes' entities?
^
because I assume that every sample player that uses that pool would spawn their own downstream nodes, right?
So I would need to have a way to go from the pool entity to the downstream node entities in order to query them
I saw that using effects would give me a relationship for just this
Or is there a misconception here, and this syntax would use the same node entity for all things with the pool?
Yeah, the snippet I suggested just creates one for the moment. All sample players from the pool would feed into that one effect. So not a particularly useful setup, but again it works at the moment 😅
Aaaah then everything makes sense, thanks
(This could use some documentation :p)
i think it probably is mentioned in the docs
Oh, then my bad!
or maybe it's implicit (to me anyway haha)
The pools do a bit of abstraction and "magic," which might not help with the mental model for normal node connections.
You can right now, but it's a bit annoying. Really, we'll need many-to-many relationships to do this properly.
When I first used seedling I thought that a node pool was like a thread pool
Hehe
But it’s more like a node collection
(You have to get the edges from the audio context and compare them to the FirewheelNode on the entities, etc.)
or at least, in my mental model, a node is like a commit, a pool is like a branch, and a bus is like a tag
In git terms
that's quite the analogy 🤣 can't say i've thought about it in git terms
My mind works in mysterious ways 
it do kinda be like a thread pool though
orly?
You "queue" tasks (SamplePlayers) which have to wait for availability. When there's a slot, they claim it (the actual SamplerNode), and then free the slot when they're done.
The queue is pretty short though; the SamplePlayer will have the PlaybackCompletionEvent fired after just a hundred milliseconds or so if it doesn't find a slot.
(So you don't build up a long queue of sounds that exhibit crazy latency -- they're instead just skipped.)
Oh, I thought every sample player would have their own lil node
That they then use
In a sense! But it's acquired from the pool. If it spawned a new node every time, the graph would have to be recompiled for every sound.
Thus, why we have pools.
Oooh I see
I do have it set up to allow the pools to grow though if they run out of capacity. They grow quadratically.
That's why their size is described as a range.
(initial capacity, max capacity)
I was thinking how I should use the nodes later in practice to describe the audio output per npc
Since the take in the listener location as a param
Do I just pass in a vector of listeners instead, fill some output field on the node according to that list, then only use the player's location for the actual node output?
In practice, the NPC calculations don’t need to be in the privileged audio thread
They don’t care about audio stutters I believe
Hmmmmmmmm sorry I'm not quite sure what you mean
describe the audio output per npc
Each NPC needs to know the amplitude of the sounds that are incoming at their location for the stealth system
The node setup right now only does this for the camera location
But I need it for all (well, all tagged) NPC locations
i guess the ambisonic encoding would allow you to kinda just get this information
Agreed, was just wondering if this kind of stuff should be in the node or not
Basically I want to run the entire node for each NPC
Definitely not as-is -- it would be super expensive to decode the field for every npc. Well I mean you could probably get away with it, but it would be a little wasteful.
Yeah that’s what I thought too
It's tricky though -- you can't do the decoding too lazily, or you'll miss short sounds.
or infrequently i mean
That’s fine 🙂
The NPCs only listen every 200 ms
well it's tricky from the audio perspective -- you could have a really loud sound, but if it's too short, you'll miss it if you infrequently poll
Yeah I see
In the original, they actually have a queue of sounds to be processed that they then drain when the NPCs listen
But I'm not knowledgeable on the details
I don't completely know how the ambisonic encoding works, but you might be able to accumulate the average amplitude for each channel. If you do it this way, you can basically send back nine floats that allow you to "reconstruct" an ambisonic field with that amplitude. Then, you'd decode it with the decoder, and measure the amplitude that their ears receive.
Oof that sounds like math
No it would be trivial I think -- just produce a sine wave or something with that amplitude for a few samples haha.
Hehe
Alternatively, you could just do the full processing chain in the ECS too haha
That’s what I'm thinking
Since we only really need the realtime stuff for the actual sound on the speakers
It's maybe not super efficient, but it would be a near perfect reproduction of what the NPCs perceive (except for maybe any additional effects you have in the audio graph).
It’s fine if it’s fairly rough
For the CPU visibility I just assume everything is a perfectly diffuse white sphere
You could probably reduce the cost a ton by running it with a much lower sample rate too
I was also thinking that the node could keep a buffer of its last output
And then the ECS can just read that buffer whenever it feels like it
What I would do actually is just use the audio clock + the sample playhead, and then you can know exactly what has played over the previous frame (in terms of a time range within the sample).
Oooooooooh
So then I could read those frames from the sample in the ecs
ya
And run the processing in the ecs too
(and reduce the rate by like 40x)
I hope steam audio likes that
Given that I pass the sample rate nearly everywhere
Oh well for your ECS simulation you'd just choose some lower rate.
And pass that around.
And now the simulator used a different sample rate than the processing
It doesn't say you can't have more than one context / simulator.
Although presumably you'd only want to tick the simulator once per frame.
Though I wonder if I can just reuse the existing simulator
Yeah I'd hope it doesn't need the sample rate.
Docs say to not even do that FYI
But to run it in a background thread, then check every tick if it was done
(Which makes sense to me)
Ya either way, it would be best to avoid running it 2x haha
Hehe
I'll definitely need some API help when I get here haha
damn it does require the sample rate on construction
The simulator? Yep
It’s uuuh
Not ideal
Maybe nothing bad happens if I process it with a different sampling rate 
If you lie about it (like just run ECS simulation much slower anyway), the filters will have a significantly different attenuation profile
which might mess with the amplitude calculations.
Heck
So maybe this could make more sense?
i mean it probably wouldn't be that bad if it's only 2 or 4x slower
Then the simulator only ran once, at least
well I'm not sure this really avoids the problem, you'll still have to go through a bunch of processing -- the main expensive step here will be the decoding for every NPC
Hmm I see
Well, it’s more like every NPC tries to listen every 200 ms, which is randomly offset per NPC
So I hope it won’t be too many requests per update
Realistically, we are looking at 100 NPCs max per level
But if they’re far enough from the player, the update rate can also go down to 500 ms
whew okay but that is quite a lot haha
Yeah but with a bit of luck that calculation may end up at like two active listeners per frame
(Number pulled out of a hat)
Yeah I suppose if you're okay with merely probing a short snippet of sound every 200ms, then it wouldn't be that bad.
I'll need to dig into the reference to see how they deal with inter-update sounds 
And then come back crying here hehe
Ah btw the draft PR plays sounds now!
Idk why they are buggy
But sounds!!
okay I'll see if I can get the channel stuff going here real quick and take a look 
Thanks!!!
Definitely need your help to debug the rest haha
Thanks again for your unending patience!!
Just checked and I'm fairly sure the C++ code does this, yeah
Namely checking listening every 200-500 ms for things that are playing right now. Shorter sounds seem to be ignored
Well, usually the levels are indoors
So there’s reverb
So sounds will ring out a while
But yeah if the peak amplitude was between listens, I think it’s not heard
Ooooh
It’s simpler
The sound itself is tagged with a SoundType
And if the volume that ends up at the NPC is at a threshold, it will get a fixed attention according to that tag
e.g. a thrown plate will always generate the same awareness when it was heard, no matter how loud the sound
mm, that's a touch more expensive with this approach since you'd presumably have to perform the decoding for each NPC for each sound
otherwise they'll all get mixed up into the sound field
i mean definitely still possible, but hopefully you could make the decoding really cheap
Yep
Hope so too
At least I can use a whole bunch of heuristics. Most sounds that are playing (e.g. mechanical whirring) aren’t relevant for gameplay, so I can ignore those. The only important stuffs are
- player footsteps
- other NPCs broadcasting information
- suspicious actions like lock picks, thrown objects, etc.
you could even have a special pool for semantically significant sounds
And for all of those, I can first check distances to the listener
The original code even hard-caps at 4 per frame
4 processed audios per listen per NPC that is
ya that's probably a good idea
Yeah then those can get the more complicated node that runs per NPC
(per NPC requesting the listen)
@cold isle my PR https://github.com/MaxenceMaire/audionimbus/pull/20 is ready. I left comments explaining why I did stuffs 🙂
I still need about 300 LOC of wrappers to make it actually compatible with firewheel, mostly due to missing impls on RealtimeClone, Diff, Patch
if you want, I could add a firewheel feature to audionimbus
But this here is also fine
Oh and @drowsy dome you’re also an audio expert, so feel free to help me debug https://github.com/MaxenceMaire/audionimbus-demo/pull/2 if you feel like it :p
I appreciate the mention :) yeah I'm actually looking at this rn because I need to do some things following this chain of dicussion
everyone here: I am an audio beginner. I steal soundfonts and have yet to do anything beyond parsing sf2 and creating a scrappy saw wave
hehe. so onlookers don't ask me for much info beyond midi 😆
Psshhht don’t listen to him he literally maintains a midi library
And made a virtual RAT piano
hehe to be a rat shader 🐀 
the rat piano is important
How have I not heard about this yet?
Would be cooler if the audio played correctly >:[
@slate scarab did you stumble on any low hanging fruits?
Not yet. Earlier I finished implementing the automatic up/dowmixing, so you'll be able to just toss them in sample_effects once I push that. Some tests are failing from the Firewheel update though, so I don't know how long that'll take to resolve.
Heck yeah 😄
hmmph macos angry at a lad named phonon
ya u gotta link
oh ofc
or use the auto-install feature
im using the auto-install feature actually
but uh, idk. maybe somethin up. im still parsing what's off


ah ha ive found thym issue, which is that the path appears to be different
but im surprised im the first
macOS has no audio devs confirmed
nothin like a bit of a hack in the generate_bindings_phonon part of the audionimbus-sys build script hehe
let path = tmp_dir.join("steam_audio_cache/steamaudio_core/steamaudio/include/phonon.h");
wait
ur not on ur no-alloc branch right
once you get it to run, check the master branch for how it's supposed to sound
I am
interesting. cuz I'm missing a const: audionimbus_sys::STEAMAUDIO_VERSION. I'm wondering if that's added in the build script 🤔
ok honestly...im just gonna actually install it 😆 was fun.
uuuh may be
I did so too 🙂
im honestly confused about how it's working for y'all since steam-audio/core/src/core doesn't appear anywhere for me. that path is non-existent in the steamaudio download or otherwise.
it's a submodule
that also caught me off-guard lol
git submodule update --init --recursive --checkout
I didn't realize, wow haha
imagine a world where git chooses the correct default even once
i need to enable submodules by default
oh yeah
that's crisp
the crispy audio is a feature
lobotomy simulation
yeah i can hear a lil buzz. is that the chorus? i'm trynna figure out why it's so beautiful. I noticed that moving out of the radius of one of the nodes, it sounds completely normal
I thought so too
wondering if it has to do with some phase conflict
but it's not quite normal
Since it's supposed to fade with distance
Also, check this line:
|(direct_sample, reflections_sample, reverb_sample)| {
(direct_sample * GAIN_FACTOR_DIRECT
+ reflections_sample * GAIN_FACTOR_REFLECTIONS
+ reverb_sample * GAIN_FACTOR_REVERB)
/ (GAIN_FACTOR_DIRECT
+ GAIN_FACTOR_REFLECTIONS
+ GAIN_FACTOR_REVERB)
},
By setting these factors to 0, you can selectively enable only one effect at a time
that way I discovered that the direct sample sounds correct, except it doesn't fade with distance or walls
the reflections and reverb are what sounds buzzy
heck yeah
there's a drain
i was gonna say check that you clear the buffers haha
this code:
for buff in &mut self.output_buffer {
buff.extend(std::iter::repeat_n(0.0, input_len));
}
reallocates, draining keeps the allocation
imma see if that deals with the clicking
Are you sure? I copied Corvus' design from here: https://github.com/CorvusPrudens/firewheel-ircam-hrtf/blob/97ffe3e6fa182767be8192bdee4e922fd47e9bc5/src/lib.rs#L366-L373
ok a lot of clicking is gone now: buff.resize(input_len, 0.)
this is right
oh
oh...

mr crow is this doing what I think it's doing https://github.com/CorvusPrudens/firewheel-ircam-hrtf/blob/97ffe3e6fa182767be8192bdee4e922fd47e9bc5/src/lib.rs#L339-L340
hmm. i wonder why that fixed clicking
Just printing the length also tells me it's not growing:
you're right
But I'll be honest
I'm not entirely sure what the crow code does
I get it in principle
yeah yeah ur right mb. I thought it was extending allocation every frame
makes sense hehe
(dw i'll be able to get back at it after work)
@slate scarab how come there doesn't need to be a
if self.output_buffer[0].len() < outputs[0].len() {
return ProcessStatus::ClearAllOutputs;
}
Or is that what the clear_outputs variable is for?
I added that to my code because otherwise I hit cases where the outputs are not fully full
verified though. the capacity is the same. good to know
thx!
I think my code isn't super robust here. If you run into this situation, you're screwed either way! There will be a gap.
well I run into it 👀
according to debug printing
The proper solution would be to accept some additional latency and only start draining the output when the output buffer is some factor of the size of the max buffer.
It might be fine if it's 1x or more actually.... but to be safe, you'd probably want to start draining when it's at 1.5x or greater.
If you start draining the output eagerly, there may be gaps.
something interesting: return 0. here
(If this isn't clear lmk)
Like this?
let max_buffer = outputs[0].len();
if (self.output_buffer[0].len() as f32) < max_buffer as f32 * 1.5 {
return ProcessStatus::ClearAllOutputs;
}
for (src, dst) in self.output_buffer.iter_mut().zip(outputs.iter_mut()) {
for (i, out) in src.drain(..max_buffer).enumerate() {
dst[i] = out;
}
}
ProcessStatus::outputs_not_silent()
Well that's not the max buffer -- you'll have to get it from construction. The actual buffer size of the inputs and outputs is variable.
that's silent for me
oh, it's clicking for me. huh.
but I also played around with the output buffer
oh is it buffer_size.max(FRAME_SIZE)
oh wait what
You'll probably also want a flag along the lines of "started producing output," since 1.5x may not be true intermittently.
no like you'll want to get the max_frames from the context in AudioNode::construct_processor
max_block_frames?
ya
The first time the buffer reaches some factor of that size, then you can start draining.
You can probably exactly calculate what that factor needs to be, something like max_block_frames.div_ceil(fixed_frames)
well, that would tell you how many fixed_frames worth of samples you need before draining anyway
if !self.started_draining {
if (self.output_buffer[0].len() as f32) < self.max_block_frames.get() as f32 * 1.5 {
return ProcessStatus::ClearAllOutputs;
}
self.started_draining = true;
}
let output_len = outputs[0].len();
for (src, dst) in self.output_buffer.iter_mut().zip(outputs.iter_mut()) {
for (i, out) in src.drain(..output_len).enumerate() {
dst[i] = out;
}
}
ProcessStatus::outputs_not_silent()
like that?
(where I prealloc max_block_frames * 2)
ya that should work
We're basically pipelining the audio here, and there's same startup latency.
neat
But once that latency passes, we should never fail to fill the output.
unfortunately, the sounds sounds the same as before 😄
But I bet this would have been a problem down the road
so good we tackled it 
yeah it definitely would have
does this make sense?
I just pulled a factor out of a hat
just to make sure we're bigger than the 1.5
I think it's not completely true, but I can revisit it later
rq what is the single input channel actually doing?
I take in a stereo input and average the two channels into a mono
since steam audio wants mono
you may want to git pull
I'm really really confused why the direct channels sound good but completely ignore the camera position
they respect the orientation
I successfully hear the sound source from left or right
but all walls and distances are not factored in
ig...if i just return silent at the start, whomst has filled the output buffers with the sample on output channels 0 and 1
:o
oh
it has a sample player
ok so ive been fucking around. before doing anything, I set channels 0 and 1 all to 0.
in the final loop, I pushed values only if the channel was 1
and I return *direct_sample.
here's the result:
sounds a bit like a cicada
hey man cool bug you got there
is there a ref for what these channels mean
(which channels?)
the 9 output channels
prolly here somewhere: https://valvesoftware.github.io/steam-audio/doc/capi/guide.html
that's certainly some audio
pub(crate) const AMBISONICS_NUM_CHANNELS: usize = (AMBISONICS_ORDER + 1).pow(2);
that some ambisonic shit
it's an encoding of a "sound field," so there's the concept of pressure zones and stuff
something something spherical harmonics
big math words
calculator? why not calc you now
iquick could run a circle around me right about now
well hopefully ill find the cricket
you always hear em chirp but never see em
hecking sneaky
@potent moat you up for helping out with Steam Audio?
it's actually pretty decent with an ambisonic order of 1
edit: no it isnt
sounds the same on my end
hmm
Same
i mean this is a bit dense for me it's denser than me tbh. i've been looking at uh like how sources propagate with this system vs the main branch, and I think I've entered a tailspin in terms of understanding
im wondering if for some reason, it's like setting the inputs every frame or something. like idk.
i shall return.... eventually this evening 🤣
luckily i fixed the failing tests i was experiencing earlier so i won't have any blockers
sanity check: mono input would in theory interleave a stereo source right
well doesn't matter. seems the input container used to deinterleave
alright yeah gg, not sure.
No, a mono input should sum the stereo source -- that is, every frame is just a sum of the left and right samples.
If I'm understanding you correctly anyway.
Okay, I have a pro gamer idea
I'll just fill the input with uuh [0.5; FRAME_SIZE]
and dump the output into a text file
then I check at which point in the code they diverge

well this is how it starts
that level of divergence seems fine
now let's look how it ends
0.5
@rapid hedge loving following along with your entry into audio coding
hehehe
thanks
Seriously, I'm learning sooooo much from Corvus over the last few days 😄
more or less, give or take, meh odr weniger
after the first node
looks fine I think
aha!
this looks like crap!
or
hmm
oh boy 😅 i can't wait until we have an editor so we can make scopes and spectrum analyzers
no, actually this is just because the seedling output buffer is smaller than FRAME_SIZE
but no, this doesn't make sense either

I'm debugging like God wanted /j
Hallo! Whatcha trying to do?
Bevy seedling + audionimbus
but it's not doing exactly what it should >:[
well well well
I'm no master mathematician
but - ain't +
im inclined to agree
where's this at? i haven't followed along
honestly looking at raw samples, it's difficult to tell how valuable a diff is. is there some intuition here that can be made?
would have expected the first order derivative diff might hint at something spookier...
output of the AmbisonicNode
if you df/dt i think that might be useful as well :o
Now bisecting this like a caveman
btw it might help to encapsulate the fixed buffer processing -- i can do that in a little bit here
it can very be self-contained
I'm afraid of the old and the new code processing things at different intervals
ah yeah
Same, it's a chill blog to read (and educational!)
yeah the followup panic is a critical step
hahaha
the decoding node produces almost exactly the same output, given the same input
alright, so I think it's one of the following
- the way outputs from node A are buffered and fed into node B is broken
- the way outputs from node B are buffered and returned is broken
- the Steam Audio API calls are wrong
super secret extra option: audionimbus marked something as Sync that is in fact not Sync
oh man i have to set up an ssh key for these steam repos
Oh really?
I can just send you a zip of the submodule if you want
no stand back i got this
im a hacker
@slate scarab if you fix this
I will literally
idk
send you enough money for a Happy Meal
lmao bet
no water just break it up a bit and eat it like a cracker
lucky for me, no matter where you live, a Happy Meal can only be cheaper than here
anyhoot
I hope it builds locally for you haha
yes
i was hoping everyone was just underrunning because the dev profile had no optimizations
but it's still buzzy with them 😅
the other issue is that when you set the gain of the non-direct audio to 0, the direct audio sounds correct, but has no occlusion or air resistance
also, thanks for that great testing sound file hehe
As if it's not being applied at all?
fly out of the room and away
it doesn't go down in volume
and I can't tell any difference between inside the room and outside
in contrast, the reverb and reflections, buzzy though they may be, at least respect the walls
But it's not like the direct sound is just ignoring the camera position. Rotate the camera and the audio will correctly pan from one ear to the other.
Oh really? I'm not even observing that
oh well probably because I just broke it. I hope you don't mind me using the latest commit from seedling to get the effect in the pool properly. I'm converting the queries to not be Singles now.
oh yes plz
wait, I'll give you collab access
then you can just push
done 
oh i didn't know github had a nice feature for this
oh right, really there should just be one decode node
could have one per NPC, since they have different listener positions
ya in that case you'd want an ambisonic bus probably, and you could route all ambisonic pools to it (and then route the bus to a decoder for the actual player)
but i'll keep it simple here
I could help take a look, but audionimbus is more mature than my old steam audio library
I would be super super super glad if you could double check the API calls
but you'll have to share the Happy Meal with Corvus
Corvus gets the toy though
mildly pleased meal
is the library author in the disc?
ye
but they were already pinged, so I don't want to spam ping 😄
it's @maxence8541
Gotcha
So the issue you're having rn is direct sound hrtf stuff works fine, but scene stuff doesnt?
or is it the effects ontop of direct
- direct audio sounds fine, but only the rotation of the head is respected. There's no occlusion or distance volume down thingy (air resistance?)
- reverb + reflect sounds buzzy, but correctly respects walls
@slate scarab any leads I can investigate?
I'm completely out of ideas
still working out the per-sampler configuration (for some reason it's going silent atm, even though the connections look good in firewheel's debug output)
once that's set up I shold be able to get to the meat of it
tofu you mean

sorry i didn't realize this was a vegeterian investigation
vegan
I need to clarify this or I may get my vegan powers removed
i am beginning to unravel the tofu
hmm
I'll have to look at what audionimbus does, distance attenuation worked fine out of box when I made the other library
Well the issue with that is that the settings aren't being updated -- I've fixed that bit and the distance / occlusion is working as expected.
oh, same with the orientation
oh heck yeah
thanks!
wow the occlusion is really harsh without reflections
ya, it's just a direct raytrace I believe
turn the corner and it silent haha
there's a lot of types of reflections and reverb too
may I request 1x git push, kind master crow?
gets really interesting
ya just a min, i got a couple things in progress here
@potent moat do you happen to know how pathing is setup?
audionimbus doesn't have a demo for that
but from what I can gleam, the API suggests you have to setup probes manually
is that true?
Do you, like, just spawn them in a 3D grid?
They don't have scene stuff? Or wat
sure they do 😄
I'm just not sure if this means I can tell Steam Audio to set these probes up for me
Oh I'm not entirely sure but I think it's manual
okay pushed, lmk if you run into any issues getting it building again @rapid hedge
it should just work
yay can confirm occlusion works!!
Oh ya look a bit up from that portion of the docs jan
let me check the diff
You specify a matrix of probe position and then put that in an array
OOOOOO
then you can bake things using that probe array/batch
ya, tbh I'm certain you could make a smart voxel based autoplacer to get good probes
But that's future smart stuffs
When I was looking into this a while ago I believe that'd what microsofts audio engine did
Honestly if you used something like rerecast you could prob get something similar in good autoplacing
better as a separate library tho lol
@slate scarab how does this work order-wise? Does the signal first go through the effects, then the volume node, then the chained node?
Yeah so it might not be super clear from the bundle, but the node on the pool itself is always last. All members of the pool are routed to it. So it's SamplerNode -> AmbisonicNode-> VolumeNode -> Decoder
The docs mention this almost in passing, so I suppose that could be expanded on.
oh to be clear *last in the pool, the decoder is outside of the pool, so we're chaining the pool (i.e. the volume node) into it
aaah and I see where I used the wrong reverb_effect_params. But I thought the processor was rebuilt when the node changed. Is that not the case?
Namely I would have expected a changed simulation_outputs to rebuild the processor
No, only when the configuration changes. Rebuilding any times the param changes would be quite inefficient! That's why we have the diffing.
ah, gotcha 🙂
Does this already allow doing 3 separate (SomethingNode -> VolumeNode)?
Or does that require more setup
no because that would be a graph, not a mere chain
all the items in sample_effects! are chained one into the next
When we flesh out the "subgraph" idea, that can change.
If this were a proper integration library, how would one encode things like the AMBISONICS_ORDER so that they're user-configurable?
Given that all nodes and the simulator depend on a common definition
I guess you could set it up as part of the plugin? Or automatically mutate all nodes once a resource with it changes?
Oh and @potent moat there's no way to change the sampling rate of the simulator without completely rebuilding it, right?
Also I wonder if the audionimbus::Context should be hidden from the user, or proudly exposed so that they can do whatever
If the integration is purely forbevy_seedling, I'd use a config struct with a private field. Immediately upon insertion, you could mutate it to match some ambisonic order resource.
If you also mutate it in response to the user adjusting the ambisonic order, it will correctly rebuild all the nodes.
there's something lovely about the idea of "just add this node and it works magically" without having to even know what a context is
But that would require ugly things like hiding a lot of state behind Options because the user could spawn everything before the context even exists
Also the AudionimbusPool looks very neat now, but if behind a library, a user would probably want to configure the different nodes
So I can imagine they would still need to manually spawn the effects
(unless I understood the API wrong)
Well really you could do whatever you want. For example, you could expose the important configs as some meta-component that a user inserts on the pool node, and then it handles automatically adding the effects.
Ooooh that's a neat idea
I like that
Once this works I'll basically copy-paste it into the Thief AI to iterate on it, but I can see myself turning it into a little library later 🙂
I suppose you could also express the pool creation as an event 
But I think it's already great for users if there's just an example for steam audio + seedling at all
Fixed some lints, but if it conflicts with your local changes feel free to force push
yeah, steam audio takes all the audio settings as const ptrs and I don't think they store them, so you'd have to rebuild hrtfs, effects, etc.
but really sampling rate shouldn't be changing at runtime often
idk if any games even really handle that
Corvus says it happens when switching output devices
I suppose yeah
ya happens easily in that scenario
Which sounds like something I should support 😄
I don't think you need to rebuild everything though
Scenes and probes and such can be untouched I think
yeah makes sense
I think it's mainly hrtfs, effects
sources
Ya
and the nodes by extension
meh
I'm just dreading the bookkeeping
it's not much
but it's boring 😄
dear Steam Audio, just give me a method to change the sampling rate
thanks
Yeah, would be nice if they handled that for you
I mean MAYBE they do I have no idea, the ptrs are const but who knows if they store the ptrs
hmmm still failing to combat that buzz
did I heck up the output buffer or something like that?
wha
HOLY SHIT AAAAA
@drowsy dome check this out
I see you got sick of the fullscreen
(me too)
oh sorry haha
no worries that was from Max' original code
i don't got time for it slowly moving to another window in macos
ooooh so my very very smart decision to reuse the effect because it was two times the same thing was not as smart
welp
And here I thought an "effect" was something like a pure function
You were using the same effect struct for the reverb and reflection, but that's illegal! It was likely rapidly cycling between the different settings, and depending on the kind of things it's doing, it can sound torn up like that.
I see
It can happen with the HRTF too even in normal use -- if something moves too quickly across the sound field, it can sound artifacty like that.
yeah in my mind the effect was the config for a pure function to be applied
not something that implied some kind of state
makes sense now 😄
ya it's storing state for filters and stuff
heck yeah
this is great!!!
I have some ideas of how to automatically read the topology of TrenchBroom maps into steam audio
should be simple
then we can draw arbitrary halls to test the effects 🙂
aaaaah this is cool!
@cold isle it works now!
There's also some optimization opportunities -- for example, we don't need a staging buffer for the decoder, since it already has its buffers arranged as pointers.
@slate scarab set up your GitHub Sponsors and I'll treat you to a meal
no joke
I'm just playing around with the program right now, this is amazing 😄
Thanks a bunch again!!!
Nice!
wait huh
wait wtf
it was fixed
ok im taking a look i am also sustenencing
where this be
where where
i really wanna see
my brain is tingling
oh wtf it's ont he branch
ok okokokokok
wtf how does fcba530 fix the buzz
im so confused
was it using the reflection effect twice
two effect better than one
Shit
yeah turns out the reflection effect implies state bound to it on the steam audio side
But how did you know 😂
so you need the exact same effect twice so it doesn't overwrite its own state
(since reverb and reflection are the same thing, just with flipped listeners)
Still not sure how that made buzz
it oscillated between two effects
It's kinda like if you modulated the parameters of a filter at like 500hz
which in this case would be the equivalent of the camera zooming around all over the place
It's cycling fast enough that the oscillation rises into the audible spectrum (or if it automatically handles smoothing, then maybe there are secondary artifacts that contribute).
m
now the important question
That’s lovely I like audio engineering infinitesimally less now
how does the CAW sound
real questions
caw sounds pretty decent 👌
can we get a “coo”
one thing i thought was interesting is that the binaural setting sounds overly muffled
the high frequency attenuation was pretty strong
but maybe that's partly because the scale is huge or something
is it enough to do binaural: true?
ya that's what i mean
idk i assume there isn't other setup required
oooooo
this is neat
I don't hear it muffled
but I don't have very trained ears
it just gives me some goosebumps 👀
some strong placebo they implemented in that feature
you might notice it sounding "brighter" if you face one ear directly at the source
oh yeah you're right!
let's see how bright the CAW gets
Now there I hear it kinda muffled
let's set the material to metal
it's almost like it's mirrored 😅 it sounds less muffled looking away from the source
(directly away that is, we should expect it to be very clear when it's facing an ear)
once we've optimized it up, i'd be really interested to see how much we can throw at it
I mean assuming we do a good job, the performance of Firewheel itself will be basically negligible. Really it would be a stress test of steam audio.
ive got an interesting question. using BT headphones (airpods), and playing midi commands from a midi file in my app, it seems like the headphones play nearly directly on time. But, if I press a note with my mouse cursor, or play with a connected MIDI device, the delay is audible.
is this an illusion, or is it some optimization
relatable here with my BT headphones
do u midi
no, but the delay is also audible when setting up game sounds
I even measured it a long time ago because I wanted to make sure I wasn't just imagining things
illusion i think -- the clicking allows you to easily perceive the latency
gr
ur probably right, i wish the explanation was cooler
OKOK wait
youtube videos
aha, got ya there
how they know
you can sometimes catch it doing this if you look closely when you start playing
you can get latency estimates from devices
is there any way to cheat the ear so that you can play a note ahead of time without upsetting the listener
feel like the NES would have answers
feed the audio stream into a predictive AI
ok here's the thing. i have the interesting edge case of knowing what note they will play, but i don't know when
but if I can figure out almost when, i could probably get bluetooth headphones to work
im thinking a quiet anticipatory transient
like a reverse sample
hmmm ya maybe
(I'm still so happy about the Steam Audio stuff working
)
I think with just a little more work we could wrap it up in a crate that's really nice to use. Which I think is super cool!
this work seems to be rerecast related, yeah?
Thief AI related*
oh
But I would do a backend style like in rerecast
where we have another crate for avian integration
I'd do the same here: offer a second crate that automatically generates steam audio meshes from avian colliders
And a builtin backend that does the more naive thing of just turning Mesh3d into steam audio representations
im telling you, everything there and here is moving 3d so far forward for bevy, disappointed ive not been able to use it yet, but its day is coming. I'll be sitting with you on that ol video of that workflow soon enough
but spatial audio here is like
really really cool it's workin
hehe my pleasure
I think Bevy is really lovely to do 3D in, you just have to somehow have the insider knowledge of which crates to use and how to combine them lol
and how to do things like the multicam hack
you know the one
yes, but at the same time, it feels like any game that involves a story with multiple levels is yet difficult. and I still haven't made up my mind if that's because of the nature of ECS.
ah yes, the one where you.... hack multiple cameras
yes im familiar fellow 3d devs
i probably won't know the answer to that for a decade
basically a lot of rendering features are only ever tested with one camera, but first person games typically use at least 2, usually 3 cameras. To make them work together without bugs, you need this ugly duckling:
pub(super) fn plugin(app: &mut App) {
app.add_observer(make_cameras_play_well_together);
}
fn make_cameras_play_well_together(
add: On<Add, Camera>,
mut cameras: Query<&mut Camera>,
mut commands: Commands,
) {
let entity = add.entity;
let mut camera = cameras.get_mut(entity).unwrap();
if camera.order == isize::from(CameraOrder::World) {
// Use the world model camera to determine tonemapping.
return;
}
// Needed because of https://github.com/bevyengine/bevy/issues/18902
commands.entity(entity).insert(Tonemapping::None);
// Needed because of https://github.com/bevyengine/bevy/issues/18901
// and https://github.com/bevyengine/bevy/issues/18903
camera.clear_color = ClearColorConfig::Custom(Color::NONE);
camera.output_mode = CameraOutputMode::Write {
blend_state: Some(BlendState::ALPHA_BLENDING),
clear_color: ClearColorConfig::None,
};
}
haha that's a lot of issues
@faint wigeon hates this too haha
someday
someone should make a 3d spectrogram for the phase
if possible. idk, im saying things ofc
(useful for chorus i imagine) (again idk if that's actually a thing either)
:o
we’ll have an audio pane in the editor and we’ll put that one over in the corner
you can spin it around like a little cube
wrote a shader this weekend right, it had that uh, perlin noise thing. but i moved my cube around and like, i was getting slices of the noise on the face of the cube as i moved it through space. kinda cool
would be cool to make "shadergram"
lol yeah
set it up for VR
Is it intentional / known that #[require(...)] doesn't work with pools?
Yeah, there's probably a better mechanism to make it work nicely there. The issue is that it needs a hook to insert a "type-erased" label. But I don't want to reimplement the entire Component derive macro.
Maybe there's a better solution lying around, but it certainly hasn't struck me yet.
I suppose SamplerPool<L> could register an observer for L
then the only failure case is if you never spawn a pool with that label.... in which case your samples will get re-routed to the default pool if possible.
Oh but then you must spawn the pool first. Which isn't so ideal.
meh, I see
The alternative, of course, is that you manually register your pool labels. But I despise manual registration.
it's fine though 🙂
I was wondering if I should make an own node for the NPCs. In that case, how do I tell seedling to not route it to the main bus?
Given that the audio should never actually play over the speakers in that case
just make a node with no outputs
clever!
@cold isle how do you feel about adding an optional firewheel feature to audionimbus?
For adding additional derives needed for use in nodes
If that sounds bad, I would probably use a fork of audionimbus for my own projects to not have to maintain the 300 LOC of trivial wrappers
But that’s fine too 🙂
Sounds good to me 🙂
Great, thanks 😄
Awesome work!!
How helpful do you think a bevy_seedling<>audionimbus plugin would have been for you?
I suppose it would save the effort of implementing components
Well it would have saved me all work I had to do until now haha
although I'm wondering to what extent they can be generalized to support any effect?
We discussed this a bit
Should be doable to create a node for each effect
And then configure those nodes as needed
That'd be neat 🙂
Can't wait to play Thief remastered in Rust 😉
@cold isle mind if I replace some usize with u32? Given that they're cast as i32 down the line anyways, this seems fine
I'm doing this because firewheel prefers concrete sizes
@dusky mirage this is about usize not implementing Diff / Patch
actually, given that all values are in the end cast, it seems good to do this anyways
since usize is currently lying to the user about audionimbus' capacities
Hmmm
When I change
firewheel = { version = "0.8.0-rc.1", default-features = false, optional = true }
to
firewheel = { version = "0.8.0-rc.1", default-features = false, optional = true, features = ["std"] }
bevy_ crates start appearing in my Cargo.lock
I don't like that 
Hm, it's not a direct dependency of any firewheel crates
the only bevy dependencies are subcrates
bevy_platform makes sense to me
the others not so much
probably accidental activations (bevy_ecs/std instead of bevy_ecs?/std)
sounds like it, but I couldn't find it at a glance
hopefully it's not transient
*transitive
ya idk looks well behaved in the main and subcrates
¯_(ツ)_/¯
Yes, good idea 👍 in fact I should have used u32 anywhere it's not used for indexing
done
Now the sync thingy is left
This can totally be done on the firewheel side, it's just a bit annoying
Need to create a wrapper for ReflectionEffectParams that looks the same, except for an ArcGc<OwnedGc<ReflectionEffectIR>>
hm, does the effect need to be sync? or is that just the impulse response for the effect
but yeah if it really is sync it's easier in multiple ways
easier for the ECS too
I need to give the node access to ReflectionEffectParams AFAIK
Used here in the processor:
And the derives yell at me if the node is not Sync 
well it wouldn't work as a component either if it's not sync 😅
@slate scarab I could also wrap the entire simulation output in an ArcGc<OwnedGc<T>>
Well, an Option<ArcGc<OwnedGc<T>>> because it's late-initialized
ya not super ideal
Probably better to do the OwnedGc dance just for that one reflection effect then
Though I wonder
If I clone that pointer
And put the clone behind an OwnedGc
that's not exactly in the spirit of Sync lol
the proper thing there would probably be to manually derive diff and patch for it
Though I drop the original reference right after
I don't feel up to the task for that
By it you mean ReflectionEffectParams I presume?
either that or a wrapper around the IR
but then it's still not Sync -> no component
yaaaaaaa
Actually I would be grateful if you could take a look at https://github.com/MaxenceMaire/audionimbus/pull/20 in general 🙂
have we checked the source? im trying to find the c file to see if its sync
@cold isle said it's not
oh no 😅
but it is send?
should be
oh but i guess if you clone it that kinda violates the non syncness
bingo
which is precisely what I do right now
but uuh
it's a bit sketchy, isn't it
i have some ideas, ill explore them later
basically: RwLock could work in the ECS — remove it when diffing and place it in a OwnedGc so it can be sent to audio
jank, but might work
actually i have better ideas
Can I leave that to you then? 😄
send it immediately upon producing the value as a raw event — dont bother with storing it in the params for diffing
much better
Other than this one thing, the PR is ready from my side
both the audionimbus and audionimbus-demo PRs
I'll add you as a collaborator to the audionimbus fork too, sec
it's the no-alloc branch
oki i will try to find a nice solution after work
Feel free to change whatever on the audionimbus and the audionimbus-demo PR 🙂
thanks a bunch 
@cold isle are you sure IPLReflectionEffectIR is not Sync?
It looks to me like some buffers filled by the simulation when spitting out outputs, and AFAIK they're read-only everywhere
Baaah nvm, iplReflectionEffectApply definitely mutates it
do not Sync this
in fact, the Rust API should maybe even go so far as to Mutex it 
Or at least the .apply methods should take in a mutable reference to its params to make clear what's happening
To be honest, the more I look at the C code, the less I'm sure 😅 IPLReflectionEffectIR is just supposed to be a handle while the actual impulse response is handled by SA internally. Passing the pointer around should be fine but accessing the underlying object simultaneously is unsafe
Also, the params should not impl Clone
because they're cloning the pointer that way
But then they're no longer RealtimeClone either 
eek
Okay so I think the problem becomes a lot clearer if we step back a moment and consider what should actually be in the seedling params. Right now, we have a few inputs for the simulation and processing (like source position and listener position) as well as the results of the simulation. But I don't think that really makes sense. After all, the simulation outputs are basically useless to anyone but the audio processor. Even for your AI work, it's not like those outputs are useful. They perform nontrivial transformations on the audio.
Instead, the params should just contain just the inputs, including things like DirectSimulationParameters, which is currently hard coded. Then, rather then awkwardly playing hokey pokey with the simulation results, the system that calculates them should immediately queue them to be sent to the audio processor.
oh that's possible? 👀
that sounds wayyyyyy better
and yeah the AI definitely also doesn't care about the sim outputs
The Custom variant doesn't have a Sync requirement, and it's not Clone or anything so it's legitimately not required.
I suppose I can read them from the events: &mut ProcEvents?
do they get cleared every frame?
With this setup, there's no need for the params to implement Diff and Patch, so the only wrapping you'd need to do would be to make sure the inputs are actually Send and Sync (some of them aren't).
not exactly -- more like: if they don't have a timestamp, they'll be cleared after the audio processor has been called with the opportunity to read it, and if they do have a timestamp, they won't be cleared until their time is passed
hmm so if I only run the simulation every 200 ms
do I write the old outputs into the events every frame?
oh wait
no the processor could just use the old ones itself
that's what the timestamp is for I suppose?
oh ok
so read the events, if there are newer ones, replace the ones the processor is storing
got it
ya
they'd still have to be Options since the audio procesors won't be initialized with valid simulation results, so it should just work out
in other words, if the audio processor hasn't received any simulation results ever, it should just skip processing
with this approach, the outputs don't need to be diff + patch either, right?
right
Cool!
they just need to be Send + 'static
that they should be
I'll definitely need your help to implement diff + patch for the inputs though
oh we should just skip them then?
no they don't need to implement those traits at all (you can register_simple_node)
there's probably a better name for that method but
but it still needs the position of the source and listener
mm ya i suppose that would still be useful
so yeah you could skipp everything that's not the inputs
makes sense
then audionimbus maybe doesn't need a firewheel feature after all
Hmm the inputs still need to be RealtimeClone though, right?
Which implies Clone
i don't think so
Also keep in mind you don't need to store the params struct in the audio processor if you don't want to. With this new approach they'd have a ton of inputs which aren't useful to the processor. You can match on the events directly.
The params struct does need to be Clone though for seedling, so there might be some proxying/transformation involved. Hopefully a minimal amount though.
Like for something like this https://docs.rs/audionimbus/latest/audionimbus/model/distance_attenuation/enum.DistanceAttenuationModel.html, since we can't store the callback in the ECS as-is, we might just make a simpler enum that implements Into<DistanceAttenuationModel>. A touch annoying, but I think that makes sense for an integration library.
Or maybe we could adjust it to have a more rusty API anyway
Seems like that would make more sense as a Box<dyn FnMut(f32) -> f32 + Send + Sync> or something
(or maybe just Arc<dyn Fn(f32) -> f32 + Send + Sync>... more restrictive, sure, but then it would be Clone
)
No idea how to bring that over the FFI barrier lol
I'll uh
Leave that for a follow up
nah should be easy, just put the Arc<dyn ...> in the void pointer
But the C code wants a function
Not an Arc to a function
ya so always give it a function that just calls the closure
Fair enough
I'll still do that though to not lose myself in too deep of a rabbit hole haha
How do i decide what does in the node and what goes in the config component? Could the audionimbus simulation inputs also go in the config?
hey, just learned about firewheel from the 0.17 release post and curious if firewheel is already a decent replacement for bevy audio or is it better to wait still before switching?
in my case it’s for a relatively simple 2d game, don’t need fancy spatial audio features etc
Ah yep, I'll patch that real quick. https://github.com/BillyDM/Firewheel/blob/c997f830163ae353a7ec43607b6dd84347f0391a/crates/firewheel-core/Cargo.toml#L45
well, but bevy_platform is an unconditional dep!
it shouldn't be causing this issue
i couldn't tell why it's happening either
Changing the config will cause the audio processor to be re-created and re-inserted into the graph, so you should generally put stuff in there that you don't expect to change often, or things that require costly operations or allocations like the ambisonic order.
By this point I'd consider it an excellent replacement! It has feature parity with bevy_audio, along with a ton of new functionality. If you happen to target Wasm, you can also get multi-threaded audio, which resolves the dreaded cracklies.
did i break something
Even if you don't need any spatial features, it's more efficient than bevy_audio (2-3x faster under normal conditions) and makes it easy to add things like reverb, fade ins / fade outs, and more advanced musical sequencing.
No, I think it's just an update to the Rust compiler or something.
@limber kernel Oh yeah, because the impulse response isn't being diffed/patched at all, the ArcGc<()> is not necessary. https://github.com/piedoom/Firewheel/blob/b1d0cfbb099e83a9774557366f7ff9f455868795/crates/firewheel-nodes/src/convolution.rs#L62
Yes 100% ready now imo
That being said, it would be nice to keep the data-driven API by having an impulse response parameter.
You can manually implement Diff for a custom ImpulseResponse parameter like this:
pub struct ImpulseResponse {
pub impulse: Option<ArcGc<dyn SampleResourceF32>>,
}
impl Diff for ImpulseResponse {
fn diff<E: EventQueue>(&self, baseline: &Self, path: PathBuilder, event_queue: &mut E) {
if self.impulse != baseline.impulse {
// .. construct `FftSampler` and push it to the event_queue
}
}
}
However, for this to work I think we need to add a #[patch_skip] field attribute to the Patch derive macro to skip deriving patch for a field. @slate scarab
ya we have that
Oh we do? Cool!
well, it skips both that is
Oh but I guess if you manually derive one, that's fine.
(#[diff(skip)] is the attribute -- skips diffing and patching if one or both is derived)
Yeah, we need the impulse response parameter to derive Diff, but not Patch.
If you want it to be more granular, then yeah we'll have to updated it actually
we could have skip, skip_diff, and skip_patch
which is similar to how serde does it
Or wait, I mean that the impulse repsonse parameter will manually derive Diff, but we also want it to not automatically derive Patch.
Right so in that case it should work as-is.
