Lyra V2 – a better, faster, and more versatile speech codec
opensource.googleblog.com
opensource.googleblog.com
https://www.rowetel.com/?page_id=452
Yes, but it tops out at 3200b/s:
* https://en.wikipedia.org/wiki/Codec_2
Lyra V2 seems to start there and then goes up to teens of kb/s, at which point Opus can perhaps take over the job.
I hope you aren't suggesting that encryption should be used on amateur bands.
[0] https://ham.stackexchange.com/questions/72/encrypted-traffic...
Great move for a pump and dump scheme tho, now they are moving onto CBRS LTE with a whole new token separate from HNT.
700 bits is less than the ASCII of this comment.
Opus is a very good codec, but it's not amazing CPU-wise. I work on a VR world, and audio encoding is usually our most limiting factor when running on an VPS. We have the capability to negotiate codecs, so the high cpu/low bandwidth use case is already covered.
What I'm looking for specifically:
* Low CPU usage
* Support for high bitrate, suitable for music and sounds other than voice
* Low latency
from scipy.io import wavfile
import numpy as np
import zstd
sampling_rate, samples = wavfile.read(r'data/bootleg-compress/music_orig.wav')
orig = samples.tobytes()
naive_compressed = zstd.ZSTD_compress(orig)
deltas = np.diff(samples, prepend=samples.dtype.type(0), axis=0) # Per-channel deltas.
compressed_deltas = zstd.ZSTD_compress(deltas.ravel()) # Interleave channels and compress.
decompressed_deltas = np.frombuffer(zstd.ZSTD_uncompress(compressed_deltas), dtype=samples.dtype)
decompressed = np.cumsum(decompressed_deltas.reshape(deltas.shape), axis=0, dtype=samples.dtype)
assert np.array_equal(samples, decompressed)
print(len(orig))
print(len(naive_compressed))
print(len(compressed_deltas))
giving: 17432876
15518973
12817602
Looks like my initial estimation of 2-4 was way off (when FLAC achieves ~2 this should've been a red flag), but you do get a ~1.36x reduction in space at basically memory read speed.Using an encoding for second order differences with storing -127 <= d <= 127 using 1 byte and the others 2 bytes (for an input of 16-bit audio) I got a ratio of ~1.50 for something that can still operate entirely at RAM speed:
orig = samples.tobytes()
deltas = np.diff(samples, prepend=samples.dtype.type(0), axis=0) # Per-channel deltas.
delta_deltas = np.diff(deltas, prepend=samples.dtype.type(0), axis=0) # Per-channel second-order differences.
# Many small differences, encode almost all 1-byte differences using 1 byte,
# using 3 bytes for larger differences. Interleave channels and encode.
small = np.sum(np.abs(delta_deltas.ravel()) <= 127)
bootleg = np.zeros(small + (len(delta_deltas.ravel()) - small) * 3, dtype=np.uint8)
i = 0
for dda in delta_deltas.flatten():
if -127 <= dda <= 127:
bootleg[i] = dda + 127
i += 1
else:
bootleg[i] = 255
bootleg[i + 1] = (dda + 2**15) % 256
bootleg[i + 2] = (dda + 2**15) // 256
i += 3
compressed_bootleg = zstd.ZSTD_compress(bootleg)
print(len(compressed_bootleg))
decompressed_bootleg = zstd.ZSTD_uncompress(compressed_bootleg)
result = []
i = 0
while i < len(bootleg):
if bootleg[i] < 255:
result.append(decompressed_bootleg[i] - 127)
i += 1
else:
lo = decompressed_bootleg[i + 1]
hi = decompressed_bootleg[i + 2]
result.append(256*hi + lo - 2**15)
i += 3
decompressed_delta_deltas = np.array(result, dtype=samples.dtype).reshape(delta_deltas.shape)
decompressed_deltas = np.cumsum(decompressed_delta_deltas, axis=0, dtype=samples.dtype)
decompressed = np.cumsum(decompressed_deltas, axis=0, dtype=samples.dtype)
assert np.array_equal(samples, decompressed)
Prints 11593846.But unless it's a non commercial project, the cost shouldn't be a big deal, so it's still a bit strange.
Physics are actually farmed out to the clients themselves, it's a bit of a quirky idea, but it actually works if one isn't concerned with accuracy.
Edit: Had high hopes for High Fidelity, and came very close to asking for a job there ;) Shame it’s kaput, didn’t know that :(
High Fidelity the company is still around, but they pivoted multiple times radically. Initially their plan was social VR of sorts. Then they tried to make a corporate product for meetings and such, and gave up on that right before COVID19 hit!
And after that they ripped out all the 3D and VR and scaled down to a 2D, overhead spatial audio web thing. Think something like Zoom, only you have an icon that you can move around to get closer or further to other people.
The original code still lives on, we picked it up and are working on improvements. Feel free to visit out Discord (see my profile).
It's a community-led continuation of High Fidelity, a dead commercial project. They made their own proprietary codec with excellent performance we can't use and managed to have a couple thousand people in the same server.
There is some correlation between the cost of a hardware implementation and complexity of a software implementation. SBC is a very simple codec, but AptX and LC3 might not be much better than Opus.
(Won't work with music, of course.)
It'd be interesting to see what the lift would be to get encoding & decoding running in webassembly/wasm. Further, it'd be really neat to try to take something like the tflife_model_wrapper[1] and to get it backed by something like tsjs-tflite[2] perhaps running atop for example tfjs-backend-webgpu[3].
Longer run, the web-nn[4] spec should hopefully simplify/bake-in some of these libraries to the web platform, make running inference much easier. But there's still an interesting challenge & question, that I'm not sure how to tackle; how to take native code, compile it to wasm, but to have some of the implementation provided else-where.
At the moment, Lyra V2 can already use XNNPACK[4], which does have a pretty good wasm implementation. But trying to swap out implementations, so for example we might be able to use the GPU or other accelerators, could still have some good benefits on various platforms.
[1] https://github.com/google/lyra/pull/89/files#diff-ed2f131a63...
[2] https://www.npmjs.com/package/@tensorflow/tfjs-tflite
[3] https://www.npmjs.com/package/@tensorflow/tfjs-backend-webgp...
* Variable bitrate. For many uses, the goal isn't 'fill this channel', but instead 'transmit this audio stream at the best quality possible'. That means filling the channel sometimes, but other times transmitting less data (ie. When there is silence, or when the entropy of the speech being sent is low - for example the user is saying something very predictable).
* Handling all types of audio. Even something designed for phone calls will occasionally be asked by users to transmit music, sound effects, etc. The codec should do an acceptable job at those other tasks.
2. Download a bazel binary (https://github.com/bazelbuild/bazel/releases or use package manager)
3. bazel build -c opt :encoder_main
4. bazel-bin/encoder_main --input_path=testdata/sample1_16kHz.wav --output_dir=$HOME/temp --bitrate=3200
Done!
googleblog.com = 172.217.14.65