sdroege / ebur128

Implementation of the EBU R128 loudness standard
MIT License
93 stars 15 forks source link

Support for chunked analysis #42

Closed rawler closed 3 years ago

rawler commented 3 years ago

This PR enables an application to do "chunked" analysis of long streams. When analyzing multiple hours of audio, analyzing it in chunks allows usage of multi-core to accelerate the analysis, for both decoding and the analysis itself.

rawler commented 3 years ago

Sorry for missing loudness_global_multiple in the first iteration of this. Turns out, all required for bit-exact parallel analysis is the seeding, to solve initialization of filter-states.

rawler commented 3 years ago

If we are willing to add a (feature-gated) dependency on rayon, I could perhaps add some "ParallelAnalyzer" implementation, that expects a stream of impl Samples, and through Rayon automatically chunks and analyzes the stream in parallel. I'm going to write such implementation for our application anyways. Let me know if that's desirable.

sdroege commented 3 years ago

That sounds interesting but I'm not sure how that would like API-wise. What did you have in mind for the API?

sdroege commented 3 years ago

Thanks, looks mostly good to me :)

That sounds interesting but I'm not sure how that would like API-wise. What did you have in mind for the API?

I would still be interested in this though!

rawler commented 3 years ago

That sounds interesting but I'm not sure how that would like API-wise. What did you have in mind for the API?

Sorry, I spent some days over last week building this, just to realize that decoding the audio itself is ~40% of the CPU spent in our application. So, analyzing chunks in parallel didn't really improve performance more than simply decoding in a separate thread does. If we want to chunk, we'll going to have to create separate decoders + analyzers for each chunk, and that's not something that I can see how to build in a generic way.

FWIW though; what I built and then dropped was roughly EbuR128::analyze_stream<E>(self, samples: impl Iterator<Result<impl AsRef<[f32]>, E>>) -> Result<AnalysisResult, E>. I could cleanup and bring the implementation here, if you want to write tests and maintain it, but my guess is that others will hit the same "decoding" bottleneck that I did and this will bring more pain than gain.

sdroege commented 3 years ago

I guess for that to be useful you need a really cheap compression algorithm (say, MPEG-1 Layer 2 or even cheaper like ADPCM, or WavPack) or work directly on uncompressed audio. I can see how decoding of a more sophisticated codec is strictly more expensive than measuring the loudness, even if the resampling for true peak detection is needed.

I'm not sure, it seems like a nice API to have but also doesn't seem useful in the majority of cases. Maybe you could provide it as an example, not as API, and we just put it into the examples subdirectory? :)