From dcacf70a45d33bcc0a7d2ee8af1eacf2e7c86886 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adrian=20St=C4=99pie=C5=84?= <4030444+adrian-stepien@users.noreply.github.com> Date: Tue, 12 Feb 2019 16:18:23 +0000 Subject: [PATCH 1/2] [SIGNAL] Normalization using maximum absolute value Fixed the `normalize` function to take into account the maximum of absolute values, not only the positive ones. --- acoustics/_signal.pyx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/acoustics/_signal.pyx b/acoustics/_signal.pyx index 64faedec..ebe37704 100644 --- a/acoustics/_signal.pyx +++ b/acoustics/_signal.pyx @@ -1026,12 +1026,12 @@ class Signal(numpy.ndarray): By default a 6 decibel gap is used. """ - factor = (self.max() * 10.0**(gap/20.0)) + factor = (np.abs(self).max() * 10.0**(gap/20.0)) if inplace: - self /= factor + self /= factor[..., None] return self else: - return self / factor + return self / factor[..., None] def to_wav(self, filename, depth=16): From 33d4f8365ea25915b60f3941f586e7488836dd1d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adrian=20St=C4=99pie=C5=84?= <4030444+adrian-stepien@users.noreply.github.com> Date: Tue, 12 Feb 2019 16:57:23 +0000 Subject: [PATCH 2/2] [SIGNAL] Loading backend switched to pysoundfile Thanks to pysoundfile it's possible to laod WAV and FLAC files with integer samples. (Floating point is there out of the box, but requires a bit of API refactoring). Things removed: - When saving, samples are no-longer normalized - Normalization is optional during loading - by default it's off --- acoustics/_signal.pyx | 27 +++++++++++++-------------- requirements.txt | 1 + setup.cfg | 1 + tests/test__signal.py | 5 ++++- 4 files changed, 19 insertions(+), 15 deletions(-) diff --git a/acoustics/_signal.pyx b/acoustics/_signal.pyx index ebe37704..43fae840 100644 --- a/acoustics/_signal.pyx +++ b/acoustics/_signal.pyx @@ -2,7 +2,7 @@ cimport cython cimport numpy import numpy as np import matplotlib.pyplot as plt -from scipy.io import wavfile +import soundfile as sf from scipy.signal import detrend, lfilter, bilinear, spectrogram, filtfilt, resample, fftconvolve import acoustics import itertools @@ -1034,35 +1034,34 @@ class Signal(numpy.ndarray): return self / factor[..., None] - def to_wav(self, filename, depth=16): + def to_wav(self, filename, depth=16, format="WAV"): """Save signal as WAV file. :param filename: Name of file to save to. :param depth: If given, convert to integer with specified depth. Else, try to store using the original data type. + :param format: It can be either WAV or FLAC - By default, this function saves a normalized 16-bit version of the signal with at least 6 dB range till clipping occurs. + Note that the floating point samples are not yet supported. """ data = self - dtype = data.dtype if not depth else 'int'+str(depth) - if depth: - data = (data * 2**(depth-1)-1).astype(dtype) - wavfile.write(filename, int(self.fs), data.T) - #wavfile.write(filename, int(self.fs), self._data/np.abs(self._data).max() * 0.5) - #wavfile.write(filename, int(self.fs), np.int16(self._data/(np.abs(self._data).max()) * 32767) ) + subtype = "PCM_{}".format(depth) + to_save = data + sf.write(filename, to_save.T, int(self.fs), format=format, subtype=subtype) @classmethod - def from_wav(cls, filename): + def from_wav(cls, filename, normalize=False): """ Create an instance of `Signal` from a WAV file. :param filename: Filename + :param normalize: Optionally normalize the input samples """ - fs, data = wavfile.read(filename) - data = data.astype(np.float32, copy=False).T - data /= np.max(np.abs(data)) - return cls(data, fs=fs) + data, fs = sf.read(filename) + if normalize: + data /= np.max(np.abs(data)) + return cls(data.T, fs=fs) _PLOTTING_PARAMS = { diff --git a/requirements.txt b/requirements.txt index 992d16ef..1b926ac6 100644 --- a/requirements.txt +++ b/requirements.txt @@ -5,3 +5,4 @@ six cython pandas tabulate +pysoundfile \ No newline at end of file diff --git a/setup.cfg b/setup.cfg index 164bc238..38e9ffe7 100644 --- a/setup.cfg +++ b/setup.cfg @@ -25,6 +25,7 @@ install_requires = cython pandas >= 0.15 tabulate + pysoundfile tests_require = pytest diff --git a/tests/test__signal.py b/tests/test__signal.py index 968a59e6..a240be9d 100644 --- a/tests/test__signal.py +++ b/tests/test__signal.py @@ -20,11 +20,14 @@ class test_wav(): fs = 10025 samples = int(fs*duration) channels = 3 + values = np.random.randn(channels, samples) - signal = Signal(np.random.randn(channels, samples), fs) + signal = Signal(values, fs) + signal.normalize(inplace=True) with tempfile.TemporaryFile() as file: signal.to_wav(file) + file.seek(0) signal = Signal.from_wav(file) assert signal.samples == samples assert signal.fs == fs