import numpy as np from . import main def open_audio(path:str = None, lib:str = 'auto', normalize = True) -> tuple: """Opens audio from path, returns (audio, samplerate) tuple. Audio is returned as an array with normal volume range between -1, 1. Example of returned audio: [ [0.35, -0.25, ... -0.15, -0.15], [0.31, -0.21, ... -0.11, -0.07] ]""" if path is None: from tkinter.filedialog import askopenfilename path = askopenfilename(title='select song', filetypes=[("mp3", ".mp3"),("wav", ".wav"),("flac", ".flac"),("ogg", ".ogg"),("wma", ".wma")]) path=path.replace('\\', '/') if lib=='pedalboard.io': import pedalboard.io with pedalboard.io.AudioFile(path) as f: audio = f.read(f.frames) sr = f.samplerate elif lib=='librosa': import librosa audio, sr = librosa.load(path, sr=None, mono=False) elif lib=='soundfile': import soundfile audio, sr = soundfile.read(path) audio=audio.T elif lib=='madmom': import madmom audio, sr = madmom.io.audio.load_audio_file(path, dtype=float) audio=audio.T # elif lib=='pydub': # from pydub import AudioSegment # song=AudioSegment.from_file(filename) # audio = song.get_array_of_samples() # samplerate=song.frame_rate # print(audio) # print(filename) elif lib=='auto': for i in ('madmom', 'soundfile', 'librosa', 'pedalboard.io'): try: audio,sr=open_audio(path, i) break except Exception as e: print(f'open_audio with {i}: {e}') if len(audio)>16: audio=np.array([audio, audio], copy=False) if normalize is True: audio = np.clip(audio, -1, 1) audio = audio*(1/np.max(np.abs(audio))) return audio.astype(np.float32),sr def _sr(sr): try: return int(sr) except (ValueError, TypeError): assert False, f"Audio is an array, but `sr` argument is not valid. If audio is an array, you have to provide samplerate as an integer in the `sr` argument. Currently sr = {sr} of type {type(sr)}" def write_audio(audio:np.ndarray, sr:int, output:str, lib:str='auto', libs=('pedalboard.io', 'soundfile'), log = True): """"writes audio to path specified by output. Path should end with file extension, for example `folder/audio.mp3`""" if log is True: print(f'Writing {output}...', end=' ') assert _iterable(audio), f"audio should be an array/iterable object, but it is {type(audio)}" sr = _sr(sr) if not isinstance(audio, np.ndarray): audio = np.array(audio, copy=False) if lib=='pedalboard.io': #print(audio) import pedalboard.io with pedalboard.io.AudioFile(output, 'w', sr, audio.shape[0]) as f: f.write(audio) elif lib=='soundfile': audio=audio.T import soundfile soundfile.write(output, audio, sr) del audio elif lib=='auto': for i in libs: try: write_audio(audio=audio, sr=sr, output=output, lib=i, log = False) break except Exception as e: print(e) else: assert False, 'Failed to write audio, chances are there is something wrong with it...' if log is True: print(f'Done!') def _iterable(a): try: _ = iter(a) return True except TypeError: return False def _load(audio, sr:int = None, lib:str = 'auto', channels:int = 2, transpose3D:bool = False) -> tuple: """Automatically converts audio from path or any format to [[...],[...]] array. Returns (audio, samplerate) tuple.""" # path if isinstance(audio, str): return(open_audio(path=audio, lib=lib)) # array if _iterable(audio): if isinstance(audio, main.song): if sr is None: sr = audio.sr audio = audio.audio # sr is provided in a tuple if sr is None and len(audio) == 2: if not _iterable(audio[0]): sr = audio[0] audio = audio[1] elif not _iterable(audio[1]): sr = audio[1] audio = audio[0] if not isinstance(audio, np.ndarray): audio = np.array(audio, copy=False) sr = _sr(sr) if _iterable(audio[0]): # image if _iterable(audio[0][0]): audio2 = [] if transpose3D is True: audio = audio.T for i in audio: audio2.extend(_load(audio=i, sr=sr, lib=lib, channels=channels, transpose3D=transpose3D)[0]) return audio2, sr # transposed if len(audio) > 16: audio = audio.T return _load(audio=audio, sr=sr, lib=lib, channels=channels, transpose3D=transpose3D) # multi channel elif isinstance(channels, int): if len(audio) >= channels: return audio[:channels], sr # masked mono else: return np.array([audio[0] for _ in range(channels)], copy=False), sr else: return audio, sr else: # mono return (np.array([audio for _ in range(channels)], copy=False) if channels is not None else audio), sr # unknown else: assert False, f"Audio should be either a string with path, an array/iterable object, or a song object, but it is {type(audio)}" def _tosong(audio, sr=None): if isinstance(audio, main.song): return audio else: audio, sr = _load(audio = audio, sr = sr) return main.song(audio=audio, sr = sr) def _outputfilename(path:str = None, filename:str = None, suffix:str = None, ext:str = None): """If path has file extension, returns `path + suffix + ext`. Else returns `path + filename + suffix + .ext`. If nothing is specified, returns `output.mp3`""" if ext is not None: if not ext.startswith('.'): ext = '.'+ext if path is None: path = '' if path.endswith('/') or path.endswith('\\'): path=path[:-1] if '.' in path: path = path.split('.') if path[-1].lower() in ['mp3', 'wav', 'flac', 'ogg', 'wma', 'aac', 'ac3', 'aiff']: if ext is not None: path[-1] = ext if suffix is not None: path[len(path)-2]+=suffix return ''.join(path) else: path = ''.join(path) if filename is not None: filename = filename.replace('\\','/').split('/')[-1] if '.' in filename: filename = filename.split('.') if filename[-1].lower() in ['mp3', 'wav', 'flac', 'ogg', 'wma', 'aac', 'ac3', 'aiff']: if ext is not None: filename[-1] = ext if suffix is not None: filename.insert(len(filename)-1, suffix) else: filename += [ext] filename = ''.join(filename) return f'{path}/{filename}' if path != '' else filename return f'{(path + "/") * (path != "")}{filename}{suffix if suffix is not None else ""}.{ext if ext is not None else "mp3"}' else: return f'{path}/output.mp3'