Codec2-mod updates

You thought we gave up on Codec2 just because we have already improved it considerably? Wrong!

After a short discussion with Silvano IU2KWO, an interesting proposal emerged: split the whole codec2_t structure into two separate, smaller encoder and decoder parts. That way you no longer have to push the whole structure on the stack and fill it up with FFT twiddle factors, if you only need to use one part of it. Now, if you want to encode or decode speech, just use either encoder or decoder. This simplifies things and optimizes memory usage further. This brings significant savings that are required by OpenRTX (and probably any implementation running on limited-resources MCUs). The old path using a large structure is still available (e.g. for duplex operation).

On top of that, I also tweaked the KISS FFT internals to use built-in sincosf() calls instead of using standard sin() / cos(). Using doubles is generally not a great idea, unless you really have to do so. We do not, so I took one step further and eliminated all double-precision promotions that were hiding in the code. Yes, there were many such places, especially where M_PI was used, along with some other macros.

The code is available in the split-no-double branch.

I prepared a quick benchmark. The results are below.

Codec2-mod branchcodec2_initcodec2_encoder_initcodec2_decoder_initcodec2_encodecodec2_decode
main67.905––6.43711.804
split-no-double8.9585.8243.1365.5558.559
Speedup7.6x––1.2x1.4x
Branches comparison. Times are average for a run of 1000, in milliseconds.

An STM32F411 clocked at 100MHz was used for both tests with the code compiled using -Os flag (gcc 14.3.0).

Next, using a set of 2,000 wave files, mostly 3 to 20 seconds long, I compared the vanilla Codec2 with our experimental branch. As the last time, I measured ViSQOL scores for both: original wave file -> encoder -> decoder -> decoded wave file. The average score of the new code is +0.005. That suggests the applied changes are inaudible (theoretically – real-life tests will follow). The test also calculated Hamming distance between the encoded files for both implementations – the no-double differs in less than 0.5% of the bits. This means that the new code does not generally agree with Codec2, but that does not make the bitstreams incompatible. Small rounding errors cause this but, as shown by the speech quality metric, all cancel out.

ViSQOL score / Hamming distance checks (vanilla Codec2 vs Codec2-mod split-no-doubles branch).

There is one more test that should be performed: encode samples with Codec2 and decode the bitstream with Codec2-mod, then switch the direction and listen to both files.

Big shout out to Silvano IU2KWO for nudging me in the right direction.

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *