Skip to content

Commit

Permalink
new .name attribute in Sound and Binaural objects
Browse files Browse the repository at this point in the history
  • Loading branch information
DrMarc committed Aug 9, 2024
1 parent 75bbbb6 commit 505bc01
Show file tree
Hide file tree
Showing 4 changed files with 23 additions and 20 deletions.
5 changes: 4 additions & 1 deletion slab/binaural.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,10 @@ def _set_right(self, other):

def __init__(self, data, samplerate=None, name='unnamed'):
if isinstance(data, (Sound, Signal)):
self.name = data.name
if hasattr(data, 'name'):
self.name = data.name
else:
self.name = name
if data.n_channels == 1: # if there is only one channel, duplicate it.
self.data = numpy.tile(data.data, 2)
elif data.n_channels == 2:
Expand Down
17 changes: 5 additions & 12 deletions slab/signal.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,13 +32,11 @@ class Signal:
it must have a .data attribute containing an array. If it's a list, the elements can be arrays or objects.
The output will be a multi-channel sound with each channel corresponding to an element of the list.
samplerate (int | None): the samplerate of the sound. If None, use the default samplerate.
name (str): a string label for the signal object. Default is 'unnamed'.
Attributes:
.duration: duration of the sound in seconds
.n_samples: duration of the sound in samples
.n_channels: number of channels in the sound
.times: list with the time point of each sample
.name: string label
Examples::
import slab, numpy
Expand All @@ -59,7 +57,7 @@ class Signal:
doc='The number of channels in the Signal.')

# __methods (class creation, printing, and slice functionality)
def __init__(self, data, samplerate=None, name='unnamed'):
def __init__(self, data, samplerate=None):
if hasattr(data, 'samplerate') and samplerate is not None:
warnings.warn('First argument has a samplerate property. Ignoring given samplerate.')
if isinstance(data, numpy.ndarray):
Expand Down Expand Up @@ -92,24 +90,19 @@ def __init__(self, data, samplerate=None, name='unnamed'):
self.samplerate = _default_samplerate
else:
self.samplerate = samplerate
if hasattr(data, 'name') and name == 'unnamed': # carry over name if source object has one and no new name provided
self.name = data.name
else:
self.name = name


def __repr__(self):
return f'{type(self)} (\n{repr(self.data)}\n{repr(self.samplerate)}\n{repr(self.name)})'
return f'{type(self)} (\n{repr(self.data)}\n{repr(self.samplerate)})'

def __str__(self):
return f'{type(self)} ({self.name}) duration {self.duration}, samples {self.n_samples}, channels {self.n_channels},' \
return f'{type(self)} duration {self.duration}, samples {self.n_samples}, channels {self.n_channels},' \
f'samplerate {self.samplerate}'

def _repr_html_(self):
'HTML image representation for Jupyter notebook support'
elipses = '\u2026'
class_name = str(type(self))[8:-2]
html = [f'<h4>{class_name} ({self.name}) with samplerate = {self.samplerate}</h4>']
html = [f'<h4>{class_name} with samplerate = {self.samplerate}</h4>']
html += ['<table><tr><th>#</th>']
samps, chans = self.data.shape
html += (f'<th>channel {j}</th>' for j in range(chans))
Expand Down Expand Up @@ -343,7 +336,7 @@ def _get_envelope(self, kind, cutoff):
envs = 20 * numpy.log10(envs) # convert amplitude to dB
elif not kind == 'gain':
raise ValueError('Kind must be either "gain" or "dB"!')
return Signal(envs, samplerate=self.samplerate, name='envelope')
return Signal(envs, samplerate=self.samplerate)

def _apply_envelope(self, envelope, times, kind):
# TODO: write tests for the new options!
Expand Down
20 changes: 14 additions & 6 deletions slab/sound.py
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,15 @@ def __init__(self, data, samplerate=None, name='unnamed'):
self.name = _.name
else:
# delegate to the baseclass init
super().__init__(data, samplerate, name)
super().__init__(data, samplerate)
self.name = name

def __repr__(self): # including the .name attribute
return f'{type(self)} (\n{repr(self.data)}\n{repr(self.samplerate)}\n{repr(self.name)})'

def __str__(self):
return f'{type(self)} ({self.name}) duration {self.duration}, samples {self.n_samples}, channels {self.n_channels},' \
f'samplerate {self.samplerate}'

# static methods (creating sounds)
@staticmethod
Expand Down Expand Up @@ -777,7 +785,7 @@ def repeat(self, n):
"""
sound = copy.deepcopy(self)
sound.data = numpy.vstack((sound.data,) * int(n))
sound.name = f'{n}x_{sound.name}'
sound.name = f'{n}x_{self.name}'
return sound

@staticmethod
Expand Down Expand Up @@ -869,7 +877,7 @@ def pulse(self, frequency=4, duty=0.75, gate_time=0.005):
envelope = envelope[:, None] # add an empty axis to get to the same shape as sound.data
# if data is 2D (>1 channel) broadcast the envelope to fit
out.data *= numpy.broadcast_to(envelope, out.data.shape)
out.name = f'{frequency}Hz-pulsed_{out.name}'
out.name = f'{frequency}Hz-pulsed_{self.name}'
return out

def am(self, frequency=10, depth=1, phase=0):
Expand All @@ -887,7 +895,7 @@ def am(self, frequency=10, depth=1, phase=0):
envelope = (1 + depth * numpy.sin(2 * numpy.pi * frequency * out.times + phase))
envelope = envelope[:, None]
out.data *= numpy.broadcast_to(envelope, out.data.shape)
out.name = f'{frequency}Hz-am_{out.name}'
out.name = f'{frequency}Hz-am_{self.name}'
return out

def filter(self, frequency=100, kind='hp'):
Expand All @@ -907,7 +915,7 @@ def filter(self, frequency=100, kind='hp'):
filt = Filter.band(
frequency=frequency, kind=kind, samplerate=self.samplerate, length=n)
out.data = filt.apply(self).data
out.name = f'{frequency}Hz-{kind}_{out.name}'
out.name = f'{frequency}Hz-{kind}_{self.name}'
return out

def aweight(self):
Expand All @@ -931,7 +939,7 @@ def aweight(self):
b, a = scipy.signal.filter_design.bilinear(numerators, denominators, self.samplerate)
out = copy.deepcopy(self)
out.data = scipy.signal.lfilter(b, a, self.data, axis=0)
out.name = f'aweighted_{out.name}'
out.name = f'aweighted_{self.name}'
return out

@staticmethod
Expand Down
1 change: 0 additions & 1 deletion tests/test_signal.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ def test_signal_generation():
assert sig.samplerate == samplerate
assert len(sig.times) == len(sig.data)
numpy.testing.assert_almost_equal(sig.times.max()*samplerate, n_samples, decimal=-1)
assert sig.name == 'unnamed'


def test_arithmetics():
Expand Down

0 comments on commit 505bc01

Please sign in to comment.