Creating an Action
Now that you have a log full of events, you can measure the events with some of the built-in measurment extensions that we provided, but we think that you'll probably have some code already that you'd like to put to use for detecting, classifying ... or simply plotting data. The following is a tutorial example of creating a simple event Action that can easily be run from within the XBAT system.
Let's say you have an event that you listen to and you think sounds distorted, you'd like to confirm your hypothesis that the recording device clipped because the signal was too loud. You would also perhaps like to see the aggregate spectrum of the event. Let's make an event action extension called "Plot" that does these things.
You can follow along with this example by writing all of the code yourself. If you want to try this, you can either: 1. Delete the folder /XBAT/Extensions/Actions/Event/Plot and restart MATLAB and XBAT, or 2. Call your new action something other than "Plot". We also included all the code already, so you can just try it right away if you're anxious to see the results.
Generating the Action Extension
to generate the basic extension API functions and hook up the extension to the system, type
>> generate_extension event_action Plot
The result should be this:
$XBAT_ROOT\Extensions\Actions\Event\Plot\Docs
$XBAT_ROOT\Extensions\Actions\Event\Plot\Helpers
$XBAT_ROOT\Extensions\Actions\Event\Plot\Presets
$XBAT_ROOT\Extensions\Actions\Event\Plot\private
$XBAT_ROOT\Extensions\Actions\Event\Plot\plot_ext.m
$XBAT_ROOT\Extensions\Actions\Event\Plot\private\compute.m
Extension succesfully created.
ans =
type: 'extension'
subtype: 'event_action'
name: 'Plot'
modified: '17-Aug-2006 11:34:10'
parent: []
version: ''
category: {}
author: ''
mode: 'simple'
enable: 1
fun: [1x1 struct]
parameter: []
value: []
control: []
multichannel: 0
fade: 1
required: [1x1 struct]
active: 1
restart: 0
estimate: 1
waitbar: 1
palette: [1x1 struct]
dialog: [1x1 struct]
userdata: []
>>
Even though all of the file names appear, you don't need to remember them. Just open any sound in your library that has a log, open the Event palette, and right-click on any event. Select Actions > Plot . Even though you haven't written any code yet, the Event Plot action dialog appears!
You could even run your new action at this point (although nothing would happen, of course), but instead, click on the EXT menu on your new dialog, and select Edit > Edit All ... . This will open up the two most important functions (plot_ext.m and compute.m) for the extension in your MATLAB editor.
Adding Some Basic Functionality
Looking at plot_ext.m, there is really nothing to see, this function just creates the MATLAB struct containing information about the extension. You can actually close this one, because you will never have to edit it.
Look at compute.m, at the beginning of this function, you can see the signature:
function [result,context] = compute(event,parameter,context)
this suggests that compute takes an event and some parameters, and something called a context, and returns a "result" and the (possibly altered) context.
If you type event with no semicolon at the first open line, save the file, then cancel and re-run the action, you should get this display on your MATLAB command window:
event =
id: 2
tags: {}
rating: []
notes: {}
score: []
channel: 1
time: [18.3095 22.6622]
freq: [0 2.2007e+003]
duration: 4.3527
bandwidth: 2.2007e+003
samples: [47989x1 double]
rate: 11025
level: 1
children: []
parent: []
author: 'Default'
created: 7.3291e+005
modified: []
userdata: 'IvoryBilled'
detection: [1x1 struct]
annotation: [1x1 struct]
measurement: [1x1 struct]
>>
Notice that with this event structure, you get a lot of information. In fact, you have enough data now to do all the plotting you want.
How about we add some MATLAB code to plot the waveform? In this case, it is sufficient to make a simple call to the built-in plot() function. We can make up a decent time grid by using linspace(), and we can annotate the plot using title(), xlabel(), and ylabel().
function [result,context] = compute(event,parameter,context)
% PLOT - compute
result = []; h = figure;
%--
% amplitude plot
%--
t = linspace(event.time(1), event.time(2), length(event.samples));
plot(t, event.samples);
title(['Amplitude Of Event #' int2str(event.id)]);
xlabel('Time (s)'); ylabel('Amplitude (normalized)');
%--
% return result
%--
result = h;
Note that I put "h" (the handle to the new figure I created) into the result. This is not actually necessary, but since the result variable actually gets assigned to the MATLAB base workspace, this enables you to recover the figure handle from outside the extension to do other things with it.
Now you are ready to run your action. If you click "Ok" on the action run dialog, you should get something like this:
So that's it, you can now plot the amplitude of an event. But we said we wanted to plot the spectrum too. No big deal, we can easily use MATLAB's subplot() command to do this:
function [result,context] = compute(event,parameter,context)
% PLOT - compute
result = []; h = figure;
%--
% amplitude plot
%--
subplot(2, 1, 1);
t = linspace(event.time(1), event.time(2), length(event.samples));
plot(t, event.samples);
title(['Amplitude Of Event #' int2str(event.id)]);
xlabel('Time (s)'); ylabel('Amplitude (normalized)');
%--
% spectrum plot
%--
subplot(2, 1, 2);
[H, f] = freqz(event.samples, 1, 1024, event.rate);
plot(f / 1000, 10*log10(H.*conj(H)));
title(['Spectrum Of Event #' int2str(event.id)]);
xlabel('Frequency (KHz)'); ylabel('Amplitude (dB)');
%--
% some prettification
%--
set(h, ...
'numbertitle', 'off', ...
'name', ['Event Plot (# ', int2str(event.id), ')'] ...
);
%--
% send back result
%--
result = h;
Notice that I also added one line at the end of the function to do "some prettification", this last line
set(h, ...
'numbertitle', 'off', ...
'name', ['Event Plot (# ', int2str(event.id), ')'] ...
);
has the effect of giving the new figure an informative name, instead of the default "figure 'N'".
Adding Parameters and Controls
This is great, but let's say you're interested in a better estimate of the spectrum than freqz computes. You can use MATLAB's spectrum object and methods to easily switch the estimation method, but you would like to be able to choose which method to use. You now have a parameter, so you can go back to the EXT menu, and select add > parameter__create, then edit > parameter__create.
Let's say you want to call your parameter "method", you can put this single line in parameter__create and make it happen:
parameter.method = 'periodogram';
here, I've chosen the periodogram to be the default setting, because the periodogram is robust and quick to compute. Note that to switch the parameter value at this point you need to edit "parameter__create". Nonetheless, let's go back to "compute" and set it up to use this. Here is the current state of the compute function, note the additional code to handle the parameter.
function [result,context] = compute(event,parameter,context)
% PLOT - compute
result = []; h = figure;
%--
% amplitude plot
%--
subplot(2, 1, 1);
t = linspace(event.time(1), event.time(2), length(event.samples));
plot(t, event.samples);
title(['Amplitude Of Event #' int2str(event.id)]);
xlabel('Time (s)'); ylabel('Amplitude (normalized)');
%--
% spectrum plot
%--
subplot(2, 1, 2);
%--
% initialize spectrum estimation object
%--
method = parameter.method;
spec_obj = spectrum.(method);
%--
% plot psd or pseudospectrum, depending on method
%--
switch method
case {'periodogram', 'welch', 'cov'}
psd(spec_obj, event.samples, 'Fs', event.rate);
case 'music'
pseudospectrum(spec_obj,hilbert(event.samples),'Fs',event.rate);
otherwise
end
title(['Spectrum Of Event #' int2str(event.id)]);
xlabel('Frequency (KHz)'); ylabel('Amplitude (dB)');
%--
% some prettification
%--
set(h, ...
'numbertitle', 'off', ...
'name', ['Event Plot (# ', int2str(event.id), ')'] ...
);
%--
% send back result
%--
result = h;
This will work, but what you'd probably really like to do is select the method from a drop-down menu in the action dialog. It turns out that this is also pretty easy to do. By now, you're probably getting the hang of this, go back to the EXT menu, and create the parameter__control__create function and edit it.
function control = parameter__control__create(parameter,context)
% PLOT - parameter__control__create
control = empty(control_create);
%--
% method control
%--
methods = {'periodogram', 'music', 'cov', 'welch'};
control(end + 1) = control_create( ...
'name', 'method', ...
'style', 'popup', ...
'string', methods, ...
'value', find(strcmp(parameter.method, methods)) ...
);
Now this uses a bit of XBAT-specific code. In particular, "control_create". We will have more documentation on control_create in the future, but for now, all you need to know is that it creates MATLAB structures that define a set of important uicontrol properties for a layout manager to use. In fact, the code above is completely sufficient. All you need to do is go back to the compute function, and change one line:
%--
% initialize spectrum estimation object
%--
method = parameter.method{1};
This is to handle the output of the 'popup' control, which always returns its value packed into a MATLAB cell array. Notice that you needed to write no callback code whatsoever. This was actually because gave your new control the same name as the parameter that it controls -- these sorts of interactions are built in! Now, if you close and rerun your action on your event of choice, you get something like this:
and of course, if you click "ok", you get this:
Hopefully this tutorial example will be enough to get you started plugging in your own code for detecting, classifying and measuring sounds, events, and logs. We've included a number of example extensions, and you should feel free to modify them and write new ones. Good luck!

