Skip to content

Instantly share code, notes, and snippets.

@Artoria2e5
Last active May 4, 2021 08:34
Show Gist options
  • Save Artoria2e5/64a2b0a03121e2c9af0b7943bfebf8e4 to your computer and use it in GitHub Desktop.
Save Artoria2e5/64a2b0a03121e2c9af0b7943bfebf8e4 to your computer and use it in GitHub Desktop.
Pretend that your 32-bit floating-point WAV is 32-bit integer
#!/usr/bin/env python3
"""
This cursed piece of shit lets you pretend your 32-bit floating-point
WAV is actually 32-bit integer, so FLAC may take it. Now the only encoder
that does 32-bit integer is some SVN build of FLAKE I have no intention
of compiling, so I can't test how badly it compresses.
What I can tell you is that WavPack hates it. It compressess down to about
84% of the original, which is worse than 7-zip. On the other hand the original
f32 version goes down to 58%. The test file is decoded from Open Goldberg,
Aria, AAC.
The good news is that the reverse conversion does work. There is probably a
better way to go by this if we exploit the fact that all floats involved are
in [-1, 1].
"""
import pathlib
from scipy.io import wavfile
import numpy as np
import struct
from typing import Union, BinaryIO, Callable, List
from functools import partial
handle = Union[str, BinaryIO]
def f2i_32(f: np.float32) -> np.int32:
"""
Turn a float into an int, preserving sort order.
Need a subtraction to appease signed int.
See: http://stereopsis.com/radix.html
"""
b = struct.pack(">f", f)
i: int = struct.unpack(">I", b)[0]
mask = -int(i >> 31) | 0x80000000
return np.int32(((i ^ mask) & 0xFFFFFFFF) - 0x80000000)
def i2f_32(i: np.int32) -> np.float32:
"""
Inverse of ``f2i_32``.
"""
i = (i + 0x80000000) & 0xFFFFFFFF
mask = (((i >> 31) - 1) | 0x80000000) & 0xFFFFFFFF
i ^= mask
b = struct.pack(">I", i)
return np.float32(struct.unpack(">f", b)[0])
def wav_samp(
mapping: Callable[[np.number], np.number],
infile: handle,
outfile: handle,
) -> None:
(rate, data) = wavfile.read(infile)
wavfile.write(outfile, rate, np.vectorize(mapping)(data))
wav_f2i_32 = partial(wav_samp, f2i_32)
wav_i2f_32 = partial(wav_samp, i2f_32)
def do_file(wav_map: Callable[[str, str], None], file: str, suf: str) -> None:
pf = pathlib.PurePath(file)
nf = pf.with_suffix(f".{suf}{pf.suffix}")
wav_map(file, str(nf))
def main(argv: List[str]) -> int:
# No time for argparse. I am sorry.
WAV_MAPS = {
"f2i_32": wav_f2i_32,
"i2f_32": wav_i2f_32,
}
wmap = WAV_MAPS[argv[1]]
for i in argv[2:]:
do_file(wmap, i, argv[1])
return 0
if __name__ == "__main__":
import sys
sys.exit(main(sys.argv))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment