-
-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Auto-quit rework #8070
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
base: master
Are you sure you want to change the base?
Auto-quit rework #8070
Conversation
|
To clarify, by track channels do you mean |
|
@sakertooth The multi-channel plugins PR lays the groundwork for users to be able to increase the number of track channels within an instrument / mixer channel from 2 (a single stereo pair) to a maximum of 256 (arbitrarily chosen), similar to REAPER. This PR also lays some of that groundwork, but still keeping the number of track channels fixed at 2. |
|
I should also note that I'm unsure of the proper terminology to use, so I may change some things. According to ChatGPT, a "bus" is not the right term for a single group of track channels or the overall collection of channels, since a "bus" usually refers to a "routing destination or summing path". I'm thinking "(track) channel group" might be a good channel-count-agnostic alternative to "stereo pair" or "track channel bus". And as for the class |
So if I understand correctly, a "track channel" is just an audio channel, like a left and right channel?
I don't think we should keep the number of track channels fixed at 2 in the code:
This is another reason why I suggest also using a planar format. As a matter of fact, I would probably recommend that
Those are my current thoughts. Feel free to correct me on anything you believe I am misunderstanding. I really do want to understand your perspective and work. |
Yes, though it refers to a single audio channel not two channels (left and right) together. I'm using the term "track channel pair" to refer to two track channels that form a single stereo channel.
I think you're misunderstanding me. When I said it's "fixed at 2" channels, I mean we're still using 1 pair of channels and there's no way to change that at runtime: SampleFrame* trackChannelPair = getTrackChannelPair();
auto bus = AudioBus{&trackChannelPair, 1, frames};In the future, it might look something more like this: SampleFrame** trackChannelPairs = getTrackChannelPairs();
auto bus = AudioBus{trackChannelPairs, numTrackChannelPairs, frames};As you can see, Btw, I'm considering making
That would be a lot more work and beyond the scope of this PR. It would probably also cause performance issues until we fully convert over to planar. |
Glad we are on the same page. I understood that part.
Maybe I was not clear on my end. So in an ideal scenario (disregard reality for a minute), would you agree that I don't see a strong reason for having this "track channel" and "track channel pair" distinction. IMO, design your class interfaces for what you actually want, and not for what you have to implement because of certain requirements of the problem at hand. We don't want to have to change each call site when we later want to make
I understand your concern, PS: Is Edit: I also actually think it's more of a performance hazard not using planar here though. It's not like keeping things interleaved is going to be optimal for performance. My guess is that you are striding inside your buffers to work with individual channels anyways (prevents SIMD optimizations). That's still a performance cost/downgrade, and a planar format is quite frankly the easiest, most intuitive way to think of The only potential performance cost I can imagine using planar for Edit 2:
I take that back. Converting interleaved to planar will always result in some striding somewhere, but at least it is just copies and not any more complicated processing you eventually want to do for individual channels. Then again, we would need to profile to see which is better. |
|
I'll try and review this at some point and maybe formalize any concerns I have a bit more pragmatically. |
sakertooth
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since we aren't using planar yet and do not want to convert to/from it on the fly, I don't think we are in the position to have something like AudioBus as of right now, especially since there are disagreements that will require more discussion/work before we do something like that. Is AudioBus truly a requirement for your auto quit rework?
|
@sakertooth Sorry for the late response.
Yes, eventually
Yeah, it's starting to look like we should design this with first class support for dynamically-sized groups of channels, and squashing all the track channels into a single array of What do you think of something like this? inline constexpr track_ch_t MaxTrackChannelsPerGroup = 32;
inline constexpr unsigned int MaxTrackChannelGroups = 256;
class AudioBus
{
public:
// ...
class ChannelGroup
{
public:
// getters/setters here
private:
std::unique_ptr<float[]> m_buffer; // interleaved (at least for now)
track_ch_t m_channels; // # of channels in `m_buffer` (`MaxTrackChannelsPerGroup` maximum) - currently only 2 is used
std::bitset<MaxTrackChannelsPerGroup> m_quietChannels;
};
auto channelGroup(unsigned int index) const -> const ChannelGroup& { return m_groups.at(index); }
private:
ArrayVector<ChannelGroup, MaxTrackChannelGroups> m_groups;
f_cnt_t m_frames;
bool m_silenceTrackingEnabled = false;
};As you can see, this uses groups of a dynamic number of channels rather than groups of exactly 2 channels each.
Yes, it's a collection of audio buffers which keeps track of silent channels. I'm definitely open to changing the name.
When operating on individual channels like this class requires, it's true that interleaved buffers do not seem ideal. Though if both channels are being operated on at once (which is by far the most common situation, since L is usually mapped to L, R to R, etc.), there is no striding where we skip over every N samples, so it shouldn't be less performant. I'd imagine the cost of frequent conversions between interleaved and planar to be far more than some striding when iterating over an interleaved buffer, though I haven't measured this. I'd be willing to attempt that conversion from interleaved to planar in this PR, though it will probably be a lot of work, and we should probably figure out the channel grouping stuff first.
I can attempt to convert between planar and interleaved on the fly, though even if it doesn't work out, I don't think using interleaved should block this PR from being merged since it's what we're using already and doesn't prevent us from switching to planar in the future any more than adding a new instrument/effect plugin with interleaved processing does.
Yes, The current auto-quit system is probably usable even if multi-channel plugins and audio routing are added, though it's not suited for it and would probably be less efficient than it could be. For example, when auto-quit is enabled and plugins are put to sleep, under the current auto-quit system, all sleeping plugins would need to zero their output buffers each period rather than do nothing because some of the assumptions auto-quit relies on do not hold up in a world with pin connector routing. Under this PR, silence is tracked so they could avoid a lot of writes to buffers that are already known to be silent. Auto-quit works precisely when and where it needs to rather than applying globally across the entire effects chain. |
|
Interestingly, it looks like VST3 has an
|
inline constexpr track_ch_t MaxTrackChannelsPerGroup = 32;
inline constexpr unsigned int MaxTrackChannelGroups = 256;
class AudioBus
{
public:
// ...
class ChannelGroup
{
public:
// getters/setters here
private:
std::unique_ptr<float[]> m_buffer; // interleaved (at least for now)
track_ch_t m_channels; // # of channels in `m_buffer` (`MaxTrackChannelsPerGroup` maximum) - currently only 2 is used
std::bitset<MaxTrackChannelsPerGroup> m_quietChannels;
};
auto channelGroup(unsigned int index) const -> const ChannelGroup& { return m_groups.at(index); }
private:
ArrayVector<ChannelGroup, MaxTrackChannelGroups> m_groups;
f_cnt_t m_frames;
bool m_silenceTrackingEnabled = false;
};I think I am slowly understanding this a bit better. Your That being said, I think my stance on this being named For the Though, I am confused how we would handle situations (for example) where one of the |
This is another PR coming out of the work done in #7459, and probably the last one needed before that PR can be merged.
It reworks the auto-quit feature by introducing a new
AudioBusclass which keeps track of which track channels are currently silent as audio flows through the effects chain.When track channels going into an effect's input are not marked as quiet, it is assumed a signal is present and the plugin needs to wake up if it is asleep due to auto-quit. After a plugin processes a buffer, the silence status is updated.
When the auto-quit setting is disabled (that is, when effects are always kept running), effects are always assumed to have input noise (a non-quiet signal present at the plugin inputs), which should result in the same behavior as before.
Benefits:
This new system works so long as the silence flags for each channel remain valid at each point along the effect chain. Modifying the buffers without an accompanying update of the silence flags could violate assumptions. Through unit tests, the correct functioning of
AudioBusitself can be validated, but its usage inAudioBusHandle,Mixer, and a few other places where track channels are handled will need to be done with care.