Code covered by the BSD License  

Highlights from
EQUALIZER

image thumbnail

EQUALIZER

by

 

29 Mar 2006 (Updated )

Equalizer GUI for winsound

equalizer
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

Contact us