HiveBrain v1.2.0
Get Started
← Back to all entries
patternpythonMinor

Comparing FFTs to deduplicate IVR recordings

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
deduplicatecomparingrecordingsivrffts

Problem

I've got a Python script that traverses two file trees, checking all .wav files for duplication.

I'm still an undergrad, and have never worked with audio before. I'm not entirely sure this is going to be a valid comparison.

I lost the SO answer I was using as a guideline, but the general idea is to split the audio into chunks, FFT those chunks, then compare the results.

I also would appreciate any structure/style advice.

Note: All Olympus Recordings are valid, but I need to load them to have the ffts to compare.

```
import os
import os.path
import wave
import numpy
import struct
from shutil import copy
from subprocess import call, Popen, PIPE

oly_path = "/home/will/Desktop/soundfiles/Olympus Recordings"
titan_path = "/home/will/Desktop/soundfiles/TITAN Recordings"
wav_roots = [oly_path, titan_path]
destination_path = "/home/will/Desktop/soundfiles/output"
temp_wav_path = "/home/will/Desktop/soundfiles/temp.wav"

similarity_threshold = 95
validated_wavs = {}
bunk_wavs = {}

def get_fft_similarity(fft1, fft2):
hits = 0
len1 = len(fft1)
len2 = len(fft2)
limit = (len1 if len1= similarity_threshold:
bunk_wavs[key] = to_sort
return 0

while key in validated_wavs:
to_sort.generate_new_name()
key = to_sort.filename

for w in validated_wavs.values():
if get_fft_similarity(to_sort.fft, w.fft) >= similarity_threshold:
bunk_wavs[key] = to_sort
return 0

validated_wavs[key] = to_sort
return 1

def get_fft(fft_path):
return_list = []
frames_list = []
chunk_size = 36
start = 0
wav = wave.open(fft_path, 'r')
frames = wav.readframes(wav.getnframes())
wav.close()

while start+chunk_size fdddd", frames[start:start+chunk_size])
frames_list.append(data)
star

Solution

UPPERCASE for constants

As a convention, in Python constants are uppercase:

OLY_PATH = "/home/will/Desktop/soundfiles/Olympus Recordings"
TITAN_PATH = "/home/will/Desktop/soundfiles/TITAN Recordings"
WAV_ROOTS = [oly_path, titan_path]
DESTINATION_PATH = "/home/will/Desktop/soundfiles/output"
TEEMP_WAV_PATH = "/home/will/Desktop/soundfiles/temp.wav"


No need for temporary variables

len1 = len(fft1)
    len2 = len(fft2)


Are useless because you use them only once, thus leading to no efficiency gain and code clutter.

def get_fft_similarity(fft1, fft2):
        hits = 0
        limit = (len1 if len(fft1)<len(fft2) else len2)

        if limit == 0:
                return 0

        for i in xrange(limit):
                if (fft1[i] == fft2[i]).all():
                        hits += 1

        ratio = float(hits)/limit
        return int(ratio*100)


Also about this function:

-
float(hits)/limit you can remove float if you from __future__ import division

-
Down below:

for i in xrange(limit):
            if (fft1[i] == fft2[i]).all():
                    hits += 1


You are using explicit indexing and a counter variable. Maybe zip is and sum will simplify your code.

Use more constants

You already use some, and that is nice, but things like:

  • "Olympus"



  • ".wav"



  • "Signed Integer PCM"



  • "soxi"



  • ...



Should be in their own named constant.

Are you sure of your class?

Usually if a class has init and only one other function, maybe you can use just a function for simplicity: I am looking at class IVR_Wav

Peculiar double effect sort_wav

I noticed that sort_wav both modifies a global variable and returns 1 or 0. It is then called by validate. Maybe you could write a validator_helper to make the check and another function to update the dictionary.

Code Snippets

OLY_PATH = "/home/will/Desktop/soundfiles/Olympus Recordings"
TITAN_PATH = "/home/will/Desktop/soundfiles/TITAN Recordings"
WAV_ROOTS = [oly_path, titan_path]
DESTINATION_PATH = "/home/will/Desktop/soundfiles/output"
TEEMP_WAV_PATH = "/home/will/Desktop/soundfiles/temp.wav"
len1 = len(fft1)
    len2 = len(fft2)
def get_fft_similarity(fft1, fft2):
        hits = 0
        limit = (len1 if len(fft1)<len(fft2) else len2)

        if limit == 0:
                return 0

        for i in xrange(limit):
                if (fft1[i] == fft2[i]).all():
                        hits += 1

        ratio = float(hits)/limit
        return int(ratio*100)
for i in xrange(limit):
            if (fft1[i] == fft2[i]).all():
                    hits += 1

Context

StackExchange Code Review Q#94672, answer score: 5

Revisions (0)

No revisions yet.