Latest Thread Images
Featured Sponsor Listings
hakunamakaka
1000+ Head-Fier
Has anyone tried this? I’m currently near the last stage where I need to generate the sofa files using "join_sofa_files.py”
But I get these error messages
What does this sofa do and what are you trying to achieve ? Regards python this error is thrown when you try to access index which is not within the list.
Example"
python_list = [a, b, c] --> a has index 0, b has index 1, c index 2 and etc...
So print(python_list[4]) -> would throw exact same error. On first screen you got misstypo.
Can you share the code of join_sofa_files.py ? though this might be offtopic
Last edited:
morgin
100+ Head-Fier
Thanks for your detailed reply
I’ve virtually measured my head/ears and it has produced some folders that can generate my .sofa file. There are two folders generated for left and right ears and the join_sofa_files.py finds those two folders to convert into .sofa
This is the link to the code if posting it like I have at the bottom isn’t easy to read
https://sourceforge.net/p/mesh2hrtf...ree/SOFA_Merge_Folder/join_sofa_files.py#l590
I’ve virtually measured my head/ears and it has produced some folders that can generate my .sofa file. There are two folders generated for left and right ears and the join_sofa_files.py finds those two folders to convert into .sofa
This is the link to the code if posting it like I have at the bottom isn’t easy to read
https://sourceforge.net/p/mesh2hrtf...ree/SOFA_Merge_Folder/join_sofa_files.py#l590
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 1 | import os import numpy import sofar as sf # https://pypi.org/project/sofar/ pip install sofar import subprocess import shutil # ---------------------------------------- User Settings: -------------------------------------------- Input1 = '' # Empty by default (in mode A1), optional path to the 1st project to merge (for mode A2, B or C) Input2 = '' # Empty by default (in mode A or B), optional path to the 2nd project to merge (for mode C) # Input1 = r"C:\hrtf\SOFA_Merge_Folder" # example (use r"..." raw strings without "\" in the end!) SavePath = os.path.dirname(os.path.realpath(__file__)) # Automatically detect the working directory # Result files will be saved in a sub-folder created in "SavePath" # SavePath = r"C:\hrtf\SOFA_Merge_Folder" # alternatively specify a fixed folder (use r"..." raw strings!) Convert_to_spherical = True # keep this True, unless you have very specific demand for cartesian coordinates. # Some software only works with "spherical" coordinates of "SourcePosition" "in SOFA files Diagnostic_plot = True # shows and saves to disk the plot of the merged HRIR.sofa file: great for visual quality check. # requires: --- pyfar --- and --- matplotlib --- # ---------------------------------------- End of User Settings ----------------------------------------- # # "join_sofa_files.py" merges SOFA data from two separate Mesh2HRTF simulations for LEFT & RIGHT ear. # it uses a lot of example code from Mesh2HRTF "Output2HRTF_Main.py" & "NumCalcManager.py" # # HOW TO USE: # Main tutorial is here: https://sourceforge.net/p/mesh2hrtf/wiki/Basic_HRTF_post_processing # # mode A0 - no inputs = (recommended usage mode) same as A1, but actually executes "Output2HRTF.py" as well to run full # pre-processing in one go and applies fixes to the possible "Non-convergence issue". # # mode A1 - no inputs = scan start folder for 2 projects to merge. # 1- run this .py file from a dedicated folder (for example use folder "SOFA_Merge_Folder") # 2- Move into the "SOFA_Merge_Folder" exactly the 2 project folders that need to be merged. # 3- "open/run" this "join_sofa_files.py" file with Python. (Python3 with "sofar" & "numpy" must be installed) # (an easy Drag&Drop way to run this script is from Spyder which can be installed by Anaconda) # Without input arguments this script automatically merges the SOFA files of the 2 projects it finds. # 4- Merged SOFA files will be saved in a folder next to the project that was found. DONE. # # mode A2 - Input1 only = scan Input1 folder for 2 projects to merge. # 1- Move into any folder exactly the 2 project folders that need to be merged. # 2- "run" this "join_sofa_files.py" file and specify "Input1" path to the folder that contains projects to merge. # This script searches and merges the SOFA files from the 2 projects it finds inside "input1" path. # 3- Merged SOFA files will be saved in a folder next to the project that was found. DONE. # # NOT_IMPLEMENTED: mode B - 1 input = Use the given path to identify a pair of projects to merge # where folder-name for LEFT ends on '_L' and RIGHT ends on '_R'. # # mode C - 2 inputs = Merge the 2 projects that were given as input. # # made for Python 3.7+ see license details at the end of the script. # v0.50 2022-01-23 First working version. Tested on Windows 10. (by Sergejs Dombrovskis) # v0.90 2022-01-24 Mode-A1 is ready + most descriptions in place. (by S.D.) # v0.93 2022-02-06 added Mode-A2 and Mode-C. (by S.D.) # v1.00 2022-03-15 added Mode-A0 with automatic execution of "Output2HRTF.py" + small improvements + # added multi-SamplingRate HRIR outputs + extra sanity checks (by S.D.) # v1.10 2022-03-17 added diagnostic plotting of the merged HRIR.sofa file (by S.D.) # v1.20 2022-03-20 added effective workaround to produce usable data even if simulation encountered the # "Non-Convergence issue" - only in A0 mode (by S.D.) # def merge_write_sofa(sofa_left, sofa_right, basepath, filename, sofa_type='HRTF'): """Write complex pressure or impulse responses as SOFA file.""" # adapted from "write_to_sofa()" in "Output2HRTF_Main.py" # create empty SOFA object if sofa_type == 'HRTF': convention = "SimpleFreeFieldHRTF" # if numSources == 2 else "GeneralTF" elif sofa_type == 'HRIR': convention = "SimpleFreeFieldHRIR" # if numSources == 2 else "GeneralFIR" else: convention = 'Error - Unrecognized sofa_type provided.' sofa = sf.Sofa(convention) # write meta data sofa.GLOBAL_ApplicationName = sofa_left.GLOBAL_ApplicationName # 'Mesh2HRTF' sofa.GLOBAL_ApplicationVersion = sofa_left.GLOBAL_ApplicationVersion sofa.GLOBAL_History = sofa_left.GLOBAL_History # "numerically simulated data" if len(sofa_left.GLOBAL_Title) == 0: # title added to improve compatibility with Matlab SOFA API 1.1.3 sofa.GLOBAL_Title = 'untitled ' + sofa_type + ' SOFA data' else: sofa.GLOBAL_Title = sofa_left.GLOBAL_Title # Source and receiver data if Convert_to_spherical and \ sofa_left.SourcePosition_Type == 'cartesian' and sofa_left.SourcePosition_Units == 'meter': sofa.SourcePosition = cart2sph_in_deg(sofa_left.SourcePosition) # see sub-function sofa.SourcePosition_Units = 'degree, degree, metre' # alternatively 'degrees' sofa.SourcePosition_Type = 'spherical' else: sofa.SourcePosition = sofa_left.SourcePosition sofa.SourcePosition_Units = sofa_left.SourcePosition_Units # "meter" sofa.SourcePosition_Type = sofa_left.SourcePosition_Type # "cartesian" sofa.ReceiverPosition = [sofa_left.ReceiverPosition.reshape(3, 1), sofa_right.ReceiverPosition.reshape(3, 1)] sofa.ReceiverPosition_Units = sofa_left.ReceiverPosition_Units # "meter" sofa.ReceiverPosition_Type = sofa_left.ReceiverPosition_Type # "cartesian" if sofa_type == 'HRTF': tmp_data_real = numpy.zeros((sofa_left.Data_Real.shape[0], 2, sofa_left.Data_Real.shape[2])) # init tmp_data_imag = tmp_data_real # init for jj in range(sofa_left.Data_Real.shape[2]): # merge arrays tmp_data_real[:, 0, jj] = sofa_left.Data_Real[:, 0, jj] tmp_data_real[:, 1, jj] = sofa_right.Data_Real[:, 0, jj] tmp_data_imag[:, 0, jj] = sofa_left.Data_Imag[:, 0, jj] tmp_data_imag[:, 1, jj] = sofa_right.Data_Imag[:, 0, jj] sofa.Data_Real = tmp_data_real sofa.Data_Imag = tmp_data_imag sofa.N = sofa_left.N # frequencies # init defaults for HRTF: output_file_sampling_rates = [int(2 * sofa.N[-1])] elif sofa_type == 'HRIR': tmp_data_ir = numpy.zeros((sofa_left.Data_IR.shape[0], 2, sofa_left.Data_IR.shape[2])) # init for jj in range(sofa_left.Data_IR.shape[2]): # merge arrays tmp_data_ir[:, 0, jj] = sofa_left.Data_IR[:, 0, jj] tmp_data_ir[:, 1, jj] = sofa_right.Data_IR[:, 0, jj] sofa.Data_IR = tmp_data_ir sofa.Data_SamplingRate = sofa_left.Data_SamplingRate # fs sofa.Data_Delay = numpy.ones((1, 2)) * sofa_left.Data_Delay # init defaults for HRTF: output_file_sampling_rates = [int(sofa.Data_SamplingRate)] output_data_length = [sofa.Data_IR.shape[2]] # extra "multi-sample rate HRIR" output processing if numpy.round(sofa.Data_SamplingRate / sofa.Data_IR.shape[2]) == 150: frequency_step = 150 # detected a valid multi-sample rate frequency step elif numpy.round(sofa.Data_SamplingRate / sofa.Data_IR.shape[2]) == 75: frequency_step = 75 # detected a valid multi-sample rate frequency step else: print('\n --- Note: the Frequency Step of this dataset is not equal to 150 or 75 Hz, therefore ') print(' "multi-SamplingRate" HRIR saving is not possible. The only available SamplingRate is ' + str(sofa.Data_SamplingRate) + ' ' + sofa.Data_SamplingRate_Units + '\n') frequency_step = 0 if frequency_step > 0.5: # "multi-sample rate HRIR" output is possible common_sampling_rates = [192000, 96000, 88200, 48000, 44100] # edit this DESCENDING list if you have to for rate in common_sampling_rates: if sofa.Data_IR.shape[2] > rate / frequency_step: # add this sampling rate to output: output_file_sampling_rates.append(rate) output_data_length.append(int(rate / frequency_step)) else: raise Exception("impossible ![]() # save the merged files for f_nr in range(len(output_file_sampling_rates)): if f_nr > 0: # extra "multi-SamplingRate HRIR" output processing # trim data down to correct sampling rate: # noinspection PyUnboundLocalVariable sofa.Data_IR = sofa.Data_IR[:, :, ![]() sofa.Data_SamplingRate = float(output_file_sampling_rates[f_nr]) # https://sofa_right.readthedocs.io/en/latest/sofa_right.html#sofa_right-documentation sofa.verify() # Save as SOFA file path = os.path.join(basepath, filename[:-5] + '_' + str(output_file_sampling_rates[f_nr]) + '.sofa') # Need to delete old FILE first if file already exists if os.path.exists(path): os.remove(path) # Save sf.write_sofa(path, sofa, version='latest', compression=9) # max compression if f_nr == 0: full_reference_path = path # noinspection PyUnboundLocalVariable return full_reference_path # cart2sph conversion with DEGREE output def cart2sph_in_deg(xyz): theta_phi_r = numpy.empty(xyz.shape) # based on pyfar example https://github.com/pyfar/pyfar/blob/main/pyfar/classes/coordinates.py theta_phi_r[:, 2] = numpy.sqrt(xyz[:, 0] ** 2 + xyz[:, 1] ** 2 + xyz[:, 2] ** 2) # radius z_div_r = numpy.where(theta_phi_r[:, 2] != 0, xyz[:, 2] / theta_phi_r[:, 2], 0) theta_phi_r[:, 1] = numpy.rad2deg(numpy.arccos(z_div_r)) # colatitude theta_phi_r[:, 0] = numpy.rad2deg(numpy.mod(numpy.arctan2(xyz[:, 1], xyz[:, 0]), 2 * numpy.pi)) # azimuth # added fixes, including rounding to 2-3 decimal places: theta_phi_r[:, 2] = numpy.around(theta_phi_r[:, 2], 3) # this one round to 3 decimal places (1mm precision) theta_phi_r[:, 1] = numpy.around(- (theta_phi_r[:, 1] - 90), 2) # to match what "SOFAplotHRTF.m" does theta_phi_r[:, 0] = numpy.around(theta_phi_r[:, 0], 3) # theta_phi_r[:, 0] = numpy.around(numpy.where(theta_phi_r[:, 0] > 180, # theta_phi_r[:, 0] - 360, theta_phi_r[:, 0]), 2) return theta_phi_r def scan_for_errors(project_path): # find any broken data in this project # NOTE: made only to work with 1 source!!! num_sources = 1 lowest_corrupt_frq = 0 # init for source in range(num_sources): full_p = os.path.join(project_path, 'NumCalc', 'source_' + str(source + 1)) # list all NumCalc logs all_nc_list = [f for f in os.listdir(full_p) if f.endswith('.out') and f.startswith('NC') and os.path.isfile(os.path.join(full_p, f))] for file in all_nc_list: # read the file with open(os.path.join(full_p, file)) as ff: lines = ff.readlines() ff.close() frequency_key = ', Frequency = ' non_convergence_key = 'Warning: Maximum number of iterations is reached!' frequencies = [] # init non_convergence_freq = [] # init # find line containing the number of frequency steps for line_nr in range(len(lines)): if lines[line_nr].__contains__(frequency_key): where_is_it = lines[line_nr].find(frequency_key) # note down what is the frequency of this log and on which line # (because more than 1 frequency may be in the same worker/log-file) frequencies.append(int(lines[line_nr].strip()[where_is_it + len(frequency_key):-3])) elif lines[line_nr].startswith(non_convergence_key): if frequencies[-1] > 24000: # affects very high frequencies only print('Warning - Non-Convergence detected at over 24kHz (at ' + str(frequencies[-1]) + 'Hz)') else: print('PROBLEM!! - Non-Convergence issue in important range: @ ' + str(frequencies[-1]) + 'Hz') non_convergence_freq.append(frequencies[-1]) # note down which frequency failed if len(non_convergence_freq) > 0: # we have non-convergence if lowest_corrupt_frq == 0: lowest_corrupt_frq = min(non_convergence_freq) elif lowest_corrupt_frq > min(non_convergence_freq): lowest_corrupt_frq = min(non_convergence_freq) # keep only the lowest problematic frequency return lowest_corrupt_frq def write_new_numFrequencies(path1, path2, corrupt_frq1, corrupt_frq2): if min(corrupt_frq1, corrupt_frq2) == 0: # ignore the Zero init value lowest_corrupt_frq = max(corrupt_frq1, corrupt_frq2) else: lowest_corrupt_frq = min(corrupt_frq1, corrupt_frq2) for pro in [1, 2]: # execute for BOTH PROJECTS if pro == 1: project_path = path1 else: project_path = path2 info_path = os.path.join(project_path, 'Info.txt') # 1 Back up the Info.txt info_bckp_path = os.path.join(project_path, 'Info_BACKUP_original.txt') if not os.path.isfile(info_bckp_path): # backup does not yet exist shutil.copy2(info_path, info_bckp_path) # 2 read the original file with open(info_path) as ff: lines = ff.readlines() ff.close() # 3 Detect important information: for line_nr in range(len(lines)): if lines[line_nr].startswith('Minimum evaluated Frequency: '): min_frequency = float(lines[line_nr].strip()[len('Minimum evaluated Frequency: '):]) elif lines[line_nr].startswith('Highest evaluated Frequency: '): line_of_max_frequency = line_nr orig_max_frequency = float(lines[line_nr].strip()[len('Highest evaluated Frequency: '):]) elif lines[line_nr].startswith('Frequency Stepsize: '): frequency_step = float(lines[line_nr].strip()[len('Frequency Stepsize: '):]) elif lines[line_nr].startswith('Frequency Steps: '): line_of_frequency_steps_nr = line_nr orig_frequency_steps_nr = int(lines[line_nr].strip()[len('Frequency Steps: '):]) # 4 MODIFY the number of simulated frequency steps in the Info.txt new_max_frequency = lowest_corrupt_frq - frequency_step new_frequency_steps_nr = 1 + round((new_max_frequency - min_frequency) / frequency_step) lines[line_of_frequency_steps_nr] = ('Frequency Steps: ' + str(int(new_frequency_steps_nr)) + '\n') lines[line_of_max_frequency] = ('Highest evaluated Frequency: ' + str(new_max_frequency) + '\n') # 5 add extra modification info for the Info.txt lines.append('\n\n\n--------------------------------------------------------------------------------\n\n') lines.append('NOTE: this file was MODIFIED by "join_sofa_files.py" to reduce maximum Frequency\n') lines.append(' because simulation issue(s) were detected at higher frequencies.\n') lines.append('\n Initial "Highest evaluated Frequency" was ' + str(orig_max_frequency) + '\n') # noinspection PyUnboundLocalVariable lines.append(' Initial "Frequency Steps" was ' + str(orig_frequency_steps_nr) + '\n') # 6 OVER-WRITE the new Info.txt file f3 = open(info_path, "w") for li in lines: f3.write(li) f3.close() def plot_hrir_pair(sofa_data_path, noise_floor=-70): """ Plots both left and right ear HRIRs in an easy to diagnose format # based on tutorial: https://sofar.readthedocs.io/en/latest/working_with_sofa_files.html#working-with-sofa Params: sofa_data_path : full path to the sofa HRIR file to load and plot noise_floor : were to limit colormap to provide easy to read image = -50 # noise floor value used in "SOFAplotHRTF.m" from SOFA Matlab API = -70 # value that visually matches the look of "SOFAplotHRTF.m" from SOFA MatlabAPI """ # read the SOFA file using pyfar data_ir, source_coordinates, receiver_coordinates = pf.io.read_sofa(sofa_data_path) # find all source positions on or in the vicinity of the HORIZONTAL plane using the find_slice method _, mask = source_coordinates.find_slice('elevation', unit='deg', value=0, show=False) # do not Show the plot angles = source_coordinates.get_sph('top_elev', 'deg')[mask, 0] # noinspection PyTypeChecker axes_subplot = plt.subplots(2, 1, figsize=(8, 6), sharex=True, label=sofa_data_path) axes_subplot[0].set_size_inches(8, 10, forward=True) for Ear in [0, 1]: # Ear 0 == LEFT and 1 == RIGHT ear # main plot function ax_h, qm_h, cb_h = pf.plot.time_2d(data_ir[mask, Ear], indices=angles, dB=True, log_prefix=20, log_reference=1, unit=None, ax=axes_subplot[1][Ear], cmap=mpl.cm.get_cmap(name='hot_r'), orientation='horizontal') # YlOrBr hot hot_r RdBu natural_limits = qm_h.get_clim() # get the full range of data qm_h.set_clim([natural_limits[1] + noise_floor, natural_limits[1]]) # set limits according to noise floor ax_h[0].set_ylabel("Azimuth angle in degrees") # ax_h.set_ylim(0, 3) if Ear == 0: ax_h[0].set_title("Left ear HRIR (Horizontal plane)") elif Ear == 1: ax_h[0].set_title("Right ear HRIR (Horizontal plane)") axes_subplot[0].savefig((sofa_data_path[:-5] + '.png'), dpi=100) # ToDo - accept different input scenarios: # A (DONE) - no inputs = scan start folder for 2 projects to merge. # B - 1 input = Use the given path to identify a pair of projects to merge # where folder-name for LEFT ends on '_L' and RIGHT ends on '_R'. # C - 2 inputs = Merge the 2 projects that were given as input. # # Identify starting conditions print('join_sofa_files started with SavePath = "' + SavePath + '"') if len(Input1) > 0 and (os.path.basename(Input1)[-2:] == "_L" or os.path.basename(Input1)[-2:] == "_R"): ProjectName_withL = True # we can search for a mathing pairs of "_L" and "_R" projects else: ProjectName_withL = False # unless "Input2" is specified, search in this folder for 2 merge-able projects if len(Input1) == 0 or (not ProjectName_withL and len(Input2) == 0): # mode A = scan folder for 2 projects to merge. counter = 0 # init if len(Input1) == 0: Search_Folder = SavePath # no inputs - search folder where the script is (or any custom SavePath) else: Search_Folder = Input1 # search in the first Input1 path Path1 = '' # init Path2 = '' # init for subdir in os.listdir(Search_Folder): # we only search folders that do not start with "." (extra scripts or files are allowed) if os.path.isdir(os.path.join(Search_Folder, subdir)) and not subdir.startswith('.'): counter += 1 if counter == 1: Path1 = os.path.join(Search_Folder, subdir) # project 1 elif counter == 2: Path2 = os.path.join(Search_Folder, subdir) # project 2 else: raise Exception('more than 2 folders to merge in the Search_Folder = "' + Search_Folder + '"') if len(Path2) == 0: raise Exception('less than 2 folders to merge in the Search_Folder = "' + Search_Folder + '"') elif len(Input2) == 0: # mode B - 1 input = Use the given path to identify a pair of projects to merge # where folder-name for LEFT ends on '_L' and RIGHT ends on '_R'. raise Exception("not implemented") else: # mode C - 2 inputs = Merge the 2 projects that were given as input. Path1 = Input1 Path2 = Input2 # DONE - autodetect details of the input scenario: # a - folder with Mesh2HRTF project # b - folder with .sofa files # c - complete file path to "HRTF_<something>.sofa" of "HRIR_<something>.sofa" if os.path.isdir(Path1): # check if on Path1 is a Mesh2HRTF project folder that is not yet post-processed: Lowest_corrupt_frq_1 = 0 # init Lowest_corrupt_frq_2 = 0 # init if not os.path.isdir(os.path.join(Path1, 'Output2HRTF')) and os.path.isfile(os.path.join(Path1, 'Output2HRTF.py')): # Mode-A0 detected: print('\nChecking for errors in "' + Path1 + '"...\n') Lowest_corrupt_frq_1 = scan_for_errors(Path1) # find any broken data on Path1 print('\nChecking for errors in "' + Path2 + '"...\n') Lowest_corrupt_frq_2 = scan_for_errors(Path2) # find any broken data on Path1 if Lowest_corrupt_frq_1 > 1 or Lowest_corrupt_frq_2 > 1: # there was coppupt data in the simulation # exclude all frequencies that are corrupt by specifying the last Corrupt one. write_new_numFrequencies(Path1, Path2, Lowest_corrupt_frq_1, Lowest_corrupt_frq_2) print('\nRunning Mesh2HRTF post-processing in "' + Path1 + '"...\n') os.chdir(Path1) # necessary for the 'Output2HRTF.py' subprocess.call("Output2HRTF.py", shell=True) # run the normal Mesh2HRTF post-processing # noinspection PyUnboundLocalVariable os.chdir(Search_Folder) # change back to Search_Folder (just in case) # check if on Path2 is a Mesh2HRTF project folder that is not yet post-processed: if not os.path.isdir(os.path.join(Path2, 'Output2HRTF')) and os.path.isfile(os.path.join(Path2, 'Output2HRTF.py')): print('\nRunning Mesh2HRTF post-processing in "' + Path2 + '"...\n') os.chdir(Path2) # necessary for the 'Output2HRTF.py' subprocess.call("Output2HRTF.py", shell=True) # run the normal Mesh2HRTF post-processing os.chdir(Search_Folder) # change back to Search_Folder (just in case) if os.path.isdir(os.path.join(Path1, 'Output2HRTF')): # a - folder with Mesh2HRTF project SofaFLDR_1 = os.path.join(Path1, 'Output2HRTF') SofaFLDR_2 = os.path.join(Path2, 'Output2HRTF') else: # b - folder with .sofa files SofaFLDR_1 = Path1 SofaFLDR_2 = Path2 AllSofaFiles1 = os.listdir(SofaFLDR_1) # searching for '*.sofa' files AllSofaFiles2 = os.listdir(SofaFLDR_2) FolderName = os.path.basename(Path1) else: # os.path.isfile(Path1): # c - complete file path to "HRTF_<something>.sofa" of "HRIR_<something>.sofa" AllSofaFiles1 = [os.path.basename(Path1)] AllSofaFiles2 = [os.path.basename(Path2)] SofaFLDR_1 = os.path.dirname(Path1) SofaFLDR_2 = os.path.dirname(Path2) FolderName = os.path.basename(SofaFLDR_1) if FolderName.endswith('_L') or FolderName.endswith('_R'): FolderName = FolderName[:-2] # drop the "_L" or "_R" from the folder name (tidy up) BasePath = os.path.join(SavePath, 'SOFA_' + FolderName + '_merged') # autodetect which is Left and which is Right based on source coord. HRTFsofa_read_1 = sf.read_sofa(os.path.join(SofaFLDR_1, AllSofaFiles1[0])) if HRTFsofa_read_1.ReceiverPosition.shape[0] > 1: # more than one receiver in this SOFA file raise Exception('The SOFA file is not for a single ear! File in: "' + SofaFLDR_1 + '"') if HRTFsofa_read_1.ReceiverPosition[0, 1] > 0: # sofa file is for Left ear (positive Y coordinate) AllSofaFiles_L = AllSofaFiles1 AllSofaFiles_R = AllSofaFiles2 SofaFLDR_L = SofaFLDR_1 SofaFLDR_R = SofaFLDR_2 else: # flip inputs because the 2nd folder contains Left ear AllSofaFiles_R = AllSofaFiles1 AllSofaFiles_L = AllSofaFiles2 SofaFLDR_R = SofaFLDR_1 SofaFLDR_L = SofaFLDR_2 # Save complex pressure as SOFA file print('\n Merging HRTF SOFA files from: "' + os.path.basename(Path1) + '" and "' + os.path.basename(Path2) + '"') del SofaFLDR_2, SofaFLDR_1, AllSofaFiles2, AllSofaFiles1, Path1, Path2 for ii in range(2): # range(len(AllSofaFiles_L)) # # Load the data sofa_read_L = sf.read_sofa(os.path.join(SofaFLDR_L, AllSofaFiles_L[ii])) sofa_read_R = sf.read_sofa(os.path.join(SofaFLDR_R, AllSofaFiles_R[ii])) # detect data type: if sofa_read_L.GLOBAL_DataType == 'FIR': SOFA_Type = 'HRIR' # sanity checks that the files can be merged if sofa_read_L.Data_SamplingRate != sofa_read_R.Data_SamplingRate: raise Exception("The SamplingRates of the two input SOFA files do not match!") elif sofa_read_L.Data_IR.shape[1] != 1 or sofa_read_R.Data_IR.shape[1] != 1: raise Exception("The input SOFA files contain more than one ear - impossible to merge") elif sofa_read_L.Data_IR.shape[2] != sofa_read_R.Data_IR.shape[2]: raise Exception("The number of steps in the two input SOFA files do not match!") elif sofa_read_L.ReceiverPosition_Units != sofa_read_R.ReceiverPosition_Units: raise Exception("The ReceiverPosition_Units in the two input SOFA files do not match!") else: SOFA_Type = 'HRTF' # sanity checks that the files can be merged if sofa_read_L.N[0] != sofa_read_R.N[0] or sofa_read_L.N[-1] != sofa_read_R.N[-1]: raise Exception("The sampling Frequencies of the two input SOFA files do not match!") if ii == 0 and not os.path.isdir(BasePath): os.mkdir(BasePath) # create new output folder # noinspection PyTypeChecker Full_Ref_Path = merge_write_sofa(sofa_read_L, sofa_read_R, BasePath, AllSofaFiles_L[ii], SOFA_Type) if SOFA_Type == 'HRIR': # note down what should be plotted in the end SOFA_to_plot = Full_Ref_Path # make a simple copy of the Info.txt if os.path.isfile(os.path.join(os.path.dirname(SofaFLDR_L), 'Info.txt')): shutil.copy2(os.path.join(os.path.dirname(SofaFLDR_L), 'Info.txt'), os.path.join(BasePath, 'Info.txt')) print('\n Done: Merged SOFA files are saved in:\n "' + BasePath + '"\n') if Diagnostic_plot: # noinspection PyBroadException try: ############################################################################# # plotting part: it is OK if plotting crashes, because the merging is already complete. # these imports are here so that key functionality works even without "pyfar" and "matplotlib" import pyfar as pf # https://pypi.org/project/sofar/ pip install pyfar import matplotlib as mpl import matplotlib.pyplot as plt # noinspection PyUnboundLocalVariable plot_hrir_pair(SOFA_to_plot, -50) ############################################################################# except Exception: print('\n WARNING: plotting command crashed. Likely reasons are:') print(' 1 - missing "pyfar" or missing "matplotlib"') print(' (to install "pyfar" in command line type "pip install pyfar" ') print(' 2 - obsolete python packages (especially numpy) - try running: "pip list --outdated"') print(' (to update a package use for example: "pip install --upgrade numpy"') print(' 3 - actual bug in this script (debug, or open an issue if you need this plot)\n') input('Hit Enter to close - Plotting is complete ![]() else: input('Hit Enter to exit ![]() # # made for the Mesh2HRTF codebase # # mesh2hrtf.sourceforge.net # # Mesh2HRTF is licensed under the GNU Lesser General Public License as # published by the Free Software Foundation, either version 3 of the License, # or (at your option) any later version. Mesh2HRTF is distributed in the hope # that it will be useful, but WITHOUT ANY WARRANTY; without even the implied # warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. You should have received a # copy of the GNU LesserGeneral Public License along with Mesh2HRTF. If not, # see <http://www.gnu.org/licenses/lgpl.html>. # # If you use Mesh2HRTF: # - Provide credits: # "Mesh2HRTF, H. Ziegelwanger, ARI, OEAW (mesh2hrtf.sourceforge.net)" # - In your publication, cite both articles: # [1] Ziegelwanger, H., Kreuzer, W., and Majdak, P. (2015). "Mesh2HRTF: # Open-source software package for the numerical calculation of head- # related transfer functions," in Proceedings of the 22nd ICSV, # Florence, IT. # [2] Ziegelwanger, H., Majdak, P., and Kreuzer, W. (2015). "Numerical # calculation of listener-specific head-related transfer functions and # sound localization: Microphone model and mesh discretization," The # Journal of the Acoustical Society of America, 138, 208-222. |
Last edited:
hakunamakaka
1000+ Head-Fier
HRTFsofa_read_1 variable throws an error because there is no value assigned to AllSofaFiles1.
In script you need to provide inputs depending on the mode that you are using.
Input1 = '' # Empty by default (in mode A1), optional path to the 1st project to merge (for mode A2, B or C)
Input2 = '' # Empty by default (in mode A or B), optional path to the 2nd project to merge (for mode C)
It should look similarly to the below:
Input1 = "C:/pathtothefile/youwantTomerge"
Input2 might be needed as well depending on your setup, but I have no idea what sofa is and what these different modes do
In script you need to provide inputs depending on the mode that you are using.
Input1 = '' # Empty by default (in mode A1), optional path to the 1st project to merge (for mode A2, B or C)
Input2 = '' # Empty by default (in mode A or B), optional path to the 2nd project to merge (for mode C)
It should look similarly to the below:
Input1 = "C:/pathtothefile/youwantTomerge"
Input2 might be needed as well depending on your setup, but I have no idea what sofa is and what these different modes do
Last edited:
morgin
100+ Head-Fier
Thank you soooooooo much I’ll give this a try I spent hours trying different combinations of the folder locations.HRTFsofa_read_1 variable throws an error because there is no value assigned to AllSofaFiles1.
In script you need to provide inputs depending on the mode that you are using.
Input1 = '' # Empty by default (in mode A1), optional path to the 1st project to merge (for mode A2, B or C)
Input2 = '' # Empty by default (in mode A or B), optional path to the 2nd project to merge (for mode C)
It should look similarly to the below:
Input1 = "C:/pathtothefile/youwantTomerge"
Input2 might be needed as well depending on your setup, but I have no idea what sofa is and what these different modes do
hakunamakaka
1000+ Head-Fier
Thank you soooooooo much I’ll give this a try I spent hours trying different combinations of the folder locations.
No problem. Script needs to know your file locations and you will have to adjust it if you will work in different directories
sander99
Headphoneus Supremus
A sofa file can contain someone's HRTF as a collection of impulces (impulce responses) for many different directions, for every direction one impulce to the left ear and one impulce to the right ear.I have no idea what sofa is
For example Genelec Aural ID used sofa files containing 836 different directions.
I don't know how many you get from Mesh2hrtf, @morgin do you know? Or can you maybe choose how many and which ones?
I don't know how the file is structered
morgin
100+ Head-Fier
I have no idea, I’m learning as I go but I think it’s many because the whole process took my 16gb ram i7 7700k few hours to processA sofa file can contain someone's HRTF as a collection of impulces (impulce responses) for many different directions, for every direction one impulce to the left ear and one impulce to the right ear.
For example Genelec Aural ID used sofa files containing 836 different directions.
I don't know how many you get from Mesh2hrtf, @morgin do you know? Or can you maybe choose how many and which ones?
I don't know how the file is structered
Last edited:
morgin
100+ Head-Fier
I'm now getting this error. The file should just be double click with the folder next to this .py file, the tutorial does not say to change any locations. But i tried changing input 1 to one of the folder and input 2 to the second folder this is what i'm getting
Microsoft Windows [Version 10.0.22000.588]
(c) Microsoft Corporation. All rights reserved.
C:\mesh2hrtf-tools\SOFA_Merge_Folder>python3 join_sofa_files.py
File "C:\mesh2hrtf-tools\SOFA_Merge_Folder\join_sofa_files.py", line 9
Input1 = r"C:\mesh2hrtf-tools\SOFA_Merge_Folder" Empty by default (in mode A1), optional path to the 1st project to merge (for mode A2, B or C)
^
SyntaxError: invalid decimal literal
C:\mesh2hrtf-tools\SOFA_Merge_Folder>python3 join_sofa_files.py
join_sofa_files started with SavePath = "C:\mesh2hrtf-tools\SOFA_Merge_Folder"
Traceback (most recent call last):
File "C:\mesh2hrtf-tools\SOFA_Merge_Folder\join_sofa_files.py", line 473, in <module>
HRTFsofa_read_1 = sf.read_sofa(os.path.join(SofaFLDR_1, AllSofaFiles1[0]))
IndexError: list index out of range
C:\mesh2hrtf-tools\SOFA_Merge_Folder>python3 join_sofa_files.py
join_sofa_files started with SavePath = "C:\mesh2hrtf-tools\SOFA_Merge_Folder"
Traceback (most recent call last):
File "C:\mesh2hrtf-tools\SOFA_Merge_Folder\join_sofa_files.py", line 473, in <module>
HRTFsofa_read_1 = sf.read_sofa(os.path.join(SofaFLDR_1, AllSofaFiles1[0]))
IndexError: list index out of range
C:\mesh2hrtf-tools\SOFA_Merge_Folder>
Microsoft Windows [Version 10.0.22000.588]
(c) Microsoft Corporation. All rights reserved.
C:\mesh2hrtf-tools\SOFA_Merge_Folder>python3 join_sofa_files.py
File "C:\mesh2hrtf-tools\SOFA_Merge_Folder\join_sofa_files.py", line 9
Input1 = r"C:\mesh2hrtf-tools\SOFA_Merge_Folder" Empty by default (in mode A1), optional path to the 1st project to merge (for mode A2, B or C)
^
SyntaxError: invalid decimal literal
C:\mesh2hrtf-tools\SOFA_Merge_Folder>python3 join_sofa_files.py
join_sofa_files started with SavePath = "C:\mesh2hrtf-tools\SOFA_Merge_Folder"
Traceback (most recent call last):
File "C:\mesh2hrtf-tools\SOFA_Merge_Folder\join_sofa_files.py", line 473, in <module>
HRTFsofa_read_1 = sf.read_sofa(os.path.join(SofaFLDR_1, AllSofaFiles1[0]))
IndexError: list index out of range
C:\mesh2hrtf-tools\SOFA_Merge_Folder>python3 join_sofa_files.py
join_sofa_files started with SavePath = "C:\mesh2hrtf-tools\SOFA_Merge_Folder"
Traceback (most recent call last):
File "C:\mesh2hrtf-tools\SOFA_Merge_Folder\join_sofa_files.py", line 473, in <module>
HRTFsofa_read_1 = sf.read_sofa(os.path.join(SofaFLDR_1, AllSofaFiles1[0]))
IndexError: list index out of range
C:\mesh2hrtf-tools\SOFA_Merge_Folder>
hakunamakaka
1000+ Head-Fier
I'm now getting this error. The file should just be double click with the folder next to this .py file, the tutorial does not say to change any locations. But i tried changing input 1 to one of the folder and input 2 to the second folder this is what i'm getting
Microsoft Windows [Version 10.0.22000.588]
(c) Microsoft Corporation. All rights reserved.
C:\mesh2hrtf-tools\SOFA_Merge_Folder>python3 join_sofa_files.py
File "C:\mesh2hrtf-tools\SOFA_Merge_Folder\join_sofa_files.py", line 9
Input1 = r"C:\mesh2hrtf-tools\SOFA_Merge_Folder" Empty by default (in mode A1), optional path to the 1st project to merge (for mode A2, B or C)
^
SyntaxError: invalid decimal literal
C:\mesh2hrtf-tools\SOFA_Merge_Folder>python3 join_sofa_files.py
join_sofa_files started with SavePath = "C:\mesh2hrtf-tools\SOFA_Merge_Folder"
Traceback (most recent call last):
File "C:\mesh2hrtf-tools\SOFA_Merge_Folder\join_sofa_files.py", line 473, in <module>
HRTFsofa_read_1 = sf.read_sofa(os.path.join(SofaFLDR_1, AllSofaFiles1[0]))
IndexError: list index out of range
C:\mesh2hrtf-tools\SOFA_Merge_Folder>python3 join_sofa_files.py
join_sofa_files started with SavePath = "C:\mesh2hrtf-tools\SOFA_Merge_Folder"
Traceback (most recent call last):
File "C:\mesh2hrtf-tools\SOFA_Merge_Folder\join_sofa_files.py", line 473, in <module>
HRTFsofa_read_1 = sf.read_sofa(os.path.join(SofaFLDR_1, AllSofaFiles1[0]))
IndexError: list index out of range
C:\mesh2hrtf-tools\SOFA_Merge_Folder>
You can't leave such line in Input1 field, python syntax doesn't understand anything past double quotes.
It should be like this
Input1 = "C:\mesh2hrtf-tools\SOFA_Merge_Folder"
You can do it that way too if you want to keep description
Input1 = "C:\mesh2hrtf-tools\SOFA_Merge_Folder" #Empty by default (in mode A1), optional path to the 1st project to merge (for mode A2, B or C)
Last edited:
musicreo
100+ Head-Fier
- Joined
- Jun 25, 2011
- Posts
- 401
- Likes
- 152
Have you tried to put the sofa files directly into the folder with the script instead of putting them into subfolders named left and right?
I understand the error like that the script is not fiding any sofa file.
I understand the error like that the script is not fiding any sofa file.
Last edited:
hakunamakaka
1000+ Head-Fier
Script can use the same location if it’s coded that way, but it won’t be able to guess folder/file naming. In his example he didn’t put parameters correct way and that’s the reason why python throws syntax and index out of range errors.Have you tried to put the sofa files directly into the folder with the script instead of putting them into subfolders named left and right?
I understand the error like that the script is not fiding any sofa file.
Script could only guess location if these folders/files are created with default naming
Last edited:
morgin
100+ Head-Fier
I tried that with the bottom input in the pic.Input1 = "C:\mesh2hrtf-tools\SOFA_Merge_Folder"
yeah I've tried all sorts of combinations changing folder names, locations, putting the .py in one folder copying both folders into 1. Nothing I've tried works. I'm thinking maybe I'm missing some .sofa file or it wasn't fully calculated but the command said completed and no errors popped up.Have you tried to put the sofa files directly into the folder with the script instead of putting them into subfolders named left and right?
I understand the error like that the script is not fiding any sofa file.
these are the files that were generated in the left ear folder to be converted by the .py into .sofa they are the same in the right ear folder. My question is do they look right or would there be another file that could be missing

hakunamakaka
1000+ Head-Fier
Send me how user settings code part looks after your path update
musicreo
100+ Head-Fier
- Joined
- Jun 25, 2011
- Posts
- 401
- Likes
- 152
But I think that is the case here?Script could only guess location if these folders/files are created with default naming
There is no .sofa file? The .out files contain the impulse response data?these are the files that were generated in the left ear folder to be converted by the .py into .sofa they are the same in the right ear folder. My question is do they look right or would there be another file that could be missing
Users who are viewing this thread
Total: 2 (members: 0, guests: 2)