0
$\begingroup$

New Python user here! I am a musician working on a program to streamline a process of using convolution math on a folder of .wav samples. The idea is that each audio sample will be multiplied with every other sample in the folder, including itself, and written to a new folder.

The functions and classes were built upon an answer by https://dsp.stackexchange.com/users/50740/felix-beutter and his response will give a shorter version of how the audio is being converted into data that can be used in this process. The code below is in a Pycharm virtual environment, with audio samples in a directory called "Music".

I am using numpy and the FFT to create arrays from the .wav data, and then multiplying that information together.

My question is: How can I perform different mathematical functions on data that is arranged in this way? At this point, only multiplication is possible, and I wish to manipulate the data in other fashions (sorting, randomization, etc)

Below is the code allowing the multiplication of the data in the Spectrum class.

class Spectrum:
    def __init__(self, amplitudes, frequencies, frame_rate):
        self.amplitudes = np.asanyarray(amplitudes)
        self.frequencies = np.asanyarray(frequencies)
        self.frame_rate = frame_rate

    def __mul__(self, other):
        return Spectrum(self.amplitudes * other.amplitudes, self.frequencies, self.frame_rate)

    def make_wave(self):
        return Wave(np.fft.irfft(self.amplitudes), self.frame_rate)

Below is the rest of the Python code for reference.

The loops at the end are for choosing which files to multiply, naming them, and writing them to the new folder. They also call the functions to convert the .wavs into data that can be convolved together. My math fails at this point and I'm not sure I understand how these arrays are interacting with each other.

Thank you!

import numpy as np
import glob
from wave import open
import soundfile
import os
from collections import defaultdict


class Wave:
    def __init__(self, data, frame_rate):
        self.data = normalize(data)
        self.frame_rate = frame_rate

    def make_spectrum(self):
        amplitudes = np.fft.rfft(self.data)
        frequencies = np.fft.rfftfreq(len(self.data), 1 / self.frame_rate)
        return Spectrum(amplitudes, frequencies, self.frame_rate)

    def zero_padding(self, n):
        zeros = np.zeros(n)
        zeros[:len(self.data)] = self.data
        self.data = zeros

    def write(self, file):
        reader = open(file, 'w')

        reader.setnchannels(1)
        reader.setsampwidth(2)
        reader.setframerate(self.frame_rate)

        frames = self.quantize().tobytes()
        reader.writeframes(frames)
        reader.close()

    def quantize(self):
        if max(self.data) > 1 or min(self.data) < -1:
            self.data = normalize(self.data)
        return (self.data * 32767).astype(np.int16)


class Spectrum:
    def __init__(self, amplitudes, frequencies, frame_rate):
        self.amplitudes = np.asanyarray(amplitudes)
        self.frequencies = np.asanyarray(frequencies)
        self.frame_rate = frame_rate

    def __mul__(self, other):
        return Spectrum(self.amplitudes * other.amplitudes, self.frequencies, self.frame_rate)

    def make_wave(self):
        return Wave(np.fft.irfft(self.amplitudes), self.frame_rate)


def convert_wav(file):
    data, samprate = soundfile.read(file)
    soundfile.write(file, data, samprate, subtype='PCM_16')


def read_wave(file):
    reader = open(file)

    _, sampwidth, framerate, nframes, _, _ = reader.getparams()
    frames = reader.readframes(nframes)

    reader.close()

    dtypes = {1: np.int8, 2: np.int16, 4: np.int32}

    if sampwidth not in dtypes:
        raise ValueError('unsupported sample width')

    data = np.frombuffer(frames, dtype=dtypes[sampwidth])

    num_channels = reader.getnchannels()
    if num_channels == 2:
        data = data[::2]
    return Wave(data, framerate)


def normalize(data):
    high, low = abs(max(data)), abs(min(data))
    return data / max(high, low)


def convolution_reverb(file1, file2, output_file):
    convert_wav(file1)
    convert_wav(file2)
    audio = read_wave(file1)
    ir = read_wave(file2)

    if len(audio.data) > len(ir.data):
        ir.zero_padding(len(audio.data))

    else:
        audio.zero_padding(len(ir.data))

    ir_spectrum = ir.make_spectrum()
    audio_spectrum = audio.make_spectrum()

    convolution = audio_spectrum * ir_spectrum
    wave = convolution.make_wave()

    wave.write(output_file)


def get_filepath(filepath):
    nn_file = os.path.split(filepath)
    return nn_file[1]


def filename(filepath):
    return 'Music/' + get_filepath(filepath)


def get_Max(id1, id2):
    return id1*(id1 > id2) + id2*(id2 >= id1)


def get_Min(id1, id2):
    return id1*(id1 < id2) + id2*(id2 <= id1)


def write_prep(to_split):
    two = os.path.split(to_split)
    one = two[1]
    to_split = one.replace('.wav', '')
    return to_split


cur_dir = os.getcwd()
convo_path = input('Please make a folder to store your new audio in: ')
try:
    os.mkdir(convo_path)
except OSError:
    print('Creation of directory %s failed' % convo_path)
    convo_path = input('One word please, you can rename it later lolll: ')
else:
    print('Successfully created the directory %s' % convo_path)



data_dir = 'Music/'
data = glob.glob(data_dir + '*.*')
con_dir = str(convo_path) + '/'
con_files = glob.glob(con_dir + '*.*')
unique = defaultdict(lambda: [])
audio_files = data
conv_name_set = data
group = 1
for id1 in range(len(audio_files)):
    for id2 in range(len(conv_name_set)):
        idMax = get_Max(id1, id2)
        idMin = get_Min(id1, id2)
        max_min = []
        max_min.append(idMax)
        max_min.append(idMin)
        true_key = str(group)
        put_to_dict = {true_key: max_min}
        unique.update(put_to_dict)
        group += 1


for tupleSet in unique.items():
    fileNum1 = tupleSet[1][0]
    fileNum2 = tupleSet[1][1]
    filename1 = filename(audio_files[fileNum1])
    filename2 = filename(conv_name_set[fileNum2])
    x = write_prep(filename1)
    y = write_prep(filename2)
    convolution_reverb(filename1, filename2,
                       con_dir + str(x) +
                       '_mult_' + str(y) + '.wav')
    print(f'smashing {x} and {y}')

print('Enjoy!')
```
$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.