function equalizer
%EQUALIZER Audio equalizer GUI
% EQUALIZER starts an equalizer for audio input on a PC. This is
% created as a demonstration of ANALOGINPUT and GUI. It performs
% real-time power spectral density estimation on acquired audio data and
% displays them in frequency bins. This may not be how a real equalizer
% works, so it should be used simply as a demo.
%
% The top panels show the raw audio signals (time series), and the bottom
% two panels show the spectral densities. Clicking on the panels changes
% the colors of the signals.
%
% Pressing the space bar (when the window is in focus) shrinks or grows
% the window to show all 4 panels or the bottom 2 panels. Pressing the
% escape key closes the equalizer.
%
% It requires the Data Acquisition Toolbox. Also, it may only work on PCs
% since it uses the winsound adaptor. Because of the high sampling and
% refresh rate, it may consume significant processing power.
%
% This will only work in MATLAB R14 or later (because it uses nested
% functions).
%
% Example:
% load handel;
% equalizer;
% soundsc(y, Fs);
%
% See also ANALOGINPUT.
% Copyright 2006 The MathWorks, Inc.
% VERSIONS:
% v1.0 - first version.
% v1.1 - added an example code (March 30, 2006)
% v2.0 - converted to nested functions (November 11, 2006)
%
% Jiro Doke
% Feb 21, 2006
shh = get(0, 'ShowHiddenHandles');
set(0, 'ShowHiddenHandles', 'on');
fh = findobj('type', 'figure', 'tag', 'equalizer');
set(0, 'ShowHiddenHandles', shh);
if ishandle(fh);
close(fh);
end
refreshRate = 0.05; % sec
sampleRate = 44100; % Hz
numFreqs = floor(log2(sampleRate/2))-4;
frequencies = {'32','64','128','256','512','1k','2K','4k','8k','16k'};
ai = analoginput('winsound');
addchannel(ai,[1 2]);
sampleRate = setverify(ai, 'SampleRate', sampleRate);
ai.TimerPeriod = refreshRate;
spt = round(sampleRate * refreshRate);
ai.SamplesPerTrigger = spt;
% determine how many samples to PEEK
numSamples = min([500, spt]);
un = get(0, 'units');
set(0, 'units', 'pixels');
screenSize = get(0, 'ScreenSize');
set(0, 'units', un);
sW = screenSize(3);
sH = screenSize(4);
wd = 400;
ht = 180;
labelColor = [.1 .1 .1];
fh = figure(...
'tag' , 'equalizer', ...
'units' , 'pixels', ...
'position' , round([(sW-wd)/2, (sH-ht)/2, wd, ht]), ...
'numbertitle' , 'off', ...
'color' , [0 0 0], ...
'menu' , 'none', ...
'resize' , 'off', ...
'interruptible' , 'off', ...
'keypressfcn' , @keyPressFcn, ...
'busyaction' , 'cancel', ...
'defaultAxesColor' , [0 0 0], ...
'defaultAxesXColor' , [.3 .3 .3], ...
'defaultAxesYColor' , [.2 .2 .2], ...
'defaultAxesUnits' , 'normalized', ...
'defaultAxesFontName' , 'Arial', ...
'defaultAxesFontSize' , 7, ...
'name' , sprintf('Sampling - %3.1f kHz, Refresh - %3.1f Hz', ...
sampleRate/1000, 1/refreshRate), ...
'deleteFcn' , @delFun, ...
'doublebuffer' , 'on');
% left raw signal
axes(...
'position' , [.01 .5 .48 .49], ...
'box' , 'on', ...
'ticklength' , [0 0], ...
'xtick' , [], ...
'xticklabel' , '', ...
'ytick' , [], ...
'yticklabel' , '', ...
'buttondownfcn' , {@changeColor, 1}, ...
'tag' , 'LeftSignal');
h(1) = line(1:numSamples, zeros(1, numSamples), ...
'color' , [.5 1 1], ...
'buttondownfcn' , {@changeColor, 1});
ylim([-1 1]); xlim([1 numSamples]);
% right raw signal
axes(...
'position' , [.51 .5 .48 .49], ...
'box' , 'on', ...
'ticklength' , [0 0], ...
'xtick' , [], ...
'xticklabel' , '', ...
'ytick' , [], ...
'yticklabel' , '', ...
'buttondownfcn' , {@changeColor, 1}, ...
'tag' , 'RightSignal');
h(2) = line(1:numSamples, zeros(1, numSamples), ...
'color' , [.5 1 1], ...
'buttondownfcn' , {@changeColor, 1});
ylim([-1 1]); xlim([1 numSamples]);
eqYLim = [0 1];
% left equalizer
axes('position', [.01 .09 .48 .4]);
h(3) = bar(1:numFreqs, zeros(1, numFreqs), .6);
hText = text(.5, 0.6 , 'L', ...
'horizontalalignment' , 'left', ...
'verticalalignment' , 'middle', ...
'fontweight' , 'bold', ...
'fontunits' , 'pixels', ...
'fontname' , 'Arial', ...
'fontsize' , 60, ...
'buttondownfcn' , {@changeColor, 2}, ...
'color' , labelColor);
% put text behind bar.
chH = get(gca, 'children');
swapA = find(chH == h(3));
swapB = find(chH == hText);
chH([swapA, swapB]) = chH([swapB, swapA]);
set(gca, ...
'children' , chH, ...
'box' , 'on', ...
'ticklength' , [0 0], ...
'xticklabel' , sprintf('%s|', frequencies{1:numFreqs}), ...
'xlim' , [0 numFreqs+1], ...
'ytick' , [], ...
'yticklabel' , '', ...
'ylim' , eqYLim, ...
'buttondownfcn' , {@changeColor, 2}, ...
'tag' , 'LeftEq');
% right equalizer
axes('position', [.51 .09 .48 .4]);
h(4) = bar(1:numFreqs, zeros(1, numFreqs), .6);
hText = text(.5, 0.6 , 'R', ...
'horizontalalignment' , 'right', ...
'verticalalignment' , 'middle', ...
'fontweight' , 'bold', ...
'fontunits' , 'pixels', ...
'fontname' , 'Arial', ...
'fontsize' , 60, ...
'buttondownfcn' , {@changeColor, 2}, ...
'color' , labelColor);
% put text behind bar.
chH = get(gca, 'children');
swapA = find(chH == h(4));
swapB = find(chH == hText);
chH([swapA, swapB]) = chH([swapB, swapA]);
set(gca, ...
'children' , chH, ...
'box' , 'on', ...
'ticklength' , [0 0], ...
'xticklabel' , sprintf('%s|', frequencies{1:numFreqs}), ...
'xlim' , [0 numFreqs+1], ...
'xdir' , 'reverse', ...
'ytick' , [], ...
'yticklabel' , '', ...
'ylim' , eqYLim, ...
'buttondownfcn' , {@changeColor, 2}, ...
'tag' , 'RightEq');
set(h(3:4), ...
'facecolor' , [.5 .5 1], ...
'edgecolor' , [.5 .5 1], ...
'buttondownfcn' , {@changeColor, 2});
handles = guihandles(fh);
handles.ai = ai;
handles.state = 'full';
handles.wd = wd;
handles.ht = ht;
handles.objH = h;
set(fh, 'HandleVisibility', 'off');
nfft = 2 ^ (nextpow2(numSamples)+1);
freqs = sampleRate/2*linspace(0,1,nfft/2);
edges = 2.^(4.5:1:14.5);
[n, bin] = histc(freqs, edges);
binMat = zeros(length(n)-1, length(bin));
for id = 1:length(n)-1
binMat(id, :) = bin == id;
end
% raw signal colors
colorOrder1 = [.5 1 1;1 .5 1;1 1 .5;.5 1 1];
% psd colors
colorOrder2 = [.5 .5 1;1 .5 .5;.5 1 .5;.5 .5 1];
set(ai, 'TriggerRepeat', Inf);
set(ai, 'TimerFcn' , @showSample);
% start ANALOGINPUT
start(ai);
%--------------------------------------------------------------------------
% changeColor
% When the graphs (raw signal or psd) are clicked, the color changes by
% cycling through preset colors.
%--------------------------------------------------------------------------
function changeColor(varargin)
switch varargin{3}
case 1
fc = find(all(colorOrder1 == repmat(get(handles.objH(1), ...
'color'), 4, 1), 2));
set(handles.objH(1:2), 'color', colorOrder1(fc(1)+1,:));
case 2
fc = find(all(colorOrder2 == repmat(get(handles.objH(3), ...
'facecolor'), 4, 1), 2));
set(handles.objH(3:4), 'facecolor', colorOrder2(fc(1)+1,:), ...
'edgecolor', colorOrder2(fc(1)+1,:));
end
end
%--------------------------------------------------------------------------
% delFun
% This is called when the figure is closed
%--------------------------------------------------------------------------
function delFun(varargin)
stop(handles.ai);
delete(handles.ai);
end
%--------------------------------------------------------------------------
% showSample
% This function is called at sampleRate intervals. It PEEKS analog input
% data and performs an FFT
%--------------------------------------------------------------------------
function showSample(varargin)
% grab the data from the buffer
try
y = peekdata(ai,numSamples);flushdata(ai);
catch
y = [];
end
if ~isempty(y) && size(y, 1) == numSamples
% perform fft
fc = fft(y, nfft)/(2*numSamples);
Y = 2*abs(fc);
Y = Y(1:nfft/2,:);
Y = binMat*Y;
set(h, {'ydata'}, ...
[mat2cell(y', [1 1], numSamples); mat2cell(Y', [1 1], 10)]);
end
end
%--------------------------------------------------------------------------
% keyPressFcn
% Key press interface.
%--------------------------------------------------------------------------
function keyPressFcn(varargin)
obj = varargin{1};
k = get(obj, 'CurrentKey');
switch k
case 'escape'
close(obj);
case 'space'
shrinkWin;
case 's'
if strcmpi(get(handles.ai, 'running'), 'on')
stop(handles.ai);
else
start(handles.ai);
end
end
end
%--------------------------------------------------------------------------
% shrinkWin
% Toggles equalizer GUI state between Raw Signal+EQ and EQ only. It
% animates window shrinking/growing
%--------------------------------------------------------------------------
function shrinkWin
switch handles.state
case 'full'
handles.state = 'min';
set([handles.equalizer, handles.LeftSignal, ...
handles.RightSignal, handles.LeftEq, handles.RightEq], ...
'units', 'pixels');
figPos = get(handles.equalizer, 'position');
for iPix = 0:3:round(handles.ht/2)
set(handles.equalizer, 'position', ...
[figPos(1), figPos(2)+iPix, figPos(3), figPos(4)-iPix]);
pause(0.01);
end
case 'min'
handles.state = 'full';
set([handles.equalizer, handles.LeftSignal, ...
handles.RightSignal, handles.LeftEq, handles.RightEq], ...
'units', 'pixels');
figPos = get(handles.equalizer, 'position');
for iPix = 0:3:round(handles.ht/2)
set(handles.equalizer, 'position', ...
[figPos(1), figPos(2)-iPix, figPos(3), figPos(4)+iPix]);
pause(0.01);
end
end
end
end