Source code for foucluster.transform

import os
import glob
import json
import subprocess
import multiprocessing as mp
import numpy as np
from scipy.io.wavfile import read
from .plot import fourier_plot, song_plot


[docs]def mp3_to_wav(mp3_file, wav_file, encoder='mpg123'): """ Transform mp3 file into wav format calling bash and using mpg123 or ffmpeg. :param str mp3_file: path to the mp3 file. :param str wav_file: path to the new wav file. :param str encoder: Encode to use. It could be mpg123 or ffmpeg. :return: """ if encoder == 'mpg123': bash_command = ['mpg123', '-w', wav_file, '--mono', mp3_file] else: bash_command = ['ffmpeg', '-i', mp3_file, wav_file] subprocess.run(bash_command)
[docs]def wav_to_fourier(wav_file, rate_limit=6000.0, step=1.0): """ WAV file is loaded and transformed into Fourier Series. This Fourier Series is limited. :param str wav_file: :param float rate_limit: :param float step: :return: """ rate, aud_data = read(wav_file) # Should be mono if len(aud_data) != len(aud_data.ravel()): aud_data = np.mean(aud_data, axis=1) # Zero padding len_data = len(aud_data) channel_1 = np.zeros(2 ** (int(np.ceil(np.log2(len_data))))) channel_1[0:len_data] = aud_data # Fourier analysis fourier = np.abs(np.fft.fft(channel_1)) freq = np.linspace(0, rate, len(fourier)) freq, fourier = limit_by_freq(freq, fourier, upper_limit=rate_limit) freq, fourier = group_by_freq(freq, fourier, step=step) a = np.max(fourier) / 100.0 # Max frequency will be 100.0 fourier = fourier / a return freq, fourier
def group_by_freq(freq, features, step=1.0): """ :param freq: :param features: :param step: :return: """ min_freq = int(np.min(freq)) max_freq = int(np.max(freq)) length = int((max_freq - min_freq) / step) + 1 new_freq = np.empty(length, dtype=np.float) new_features = np.empty(length, dtype=np.float) i = 0 for freq_i in np.arange(min_freq, max_freq, step): mask_1 = freq >= freq_i mask_2 = freq < freq_i + step mask = mask_1 * mask_2 new_freq[i] = np.mean(freq[mask]) new_features[i] = np.mean(features[mask]) i += 1 new_freq = np.array(new_freq, dtype=np.float) new_features = np.array(new_features, dtype=np.float) return new_freq, new_features def limit_by_freq(freq, features, upper_limit, lower_limit=None): """ Limit arrays of frequency and features by maximum frequency and bottom frequency. :param freq: array of frequencies. :param features: array of amplitude. :param float upper_limit: maximum frequency. :param float lower_limit: minimum frequency. :return: """ # Copy into arrays, in order to apply mask freq = np.array(freq, dtype=np.float) features = np.array(features, dtype=np.float) # Mask for bottom limit if lower_limit is not None: bottom_mask = freq >= lower_limit features = features[bottom_mask] freq = freq[bottom_mask] # Mask for upper limit upper_mask = freq <= upper_limit features = features[upper_mask] freq = freq[upper_mask] return freq, features def dict_to_array(song_dict): """ :param dict song_dict: load form dictionary to array :return: """ freq = np.array([k for k in song_dict.keys()], dtype=np.float) features = np.array([v for v in song_dict.values()], dtype=np.float) return freq, features
[docs]def time_to_frequency(song, source_folder, temp_folder, output_folder, rate_limit=6000.0, overwrite=True, plot=True, image_folder=None, encoder='mpg123', step=5.0): """ Transform a MP3 song into WAV format, and then into Fourier series. :param str song: name of the song, with MP3 extension. :param str source_folder: folder where MP3 files are. :param str output_folder: folder where pickle files from frequency series are saved. :param str temp_folder: folder where wav files are saved. :param float rate_limit: maximum frequency of the frequency series. :param bool overwrite: :param bool plot: if True, frequency series is plotted. :param image_folder: if plotting is True, is the folder where the Fourier data is saved. :param str encoder: encoder from MP3 to WAV. :param float step: step of the Fourier series. :return: """ song_name = os.path.splitext(song)[0] json_name = song_name + '.json' # Name of files mp3_file = os.path.join(source_folder, song) wav_file = os.path.join(temp_folder, song_name + '.wav') full_json_name = os.path.join(output_folder, json_name) if not os.path.isfile(full_json_name) or overwrite is True: # Fourier transformation try: if not os.path.isfile(wav_file) or overwrite is True: mp3_to_wav( mp3_file=mp3_file, wav_file=wav_file, encoder=encoder) frequencies, fourier_series = wav_to_fourier(wav_file=wav_file, rate_limit=rate_limit, step=step) # Save as JSON json_to_save = {song: {str(x): y for x, y in zip(frequencies, fourier_series)}} with open(full_json_name, 'w') as output: json.dump(json_to_save, output) # Plotting if plot is True: fourier_plot(freq=frequencies, features=fourier_series, folder=image_folder, filename=song_name) except MemoryError: print('{} gives MemoryError'.format(song_name))
[docs]def all_songs(source_folder, output_folder, temp_folder, rate_limit=6000.0, overwrite=True, plot=False, image_folder=None, multiprocess=False, encoder='mpg123', step=5.0): """ Transform a directory full of MP3 files into WAV files, and then into Fourier series, working with directories. :param str source_folder: folder where MP3 files are. :param str output_folder: folder where pickle files from frequency series are saved. :param str temp_folder: folder where wav files are saved. :param float rate_limit: maximum frequency of the frequency series. :param bool overwrite: :param bool plot: if True, frequency series is plotted. :param image_folder: if plotting is True, is the folder where the Fourier data is saved. :param bool multiprocess: if True, encoding and Fourier transform are run in several cores. It may be unstable (consume to much RAM). :param str encoder: encoder from MP3 to WAV. :param float step: step of the Fourier series. """ merged_file = os.path.join(output_folder, 'merged_file.json') os.makedirs(temp_folder, exist_ok=True) os.makedirs(output_folder, exist_ok=True) if os.path.isfile(merged_file): os.remove(merged_file) if plot: os.makedirs(image_folder, exist_ok=True) if multiprocess is True: songs = [(song, source_folder, temp_folder, output_folder, rate_limit, overwrite, plot, image_folder, encoder, step) for song in os.listdir(source_folder)] with mp.Pool(processes=max(mp.cpu_count() - 1, 1)) as p: p.starmap(time_to_frequency, songs) else: [time_to_frequency(song=song, source_folder=source_folder, temp_folder=temp_folder, output_folder=output_folder, rate_limit=rate_limit, overwrite=overwrite, plot=plot, image_folder=image_folder, encoder=encoder, step=step) for song in os.listdir(source_folder)] read_files = glob.glob(os.path.join(output_folder, '*.json')) with open(merged_file, 'w') as outfile: file_contents = [open(f).read() for f in read_files] outfile.write('[{}]'.format(','.join(file_contents)))