corrscope/tests/test_wave.py

109 wiersze
3.2 KiB
Python
Czysty Zwykły widok Historia

import warnings
2018-07-20 08:12:28 +00:00
import numpy as np
from numpy.testing import assert_allclose
2018-07-20 08:12:28 +00:00
import pytest
from delayed_assert import expect, assert_expectations
2018-07-20 08:12:28 +00:00
2018-12-20 10:31:55 +00:00
from corrscope.wave import Wave
2018-07-20 08:12:28 +00:00
prefix = 'tests/wav-formats/'
wave_paths = [
# 2000 samples, with a full-scale peak at data[1000].
'u8-impulse1000.wav',
's16-impulse1000.wav',
's32-impulse1000.wav',
'f32-impulse1000.wav',
'f64-impulse1000.wav',
]
@pytest.mark.parametrize("wave_path", wave_paths)
def test_wave(wave_path):
with warnings.catch_warnings(record=True) as w:
# Cause all warnings to always be triggered.
warnings.simplefilter("always")
wave = Wave(None, prefix + wave_path)
data = wave[:]
# Audacity dithers <=16-bit WAV files upon export, creating a few bits of noise.
# As a result, amin(data) <= 0.
assert -0.01 < np.amin(data) <= 0
assert 0.99 < np.amax(data) <= 1
2018-07-20 08:12:28 +00:00
# check for FutureWarning (raised when determining wavfile type)
warns = [o for o in w if issubclass(o.category, FutureWarning)]
assert not [str(w) for w in warns]
def test_stereo_merge():
""" Ensure stereo channels are combined properly, when indexing by slices
*or* ints. """
# Contains a full-scale sine wave in left channel, and silence in right.
# λ=100, nsamp=2000
wave = Wave(None, prefix + 'stereo-sine-left-2000.wav')
period = 100
nsamp = 2000
# [-1, 1) from [-32768..32768)
int16_step = (1 - -1) / (2 ** 16)
assert int16_step == 2**-15
# Check wave indexing dimensions.
assert wave[0].shape == ()
assert wave[:].shape == (nsamp,)
# Check stereo merging.
assert_allclose(wave[0], 0)
assert_allclose(wave[period], 0)
assert_allclose(wave[period//4], 0.5, atol=int16_step)
def check_bound(obj):
amax = np.amax(obj)
assert amax.shape == ()
assert_allclose(amax, 0.5, atol=int16_step)
assert_allclose(np.amin(obj), -0.5, atol=int16_step)
check_bound(wave[:])
def test_stereo_mmap():
wave = Wave(None, prefix + 'stereo-sine-left-2000.wav')
assert isinstance(wave.data, np.memmap)
def test_wave_subsampling():
wave = Wave(None, 'tests/sine440.wav')
# period = 48000 / 440 = 109.(09)*
wave.get_around(1000, region_nsamp=501, stride=4)
# len([:region_len:subsampling]) == ceil(region_len / subsampling)
# If region_len % subsampling != 0, len() != region_len // subsampling.
stride = 4
region = 100 # diameter = region * stride
for i in [-1000, 50000]:
data = wave.get_around(i, region, stride)
assert (data == 0).all()
2018-11-18 05:44:50 +00:00
def test_stereo_doesnt_overflow():
""" Ensure loud stereo tracks do not overflow. """
wave = Wave(None, 'tests/stereo in-phase.wav')
samp = 100
stride = 1
data = wave.get_around(wave.nsamp // 2, samp, stride)
expect(np.amax(data) > 0.99)
expect(np.amin(data) < -0.99)
2018-11-18 05:44:50 +00:00
# In the absence of overflow, sine waves have no large jumps.
# In the presence of overflow, stereo sum will jump between INT_MAX and INT_MIN.
# np.mean and rescaling converts to 0.499... and -0.5, which is nearly 1.
expect(np.amax(np.abs(np.diff(data))) < 0.5)
assert_expectations()