pythonsignal-processingwavenoise-reduction

How to compute inverse of a wave sound file in Python?


I am trying to compute inverse of a 16 bit mono wave file(8000Hz) using Python without using almost any external module except wave which is used to read samples from the wave file. I've tried reading all the 16 bit samples and computing 0xFFFF - sample for each sample and writing that to another wave file. But when I mix the two audio files together, they don't silence each other. I've read some stuff on the internet about DFT, FFT and such these but I don't understand much from them even through I've got a strong mathematics background from highschool. Any hint is appreciated. My code:

import wave
from dataclasses import dataclass
from typing import List


@dataclass
class SoundClip16b:
    data: List["int"]

    def __len__(self):
        return len(self.data)

    def inverted(self):
        inv = list()
        for d in self.data:
            inv.append(0xFFFF - d)
        return SoundClip16b(data=inv)

    def to_bytes(self):
        databyte = b""
        for i in self.data:
            b1 = i >> 8
            b2 = i & 0xFF
            databyte += bytes([b1, b2])
        return databyte

    @staticmethod
    def from_bytes(bytedata):
        data = list()
        for index, byte in enumerate(bytedata[:-1:2]):
            next_byte = bytedata[index+1]
            data.append((byte << 8) + next_byte)

        return SoundClip16b(data=data)


w_noise = wave.open("noise.wav")
w_antinoise = wave.open("antinoise.wav", "w")
clip = SoundClip16b.from_bytes(w_noise.readframes(-1))
inverted_clip = clip.inverted()
w_antinoise.setnchannels(1)
w_antinoise.setsampwidth(2)
w_antinoise.setframerate(8000)
w_antinoise.writeframes(inverted_clip.to_bytes())
w_antinoise.close()

Edit: I also have tried by assuming that the 16 bit samples are signed integers rather than unsigned ones.


Solution

  • Thanks to Jan, the problem was with endianess. The wave was little endian while my code was assuming it was big endian. This code worked for me right:

    import wave
    import struct
    from dataclasses import dataclass
    from typing import List
    
    
    @dataclass
    class SoundClip16b:
        data: List["int"]
    
        def __len__(self):
            return len(self.data)
    
        def inverted(self):
            inv = list()
            for d in self.data:
                inv.append(~d)
            return SoundClip16b(data=inv)
    
        def to_bytes(self):
            databyte = b""
            for d in self.data:
                databyte += struct.pack("<h", d)
            return databyte
    
        @staticmethod
        def from_bytes(bytedata):
            return SoundClip16b(
                data=[x[0] for x in struct.iter_unpack("<h", bytedata)]
            )
    
    
    w_noise = wave.open("noise.wav")
    w_antinoise = wave.open("antinoise.wav", "w")
    clip = SoundClip16b.from_bytes(w_noise.readframes(-1))
    inverted_clip = clip.inverted()
    w_antinoise.setnchannels(1)
    w_antinoise.setsampwidth(2)
    w_antinoise.setframerate(8000)
    w_antinoise.writeframes(inverted_clip.to_bytes())
    w_antinoise.close()