itaVA_tutorial.m 12.8 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
%% Short tutorial for itaVA (Virtual Acoustics (VA) Matlab client)
% covers basic operations for setting up a simple example of a virtual scene
% (binaural synthesis for 1 listener, 1 static / 1 moving virtual sound source)
% as well as synchronized playback of multiple virtual sound sources
%
% NOTE: The user needs a running version of VAServer.exe / VAGUI.exe (+ dependencies)
% ( please contact {jst, fpa}@akustik.rwth-aachen.de ).
% Set up the (absolute) path of the VA deploy dir (see below).
% Configuration settings are based on VACore.ini / VAGUI.ini files (located in conf directory).
% Additionally, it is necessary to select the correct audio driver backend 
% (e.g. Portaudio, ASIO4All)
%
% Explore itaVA by typing "doc itaVA" in Matlab command window
%
% Author:  Florian Pausch, fpa@akustik.rwth-aachen.de
% Version: 2015-06-24 (compatible with VA.2016-03-23 (and probably higher))
17
% Revision: Ernesto Accolti 2018-02-20 (update for VA.2018a_preview)
18
19
20

%% Step 1: Initializations
% Select VA environment
21
VAsel = 0;  % 0: start VAServer.exe (without GUI for visualization of virtual environment), 
22
23
24
25
            % 1: start VAGUI.exe (with visualization of virtual environment)
            % HINT: Press Ctrl+A in VAGUI window to show and arrange all VAGUI windows conveniently
            
