brainblow's picture
Duplicate from dpe1/beat_manipulator
df5dab1
raw
history blame contribute delete
No virus
7.3 kB
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'