Commit 44213cb2 authored by Lukas Aspöck's avatar Lukas Aspöck

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

parents da448aa3 ba929e71
......@@ -471,6 +471,7 @@ classdef itaHRTF < itaAudio
chCoord.theta(2:2:2*props.numRecords) = thetaM;
this.mMetadata = metadata;
this.samplingRate = props.samplerate;
this.data = data;
this.mDirCoord = itaCoordinates([radius thetaM phiM],'sph');
this.channelCoordinates = chCoord;
......
......@@ -51,12 +51,15 @@ ms.twait = 0.03;
coords = ita_generateSampling_equiangular(5,5);
coords_cut = coords.n(coords.theta_deg == 90);
iMS.measurementSetup = ms;
iMS.measurementPositions = coords_cut;
iMS.waitBeforeMeasurement = 1;
saveName = 'test';
iMS.dataPath = saveName;
iMS.reference;
iMS.doSorting = 0;
iMS.run;
% always leave the turntable in reference position
......
......@@ -10,7 +10,7 @@ function result = ita_HRTFarc_postProcess(varargin)
% 'opt2' (defaultopt1) : description
% 'opt3' (defaultopt1) : description
%
% Example:
% Example:
% audioObjOut = ita_HRTF_arc_postProcess(audioObjIn)
%
% See also:
......@@ -111,7 +111,7 @@ for index = 1:numFiles
allMeasurementsRaw(index,index2) = tmp;
data_crop = ita_time_window(tmp,options.tw);% Q: options.tw = [6ms 8ms]
data_crop = ita_time_window(tmp,options.tw,'time');% Q: options.tw = [6ms 8ms]
% make end sample be div by 4 for daff export
endSample = round(options.tw(2) .* tmp(1).samplingRate)+1;
endSample = endSample + mod(endSample,4);
......
......@@ -183,7 +183,7 @@ classdef itaMotorNanotec_HRTFarc < itaMotorNanotec
sArgs = this.sArgs_default_motor;
sArgs.continuous = false;
if ~isempty(varargin)
sArgs = ita_parse_arguments(sArgs,varargin);
[sArgs,~] = ita_parse_arguments(sArgs,varargin);
end
if sArgs.continuous
ret = this.prepare_move(position, sArgs);
......
......@@ -158,7 +158,7 @@ classdef itaMotorNanotec_Turntable < itaMotorNanotec
sArgs.direct = true;
sArgs.speed = this.sArgs_default_motor.speed;
if ~isempty(varargin)
sArgs = ita_parse_arguments(sArgs,varargin);
[sArgs,~] = ita_parse_arguments(sArgs,varargin);
end
if sArgs.continuous
ret = this.prepare_move(position, 'speed', sArgs.speed,'continuous', true);
......
......@@ -111,11 +111,12 @@ classdef itaEimar < itaMeasurementTasksScan
% </ITA-Toolbox>
properties
waitForSerialPort = 0.020; % Time to wait between two commands
timeout_response = 0.1; % Time in seconds within each motor has to response to a command!
timeout_move = 300; % Time in seconds within each device has to reach their new position!
waitForSerialPort = 0.020; % Time to wait between two commands
timeout_response = 0.1; % Time in seconds within each motor has to response to a command!
timeout_move = 300; % Time in seconds within each device has to reach their new position!
failed_command_repititions = 5; % How often do we repeat commands until we throw an error?
doSorting = true; % sort the measurement coordinates for arm due to shorter measurement time
doSorting = true; % sort the measurement coordinates for arm due to shorter measurement time
armCorrectionAngle = 0; % Correct the position of the arm. Check at 90° if horizontal. Value <0 -> higher position
end
% *********************************************************************
properties (Access = protected, Hidden = true)
......@@ -924,7 +925,7 @@ classdef itaEimar < itaMeasurementTasksScan
return;
end
angle = angle - 119.01; % Larger substractive value: Higher position. Checkt at 90°.
angle = angle - 119.01 + this.armCorrectionAngle; % Larger substractive value: Higher position. Checkt at 90�.
......@@ -938,7 +939,7 @@ classdef itaEimar < itaMeasurementTasksScan
% 64. 254="Vorschubkonstantenmodus", 255=Adaptive Stepdivider
% Closed_loop = Turn on the closed loop regulation
% Acceleration_ramp = Value in Hz/ms
% Gear_ratio = Getriebeübersetzung
% Gear_ratio = Gear Ratio
% Current = Maximum current in percent
% Ramp_mode = 0=trapez, 1=sinus-ramp, 2=jerkfree-ramp
% -------------------------------------------------------------
......@@ -1000,11 +1001,11 @@ classdef itaEimar < itaMeasurementTasksScan
this.add_to_commandlist(sprintf('#%d:CL_KD_v_Z=%d\r' , this.motorID_arm, D_zaehler));
this.add_to_commandlist(sprintf('#%d:CL_KD_v_N=%d\r' , this.motorID_arm, D_nenner));
% Pos-Kreis:
% Für 10°/s
% For 10 deg/s
%P = 100;% (400 = default)
%I = 1.5;% (2 = default)
%D = 300;% (700 = default)
% Für 3°/s
% For 3 deg/s
P = 200;% (400 = default)
I = 1.0;% (2 = default)
D = 300;% (700 = default)
......
function varargout = ita_device_list_ITA(mode,token,varargin)
% ITA_DEVICE_LIST - the ITA device List
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% PLEASE BE VERY CAREFUL WHEN EDITING THE DEVICE LIST
% BY HAND, CONSULT PDI OR MMT BEFORE MAKING CHANGES
% PLEASE BE VERY CAREFUL WHEN EDITING THE DEVICE LIST BY HAND, PLEASE
% CONSULT THE ITA-TOOLBOX DEVELOPER TEAM BEFORE MAKING ANY CHANGES
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% Syntax:
% res = ita_device_list() returns all devices in a cell
......@@ -31,12 +31,6 @@ function varargout = ita_device_list_ITA(mode,token,varargin)
% </ITA-Toolbox>
% if exist('ita_device_list_ITA.m','file')
% varargout = ita_device_list_ITA(mode,token,varargin{:});
% return;
% end
if nargin == 3
hwch = varargin{1};
else
......@@ -49,7 +43,7 @@ if nargin >= 1 && isa(mode,'itaMeasurementChainElements')
MCE = mode;
devHandle = ita_device_list_handle;
list = devHandle(); %get entire list
[elementfound idx] = ismember(MCE.name,list(:,1)); %find element
[elementfound, idx] = ismember(MCE.name,list(:,1)); %find element
if elementfound
sens = list{idx,2};
% picModel = ita_model2picture(list{idx,3});
......@@ -64,7 +58,7 @@ if nargin >= 1 && isa(mode,'itaMeasurementChainElements')
% MCE.picModel = picModel;
end
else
ita_verbose_info('Element not in list',1);
ita_verbose_info(['Element not found in the device list: ', MCE.name], 1);
end
......@@ -111,29 +105,25 @@ if nargin >= 2
start_idx = strfind(token,'[');
end_idx = strfind(token,']');
if ~isempty(start_idx) && ~isempty(end_idx)
token = token(start_idx+1:end_idx-1); %get the name
name = token(start_idx+1:end_idx-1); %get the name
else
name = token;
end
name = token;
for idx = 1:size(device,1)
if strcmpi(token,device{idx,1})
if strcmpi(name,device{idx,1})
res = itaValue(device{idx,2});
break
end
end
% % etwas schoener
% idxDevice = find(strcmpi(device(:,1), token));
% if ~isempty(idxDevice)
% res = itaValue(device{idxDevice,2});
% end
start_idx = strfind(token,'(');
end_idx = strfind(token,')');
if ~isempty(start_idx) && ~isempty(end_idx)
res = itaValue(token(start_idx+1:end_idx-1));
end
if isempty(res)
res = itaValue(-1);
disp(['element not in list: ' token '.'])
ita_verbose_info(['Element not found in the device list: ', token], 1)
start_idx = strfind(token,'(');
end_idx = strfind(token,')');
if ~isempty(start_idx) && ~isempty(end_idx)
res = itaValue(token(start_idx+1:end_idx-1));
else
res = itaValue(-1);
end
end
end
end
......@@ -452,6 +442,11 @@ device(end+1,:) = { 'ITA-KK KE4 Child right','0.0098573 V/Pa','ke4',0};
device(end+1,:) = { 'Neumann-KK left','1 V/Pa','schoeps',1};
device(end+1,:) = { 'Neumann-KK right','1 V/Pa','schoeps',1};
% values are taken from calibration sheets by DKD (Deutscher
% Kalibrierdienst)
device(end+1,:) = { 'GRAS Headphone Testfixture Left','0.01211 V/Pa','RA0401',1};
device(end+1,:) = { 'GRAS Headphone Testfixture Right','0.0124 V/Pa','RA0401',1};
device(end+1,:) = { 'HEAD HMS III with ear simulator - IEC 711','0.01165 V/Pa','none',0};
device(end+1,:) = { 'Hoertnix BTE right front_hwch01','0.0159 V/Pa','none',1};
......
......@@ -348,6 +348,10 @@ classdef itaOptitrack < handle
%% DESTRUCTOR %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function destroy(Optitrack_obj)
delete(Optitrack_obj);
warning('[itaOptitrack] Method .destroy will be changed to Method .delete in future versions');
end
function delete(Optitrack_obj)
% delete Optitrack_obj (invalid handle remains -> clear('Optitrack_obj'))
if Optitrack_obj.isConnected
......
function [ ref_factor, ref_db ] = calibrate( obj, cal_time, channels, method )
% Default track_id and channel_num is 1, time can be relative
% seconds of record or absolute date
% methods: 'rms', 'maximum' or 'wideband' (default)
if nargin < 4
method = 'wideband';
end
if nargin < 3
channels = 1;
end
% Extract non-calibrated (!) signal
cal_signal = obj.extract( cal_time, channels, false );
if strcmp( method, 'maximum' )
% Variates the sample truncation and uses (jst)
max_samples = 60;
for l = 1:max_samples
calibration_signal_chunk = ita_time_crop( cal_signal, [1 cal_signal.nSamples-l], 'samples' );
power(l) = max( abs( calibration_signal_chunk.freqData ) );
end
ref_db = 20*log10( max( power ) ); % == 94 dB (Sound Calibrator Type 4231)
ref_factor = max( power );
elseif strcmp( method, 'wideband' )
% Include neighbouring frequency bins around target frequency
% of 1kHz (mgu)
neighborBins = 20;
nSamplesMaxCut = 100;
justMaxVec = zeros(nSamplesMaxCut,1);
maxAndNeighborVec = zeros(nSamplesMaxCut,1);
for iSamplesCut = 1:nSamplesMaxCut
tmpSine = cal_signal; % copy original
tmpSine.timeData = tmpSine.timeData(1:end-iSamplesCut,1); % truncate signal
[justMaxVec(iSamplesCut), idxMax] = max(abs(tmpSine.freqData));
maxAndNeighborVec(iSamplesCut) = sqrt( sum(abs(tmpSine.freqData(idxMax-neighborBins:idxMax+neighborBins)).^2) );
end
ref_db = 20*log10( max( maxAndNeighborVec ) );
ref_factor = max( maxAndNeighborVec );
else
% Use root-mean-square (ITA Toolbox RMS)
rms = cal_signal.rms;
if rms > 0
ref_db = 20*log10( rms );
else
ref_db = -Inf;
end
ref_factor = rms;
end
track_ids = obj.get_track_ids( channels );
for t = 1:numel( track_ids )
obj.tracks{ track_ids( t ) }.cal_ref_factor = ref_factor;
end
end
function disp( obj )
% Displays itaZOOMSession
% <ITA-Toolbox>
% This file is part of the ITA-Toolbox. All rights reserved.
% You can find the license for this m-file in the application folder.
% </ITA-Toolbox>
disp_str = '';
disp_str = [ disp_str sprintf( ' -- itaZOOMSession -----------\n' ) ];
disp_str = [ disp_str sprintf( '\tProject name: %s\n', obj.project_name ) ];
disp_str = [ disp_str sprintf( '\tPath: %s\n', obj.path ) ];
disp_str = [ disp_str sprintf( '\tStartdate: %s\n', datestr( obj.startdate ) ) ];
disp( disp_str )
end
function snippet = extract( obj, extract_seconds, channels, calibrated )
% Default channel_num is 1, time can be relative
% seconds of record or absolute date
if numel( extract_seconds ) ~= 2
error( 'Time for extraction has to be a 1-by-2 vector with start and end value' )
end
if ( sum( extract_seconds < 0 ) || sum( extract_seconds > obj.trackLength ) )
error( 'Requested time [ %1.1fs %1.1fs ] is not (or only partly) contained in this session with track length of %1.1fs', extract_seconds( 1 ), extract_seconds( 2 ), obj.trackLength )
end
if nargin < 4
calibrated = true;
end
if nargin < 3
channels = 1;
end
if ~obj.has_channels( channels )
error( 'Could not find tracks for requested channels %i', channels )
end
snippet = itaAudio();
for c = 1:numel( channels )
track_ids = obj.get_track_ids( channels( c ) );
ets = extract_seconds; % sliding
bFirstPart = false;
bSecondPart = false;
for i=1:numel( obj.tracks{ track_ids }.part_ids )
if obj.tracks{ track_ids }.part_ids( i ) > 0
track_part_str = sprintf( '%04d', obj.tracks{ track_ids }.part_ids( i ) );
track_base_name = [ obj.identifier '_Tr' num2str( channels ) '-' track_part_str ];
else
track_base_name = [ obj.identifier '_Tr' num2str( channels ) ];
end
track_full_path = fullfile( obj.path, [ track_base_name '.wav' ] );
track_part_meta = ita_read_wav( track_full_path, 'metadata' );
part_track_length = obj.tracks{ track_ids }.trackLength_parts( i );
if ets > track_part_meta.trackLength
ets = ets - part_track_length;
continue;
end
if ets <= track_part_meta.trackLength
if ets >= 0
% requested snippet is completely in this part.
snippet = ita_read( track_full_path, ets, 'time' );
snippet.channelUnits = obj.tracks{ track_ids }.channelUnits;
if obj.tracks{ track_ids }.cal_ref_factor ~= 1 && calibrated
snippet = ita_amplify( snippet, 1 / obj.tracks{ track_ids }.cal_ref_factor );
end
return;
end
end
% Snippet overlaps track parts
if ets( 1 ) < track_part_meta.trackLength && ets( 2 ) > track_part_meta.trackLength
% beginning if snippet is in this part.
snippet_first = ita_read( track_full_path, [ ets( 1 ) track_part_meta.trackLength ], 'time' );
bFirstPart = true;
if obj.tracks{ track_ids }.cal_ref_factor ~= 1 && calibrated
snippet_first = ita_amplify( snippet_first, 1 / obj.tracks{ track_ids }.cal_ref_factor );
end
ets = ets - part_track_length;
elseif ets( 1 ) < 0 && ets( 2 ) > 0
% end of snippet is in this part.
snippet_second = ita_read( track_full_path, [ 0 ets( 2 ) ], 'time' );
bSecondPart = true;
if obj.tracks{ track_ids }.cal_ref_factor ~= 1 && calibrated
snippet_second = ita_amplify( snippet_second, 1 / obj.tracks{ track_ids }.cal_ref_factor );
end
end
end
if bFirstPart && bSecondPart
snippet = ita_append( snippet_first, snippet_second ); % Concat time data, already calibrated
snippet.channelUnits = 'Pa';
return
end
end
error( 'Requested time snippet is not found in this session, unkown error' )
end
function [ ref_factor, ref_db ] = get_calibration_factors( obj, channels )
% Returns the calibration factor (number time data has to be
% devided) and level to be subtrated for given channels
% (or all if channels option missing)
if nargin < 2
channels = 1:obj.channels;
end
ref_factor = zeros( numel( channels ), 1 );
for c=1:channels
assert( numel( c ) == 1 );
track_id = obj.get_track_ids( c );
ref_factor( c ) = obj.tracks{ track_id }.cal_ref_factor;
end
ref_db = 20 * log10( ref_factor );
end
function [ track_ids ] = get_track_ids( obj, channels )
% Returns the tracks that correspond to a certain audio channel
% on the ZOOM device (track ids are not necessarily in the same order
% as channels and there might be less tracks than the used
% channel number (i.e. if only channel 3 has been recorded)
track_ids = [];
for t = 1:numel( obj.tracks )
if obj.tracks{ t }.channel_idx == channels
track_ids( end + 1 ) = t;
end
end
end
function [ ret ] = has_channels( obj, channels )
% Checks if given channels are available
ret = zeros( numel( channels ), 1 );
for t = 1:numel( obj.tracks )
for c = 1:numel( channels )
if obj.tracks{ t }.channel_idx == channels( c )
ret( c ) = true;
end
end
end
end
\ No newline at end of file
classdef itaZOOMSession < handle
%ITAZOOMSESSION Lightweight class around a ZOOM session (a 'ZOOM0001' folder with
%recorded tracks) that supports easier calibration and extraction of
%time data
% <ITA-Toolbox>
% This file is part of the ITA-Toolbox. All rights reserved.
% You can find the license for this m-file in the application folder.
% </ITA-Toolbox>
properties(Hidden = true, Access = private)
session_valid = false;
session_ready = false;
session_calibrated = false;
tracks = cell( 0 );
end
properties( Hidden = false, Access = public )
path = '';
subfolder = '';
project_name = 'Unnamed ZOOM session';
identifier = '';
index = 0;
startdate;
channels = 0;
trackLength = 0;
samplingRate = 0;
domain = 'time';
end
methods
function obj = itaZOOMSession( session_path )
%%itaZOOMSession Create an empty ZOOM session object. If a path
% is given as first argument, this session will be loaded, too.
if nargin == 1
obj.load( session_path );
end
end
end % methods
end % class
function load( obj, session_path )
%%LOAD load a ZOOM session (folder similar to ZOOM0001)
% This method only reads metadata like start date and the file structure.
% To get content (recorded audio data) please use extract()
obj.session_valid = false;
obj.session_ready = false;
obj.session_calibrated = false;
obj.path = session_path;
[ session_subfolder, obj.identifier, ~ ] = fileparts( obj.path );
try
[ session_folder, obj.subfolder, ~ ] = fileparts( session_subfolder );
[ ~, obj.project_name, ~ ] = fileparts( session_folder );
catch
end
if isempty( obj.identifier )
lst = dir('*.hprj' );
if numel( lst ) ~= 1
error 'Could not interpret the given path as a zoom session';
else
[ ~, obj.identifier ] = fileparts( lst.name );
end
end
if strcmpi( 'ZOOM', obj.identifier( 1:4 ) )
id_cells = textscan( obj.identifier( 5:8 ), '%d' );
obj.index = id_cells{ 1 };
end
obj.session_ready = true;
lst = dir( obj.path );
for i = 1:numel( lst )
% Skip folders
if lst( i ).isdir
continue
end
[ ~, base_name, ext ] = fileparts( lst( i ).name );
% Read start date (if coded in file name)
if numel( base_name ) == 15
if strcmpi( '.hprj', ext )
dstr = lst( i ).name( 1:end-5 );
obj.startdate = datenum( dstr, 'yymmdd-HHMMSS' );
end
end
% Read track files (raw)
if strcmpi( '.wav', ext )
if strcmpi( obj.identifier, lst( i ).name( 1:numel( obj.identifier ) ) )
% Channel number
track_channel = regexp( lst( i ).name, '_Tr(\d+)', 'tokens' );
if numel( track_channel ) ~= 1
error( 'Channel number could not be extracted from track. Reading session metadata aborted.' );
end
channel_idx = str2double( track_channel{ 1 } );
track_id = 0;
if ~obj.has_channels( channel_idx )
track_id = numel( obj.tracks ) + 1;
obj.tracks{ track_id } = struct(); % append
obj.tracks{ track_id }.channel_idx = channel_idx;
obj.tracks{ track_id }.part_ids = [];
obj.tracks{ track_id }.path = fullfile( obj.path, lst( i ).name );
meta = ita_read( obj.tracks{ track_id }.path, 'metadata' );
obj.trackLength = meta.trackLength;
obj.tracks{ track_id }.trackLength_parts( 1 ) = meta.trackLength;