from __future__ import print_function
import os
import soundfile as sf
import numpy as np
[docs]class Source(object):
"""
An audio Target which is a linear mixture of several sources
Attributes
----------
name : str
Name of this source
path : str
Absolute path to audio file
gain : float
Mixing weight for this source
"""
def __init__(self, name=None, path=None):
self.name = name
self.path = path
self.gain = 1.0
self._audio = None
self._rate = None
@property
def audio(self):
"""array_like: [shape=(num_samples, num_channels)]
"""
# return cached audio if explicitly set by setter
if self._audio is not None:
return self._audio
# read from disk to save RAM otherwise
else:
if os.path.exists(self.path):
audio, rate = sf.read(self.path, always_2d=True)
self._rate = rate
return audio
else:
self._rate = None
self._audio = None
raise ValueError("Oops! %s cannot be loaded" % self.path)
@property
def rate(self):
"""int: sample rate in Hz
"""
# load audio to set rate
if self._rate is None:
if os.path.exists(self.path):
audio, rate = sf.read(self.path, always_2d=True)
self._rate = rate
return rate
else:
self._rate = None
self._audio = None
raise ValueError("Oops! %s cannot be loaded" % self.path)
return self._rate
@audio.setter
def audio(self, array):
self._audio = array
@rate.setter
def rate(self, rate):
self._rate = rate
def __repr__(self):
return self.path
# Target Track from dsdtools DB mixed from several DSDSource Tracks
[docs]class Target(object):
"""
An audio Target which is a linear mixture of several sources
Attributes
----------
sources : list[Source]
list of ``Source`` objects for this ``Target``
"""
def __init__(self, sources):
self.sources = sources # List of DSDSources
@property
def audio(self):
"""array_like: [shape=(num_samples, num_channels)]
mixes audio for targets on the fly
"""
mix_list = []*len(self.sources)
for source in self.sources:
if source.audio is not None:
mix_list.append(
source.gain * source.audio
)
return np.sum(np.array(mix_list), axis=0)
def __repr__(self):
parts = []
for source in self.sources:
parts.append(source.name)
return '+'.join(parts)
# dsdtools Track which has many targets and sources
[docs]class Track(object):
"""
An audio Track which is mixture of several sources
and provides several targets
Attributes
----------
name : str
Track name
path : str
Absolute path of mixture audio file
subset : {'Test', 'Dev'}
belongs to subset
targets : OrderedDict
OrderedDict of mixted Targets for this Track
sources : Dict
Dict of ``Source`` objects for this ``Track``
"""
def __init__(
self,
filename,
track_id=None,
track_artist=None,
track_title=None,
subset=None,
path=None
):
self.filename = filename
try:
split_name = filename.split(' - ')
self.id = int(split_name[0])
self.artist = split_name[1]
self.title = split_name[2]
except ValueError:
self.id = 0
self.artist = track_artist
self.title = track_title
self.path = path
self.subset = subset
self.targets = None
self.sources = None
self._audio = None
self._rate = None
@property
def audio(self):
"""array_like: [shape=(num_samples, num_channels)]
"""
# return cached audio it explicitly set bet setter
if self._audio is not None:
return self._audio
# read from disk to save RAM otherwise
else:
if os.path.exists(self.path):
audio, rate = sf.read(self.path, always_2d=True)
self._rate = rate
return audio
else:
self._rate = None
self._audio = None
raise ValueError("Oops! %s cannot be loaded" % self.path)
@property
def rate(self):
"""int: sample rate in Hz
"""
# load audio to set rate
if self._rate is None:
if os.path.exists(self.path):
audio, rate = sf.read(self.path, always_2d=True)
self._rate = rate
return rate
else:
self._rate = None
self._audio = None
raise ValueError("Oops! %s cannot be loaded" % self.path)
return self._rate
@audio.setter
def audio(self, array):
self._audio = array
@rate.setter
def rate(self, rate):
self._rate = rate
def __repr__(self):
return "\n%d >> %s" % (self.id, self.path)