def test_window_edges(): """Test windowing signal edges.""" sig = np.ones((2, 1000)) fs = 44100 assert_raises(ValueError, window_edges, sig, fs, window='foo') # bad win assert_raises(RuntimeError, window_edges, sig, fs, dur=1.0) # too long assert_raises(ValueError, window_edges, sig, fs, edges='foo') # bad type x = window_edges(sig, fs, edges='leading') y = window_edges(sig, fs, edges='trailing') z = window_edges(sig, fs) assert_true(np.all(x[:, 0] < 1)) # make sure we actually reduced amp assert_true(np.all(x[:, -1] == 1)) assert_true(np.all(y[:, 0] == 1)) assert_true(np.all(y[:, -1] < 1)) assert_allclose(x + y, z + 1)
def test_window_edges(): """Test windowing signal edges.""" sig = np.ones((2, 1000)) fs = 44100 pytest.raises(ValueError, window_edges, sig, fs, window='foo') # bad win pytest.raises(RuntimeError, window_edges, sig, fs, dur=1.0) # too long pytest.raises(ValueError, window_edges, sig, fs, edges='foo') # bad type x = window_edges(sig, fs, edges='leading') y = window_edges(sig, fs, edges='trailing') z = window_edges(sig, fs) assert (np.all(x[:, 0] < 1)) # make sure we actually reduced amp assert (np.all(x[:, -1] == 1)) assert (np.all(y[:, 0] == 1)) assert (np.all(y[:, -1] < 1)) assert_allclose(x + y, z + 1)
def gen_am_tone(stim_fs=44100, stim_dur=1.0, stim_rms=0.01, carrier_freq=1000, mod_ind=0.8, mod_freq=40): """Returns an RMS normalized amplitude modulated sinusoidal time series Args: stim_fs (int): sampling frequency for generated stimuli defaults to 44100 Hz. stim_dir : float stim_rms : float output_dir : string carrier_freq : int stim_db : int mod_ind : float mod_freq : int Returns: tone """ t = np.arange(int(stim_fs * stim_dur)) / float(stim_fs) tone = np.sin(2 * np.pi * carrier_freq * t) * (1 + np.sin(2 * np.pi * mod_freq * t) * mod_ind) tone *= stim_rms * np.sqrt(2) tone = stimuli.window_edges(tone, stim_fs, 0.025, edges='both') return tone
Generate more advanced auditory stimuli ======================================= This shows the methods that we provide that facilitate generation of more advanced stimuli. """ import numpy as np from expyfun.stimuli import convolve_hrtf, play_sound, window_edges fs = 44100 dur = 0.5 freq = 500. # let's make a square wave sig = np.sin(freq * 2 * np.pi * np.arange(dur * fs, dtype=float) / fs) sig = ((sig > 0) - 0.5) / 5. # make it reasonably quiet for play_sound sig = window_edges(sig, fs) play_sound(sig, norm=False, wait=True) move_sig = np.concatenate([convolve_hrtf(sig, fs, ang) for ang in range(-90, 91, 15)], axis=1) play_sound(move_sig, norm=False, wait=True) import matplotlib.pyplot as mpl mpl.ion() t = np.arange(move_sig.shape[1]) / float(fs) mpl.plot(t, move_sig.T) mpl.xlabel('Time (sec)')
This shows how to make simple vocoded stimuli. @author: larsoner """ import numpy as np import matplotlib.pyplot as mpl from expyfun.stimuli import vocode, play_sound, window_edges, read_wav, rms from expyfun import fetch_data_file print(__doc__) data, fs = read_wav(fetch_data_file('audio/dream.wav')) data = window_edges(data[0], fs) t = np.arange(data.size) / float(fs) # noise vocoder data_noise = vocode(data, fs, mode='noise') data_noise = data_noise * 0.01 / rms(data_noise) # sinewave vocoder data_tone = vocode(data, fs, mode='tone') data_tone = data_tone * 0.01 / rms(data_tone) # poisson vocoder data_click = vocode(data, fs, mode='poisson', rate=400) data_click = data_click * 0.01 / rms(data_click) # combine all three cutoff = data.shape[-1] // 3 data_allthree = data_noise.copy() data_allthree[cutoff:2 * cutoff] = data_tone[cutoff:2 * cutoff]
This shows the methods that we provide that facilitate generation of more advanced stimuli. """ import numpy as np import matplotlib.pyplot as plt from expyfun import building_doc from expyfun.stimuli import convolve_hrtf, play_sound, window_edges fs = 24414 dur = 0.5 freq = 500. # let's make a square wave sig = np.sin(freq * 2 * np.pi * np.arange(dur * fs, dtype=float) / fs) sig = ((sig > 0) - 0.5) / 5. # make it reasonably quiet for play_sound sig = window_edges(sig, fs) play_sound(sig, fs, norm=False, wait=True) move_sig = np.concatenate( [convolve_hrtf(sig, fs, ang) for ang in range(-90, 91, 15)], axis=1) if not building_doc: play_sound(move_sig, fs, norm=False, wait=True) t = np.arange(move_sig.shape[1]) / float(fs) plt.plot(t, move_sig.T) plt.xlabel('Time (sec)') plt.show()
def generate_stimuli(num_trials=10, stim_dur=0.08, fs=24414., rms=0.01, ramp_noise=0.03, ramp_tone=0.06, output_dir=None, save_as='mat', rand_seed=0): """Make stimuli and save in various formats. Optimized for saving as MAT files, but can also save directly as WAV files, or can return a python dictionary with sinewave data as values. Parameters ---------- num_trials : int Number of trials you want in your experiment. Ignored if save_as is not 'mat'. stim_dur : float Duration of the tones in seconds. ramp_noise : float Duration of the onset and offset ramps in seconds for noiseburst stim. ramp_tone : float Duration of the onset and offset ramps in seconds for tonecomplex stim. fs : float | None Sampling frequency of resulting sinewaves. Defaults to 24414.0625 (a standard rate for TDTs) if no value is specified. rms : float RMS amplitude to which all sinwaves will be scaled. output_dir : str | None Directory to output the files into. If None, the current directory is used. save_as : str Format in which to return the sinewaves. 'dict' returns sinewave arrays as values in a python dictionary; 'wav' saves them as WAV files at sampling frequency 'fs'; 'mat' saves them as a MAT file along with related variables 'fs', 'freqs', 'trial_order', and 'rms'. rand_seed : int | None Seed for the random number generator. Returns ------- wavs : dict | None If `save_as` is `'dict'`, then this will be a dict, else None. finalstim_tc : finalstim_nb : """ if rand_seed is None: rng = np.random.RandomState() else: rng = np.random.RandomState(rand_seed) # check input arguments if save_as not in ['dict', 'wav', 'mat']: raise ValueError('"save_as" must be "dict", "wav", or "mat"') if fs is None: fs = get_tdt_rates()['25k'] # General params: n = int(stim_dur * fs) # total number of samples t = np.linspace(0, stim_dur, n, endpoint=False) # time index for ploting #### make tone complex######################################################### tonecomp = np.zeros(24414. * stim_dur, float) fund = 250.0 # fundamental frequency for x in xrange(1, 5): freq = fund*x tonecomp = tonecomp + np.sin(freq * 2 * np.pi * np.arange (int(fs * stim_dur)) / float(fs)) # windowing and onset/offset finalstim_tc = window_edges(tonecomp, fs, ramp_tone, -1, 'hamming') return finalstim_tc ##### make noise burst######################################################### # add 50 points extra nb = np.random.normal(0, 1.0, int(fs * stim_dur) + 50) ### highpass cut-off freq of 1500Hz using 100th order Hamming ### b = sig.firwin(101, 1500. / (fs / 2), pass_zero=False) # False - highpass # nyq_rate = fs / 2 # have to add '1' order filtered_stim = sig.lfilter(b, 1.0, nb) ### cut off extra 50 points from noiseburst ### filtered_stim = filtered_stim[50:] # windowing and onset/offset nb_ramped = window_edges(nb[50:], fs, ramp_noise, -1, 'hamming') finalstim_nb = np.multiply(nb_ramped, filtered_stim) return finalstim_nb
This shows how to make simple vocoded stimuli. @author: larsoner """ import numpy as np import matplotlib.pyplot as plt from expyfun.stimuli import vocode, play_sound, window_edges, read_wav, rms from expyfun import fetch_data_file print(__doc__) data, fs = read_wav(fetch_data_file('audio/dream.wav')) data = window_edges(data[0], fs) t = np.arange(data.size) / float(fs) # noise vocoder data_noise = vocode(data, fs, mode='noise') data_noise = data_noise * 0.01 / rms(data_noise) # sinewave vocoder data_tone = vocode(data, fs, mode='tone') data_tone = data_tone * 0.01 / rms(data_tone) # poisson vocoder data_click = vocode(data, fs, mode='poisson', rate=400) data_click = data_click * 0.01 / rms(data_click) # combine all three cutoff = data.shape[-1] // 3 data_allthree = data_noise.copy() data_allthree[cutoff:2 * cutoff] = data_tone[cutoff:2 * cutoff]
# White noise burst nb = np.random.uniform(-0.5, 0.5, (int(fs * stim_dur))) ### highpass cut-off freq of 1500Hz using 100th order Hamming ### b = sig.firwin(101, 1500. / (fs / 2), pass_zero=False) # False - highpass # nyq_rate = fs / 2 # have to add '1' order #filtered_stim = sig.lfilter(b, 1.0, nb) filtered_stim = np.convolve(nb, b) #### cut off extra 50 points from noiseburst ### filtered_stim = filtered_stim[50:-50] # not sure why I end up with extra # windowing and onset/offset finalstim_nb = window_edges(filtered_stim, fs, ramp_noise, -1, 'hamming') #nb_ramped *= 0.01 * np.sqrt(2) # only works for sine tones #x /= (xs rms) # sets the rms equal to 1 so then multiply by the target rms #finalstim_nb = nb_ramped*noiseamp # check the rms ############################################################################ tonecomp = np.zeros(24414. * stim_dur, float) fund = 250.0 # fundamental frequency for x in xrange(1, 5): freq = fund*x tonecomp = tonecomp + np.sin(freq * 2 * np.pi * np.arange (int(fs * stim_dur)) / float(fs))
fs = 44100 fc = 2e3 sound_len = int(np.round(isi * 0.8 * fs)) sound_dur = float(sound_len) / fs # sounds = stim.window_edges(np.random.randn(1, sound_len), fs, # sound_dur / 2.5) # b, a = sig.butter(2, fc / (fs / 2)) # sounds = sig.lfilter(b, a, sounds) t = np.arange(sound_len, dtype=float) / fs sounds = np.zeros(sound_len) f0 = 200 for f in range(f0, 1301, f0): sounds += np.sin(2 * np.pi * t * f) sounds *= base_vol / stim.rms(sounds, keepdims=True) sounds *= np.exp(-t / sound_dur * 4) sounds = stim.window_edges(sounds, fs, 0.01) else: assert(len(sound_files) == 1) temp = [] for wav in sound_files: temp += [stim.read_wav(wav)[0]] fs = stim.read_wav(sound_files[0])[1] lens = [w.shape[1] for w in temp] sounds = np.zeros((2, np.max(lens))) for si, l in enumerate(lens): sounds[si, :l] = temp[si] sounds = sig.resample(sounds, 44100 * sounds.shape[1] / fs, axis=1) fs = 44100 sound_len = sounds.shape[1]
### highpass cut-off freq of 1500Hz using 100th order Hamming ### b = sig.firwin(101, 1500. / (fs / 2), pass_zero=False) # False for highpass # nyq_rate = fs / 2 # have to add '1' order filtered_stim = sig.lfilter(b, 1.0, nb) #plt.plot(filtered_stim) ### cut off extra 50 points from noiseburst ### filtered_stim = filtered_stim[50:] ### windowing - onset and offset ramps ### toneramp = 0.006 noiseramp = 0.003 nb_ramped = window_edges(nb[50:], fs, noiseramp, -1, 'hamming') finalstim_nb = np.multiply(nb_ramped, filtered_stim) finalstim_tc = window_edges(tonecomp, fs, toneramp, -1, 'hamming') ### create a two beep stimulus ### # 8ms + 50ms + 8ms = 195 + 1220 + 195 which is a total of 1610 samples interval = 0.050 gap = np.zeros(24414. * interval, float) # if one beep: onebeep = np.concatenate((finalstim_tc, gap), axis=1) # if two beep:
import numpy as np import matplotlib.pyplot as plt from expyfun.stimuli import window_edges, read_wav, rms from expyfun import fetch_data_file print(__doc__) ############################################################################### # Load data # --------- # Get 2 seconds of data data_orig, fs = read_wav(fetch_data_file('audio/dream.wav')) stop = int(round(fs * 2)) data_orig = window_edges(data_orig[0, :stop], fs) t = np.arange(data_orig.size) / float(fs) # look at the waveform fig, ax = plt.subplots() ax.plot(t, data_orig) ax.set(xlabel='Time (sec)', ylabel='Amplitude', title='Original', xlim=t[[0, -1]]) fig.tight_layout() ############################################################################### # Normalize it # ------------ # :class:`expyfun.ExperimentController` by default has ``stim_rms=0.01``. This