Source code for brian2hears.hrtf.ircam

import os
import re
from glob import glob

from scipy.io import loadmat  # NOTE: this requires scipy 0.7+
import numpy as np

from brian2 import kHz

from .hrtf import *

__all__ = ['IRCAM_LISTEN']


[docs]class IRCAM_LISTEN(HRTFDatabase): ''' :class:`HRTFDatabase` for the IRCAM LISTEN public HRTF database. For details on the database, see the `website <http://recherche.ircam.fr/equipes/salles/listen/>`__. The database object can be initialised with the following arguments: ``basedir=None`` The directory where the database has been downloaded and extracted, e.g. ``r'D:\HRTF\IRCAM'``. Multiple directories in a list can be provided as well (e.g IRCAM and IRCAM New). Note that if you set this to None, it will use the environment variable IRCAM_LISTEN if that has been set. ``compensated=False`` Whether to use the raw or compensated impulse responses. ``samplerate=None`` If specified, you can resample the impulse responses to a different samplerate, otherwise uses the default 44.1 kHz. The coordinates are pairs ``(azim, elev)`` where ``azim`` ranges from 0 to 345 degrees in steps of 15 degrees, and elev ranges from -45 to 90 in steps of 15 degrees. After loading the database, the attribute 'subjects' gives all the subjects number that were detected as installed. **Obtaining the database** The database can be downloaded `here <http://recherche.ircam.fr/equipes/salles/listen/download.html>`__. Each subject archive should be extracted to a folder (e.g. IRCAM) with the names of the subject, e.g. IRCAM/IRC_1002, etc. ''' def __init__(self, basedir=None, compensated=False, samplerate=None): if basedir is None: basedir = os.getenv('IRCAM_LISTEN') if basedir is None: raise ValueError("You need to provide a directory for the IRCAM LISTEN database, or set the environment " "variable IRCAM_LISTEN to point to it.") if not isinstance(basedir, (list, tuple)): basedir = [basedir] self.basedir = basedir self.compensated = compensated names = [] for basedir in self.basedir: names += glob(os.path.join(basedir, 'IRC_*')) splitnames = [os.path.split(name) for name in names] p = re.compile('IRC_\d{4,4}') self.subjects = [int(name[4:8]) for base, name in splitnames if not (p.match(name[-8:]) is None)] if samplerate is not None: raise ValueError('Custom samplerate not supported.') self.samplerate = samplerate def load_subject(self, subject, rounddot5 = False): subject = str(subject) if subject[0] == '3': # this is the case only for stuffed animals recordings # IRC_30.. samplerate = 192*kHz else: samplerate = 44.1*kHz ok = False k = 0 while k < len(self.basedir) and not ok: try: filename = os.path.join(self.basedir[k], 'IRC_' + subject) if self.compensated: filename = os.path.join(filename, 'COMPENSATED/MAT/HRIR/IRC_' + subject + '_C_HRIR.mat') else: filename = os.path.join(filename, 'RAW/MAT/HRIR/IRC_' + subject + '_R_HRIR.mat') m = loadmat(filename, struct_as_record=True) ok = True except IOError: ok = False k += 1 if not ok: raise IOError("Couldn't find the HRTF files for subject "+str(subject)) if 'l_hrir_S' in m.keys(): # RAW DATA affix = '_hrir_S' else: # COMPENSATED DATA affix = '_eq_hrir_S' l, r = m['l' + affix], m['r' + affix] azim = l['azim_v'][0][0][:, 0] elev = l['elev_v'][0][0][:, 0] if len(azim) == len(elev) and len(azim) == 1: # it is the case with IRCAM_New db # - the coordinates are 1xN instead of Nx1 # - some measures that should be at the same elevation are # at very close but different elevations (7.47 # vs. 7.5). This is annoying for interpolation. Hence I # allow one to round the elevations conv = lambda x : x if rounddot5: conv = lambda x: np.round(2*x)/2 azim = conv(l['azim_v'][0][0][0, :]) elev = l['elev_v'][0][0][0, :] coords = make_coordinates(azim=azim, elev=elev) l = l['content_m'][0][0] r = r['content_m'][0][0] # self.data has shape (num_ears=2, num_indices, hrir_length) data = np.vstack((np.reshape(l, (1,) + l.shape), np.reshape(r, (1,) + r.shape))) hrtfset = HRTFSet(data, samplerate, coords) hrtfset.name = 'IRCAM_'+subject return hrtfset