def test_dpss_windows(): """Test computation of DPSS windows.""" import nitime as ni N = 1000 half_nbw = 4 Kmax = int(2 * half_nbw) dpss, eigs = dpss_windows(N, half_nbw, Kmax, low_bias=False) with pytest.warns(None): # conversions dpss_ni, eigs_ni = ni.algorithms.dpss_windows(N, half_nbw, Kmax) assert_array_almost_equal(dpss, dpss_ni) assert_array_almost_equal(eigs, eigs_ni) dpss, eigs = dpss_windows(N, half_nbw, Kmax, interp_from=200, low_bias=False) with pytest.warns(None): # conversions dpss_ni, eigs_ni = ni.algorithms.dpss_windows(N, half_nbw, Kmax, interp_from=200) assert_array_almost_equal(dpss, dpss_ni) assert_array_almost_equal(eigs, eigs_ni)
def window_edges(sig, fs, dur=0.01, axis=-1, window='hann', edges='both'): """Window the edges of a signal (e.g., to prevent "pops") Parameters ---------- sig : array-like The array to window. fs : float The sample rate. dur : float The duration to window on each edge. The default is 0.01 (10 ms). axis : int The axis to operate over. window : str The window to use. For a list of valid options, see ``scipy.signal.get_window()``, but can also be 'dpss'. edges : str Can be ``'leading'``, ``'trailing'``, or ``'both'`` (default). Returns ------- windowed_sig : array-like The modified array (float64). """ fs = float(fs) sig = np.array(sig, dtype=np.float64) # this will make a copy sig_len = sig.shape[axis] win_len = int(dur * fs) if win_len > sig_len: raise RuntimeError('cannot create window of size {0} samples (dur={1})' 'for signal with length {2}' ''.format(win_len, dur, sig_len)) if window == 'dpss': from mne.time_frequency.multitaper import dpss_windows win = dpss_windows(2 * win_len + 1, 1, 1)[0][0][:win_len] win -= win[0] win /= win.max() else: win = signal.windows.get_window(window, 2 * win_len)[:win_len] valid_edges = ('leading', 'trailing', 'both') if edges not in valid_edges: raise ValueError('edges must be one of {0}, not "{1}"' ''.format(valid_edges, edges)) # now we can actually do the calculation flattop = np.ones(sig_len, dtype=np.float64) if edges in ('trailing', 'both'): # eliminate trailing flattop[-win_len:] *= win[::-1] if edges in ('leading', 'both'): # eliminate leading flattop[:win_len] *= win shape = np.ones_like(sig.shape) shape[axis] = sig.shape[axis] flattop.shape = shape sig *= flattop return sig
def test_dpss_windows(): """Test computation of DPSS windows.""" import nitime as ni N = 1000 half_nbw = 4 Kmax = int(2 * half_nbw) dpss, eigs = dpss_windows(N, half_nbw, Kmax, low_bias=False) dpss_ni, eigs_ni = ni.algorithms.dpss_windows(N, half_nbw, Kmax) assert_array_almost_equal(dpss, dpss_ni) assert_array_almost_equal(eigs, eigs_ni) dpss, eigs = dpss_windows(N, half_nbw, Kmax, interp_from=200, low_bias=False) dpss_ni, eigs_ni = ni.algorithms.dpss_windows(N, half_nbw, Kmax, interp_from=200) assert_array_almost_equal(dpss, dpss_ni) assert_array_almost_equal(eigs, eigs_ni)
def texture_ERB(n_freqs=20, n_coh=None, rho=1., seq=('inc', 'nb', 'inc', 'nb'), fs=24414.0625, dur=1., SAM_freq=7., random_state=None, freq_lims=(200, 8000), verbose=True): """Create ERB texture stimulus Parameters ---------- n_freqs : int Number of tones in mixture (default 20). n_coh : int | None Number of tones to be temporally coherent. Default (None) is ``int(np.round(n_freqs * 0.8))``. rho : float Correlation between the envelopes of grouped tones (default is 1.0). seq : list Sequence of incoherent ('inc'), coherent noise envelope ('nb'), and SAM ('sam') mixtures. Default is ``('inc', 'nb', 'inc', 'nb')``. fs : float Sampling rate in Hz. dur : float Duration (in seconds) of each token in seq (default is 1.0). SAM_freq : float The SAM frequency to use. random_state : None | int | np.random.RandomState The random generator state used for band selection and noise envelope generation. freq_lims : tuple The lower and upper frequency limits (default is (200, 8000)). verbose : bool If True, print the resulting ERB spacing. Returns ------- x : ndarray, shape (n_samples,) The stimulus, where ``n_samples = len(seq) * (fs * dur)`` (approximately). Notes ----- This function requires MNE. """ from mne.time_frequency.multitaper import dpss_windows from mne.utils import check_random_state if not isinstance(seq, (list, tuple, np.ndarray)): raise TypeError('seq must be list, tuple, or ndarray, got %s' % type(seq)) known_seqs = ('inc', 'nb', 'sam') for si, s in enumerate(seq): if s not in known_seqs: raise ValueError('all entries in seq must be one of %s, got ' 'seq[%s]=%s' % (known_seqs, si, s)) fs = float(fs) rng = check_random_state(random_state) n_coh = int(np.round(n_freqs * 0.8)) if n_coh is None else n_coh rise = 0.002 t = np.arange(int(round(dur * fs))) / fs f_min, f_max = freq_lims n_ERBs = _cams(f_max) - _cams(f_min) del f_max spacing_ERBs = n_ERBs / float(n_freqs - 1) if verbose: print('This stim will have successive tones separated by %2.2f ERBs' % spacing_ERBs) if spacing_ERBs < 1.0: warnings.warn('The spacing between tones is LESS THAN 1 ERB!') # Make a filter whose impulse response is purely positive (to avoid phase # jumps) so that the filtered envelope is purely positive. Use a DPSS # window to minimize sidebands. For a bandwidth of bw, to get the shortest # filterlength, we need to restrict time-bandwidth product to a minimum. # Thus we need a length*bw = 2 => length = 2/bw (second). Hence filter # coefficients are calculated as follows: b = dpss_windows(int(np.floor(2 * fs / 100.)), 1., 1)[0][0] b -= b[0] b /= b.sum() # Incoherent envrate = 14 bw = 20 incoh = 0. for k in range(n_freqs): f = _inv_cams(_cams(f_min) + spacing_ERBs * k) env = _make_narrow_noise(bw, envrate, dur, fs, rise, rng) env[env < 0] = 0 env = np.convolve(b, env)[:len(t)] incoh += _scale_sound( window_edges(env * np.sin(2 * np.pi * f * t), fs, rise, window='dpss')) incoh /= rms(incoh) # Coherent (noise band) stims = dict(inc=0., nb=0., sam=0.) group = np.sort(rng.permutation(np.arange(n_freqs))[:n_coh]) for kind in known_seqs: if kind == 'nb': # noise band env_coh = _make_narrow_noise(bw, envrate, dur, fs, rise, rng) else: # 'nb' or 'inc' env_coh = 0.5 + np.sin(2 * np.pi * SAM_freq * t) / 2. env_coh = window_edges(env_coh, fs, rise, window='dpss') env_coh[env_coh < 0] = 0 env_coh = np.convolve(b, env_coh)[:len(t)] if kind == 'inc': use_group = [] # no coherent ones else: # 'nb' or 'sam' use_group = group for k in range(n_freqs): f = _inv_cams(_cams(f_min) + spacing_ERBs * k) env_inc = _make_narrow_noise(bw, envrate, dur, fs, rise, rng) env_inc[env_inc < 0] = 0. env_inc = np.convolve(b, env_inc)[:len(t)] if k in use_group: env = np.sqrt(rho) * env_coh + np.sqrt(1 - rho**2) * env_inc else: env = env_inc stims[kind] += _scale_sound( window_edges(env * np.sin(2 * np.pi * f * t), fs, rise, window='dpss')) stims[kind] /= rms(stims[kind]) stim = np.concatenate([stims[s] for s in seq]) stim = 0.01 * stim / rms(stim) return stim
def texture_ERB(n_freqs=20, n_coh=None, rho=1., seq=('inc', 'nb', 'inc', 'nb'), fs=24414.0625, dur=1., SAM_freq=7., random_state=None, freq_lims=(200, 8000), verbose=True): """Create ERB texture stimulus Parameters ---------- n_freqs : int Number of tones in mixture (default 20). n_coh : int | None Number of tones to be temporally coherent. Default (None) is ``int(np.round(n_freqs * 0.8))``. rho : float Correlation between the envelopes of grouped tones (default is 1.0). seq : list Sequence of incoherent ('inc'), coherent noise envelope ('nb'), and SAM ('sam') mixtures. Default is ``('inc', 'nb', 'inc', 'nb')``. fs : float Sampling rate in Hz. dur : float Duration (in seconds) of each token in seq (default is 1.0). SAM_freq : float The SAM frequency to use. random_state : None | int | np.random.RandomState The random generator state used for band selection and noise envelope generation. freq_lims : tuple The lower and upper frequency limits (default is (200, 8000)). verbose : bool If True, print the resulting ERB spacing. Returns ------- x : ndarray, shape (n_samples,) The stimulus, where ``n_samples = len(seq) * (fs * dur)`` (approximately). Notes ----- This function requires MNE. """ from mne.time_frequency.multitaper import dpss_windows from mne.utils import check_random_state if not isinstance(seq, (list, tuple, np.ndarray)): raise TypeError('seq must be list, tuple, or ndarray, got %s' % type(seq)) known_seqs = ('inc', 'nb', 'sam') for si, s in enumerate(seq): if s not in known_seqs: raise ValueError('all entries in seq must be one of %s, got ' 'seq[%s]=%s' % (known_seqs, si, s)) fs = float(fs) rng = check_random_state(random_state) n_coh = int(np.round(n_freqs * 0.8)) if n_coh is None else n_coh rise = 0.002 t = np.arange(int(round(dur * fs))) / fs f_min, f_max = freq_lims n_ERBs = _cams(f_max) - _cams(f_min) del f_max spacing_ERBs = n_ERBs / float(n_freqs - 1) if verbose: print('This stim will have successive tones separated by %2.2f ERBs' % spacing_ERBs) if spacing_ERBs < 1.0: warnings.warn('The spacing between tones is LESS THAN 1 ERB!') # Make a filter whose impulse response is purely positive (to avoid phase # jumps) so that the filtered envelope is purely positive. Use a DPSS # window to minimize sidebands. For a bandwidth of bw, to get the shortest # filterlength, we need to restrict time-bandwidth product to a minimum. # Thus we need a length*bw = 2 => length = 2/bw (second). Hence filter # coefficients are calculated as follows: b = dpss_windows(int(np.floor(2 * fs / 100.)), 1., 1)[0][0] b -= b[0] b /= b.sum() # Incoherent envrate = 14 bw = 20 incoh = 0. for k in range(n_freqs): f = _inv_cams(_cams(f_min) + spacing_ERBs * k) env = _make_narrow_noise(bw, envrate, dur, fs, rise, rng) env[env < 0] = 0 env = np.convolve(b, env)[:len(t)] incoh += _scale_sound(window_edges( env * np.sin(2 * np.pi * f * t), fs, rise, window='dpss')) incoh /= rms(incoh) # Coherent (noise band) stims = dict(inc=0., nb=0., sam=0.) group = np.sort(rng.permutation(np.arange(n_freqs))[:n_coh]) for kind in known_seqs: if kind == 'nb': # noise band env_coh = _make_narrow_noise(bw, envrate, dur, fs, rise, rng) else: # 'nb' or 'inc' env_coh = 0.5 + np.sin(2 * np.pi * SAM_freq * t) / 2. env_coh = window_edges(env_coh, fs, rise, window='dpss') env_coh[env_coh < 0] = 0 env_coh = np.convolve(b, env_coh)[:len(t)] if kind == 'inc': use_group = [] # no coherent ones else: # 'nb' or 'sam' use_group = group for k in range(n_freqs): f = _inv_cams(_cams(f_min) + spacing_ERBs * k) env_inc = _make_narrow_noise(bw, envrate, dur, fs, rise, rng) env_inc[env_inc < 0] = 0. env_inc = np.convolve(b, env_inc)[:len(t)] if k in use_group: env = np.sqrt(rho) * env_coh + np.sqrt(1 - rho ** 2) * env_inc else: env = env_inc stims[kind] += _scale_sound(window_edges( env * np.sin(2 * np.pi * f * t), fs, rise, window='dpss')) stims[kind] /= rms(stims[kind]) stim = np.concatenate([stims[s] for s in seq]) stim = 0.01 * stim / rms(stim) return stim