% Select location of *.wav/*.daff/*.ini files (use absolute paths instead of relative paths)
26
27
deployDir = uigetdir(pwd,'Select location of *.wav/*.daff/*.ini files.Root directory of VA deploy (with folders bin, conf, data)'); % 'D:\VA_deploy\VA.2016-03-23'; % root directory of VA deploy (with folders bin, conf, data)

28
29
30
31
32
33
34
35
36
37
38
39
40
41

% Do you use Natural Point's Optitrack tracking system?
useTracker = false;

if VAsel==0
    % start VAServer.exe (in Server mode) if not running already
    [~,searchresult]=system('tasklist /FI "imagename eq VAServer.exe" /fo table /nh');
    if ~strcmp(strtrim(searchresult(1:13)),'VAServer.exe')
        % input parameters: deployDir\VAServer.exe localhost:12340 deployDir\VACore.ini &
        system([fullfile(deployDir,'\bin\VAServer.exe localhost:12340 '),fullfile(deployDir,'\conf\VACore.ini &')]);
        pause(1) % start-up may take some time on old PC's, pause() to avoid errors
    end
else
    % start VAGUI.exe if not running already
42
43
    [~,searchresult]=system('tasklist /FI "imagename eq Redstart.exe" /fo table /nh');
    if ~strcmp(strtrim(searchresult(1:10)),'Redstart.exe')
44
        % input parameters: deployDir\VAGUI.exe deployDir\VACore.ini deployDir\VAGUI.ini &
45
        system([fullfile(deployDir,'\bin\Redstart.exe '),fullfile(deployDir,'\conf\VACore.ini '),fullfile(deployDir,'\conf\VAGUI.ini &')]);
46
47
48
        pause(1) % start-up may take some time on old PC's, pause() to avoid error
    end
end
49
50
 
%% Step 1.5: Create or select a MyBinauralHeadphoneSession and Press start button in Redstart VA GUI
51
52
53
54
55

%% Step 2: Create itaVA object and connect to VAServer
a = itaVA;

% Connect to VAServer (must be running and listening to default port on localhost)
56
if ~a.get_connected % only connect if no connection to server is established
57
58
59
60
61
62
63
64
65
    a.connect('localhost')
end

% Reset VA and clear the scene
a.reset()


%% Step 3: Set global output gain (optionally set reproduction module)
% set global gain of VA output
66
a.set_output_gain(0.3); % value between 0 (-inf dB) and 1 (0 dB) 
67
68

% % query available reproduction modules (cf. VACore.ini)
69
% modules = a.get_modules;
70
71
72
73
74
75
% 
% Example 1: set reproduction module for binaural synthesis, e.g. 'hprep' for a
% binaural synthesis played back over headphones and set HPEQ file
% command_struct = struct();
% command_struct.hpirinv = '$(VADataDir)/HPEQ/HD600_all_eq_128_stereo.wav';
% command_struct.gain = 0.1;
76
% a.call_module( 'Headphones:MyHD600', command_struct )
77
78
79
80
81
82

% Example 2: listener dumping for binaural freefield renderer
% command_struct = struct();
% command_struct.command = 'STARTDUMPLISTENERS';
% command_struct.gain = .1;
% command_struct.FilenameFormat = 'ListenerFuerMeckingjay$(ListenerID).wav';
83
% a.call_module( 'BinauralFreefield:MyBinauralFreefield', command_struct )
84
% command_struct.command = 'STOPDUMPLISTENERS';
85
% a.call_module( 'BinauralFreefield:MyBinauralFreefield', command_struct )
86
87
88
89
90


%% Step 4: Create a listener and assign a HRIR set
% load HRTF set stored in VADataDir\HRIR

91
HRIRSet = a.create_directivity(fullfile(deployDir,'data\ITA_Artificial_Head_5x5_44kHz_128.v17.ir.daff'));
92

93
% HRIRSet = a.create_directivity('$(VADataDir)\HRIR\ITA-Kunstkopf_HRIR_AP11_Pressure_Equalized_3x3_256.daff'); 
94
95

% create a listener and assign the HRTF set
96
97
98
L       = a.create_sound_receiver('Listener'); % input parameters: (displayed) name (in VAGUI) / auralization mode / ID of HRIR data set
a.set_sound_receiver_directivity( L, HRIRSet );

99
100
101
102
103
104
105
106
107
108
109
110
111
LHeight = 1.2; % height of listener's interaural axis [m]

% set position/orientation of listener L in virtual world in VA world coordinates / openGL coordinates [m]:
%       center of coordinate system: (x,y,z)=(0,0,0), right-handed coordinate system
%       listener is looking into -z direction (default) 
%       (positive) offset to the right  -> +x
%       (positive) offset in height     -> +y
%
% use a.setListenerPositionOrientationYPR() to specify 
% the listener's orientation in the virtual world in yaw-pitch-roll coordinates, or
% a.setListenerPositionOrientationVelocityVU() to specify 
% the listener's orientation in the virtual world by view-up vectors

112
113
114
a.set_sound_receiver_position(L, [0 LHeight 0])
a.set_sound_receiver_orientation(L, eul2quatb([0,0,0])) % the midpoint of the listener's interaural axis is now at a height of LHeight metres, no additional lateral offset

115
116
117
118
119
120
121
122
123
124
125
126
127
%                                                                the listener is facing into -z direction

% Infos for orientation commands:
% A positive rotation is always applied clockwise wrt. the respective axis [deg]
% Yaw:   rotation wrt. y axis, turning
% Pitch: rotation wrt. x axis, nodding
% Roll:  rotation wrt. -z axis, tilting 

% NOTE: When using CTC reproduction module, additionally set the real-world
%       position and orientation of the listener (wrt. loudspeaker positions) 
%       by using the command setListenerRealWorldHeadPositionOrientationVU()

% activate the listener, i.e. listen to the existing virtual sound sources
128
a.set_active_sound_receiver(L)
129
130

if useTracker
131
132
    a.set_tracked_sound_receiver(L)
    a.connect_tracker()
133
134
end

135
LPosTracked = a.get_sound_receiver_position(L);
136
137
138
139
140
LHeightTracked = LPosTracked(2);


%%  Step 5: Create a static virtual sound source:
%   S1: static sound source at defined position
141
142
143
144
S1 = a.create_sound_source('Source 1');        % name of the sound source as string

a.set_sound_source_position(S1,[-2 LHeightTracked 0])
a.set_sound_source_orientation(S1,eul2quatb([0,0,-90])) % eul2quatb([0,0,-90])
145
146
147
148
149
150

                                             % the virtual sound source is now positioned 
                                             % on the left side of the listener, 
                                             % at a height of LHeightTracked metres, 
                                             % at a distance of 2 metres relative to the midpoint of the interaural axis
                                             % and facing in +x direction
151
152

%                                            
153
% Create an audiofile signal source for the sound source (based on a mono wave file)
154
155
156
X1      = a.create_signal_source_buffer_from_file(fullfile(deployDir,'\data\WelcomeToVA.wav'));
% XX1     = a.loadSound(fullfile(deployDir,'\data\WelcomeToVA.wav')); % load wave file to get additional information (nsamples, duration, ...)
% XX1Info = a.getSoundInfo(XX1); % get info about signal source, i.e. wave file
157
158

% ...and link the signal source to the sound source
159
a.set_sound_source_signal_source(S1,X1)
160
161

% optionally set volume of sound source
162
a.set_sound_source_sound_power(S1,0.05); % value between 0 (-inf dB) and 1 (0 dB) 
163
164

% set playback state of audiofile signal source 
165
166
a.set_signal_source_buffer_looping( X1, true );         % looping yes/no?
a.set_signal_source_buffer_playback_action( X1, 'play' ) % e.g. plays the audiofile signal source
167
% listen to the virtual scene for the length of the audiofile signal source
168
169
170
% java.util.concurrent.locks.LockSupport.parkNanos(XX1Info.duration*10^9);
pause(5)
a.set_signal_source_buffer_playback_action( X1, 'stop' ) % stop playback
171
172
173
174
175


%%  Step 6: Create a moving virtual sound source (with directivity):
%   S2: moving virtual sound source (on a pre-defined trajectory)

176
S2  = a.create_sound_source('Source 2');    % name of the sound source as string
177
178

% Create an audiofile signal source for the sound source (based on a mono wave file)
179
180
181
X2  = a.create_signal_source_buffer_from_file(fullfile(deployDir,'\data\WelcomeToVA.wav'));
% XX2 = a.loadSound(fullfile(deployDir,'\data\Audiofiles\lang_short.wav')); % load wave file to get additional information (nsamples, duration)
% XX2Info = a.getSoundInfo(XX2); % get info about signal source, i.e. wave file
182
183

% ...and link the signal source to the sound source
184
a.set_sound_source_signal_source(S2,X2);
185
186

% load a directivity file in *.daff file format (e.g. directivity of a trumpet)
187
DirS2 = a.create_directivity(fullfile(deployDir,'\data\Singer.v17.ms.daff'));
188
% set directivity of S2
189
a.set_sound_source_directivity(S2,DirS2);
190
191
192
193
194
195
196
197
198

% define a simple trajectory: the virtual sound source S2 shall move on a
% circle on the horizontal plane with constant radius from phi=(3*pi/2):(pi/2) (counter-clockwise rotation)
% Note: for the definition of phi/theta, the local coordinate system of the
%       listener is used (view/up direction: -z/y axis), i.e. phi=0 is in look
%       direction of the listener and increases counterclockwise

circleR     = 2;        % radius of trajectory [m]
nlegs       = 200;      % number of equidistant trajectory legs
199
Tvel         = 10;        % time to pass nlegs points
200
phi_start   = pi/4;     % start azimuth angle in [rad]
201
202
phi_end     = 9*pi/4;   % end azimuth angle in [rad]
theta = pi/2;     % zenith angle in [rad]
203

204
phi = linspace(phi_start,phi_end,nlegs);
205

206
207
208
traj_cart(:,1) = circleR*sin(phi); 
traj_cart(:,2) = circleR*cos(phi);
traj_cart(:,3) = LHeight + circleR*cos(theta);
209
210

% set initial position of S2 (use first position of trajectory) and default orientation
211
212
213
214
215
216
a.set_sound_source_position(S2, [traj_cart(1,1) traj_cart(1,2) traj_cart(1,3)]);
a.set_sound_source_orientation(S2,eul2quatb([0,0,0]));
% a.setAudiofileSignalSourceIsLooping(X1,true) % looping yes/no?
a.set_signal_source_buffer_looping( X2, true );         % looping yes/no?

% a.set_sound_source_signal_source(S2,X2)
217
218

% set period of high-precision timer [s] (for precise position updates in the following update loop)
219
a.set_timer(Tvel/nlegs); 
220
221
for idx = 1:nlegs
    if idx==1 % start playback during first loop cycle
222
       a.set_signal_source_buffer_playback_action(X2, 'play')
223
224
225
    end
    
    % wait for a signal of the high-precision timer
226
    a.wait_for_timer();
227
    
228
229
230
    % update source position and view/up direction of S2 (virtual sound source always points at listener)
    a.set_sound_source_position(S2, [traj_cart(idx,1), traj_cart(idx,2), traj_cart(idx,3)]);
   
231
    if idx==nlegs % optionally: stop playback during last loop cycle
232
       a.set_signal_source_buffer_playback_action(X2, 'STOP') 
233
234
235
236
237
238
239
240
241
    end
end


%% Step 7: Use synchronized scene actions
%  Set setAudiofileSignalSourcePlaybackAction for the two existing sources
%  simultaneously (Note: no spatial separation if same AudiofileSignalSource is used 
%  for both SoundSources) 

242
% everything between .lock_update and .unlock_update will be triggered in
243
% one cycle to allow for synchronized scene events
244
245
246
247
248
a.lock_update;
a.set_signal_source_buffer_playback_action(X1, 'PLAY')
a.set_signal_source_buffer_playback_action(X2, 'PLAY')
a.unlock_update;
%%
249
% wait until longer AudiofileSignalSource is played back completely
250
java.util.concurrent.locks.LockSupport.parkNanos(max(5,Tvel)*10^9); 
251

252
253
254
255
a.lock_update;
a.set_signal_source_buffer_playback_action(X1, 'STOP')
a.set_signal_source_buffer_playback_action(X2, 'STOP')
a.unlock_update;
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281

% % test timer accuracy
% nnlegs = 1000;
% a.setTimer(1/nnlegs); %synced 1kHz update rate for scene modification
% timestamps = zeros(nnlegs,1);
% for idx=1:nnlegs
%     a.waitForTimer(); % wait residual time
%     a.lockScene();
%     timestamps(idx) = a.getCoreClock(); % Modification entry time
%     
%     % do something synchronous in your scene
%     
%     a.unlockScene();
% end
% 
% hist(timestamps(2:end)-timestamps(1:end-1),nnlegs-1)
% grid on
% title('Timer accuracy')
% xlabel('Difference of time stamps in loop [s]')
% ylabel('Number of occurences')


%% Step 8: Clean up (optionally) + disconnect from VAServer
java.util.concurrent.locks.LockSupport.parkNanos(3*10^9); % wait 3s before scene is cleared

% % delete sound sources
282
283
284
% a.delete_sound_source(S1);
% a.delete_sound_source(S2);

285
% % delete listener
286
% a.delete_sound_receiver(L);
287
288
289
290
291
292

% reset VA and clear the scene
a.reset()

if useTracker
    % disconnect from tracker
293
    a.disconnect_tracker
294
295
296
end

% disconnect itaVA object from server
297
a.disconnect
298
299