Tuesday, July 15, 2008

Sound Card Spectral Analyser GUI

Glancing at the title of this post you might be mistaken for thinking I have repeated my previous post! This is not the case.

SoundcardSpectralAnalyserGui

This GUI (developed using GUIDE) took the SoundcardSpectralAnalysis functionality and wrapped it in a Matlab class. An object of this class was then utilised by the GUI in order to display real time analysis of acoustic data sampled off the system sound card.

Object Oriented Matlab

A Matlab class named SoundcardSpectralAnalyser was developed. It has a constructor, and start(), stop() and set() methods.

Unfortunately, as I am not yet using Matlab 2008a, I do not have access to its wonderful new OO interface. I will not go into too much detail regarding the older OO mechanic because eventually it will be outdated... And it is nothing to be celebrated really. (Hmmm maybe i should register ihatematlab.blogspot.com)

Constructor
The responsibility of the constructor is to initialise any member variables and define the returned structure as a Matlab class of type 'SoundcardSpectralAnalyser'.

Note that the constructor calls out to the set() method to initialise parameters from the variable length argument list. This has to be called AFTER the class type declaration to ensure the correct set method is used.
function this = SoundcardSpectralAnalyser(time_plot, freq_plot, varargin)

% Initialise default parameters if not supplied
this.Fs = 44000;
this.n_bits = 16;
this.n_channels = 2;
this.update_rate = 5;

this.time_plot = time_plot;
this.freq_plot = freq_plot;

this.audio_recorder = [];

this = class(this, 'SoundcardSpectralAnalyser');

% Set parameters as supplied
this = set(this, varargin{:});

end

Start()
The start method extracted the initialisation and audiorecorder starting functionality from the SoundcardSpectrumAnalysis function. Separating this ensures that an object of SoundcardSpectrumAnalyser can be constructed at one time but not started until a later point in time. Also, subsequent audiorecorder objects will be using the current analysis parameters (sample rate, sample size, number of channels and update rate).


The start() method also contains the TimerFcn callback responsible for updating the supplied plots. This is functionally unchanged from the script version.
function this = start(this)

% Setup the audiorecorder which will acquire data off default soundcard
this.audio_recorder = audiorecorder(this.Fs, this.n_bits, this.n_channels);

set(this.audio_recorder, 'TimerFcn', {@audioRecorderTimerCallback, ...
this.time_plot, this.freq_plot});
set(this.audio_recorder, 'TimerPeriod', 1/this.update_rate);
set(this.audio_recorder, 'BufferLength', 1/this.update_rate);

record(this.audio_recorder);
end

Stop
()
The stop method is responsible for stopping the audiorecorder object if it had been created.

function this = stop(this)  

if (~isempty(this.audio_recorder))
stop(this.audio_recorder);
end
end

Set()
The set method takes in a variable length argument list
. This list comprises of Value/Key parameter pairs for setting the sample rate, sample size, number of channels and update rate. All pairs can be set simultaneously or 1 at a time.

I was considering using the inputparser class to do this behaviour, but unfortunately I could not figure out how I could set only 1 of the parameters externally without the remaining parameters in the struct being returned as the specified defaults. Also, it was not available in my oldest version of Matlab (R14 SP2).
function this = set (this, varargin)

if (mod(length(varargin), 2) ~= 0)
warning('Parameters must be supplied in Key/Value pairs.');
return;
end

for i_param = 1:2:(length(varargin) - 1)

switch varargin{i_param}
case 'Fs'
this.Fs = varargin{i_param+1};
case 'SampleSize'
this.n_bits = varargin{i_param+1};
case 'Channels'
this.n_channels = varargin{i_param+1};
case 'UpdateRate'
this.update_rate = varargin{i_param+1};
otherwise
warning('Unknown parameter : %s\n', varargin{i_param});
end
end
end

GUI Development in GUIDE


I developed the GUI using Matlab's inbuilt GUI editor known as GUIDE (Graphical User Interface Development Environment). This provides a 'designer' like layout tool which will create the required .fig and .m files for your GUI. It also automatically creates hooks for object callbacks in the .m file for your convenience.

I will not go into too much detail regarding the GUIDE tool. I'd personally recommend firing up the editor (type 'guide' at the Matlab Command Window) and just throw some things on there, put some fprintf() statements in the generated callbacks and go click crazy. I will go into a couple of things that annoy about GUI development in Matlab in particular.

Disclaimer: This GUI was developed using R14 SP2... Guide may have been improved in later releases. I'm yet to investigate and the issues I have found with it may have been fixed!

Figure Resizing
Ok, the default behaviour of a Matlab GUI is to disallow resizing of the main figure window. This is convenient for a Matlab GUI developer... not so much for a Matlab GUI user. Particularly if there a plots involved as users with high resolution screens may wish to take advantage of their expensive toys.

So.. you bring up the Property Inspector for the main figure and set the Resize property to 'on'. Run the GUI and *yay* you can resize the window. But all the contents stay wedged in the bottom left. Hmmm thats no better.

Now, go into the Tools->GUI Options... menu and change the resize behaviour to 'Proportional'. Run the GUI and observe the behaviour.

Hey, thats a little bit better. Any Axes you have in the GUI are being proportionally resized and overall, things look good. However, the proportional resize affects ALL objects - pushbuttons, panels. Things can start to look a bit odd and often careful laying out of objects turns into a complete mess.

So what is the alternative?

Define a ResizeFcn on the main figure window. This is then called when the user resizes the GUI window. From within this callback you can query the current figure position and size and shuffle around your objects manually.

This process is quite tedious but unfortunately required for full control of resize behaviour (There appears to be no 'anchoring' of panels/objects).

Check out SoundcardSpectralAnalyser_GUI_ResizeCallback in the main GUI .m file for an example of the code required to achieve this control.

The desired behaviour of my Resize function is to keep the Parameters panel at the bottom of the figure, keeping its height constant but adjusting the width to match the figure. The remaining figure space is divided equally for the time and frequency domain plots (ensuring there was enough room around the axes for labels).

I did not put any limitations in the Resize function to enforce a minimum figure size as at 30 lines it was getting long enough.

Figure Axes
It was annoying having to remember to pad the size of the Axes to ensure sufficient size for the tick labels/text labels. Ideally the size specified by the Axes would be the maximum bounds containing titles/xlabel/ylabel/colorbars etc. Matlab should be smart enough to dynamically resize the drawn chart area internally to cope with this (as it does with a figure window currently.. actually that gives me an idea.. embedding a figure into a GUI for this very reason. Stay tuned).

Orphaned Objects
Something I noticed whilst developing the GUI was that I was often getting orphaned instances of my SoundcardSpectralAnalyser object (and subsequently the audiorecorder object) when there was an error and the GUI did not close properly.

When I tried to run 'clear classes' I received a warning that X instances of SoundcardSpectralAnalyser exist and classes could not be cleared. Doing a findall(0, 'Type', 'SoundcardSpectralAnalyser') yielded no results, hence there is no way of clearing these.

This may be a bug that has been fixed in more recent versions.

Overall, I feel that Matlab GUIs have a long way to go from that implemented in R14 SP2. I will have a look at 2007b (most recent installed version that I use) and see if things have improved. With care, reasonable GUIs can be developed in Matlab but most of the effort will go into tasks that should really be much simpler.

2 comments:

  1. This comment has been removed by the author.

    ReplyDelete
  2. Hi, can you hep with my query that how will the source code will have to change if we want to plot the same sound data on gui from a sound file (say .mp3 or .wav)stored on computer?? instead of the soundcard. Need helo ASAP thanks in advance :)

    ReplyDelete