Commit 016c33fe authored by Lukas Aspöck's avatar Lukas Aspöck
Browse files

Merge branch 'master' of https://git.rwth-aachen.de/ita/toolbox

parents cad36303 7d63033b
......@@ -128,7 +128,7 @@ end
keyname = 'Git Version';
if ~any( strcmpi( { daff_write_args.metadata(:).name }, keyname ) )
versionHash = ita_git_getMasterCommitHash;
versionHash = ita_git_commit_id();
daff_write_args.metadata = daffv17_add_metadata( daff_write_args.metadata, keyname, 'String', versionHash );
end
......
function result = ita_read_sofa_hrtf(filename,varargin)
%ITA_READ_SOFA - +++ Short Description here +++
% This function ++++ FILL IN INFO HERE ++*
% <ITA-Toolbox>
% This file is part of the ITA-Toolbox. Some rights reserved.
% You can find the license for this m-file in the license.txt file in the ITA-Toolbox folder.
% </ITA-Toolbox>
% Author: Jan Gerrit Richter -- Email: jan.richter@akustik.rwth-aachen.de
% Created: 30-Sep-2014
% Updated: 14-Oct-2020 Hark Braren -- Email: hark.braren@akustik.rwth-aachen.de
%% Return type of data this function can read
if nargin == 0
result{1}.extension = '.sofa';
result{1}.comment = 'SOFA Files (*.sofa)';
return
end
sArgs = struct('samplingRate',-1);
ita_parse_arguments(sArgs,varargin);
%% check if sofa is installed
if ~exist('SOFAstart.m','file')
error('SOFA not installed. Run ita_sofa_install');
end
%% Open the sofaFile
if ~exist(filename,'file')
f=filesep;
filename=[SOFAdbPath f 'SOFA' f filename];
end
handleSofa = SOFAload(filename);
%% check HRTF
isHrtf = ismember(handleSofa.GLOBAL_SOFAConventions,{'SimpleFreeFieldHRIR','SimpleFreeFieldHRTF','SimpleFreeFieldTF'});
if ~isHrtf
error("%s: Datatype not typically used for HRTFs",handleSofa.GLOBAL_SOFAConventions);
end
%% get positional data
% head position coordinates and orientation
recieverCoordinates = ita_sofa_getCoordinates(handleSofa,'channelCoordinateType','ReceiverPosition');
listenerView = ita_sofa_getCoordinates(handleSofa,'channelCoordinateType','ListenerView');
listenerUp = listenerView;
listenerUp.(listenerUp.coordSystem) = handleSofa.ListenerUp;
%source coordinates
sourceCoordinates = ita_sofa_getCoordinates(handleSofa,'channelCoordinateType','SourcePosition');
%% get datatype info
switch handleSofa.GLOBAL_DataType
case 'TF'
audioData = getSofaFreqData(handleSofa);
case {'FIR','FIR-E','FIRE'}
audioData = getSofaTimeData(handleSofa);
case 'SOS'
error('SOS type not yet implemented for ITA-Toolbox');
end
% set direction
audioData(1).channelCoordinates = sourceCoordinates;
audioData(2).channelCoordinates = sourceCoordinates;
%% final data as HRTF
if isa(audioData,'itaResult')
result = audioData;
return
else
result = itaHRTF(audioData);
end
result.objectCoordinates = recieverCoordinates;
result.objectUpVector = listenerUp;
result.objectViewVector = listenerView;
%% add metaData as userData
result.userData = getSofaMetadata(handleSofa);
end
function audioData = getSofaTimeData(handleSofa)
data = handleSofa.Data.IR;
samplingrate = handleSofa.Data.SamplingRate;
if size(data,2) ~= 2
error('unknown data structure')
end
leftEarData = itaAudio(squeeze(data(:,1,:))',samplingrate,'time');
rightEarData = itaAudio(squeeze(data(:,2,:))',samplingrate,'time');
audioData = [leftEarData, rightEarData];
end
function [audioData] = getSofaFreqData(handleSofa,sArgs)
% handle frequency domain transfer functions:
% decide wether to return itaAudio or itaResult object
if ~ismember(handleSofa.N_LongName,{'f','frequency'})
error('Unspecified content of N in sofa object');
end
if ~ismember(handleSofa.N_Units,{'hertz','hz','Hertz','Hz'})
error('Uknown frequency unit in sofa object');
end
frequencies = handleSofa.N;
data = complex(handleSofa.Data.Real,handleSofa.Data.Imag);
if std(diff(frequencies)) > 0.01*mean(diff(frequencies))
ita_verbose_info('Please check frequency Vector. It seems there are some values missing - returnin itaResult',0);
dataL = itaResult(squeeze(data(:,1,:)).',frequencies,'freq');
dataR = itaResult(squeeze(data(:,2,:)).',frequencies,'freq');
audioData = [dataL, dataR];
return
else
ita_verbose_info('Automatically converting from equidistantly sampled frequency domain data, handle with care',1)
end
samplingrate = 2*max(frequencies);
% check number of ears
switch size(data,2)
case 1
ita_verbose_info('ita_read_sofa_hrtf: Only data of one ear present, using it for both ears',0)
dataL = itaAudio(squeeze(data(:,1,:)).',samplingrate,'freq');
dataR = dataL;
case 2
dataL = itaAudio(squeeze(data(:,1,:)).',samplingrate,'freq');
dataR = itaAudio(squeeze(data(:,2,:)).',samplingrate,'freq');
otherwise
error('Number of ears not matching')
end
audioData = [dataL, dataR];
end
function result = getSofaMetadata(handleSofa)
% get meta data to keep with the ITA files
%list of possible dataFields
metaDataFields = {'GLOBAL_Conventions','GLOBAL_Version','GLOBAL_SOFAConventions','GLOBAL_SOFAConventionsVersion' ...
,'GLOBAL_APIName','GLOBAL_APIVersion','GLOBAL_ApplicationName','GLOBAL_ApplicationVersion','GLOBAL_AuthorContact' ...
,'GLOBAL_Comment','GLOBAL_DataType','GLOBAL_History','GLOBAL_License','GLOBAL_Organization','GLOBAL_References' ...
,'GLOBAL_RoomType','GLOBAL_Origin','GLOBAL_DateCreated','GLOBAL_DateModified','GLOBAL_Title','GLOBAL_DatabaseName' ...
,'GLOBAL_RoomDescription','GLOBAL_ListenerShortName','API','ListenerPosition','ListenerPosition_Type','ListenerPosition_Units'...
,'EmitterPosition','EmitterPosition_Type','EmitterPosition_Units','RoomCornerA','RoomCornerA_Type','RoomCornerA_Units' ...
,'RoomCornerB','RoomCornerB_Type','RoomCornerB_Units','','','','','','',''};
for index = 1:length(metaDataFields)
if isfield(handleSofa,metaDataFields{index})
result.(metaDataFields{index}) = handleSofa.(metaDataFields{index});
end
end
end %getSofaMetadata()
\ No newline at end of file
function varargout = ita_write_sofa_hrtf(varargin)
%ITA_WRITE_SOFA_HRTF - +++ Write HRTF as SOFA file +++
% This function writes a itaHRTF into a .sofa file following the SOFA
%
% Syntax:
% ita_write_sofa_hrtf(hrtfObj, fileName)
%
% Options:
% userData ([]): struct with user specific SOFA entries see
% <userDataFields> below
% e.g. userData = struct('GLOBAL_AuthorContact','Max Mustermann: mmu@xyz.de',...
% 'GLOBAL_Comment','Example Comment')
% See also:
% ita_read_sofa_hrtf, ita_write, itaHRTF
%
% Reference page in Help browser
% <a href="matlab:doc ita_write_sofa_hrtf">doc ita_write_sofa_hrtf</a>
% <ITA-Toolbox>
% This file is part of the ITA-Toolbox. Some rights reserved.
% You can find the license for this m-file in the license.txt file in the ITA-Toolbox folder.
% </ITA-Toolbox>
% Author: Hark Braren -- Email: hark.braren@akustik.rwth-aachen.de
% Created: 12-Apr-2021
userDataFields = {'GLOBAL_AuthorContact',... %Who created it, automatically takes info from toolbox preferences
'GLOBAL_Comment',...
'GLOBAL_History',...
'GLOBAL_License',... % e.g. CC 4.0 BY - http://creativecommons.org/licenses/by/4.0/
'GLOBAL_ListenerShortName',... % e.g. KEMAR
'GLOBAL_Organization',... % (IHTA)
'GLOBAL_References',... % corresonding paper
'GLOBAL_DateCreated',...
'GLOBAL_DateModified',...
'GLOBAL_Title',...
'GLOBAL_DatabaseName',... %
'GLOBAL_Origin',...
'GLOBAL_RoomType',... %(freefield)
};
%% Initialization and Input Parsing
if nargin == 0 % Return possible argument layout
result{1}.extension = '*.sofa';
result{1}.comment = 'SOFA Files (*.sofa)';
return;
end
sArgs = struct('pos1_data','itaHRTF','pos2_filename','char','userData',[]);
[data, fileName, sArgs] = ita_parse_arguments(sArgs,varargin);
%% check if sofa is installed
if ~exist('SOFAstart.m','file')
error('SOFA not installed. Run ita_sofa_install');
end
SOFAstart
%% get sofa definitions and domain specific data
switch data.domain
case 'time'
Obj=SOFAgetConventions('SimpleFreeFieldHRIR');
%time data
Obj.Data.IR = zeros(data.nDirections, 2, data.nSamples);
Obj.Data.IR(:,1,:) = data.getEar('L').timeData.';
Obj.Data.IR(:,2,:) = data.getEar('R').timeData.';
%data info
Obj.Data.SamplingRate = data.samplingRate;
Obj.Data.SamplingRate_Units = 'hertz';
case 'freq'
Obj=SOFAgetConventions('SimpleFreeFieldHRTF');
%freq data
Obj.Data.Real = zeros(data.nDirections, 2, data.nBins);
Obj.Data.Imag = zeros(data.nDirections, 2, data.nBins);
Obj.Data.Real(:,1,:) = real(data.getEar('L').freqData).';
Obj.Data.Real(:,2,:) = real(data.getEar('R').freqData).';
Obj.Data.Imag(:,1,:) = imag(data.getEar('L').freqData).';
Obj.Data.Imag(:,2,:) = imag(data.getEar('R').freqData).';
%data info
Obj.N = data.freqVector;
Obj.N_LongName = 'frequency';
Obj.N_Units = 'hertz';
end
%% General Info
Obj.GLOBAL_ApplicationName = 'ITA-Toolbox';
Obj.GLOBAL_Organization = 'Institute for Hearing Technology and Acoustics, RWTH Aachen University';
Obj.GLOBAL_ApplicationVersion = num2str(ita_toolbox_version_number);
%The following will be overwritten with userdata is supplied
Obj.GLOBAL_DateCreated = date;
Obj.GLOBAL_AuthorContact = [ita_preferences('AuthorStr') ': ' ita_preferences('EmailStr')];
%% HRTF Info
%head position
Obj.ListenerPosition_Type = 'cartesian';
Obj.ListenerPosition_Units = 'metre';
Obj.ListenerPosition = [0 0 0];
% ear position for each measurement [0 +w 0; 0 -w 0] in hrtf.objectCoordinates
Obj.ReceiverPosition_Type = 'cartesian';
Obj.ReceiverPosition_Units = 'metre';
if ~isempty(data.objectCoordinates.cart)
Obj.ReceiverPosition = repmat(data.objectCoordinates.cart,1,1,data.nDirections);
else
Obj.ReceiverPosition = repmat([0 0.09 0; 0 -0.09 0],1,1,data.nDirections); %default per SOFA def
end
%head orientation
Obj.ListenerView_Type = 'cartesian';
Obj.ListenerView_Units = 'metre';
if ~isempty(data.objectViewVector.cart)
Obj.ListenerView = data.objectViewVector.cart;
end
if ~isempty(data.objectUpVector.cart)
Obj.ListenerUp = data.objectUpVector.cart;
end
%Loudspeaker positions in head-related coordinates
Obj.SourcePosition_Type = 'spherical';
Obj.SourcePosition_Units = 'degree,degree,metre';
Obj.SourcePosition = sofaSphericalCoordiantes(data.dirCoord);
%% add Userdata
if ~isempty(sArgs.userData)
for iField = 1:numel(userDataFields)
if ismember(userDataFields{iField},fieldnames(sArgs.userData))
Obj.(userDataFields{iField}) = sArgs.userData.(userDataFields{iField});
end
end
end
%% update and write obj
Obj=SOFAupdateDimensions(Obj);
SOFAsave(fileName,Obj);
end
function sofaCoord = sofaSphericalCoordiantes(coords)
%transform from zenith to elevation angle
r = coords.r;
elevation = coords.theta_deg-90;
azimuth = coords.phi_deg;
sofaCoord = [azimuth,elevation,r];
end
function motorTimeOut = ita_HRTFarc_detectSwitch(audioObject)
%% find times indicating entry and exit points of the switch area
% at the beginning and end of a full arc rotation.
%
% Input : audioObject - 1 channel with motor signal
% Output: motorTimeOut = [a b c d]
% a, b = first switch range
% c, d = second switch range
% d - a : corresponds to duration extracted from moving arc rigid body (tracking data)
% d - b : 360 omitting most of the acceleration phase
% --> more suitable for rotation speed estimation
% --> preferable range to be used in case of no directional correction via tracking data
% b - a : time spent while passing the first switch
% --> to be added to start time detected from tracking data (ca. a)
% === rectify and integrate signal
absData = audioObject;
absData.timeData = abs(audioObject.timeData);
integData = ita_integrate(absData, 'domain', 'time');
% === round to 3 decimal resolution to remove effect of small fluctuations
integData.timeData = round(integData.timeData,3);
% === start of first switch pulse
switchSamples(1) = find(integData.timeData == min(integData.timeData),1,'last');
% === end of first and start of second switch pulse
% (median is used, assuming that the duration between the two peaks
% is much longer than the switch regions)
switchSamples(2) = find(integData.timeData == median(integData.timeData),1,'first');
switchSamples(3) = find(integData.timeData == median(integData.timeData),1,'last');
% === end of second switch pulse
switchSamples(4)= find(integData.timeData == max(integData.timeData),1,'first');
if switchSamples(3)-switchSamples(2)< switchSamples(2)-switchSamples(1) || ...
switchSamples(3)-switchSamples(2)< switchSamples(4)-switchSamples(3)
% plot data to debug and throw error
switches = zeros(size(integData.timeData));
switches(switchSamples) = 1;
integData.pt; hold on; plot((1:length(switches))/integData.samplingRate,switches)
error('getMotorSwitchTimes: The second peak is detected too early. Please take a look')
end
motorTimeOut = switchSamples/ integData.samplingRate;
end
\ No newline at end of file
......@@ -170,7 +170,7 @@ for index = 1:length(allMeasurements_full)
end
% append commit id to history
commitID = ita_git_getMasterCommitHash;
commitID = ita_git_commit_id();
for index = 1:length(allMeasurements_full)
if ~isempty(commitID)
allMeasurements_full(index) = ita_metainfo_add_historyline(allMeasurements_full(index),'ita_HRTFarc_postProcessContinuous',commitID);
......
......@@ -290,7 +290,7 @@ for index = 1:length(result)
end
% append commit id to history
commitID = ita_git_getMasterCommitHash;
commitID = ita_git_commit_id();
for index = 1:length(result)
if ~isempty(commitID)
result(index) = ita_metainfo_add_historyline(result(index),'ita_HRTFarc_postProcessContinuous',commitID);
......
......@@ -80,7 +80,7 @@ function [results,shift,shCoeffs,epsilon] = ita_HRTFarc_pp_process_spherical(dat
shCoeffs(:,index) = nominator\ denominatorMatrix * freqData.';
end
wb.delete
wb.close
%% reconstruction
if isempty(options.reconstructSampling)
newSampling = ita_sph_sampling_equiangular(37,72,'theta_type','[]');
......
......@@ -2,6 +2,7 @@ function varargout = ita_aurelio_control(varargin)
%ITA_AURELIO_CONTROL - Send Settings to Aurelio 2014 Frontend
% This function sends Midi Sysex Commands to the Aurelio High Precision
% Frontend (Swen Mueller, Immetro, Rio de Janiero, Brazil).
%
%
% Syntax: ita_aurelio_control(options)
% 'init' (false): set the frontend to last know values
......@@ -19,6 +20,9 @@ function varargout = ita_aurelio_control(varargin)
% Author: Pascal Dietrich - pdi@akustik.rwth-aachen.de
% reworked: Jan Richter - jri@akustik.rwth-aachen.de
% reworked: Jonas Frster - jonas.foerster@rwth-aachen.de: commands for
% CMD42 added (11/2018): for a detailed description of these commands see
% the documentation for the CMF42 (CMF42 - product description.pdf)
%% persistent
% persistent oldSettings last_input last_AmpHighPower last_Amplifier last_AmpBridgeMode last_AmpLowImpedanceMode last_Amp26dBu last_AmpAC last_AmpMono
......@@ -137,122 +141,128 @@ if sArgs.init
return
end
%% range
% if ~isempty( sArgs.inputrange )
%% 00h: coupling and feed control
% if ~isempty( sArgs.feed )
for index = 1:length(sArgs.channel)
rS = getCommandFromFeedSelect(sArgs,sArgs.channel(index),settings);
for index = 1:length(sArgs.channel)
sArgs.inputrange = min( max(settings.ch(sArgs.channel(index)).inputrange,-34) , 56);
par_number = '02';
par_value = round((- sArgs.inputrange + 56)/10); %round to nearest possible
par_value = dec2hex( bin2dec ( ['0' rS.Wait rS.Lem28 rS.Phan rS.Feed rS.ICP rS.Glift rS.AC] ));
send_sysex(rS.par_number, par_value, ita_angle2str(sArgs.channel(index)-1,2)); % send final sysex
end
% end
%% 01h: input select -- routing control -- HUHU old values needed
par_number = '01';
switch lower(settings.mode)
case 'norm' %normal mode
Mode = '0000';
case 'imp' %impedance measurement
Mode = '0001';
sArgs.feed = false;
% ChSwp = '1'; % pdi: oct 2012: for some reason
% this is not required anymore ???!!!
case {'impref','iref'}
Mode = '0010';
sArgs.feed = false;
case {'bncref','bref'}
Mode = '0100';
sArgs.input = 'gnd';
sArgs.feed = false;
case {'ampref','aref'}
Mode = '0101';
sArgs.input = 'gnd';
sArgs.feed = false;
case {'xlrref','xref','lineref'}
Mode = '0011';
sArgs.feed = false;
case 'specialref'
Mode = ['010' num2str(~ch_number) ];
sArgs.input = 'gnd';
otherwise
error('argument not correct for ''input''')
end
if ~exist('ChSwp','var')
ChSwp = '0'; %funny channel swapping for crazy people. take care, dude!
else
ita_verbose_info('Careful, channel swapping is activated.',0)
end
% get input selection
IS = getCommandFromInputSelect(settings.ch(1).inputselect);
par_value = dec2hex( bin2dec( [IS ChSwp Mode ] ) );
send_sysex(par_number, par_value, ita_angle2str(0,2));
IS = getCommandFromInputSelect(settings.ch(2).inputselect);
par_value2 = dec2hex( bin2dec( [IS ChSwp Mode ] ) );
send_sysex(par_number, par_value2, ita_angle2str(1,2)); %send to device
%% 02h: range
for index = 1:length(sArgs.channel)
sArgs.inputrange = min( max(settings.ch(sArgs.channel(index)).inputrange,-34) , 56);
par_number = '02';
par_value = round((- sArgs.inputrange + 56)/10); %round to nearest possible
% for idx = 1:numel(sArgs.channel)
% disp(idx)
% 56 - (par_value * 10)
% settings.ch(sArgs.channel(idx)).inputrange = 56 - (par_value * 10);
% encd
if sArgs.securityMode
par_value = par_value + 40;
end
par_value = ita_angle2str(par_value,2);
send_sysex(par_number, par_value, ita_angle2str(sArgs.channel(index)-1,2)); %send to device
if sArgs.securityMode
par_value = par_value + 40;
end
% end
par_value = ita_angle2str(par_value,2);
send_sysex(par_number, par_value, ita_angle2str(sArgs.channel(index)-1,2)); %send to device
end
%% input select -- routing control -- HUHU old values needed
% if ~isempty(sArgs.input)
par_number = '01';
switch lower(settings.mode)
case 'norm' %normal mode
Mode = '0000';
case 'imp' %impedance measurement
Mode = '0001';
sArgs.feed = false;
% ChSwp = '1'; % pdi: oct 2012: for some reason
% this is not required anymore ???!!!
case {'impref','iref'}
Mode = '0010';
sArgs.feed = false;
case {'bncref','bref'}
Mode = '0100';
sArgs.input = 'gnd';
sArgs.feed = false;
case {'ampref','aref'}
Mode = '0101';
sArgs.input = 'gnd';
sArgs.feed = false;
case {'xlrref','xref','lineref'}
Mode = '0011';
sArgs.feed = false;
case 'specialref'
Mode = ['010' num2str(~ch_number) ];
sArgs.input = 'gnd';
otherwise
error('argument not correct for ''input''')
end
if ~exist('ChSwp','var')
ChSwp = '0'; %funny channel swapping for crazy people. take care, dude!
else
ita_verbose_info('Careful, channel swapping is activated.',0)
end
% get input selection
IS = getCommandFromInputSelect(settings.ch(1).inputselect);
par_value = dec2hex( bin2dec( [IS ChSwp Mode ] ) );
send_sysex(par_number, par_value, ita_angle2str(0,2));
IS = getCommandFromInputSelect(settings.ch(2).inputselect);
par_value2 = dec2hex( bin2dec( [IS ChSwp Mode ] ) );
send_sysex(par_number, par_value2, ita_angle2str(1,2)); %send to device
%% 03h: sampling rate
% end
par_number = '03';
%% coupling and feed control
% if ~isempty( sArgs.feed )
for index = 1:length(sArgs.channel)
rS = getCommandFromFeedSelect(sArgs,sArgs.channel(index),settings);
if isnatural(sArgs.samplingRate / 48000)
modifier = (sArgs.samplingRate / 48000);
base_rate = 2;
elseif isnatural(sArgs.samplingRate / 44100)
modifier = sArgs.samplingRate / 44100;
base_rate = 1;
elseif isnatural(sArgs.samplingRate / 32000)
modifier = sArgs.samplingRate / 32000;
base_rate = 0;
else
error('sampling rate not supported.')
end
switch modifier
case 1
modifier = 0;
case 2