itaVA_tutorial.m 12.7 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
            % 1: start Redstart.exe (with graphical user interface)

deployDir = uigetdir(pwd,'Select VA root directory (with folders bin, conf, data)');
25

26
27
28
29
30
31
32
33
34
35
36
37
38
39

% 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
40
41
    [~,searchresult]=system('tasklist /FI "imagename eq Redstart.exe" /fo table /nh');
    if ~strcmp(strtrim(searchresult(1:10)),'Redstart.exe')
42
        % input parameters: deployDir\VAGUI.exe deployDir\VACore.ini deployDir\VAGUI.ini &
43
        system([fullfile(deployDir,'\bin\Redstart.exe '),fullfile(deployDir,'\conf\VACore.ini '),fullfile(deployDir,'\conf\VAGUI.ini &')]);
44
45
46
        pause(1) % start-up may take some time on old PC's, pause() to avoid error
    end
end
47
48
 
%% Step 1.5: Create or select a MyBinauralHeadphoneSession and Press start button in Redstart VA GUI
49
50
51
52
53

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

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

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

61
62
63
64
% Add the common data dir where to find relative file paths
a.add_search_path( fullfile( deployDir, 'data' ) );
% a.add_search_path( 'D:/my/data' ) % Add your data folder(s) accordingly,
% avoid absolute paths in VA calls!
65
66
67

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

% % query available reproduction modules (cf. VACore.ini)
71
% modules = a.get_modules;
72
73
74
75
76
77
% 
% 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;
78
% a.call_module( 'Headphones:MyHD600', command_struct )
79
80
81
82
83
84

% 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';
85
% a.call_module( 'BinauralFreefield:MyBinauralFreefield', command_struct )
86
% command_struct.command = 'STOPDUMPLISTENERS';
87
% a.call_module( 'BinauralFreefield:MyBinauralFreefield', command_struct )
88
89
90
91
92


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

93
HRIRSet = a.create_directivity( 'ITA_Artificial_Head_5x5_44kHz_128.v17.ir.daff' ); % $(DefaultHRIR) macro would work, too
94

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

% create a listener and assign the HRTF set
98
99
100
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 );

101
102
103
104
105
106
107
108
109
110
111
112
113
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

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

117
118
119
120
121
122
123
124
125
126
127
128
129
%                                                                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
130
a.set_active_sound_receiver(L)
131
132

if useTracker
133
134
    a.set_tracked_sound_receiver(L)
    a.connect_tracker()
135
136
end

137
LPosTracked = a.get_sound_receiver_position(L);
138
139
140
141
142
LHeightTracked = LPosTracked(2);


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

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

                                             % 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
153
154

%                                            
155
% Create an audiofile signal source for the sound source (based on a mono wave file)
156
X1      = a.create_signal_source_buffer_from_file( 'WelcomeToVA.wav' ); % Macro $(DemoSound) would also work here
157
158
% 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
159
160

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

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

% set playback state of audiofile signal source 
167
168
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
169
% listen to the virtual scene for the length of the audiofile signal source
170
171
172
% java.util.concurrent.locks.LockSupport.parkNanos(XX1Info.duration*10^9);
pause(5)
a.set_signal_source_buffer_playback_action( X1, 'stop' ) % stop playback
173
174
175
176
177


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

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

% Create an audiofile signal source for the sound source (based on a mono wave file)
181
X2  = a.create_signal_source_buffer_from_file( 'WelcomeToVA.wav' );
182
183
% 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
184
185

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

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

% 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
201
Tvel         = 10;        % time to pass nlegs points
202
phi_start   = pi/4;     % start azimuth angle in [rad]
203
204
phi_end     = 9*pi/4;   % end azimuth angle in [rad]
theta = pi/2;     % zenith angle in [rad]
205

206
phi = linspace(phi_start,phi_end,nlegs);
207

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

% set initial position of S2 (use first position of trajectory) and default orientation
213
a.set_sound_source_position(S2, [traj_cart(1,1) traj_cart(1,2) traj_cart(1,3)]);
214
a.set_sound_source_orientation(S2,eul2quat([0,0,0]));
215
216
217
218
% 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)
219
220

% set period of high-precision timer [s] (for precise position updates in the following update loop)
221
a.set_timer(Tvel/nlegs); 
222
223
for idx = 1:nlegs
    if idx==1 % start playback during first loop cycle
224
       a.set_signal_source_buffer_playback_action(X2, 'play')
225
226
227
    end
    
    % wait for a signal of the high-precision timer
228
    a.wait_for_timer();
229
    
230
231
232
    % 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)]);
   
233
    if idx==nlegs % optionally: stop playback during last loop cycle
234
       a.set_signal_source_buffer_playback_action(X2, 'STOP') 
235
236
237
238
239
240
241
242
243
    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) 

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

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

% % 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
284
285
286
% a.delete_sound_source(S1);
% a.delete_sound_source(S2);

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

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

if useTracker
    % disconnect from tracker
295
    a.disconnect_tracker
296
297
298
end

% disconnect itaVA object from server
299
a.disconnect
300
301