Commit dffe6d84 authored by Saskia Wepner's avatar Saskia Wepner

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

parents f7009434 909a552b
......@@ -24,3 +24,5 @@ Thumbs.db
svnaccess
*.lib
*.exp
itaRaven.ini
*.daff
function [ binOut ] = ita_3da_binauralMixdown( lsSignals, varargin )
function [ binOut ] = ita_binauralMixdown( lsSignals, varargin )
%ITA_3DA_BINAURALMIXDOWN Produces a 2-Channel binaural stream out of
%loudspeaker signal(s) and coordinates (given as optional coordinates or
%better: encoded in the itaAudio channel coordinates
......
function [ audioOut ] = ita_3da_encodeAmbisonics( audioIn, order, sourcePos )
function [ audioOut ] = ita_hoa_encodeAmbisonics( audioIn, order, sourcePos )
%ITA_ENCODEAMBISONICS Encodes a itaAudio with channel coordinates or a
% itaCoordinate with source positions into ambisonics channels
%
......
......@@ -236,7 +236,9 @@ classdef itaRavenProject < handle
obj.raven_ini = IniConfig();
obj.raven_ini.ReadFile(obj.ravenIniFile);
obj.ravenExe = obj.raven_ini.GetValues('Global', 'PathRavenExe', obj.ravenExe);
obj.raven_ini.WriteFile(obj.ravenIniFile);
if (exist(obj.ravenExe,'file'))
obj.raven_ini.WriteFile(obj.ravenIniFile);
end
end
if (~exist(obj.ravenExe,'file'))
......@@ -246,23 +248,46 @@ classdef itaRavenProject < handle
% RavenConsole
locatedRavenExe = which(obj.ravenExe(10:end));
% try default raven exe path after instalation
if isempty(locatedRavenExe)
disp('[itaRaven]: No raven binary was found! Please select path to RavenConsole.exe!');
if strcmp(computer('arch'), 'win32')
defaultInstallationPathRavenExe = 'C:\ITASoftware\Raven\bin32\RavenConsole.exe';
elseif strcmp(computer('arch'), 'win64')
defaultInstallationPathRavenExe = 'C:\ITASoftware\Raven\bin64\RavenConsole64.exe';
else
error('Only Windows OS are supported.');
end
if exist(defaultInstallationPathRavenExe,'file')
locatedRavenExe = defaultInstallationPathRavenExe;
end
end
if isempty(locatedRavenExe)
disp('[itaRaven]:');
disp('No RAVEN binary was found! Please select path to the RAVEN console application (RavenConsole.exe/RavenConsole64.exe)!');
disp('To run RAVEN simulations, an installation of the RAVEN software is required. ');
disp('Individual licenses for academic purposes are available on request.');
disp('Please contact: las@akustik.rwth-aachen.de');
[ selectedRavenExe, selectedRavenPath] = uigetfile('*.exe',' No raven binary was found! Please select path to RavenConsole.exe');
obj.ravenExe = [ selectedRavenPath selectedRavenExe];
else
obj.ravenExe = locatedRavenExe;
end
if (~ravenIniExists)
obj.raven_ini = IniConfig();
% obj.raven_ini.ReadFile(obj.ravenIniFile);
obj.raven_ini.AddSections({'Global'});
obj.raven_ini.AddKeys('Global', {'PathRavenExe'}, {obj.ravenExe});
obj.raven_ini.WriteFile(obj.ravenIniFile);
else
obj.raven_ini.SetValues('Global', {'PathRavenExe'}, {obj.ravenExe});
obj.raven_ini.WriteFile(obj.ravenIniFile);
end
obj.raven_ini.WriteFile(obj.ravenIniFile);
end
......
......@@ -8,7 +8,7 @@
%% Projektdatei einlesen
% project laden
ravenProjectPath = '..\RavenInput\Classroom\Classroom.rpf';
ravenProjectPath = 'C:\ITASoftware\Raven\RavenInput\Classroom\Classroom.rpf';
if (~exist(ravenProjectPath,'file'))
[filename, pathname] = uigetfile('Classroom.rpf', 'Please select raven project file!');
......
......@@ -204,6 +204,27 @@ classdef itaVA < handle
state = VAMatlab('get_server_state', this.handle);
end
function shutdown_server(this)
% Attempts to shut down the remote server
%
% Server shutdown may be prohibited by the server configuration.
% Shutting donw the server from client side is meant for cases
% when the server is called by a script to run e.g. a simulation
% while the rendering output is recorded. The export is triggered
% when the core is finalized (or shut down).
%
% Parameters:
%
% None
%
% Return values:
%
% None
%
VAMatlab( 'call_module', this.handle, 'VACore', struct( 'shutdown', true ) );
end
function connect_tracker( this, remote_ip, local_ip )
% Connects to a local NatNet tracking server
%
......
%% itaVA offline simulation/auralization example that uses impulse responses
% Requires VA to run with a virtual audio device that can be triggered by
% the user. Also the generic path prototype rendering module(s) has to record the output
% to hard drive.
buffer_size = 64;
sampling_rate = 44100;
%% Connect and set up simple scene
va = itaVA( 'localhost' );
L = va.create_sound_receiver( 'itaVA_Listener' );
va.set_sound_receiver_position( L, [ 0 1.7 0 ] )
H = va.create_directivity( '$(DefaultHRIR)' );
va.set_sound_receiver_directivity( L, H );
S = va.create_sound_source( 'itaVA_Source' );
X = va.create_signal_source_buffer_from_file( '$(DemoSound)' );
va.set_signal_source_buffer_playback_action( X, 'play' )
va.set_signal_source_buffer_looping( X, true );
va.set_sound_source_signal_source( S, X )
%% Example for a synchronized scene update & audio processing simulation/auralization
timestep = buffer_size / sampling_rate; % here: depends on block size and sample rate
manual_clock = 0;
va.set_core_clock( 0 );
spatialstep = 0.01;
disp( [ 'Resulting sound source speed: ' num2str( spatialstep / timestep ) ' m/s' ] )
numsteps = 3400;
disp( [ 'Simulation result duration: ' num2str( numsteps * timestep ) ' s' ] )
x = linspace( -1, 1, numsteps ) * 5; % motion from x = -5m to x = 5m
h = waitbar( 0, 'Hold on, running auralization' );
for n = 1:length( x )
% Modify scene as you please (position has no real effect for prototype generic path renderer)
pos = [ x( n ) 1.7 -1 ];
distance = sum( abs( pos - [ 0 1.7 0 ] ) );
va.set_sound_source_position( S, pos );
brir = itaAudio();
brir.timeData = zeros( 1024, 2 );
brir.timeData( 1, 1 ) = 1.0 / distance;
brir.timeData( 1, 2 ) = 0.5 / distance; % just make inbalanced and distance dependent for fun
path_update = struct();
path_update.source = S;
path_update.receiver = L;
path_update.ch1 = brir.timeData( :, 1 );
path_update.ch2 = brir.timeData( :, 2 );
path_update.delay = distance / va.get_homogeneous_medium_sound_speed(); % will generate smooth Doppler even for fast motion
va.set_rendering_module_parameters( 'MyGenericRenderer', path_update );
% Increment core clock
manual_clock = manual_clock + timestep;
va.call_module( 'manualclock', struct( 'time', manual_clock ) );
% Process audio chain by incrementing one block
va.call_module( 'virtualaudiodevice', struct( 'trigger', true ) );
waitbar( n / numsteps )
end
close( h )
va.disconnect
disp( 'Stop VA to export simulation results from rendering module(s)' )
......@@ -7,7 +7,7 @@ va = itaVA( 'localhost' );
% Create a sound receiver
L = va.create_sound_receiver( 'itaVA_Tracked_Listener' );
% OptiTrack tracker connection and sound receiver updates
% OptiTrack tracker conneection and sound receiver updates
va.set_tracked_sound_receiver( L ) % For virtual scene / rendering
va.set_tracked_real_world_sound_receiver( L ) % For CTC reproductions
va.connect_tracker
......
......@@ -95,8 +95,7 @@ end
current_natnet_dir = which( 'NatNetML.dll' );
if ~isempty( current_natnet_dir )
ainfo = NET.addAssembly( current_natnet_dir );
client = NatNetML.NatNetClientML( 0 );
natnetversion_raw = client.NatNetVersion;
natnetversion_raw = NatNetML.NatNetClientML( 0 ).NatNetVersion();
vs = sprintf( 'NatNetML (OptiTrack) %d.%d', natnetversion_raw(1), natnetversion_raw(2) );
set( handles.edit_natnet_version, 'String', vs )
end
......
classdef itaFiniteWedge < itaInfiniteWedge
properties (Access = protected)
al % aperture length
sp % start point of aperture
ep % end point of aperture
end
properties (Dependent)
length
aperture_start_point
aperture_end_point
end
methods
function obj = itaFiniteWedge( main_face_normal, opposite_face_normal, location, length, wedge_type )
% Creates an finite wedge with a location in 3-dim space
% Starting point of wedge aperture is the wedge location, end
% point is defined by length and direction of aperture
% Length must be a scalar value greater zero
if nargin < 5
wedge_type = 'wedge';
end
obj@itaInfiniteWedge( main_face_normal, opposite_face_normal, location, wedge_type );
if numel( length ) > 1 || length <= 0
error 'Length must be a scalar value greater zero'
end
obj.al = length;
obj.sp = obj.l;
n_scaled = cross( obj.main_face_normal, obj.opposite_face_normal );
if ~norm( n_scaled )
warning 'aperture enp point could not be determined since aperture direction is not defined. Please set aperture direction manually.'
else
obj.ep = obj.sp + length * obj.aperture_direction;
end
end
function obj = set.aperture_end_point( obj, length_of_aperture )
% Sets aperture direction manually (in case of linear
% dependent normals)
if norm( cross( obj.n1, obj.n2 ) )
error 'Aperture is already fixed and cannot be modified'
end
if length_of_aperture <= 0
error 'Aperture length must be a valid scalar > 0'
end
obj.ep = length_of_aperture;
end
function l = get.length( obj )
l = obj.al;
end
function sp = get.aperture_start_point( obj )
% 3-dim starting point of finite aperture
sp = obj.sp;
end
function ep = get.aperture_end_point( obj )
% 3-dim end point of finite aperture
ep = obj.ep;
end
end
end
function direction = longer_branch_on_aperture( obj, apex_point )
%Returns the direction vector of the longer one of two sections on the aperture devided
%by the aperture point
% direction: vector(normalized) pointing in direction of longer section from given aperture
% point
d1 = norm( obj.aperture_start_point - apex_point );
d2 = norm( obj.aperture_end_point - apex_point );
if d2 >= d1
direction = obj.aperture_direction;
else
direction = -obj.aperture_direction;
end
end
function b = point_on_aperture( obj, point )
% Returns true if point is on aperture of the wedge and between start and
% end point of the aperture
dim = size( point );
if dim(1) ~= 3 && dim(2) ~= 3
error( 'Point must be of dimension 3' );
end
b = zeros( numel(point) / 3, 1 );
norms = sqrt( sum( (point - obj.location).^2, 2 ) );
condition1 = norms < obj.set_get_geo_eps;
if any( condition1 )
b = b | condition1;
end
dir1 = ( point - obj.location ) ./ norms;
dir2 = ( point - obj.aperture_end_point ) ./ sqrt( sum( (point - obj.aperture_end_point).^2, 2 ) );
dir1_norms = sqrt( sum( (dir1 - obj.aperture_direction).^2, 2 ) );
dir2_norms = sqrt( sum( (dir2 + obj.aperture_direction).^2, 2 ) );
condition2 = (dir1_norms < obj.set_get_geo_eps | dir2_norms < obj.set_get_geo_eps) & norms < obj.length;
if any( condition2 )
b = b | condition2;
end
b = all(b);
end
function b = point_outside_wedge( obj, point )
% Returns true if the point is outside the solid structure of
% the finite wedge
dim = size( point );
if dim(2) ~= 3
if dim(1) ~= 3
error( 'Point(s) must be of dimension 3')
end
point = point';
dim = size( point );
end
dist_from_main_face = sum( (point - obj.location) .* obj.main_face_normal, 2 );
dist_from_opposite_face = sum( (point - obj.location) .* obj.opposite_face_normal, 2 );
b = zeros( dim(1), 1 );
mask = ( dist_from_main_face < -obj.set_get_geo_eps ) & ( dist_from_opposite_face < -obj.set_get_geo_eps );
b(~mask) = true;
end
function alpha = get_angle_from_point_to_aperture( obj, field_point, point_on_aperture )
%Returns angle (radiant) between the ray from field point to aperture point and the
%aperture of the wedge.
% output angle alpha: 0 <= alpha <= pi/2
%% assertions
dim_fp = size(field_point);
dim_pa = size(point_on_aperture);
if dim_fp(2) ~= 3
if dim_fp(1) ~= 3
error( 'Field point must be a row vector of dimension 3' );
end
field_point = field_point';
dim_fp = size(field_point);
end
if dim_pa(2) ~= 3
if dim_pa(1) ~= 3
error( 'Point on Aperture must be a row vector of dimension 3.' );
end
point_on_aperture = point_on_aperture';
dim_pa = size(point_on_aperture);
end
if dim_fp(1) ~= 1 && dim_pa(1) ~= 1
if dim_fp(1) ~= dim_pa(1)
error( 'Use same number of field points and points on aperture' );
end
end
if any( ~obj.point_outside_wedge( field_point ) )
error( 'Field point must be outside wedge' );
end
if any( ~obj.point_on_aperture( point_on_aperture ) )
error( 'No point on aperture found' )
end
%% begin
norms = sqrt( sum( (point_on_aperture - field_point).^2, 2 ) );
dir_vec = ( point_on_aperture - field_point ) ./ norms;
alpha = acos( sum( dir_vec .* obj.aperture_direction, 2 ) );
mask = alpha > pi/2;
if any(mask)
alpha(mask) = pi - alpha(mask);
end
end
function theta = get_angle_from_point_to_wedge_face( obj, point_position, reference_face )
%GET_ANGLE_FROM_SOURCE_TO_WEDGE_FACE Returns angle (radiant) between
% given point and a wedge face.
% input: point_pos arbitrary field point outside the wedge
% reference_face reference wedge face which the angle will
% be denoted from
% output: theta azimuth angle (radiant) in cylinder coordinates with
% the aperture as z axis
%% Assertions
dim = size( point_position );
if dim(2) ~= 3
if dim(1) ~= 3
error( 'Field points have to be of dimension 3' );
end
point_position = point_position';
dim = size( point_position );
end
% if any( (reference_face ~= 'main face') & (reference_face ~= 'opposite face') )
% error( 'Reference face must be either main face or opposite face. You may also use itaInfiniteWedge.get_source_facing_side().' )
% end
if any( ~obj.point_outside_wedge( point_position ) )
error('Point(s) must be outside the wedge');
end
%% Begin
e_z = repmat( obj.aperture_direction, dim(1), 1 );
e_y = zeros( dim(1), 3 );
e_y( reference_face, : ) = repmat( obj.main_face_normal, sum( reference_face ), 1 );
e_y( ~reference_face, : ) = repmat( obj.opposite_face_normal, sum( ~reference_face ), 1 );
e_x = cross( e_z, e_y, 2 );
% Calculate angle between incedent ray from source to aperture point and source facing wedge
% side
x_i = dot( point_position - obj.location, e_x, 2 ); % coordinates in new coordinate system
y_i = dot( point_position - obj.location, e_y, 2 );
theta = atan2( y_i, x_i );
theta( theta < 0 ) = theta( theta < 0 ) + 2*pi;
end
function ap = get_aperture_point( obj, source_pos, receiver_pos )
% Returns aperture point on wedge (closest point on wedge
% between source and receiver)
%% Verification
dim_src = size( source_pos );
dim_rcv = size( receiver_pos );
if dim_src(2) ~= 3
if dim_src(1) ~= 3
error( 'Source point(s) must be of dimension 3')
end
source_pos = source_pos';
dim_src = size( source_pos );
end
if dim_rcv(2) ~= 3
if dim_rcv(1) ~= 3
error( 'Receiver point(s) must be of dimension 3')
end
receiver_pos = receiver_pos';
dim_rcv = size( receiver_pos );
end
if dim_src(1) ~= 1 && dim_rcv(1) ~= 1 && dim_src(1) ~= dim_rcv(1)
error( 'Number of receiver and source positions do not match' )
end
if dim_src(1) > dim_rcv(1)
dim_n = dim_src(1);
S = source_pos;
R = repmat( receiver_pos, dim_n, 1 );
elseif dim_src(1) < dim_rcv(1)
dim_n = dim_rcv(1);
S = repmat( source_pos, dim_n, 1 );
R = receiver_pos;
else
dim_n = dim_src(1);
S = source_pos;
R = receiver_pos;
end
%% Variables
L = repmat( obj.location, dim_n, 1 );
Apex_Dir = repmat( obj.aperture_direction, dim_n, 1 );
%% Calculations
SR = R - S;
norm_of_SR = Norm( SR );
mask = norm_of_SR ~= 0;
SR_dir = SR(mask, :) ./ norm_of_SR(mask);
% initialize result vector
ap = zeros( dim_n, 3 );
% Auxilary plane spanned by SR and aux_plane_dir
aux_plane_dir = cross( SR_dir, Apex_Dir(mask, :), 2 ) ./ Norm( cross( SR_dir, Apex_Dir(mask, :), 2 ) );
aux_plane_normal = cross( SR_dir, aux_plane_dir, 2 ) ./ Norm( cross( SR_dir, aux_plane_dir, 2 ) );
% Distance of intersection of auxiliary plane and aperture direction
% from aperture location
% aux plane: dot( (x - source_point), aux_plane_normal) = 0
% aperture line: x = location + dist * aperture_direction
dist = dot( S(mask, :) - L(mask, :), aux_plane_normal, 2 ) ./ dot( Apex_Dir(mask, :), aux_plane_normal, 2 );
ap(mask, :) = L(mask, :) + dist .* Apex_Dir(mask, :);
% In case receiver and source have same position
if any( norm_of_SR == 0 )
dist = dot( R(~mask, :) - L(~mask, :), Apex_Dir(~mask, :), 2 );
ap(~mask, :) = L(~mask, :) + dist * Apex_Dir(~mask, :);
end
end
function res = Norm( A )
res = sqrt( sum( A.^2, 2 ) );
end
\ No newline at end of file
classdef itaInfiniteWedge
properties (Access = protected)
n1 % 3-dim normal vector of main face (internal)
n2 % 3-dim normal vector of opposite face (internal)
ad % 3-dim aperture direction vector (internal)
l % Internal location variable
wt % type of wedge (internal)
bc_hard % Internal boundary condition (hard = true)
end
properties (Dependent)
main_face_normal % 3-dim normal vector of main face (normalized)
opposite_face_normal % 3-dim normal vector of opposite face (normalized)
aperture_direction % 3-dim normal vector of aperture direction (normalized)
location % Location of wedge (somewhere along aperture)
opening_angle % Angle from main to opposite face in propagation medium / air (radiants)
wedge_angle % Angle from main to opposite face in solid medium of wedge (radiants)
wedge_type % 'wedge' for opening angles > pi or 'corner' for opening angles < pi
boundary_condition % boundary condition of the wedge faces (hard or soft)
end
methods
function obj = itaInfiniteWedge( main_face_normal, opposite_face_normal, location, wedge_type )
% Create a wedge by a main face normal and an opposite face
% normal
% main_face_normal: Main face normal (3-dim)
% opposite_face_normal: Opposite face normal (3-dim)
% location: Point on aperture which defines
% location of the wedge in 3_dim sapce
% wedge_type: use 'wedge' for opening angles > pi (default) and
% 'corner' for opening angles < pi
% Note: 3-dim direction vectors will be normalized automatically
%
if nargin < 4
wedge_type = 'wedge';
end
if ~isequal( wedge_type, 'wedge' ) && ~isequal( wedge_type, 'corner' )
error( 'Invalid wedge type. Use either wedge or corner' )
end
if numel( main_face_normal ) ~= 3
error 'Main face normal has to be a 3-dim vector'
end
if numel( opposite_face_normal ) ~= 3
error 'Opposite face normal has to be a 3-dim vector'
end
if numel(location) ~= 3
error( 'Location must be of dimension 3')
end
obj.n1 = main_face_normal;
obj.n2 = opposite_face_normal;
obj.l = location;
obj.wt = wedge_type;
obj.bc_hard = true;
if ~obj.validate_normals
warning 'Normalized face normals'
obj.n1 = main_face_normal ./ norm( main_face_normal );
obj.n2 = opposite_face_normal ./ norm( opposite_face_normal );
end
n_scaled = cross( obj.main_face_normal, obj.opposite_face_normal );
if ~norm( n_scaled )
warning 'Normals are linear dependent and aperture direction could not be determined. Please set aperture direction manually.'
else
obj.ad = n_scaled ./ norm( n_scaled );
end
end
function n = get.main_face_normal( obj )
n = obj.n1;
end
function n = get.opposite_face_normal( obj )
n = obj.n2;
end
function n = get.aperture_direction( obj )
% Returns normalized direction of aperture. Vectors main face normal, opposite face normal and aperture direction
% form a clockwise system.
if isempty( obj.ad )
error 'Invalid wedge, aperture direction not set and face normals are linear dependent'
end
n = obj.ad;
end
function obj = set.aperture_direction( obj, aperture_direction )
% Sets aperture direction manually (in case of linear
% dependent normals)
if norm( cross( obj.n1, obj.n2 ) )
error 'Aperture of linear independent normals is fixed can not be modified'
end
if ~norm( aperture_direction )
error 'Aperture vector must be a valid direction'
end
if norm( aperture_direction ) ~= 1
warning ' Normalizing aperture direction'
aperture_direction = aperture_direction / norm( aperture_direction );
end
if ~( dot( aperture_direction, obj.n1 ) == 0 && dot( aperture_direction, obj.n2 ) == 0 )
error 'Invalid aperture direction, vector must be perpendicular to face normals'
end
obj.ad = aperture_direction;
end
function beta = get.wedge_angle( obj )
% Returns angle from main to opposite face through solid medium
% of the wedge (radiant)
if isequal( obj.wt, 'wedge' )
s = 1;
elseif isequal( obj.wt, 'corner' )
s = -1;
end
beta = pi - s * acos(dot(obj.main_face_normal, obj.opposite_face_normal));
end
function beta_deg = wedge_angle_deg( obj )
% Get the wedge angle angle in degree
beta_deg = rad2deg( obj.wedge_angle );
end
function theta = get.opening_angle( obj )
% Returns angle from main face to opposite face through propagation medium /
% air (radiant)
theta = 2 * pi - obj.wedge_angle;
end
function theta_deg = opening_angle_deg( obj )
% Get the wedge opening angle in degree
theta_deg = rad2deg( obj.opening_angle );
end
function l = get.location( obj )
l = obj.l;
end