function distortion(action)
%DISTORTION Demonstration of distorted speech (and other signals).
%
% Martin Cooke, June 1998

if nargin < 1
	action='init';
end

switch action
case 'init'
  % does main figure window exist? If so, bring it to front
  f=findobj('Tag','distortion_fig');
  if ~isempty(f)
    figure(f);
  else  	
    savedir=pwd;  
    distortion_gui;		         % otherwise, create one
    ud=get(gcf,'UserData');
    ud.fs=8192;  
    ud.savedir=savedir;
    ud.path=fullfile(madroot,'distortion','');
    colormap(hot.*gray);  % a rather fine sepia    
    ud.numchans = 10;
    ud.lines=[];
    ud.justplayed=0;
    ud.filename=[];
    ud.lowcf =100;
    ud.highcf=4000;
    ud.selected=ones(1,ud.numchans);
    ud.distortshuffle=0;
    ud.distorttime=0;
    ud.distortcarrier='natural';
    ud.distortenv='natural';
    set([ud.timemenu ud.shufflemenu ud.envmenu ud.carriermenu],'Enable','off');
    set(gcf,'UserData',ud);
  end

case 'saveSignal'
	ud=get(gcf,'UserData');
	[f,p] = uiputfile([ud.filename '.distorted.au'],'Select filename to save distorted signal');
	if f
    s=ud.selected*ud.distorted;
    s=s/max(abs(s));
	  auwrite(s,ud.fs,[p f]);
  end

  case 'create'
  [sig,fs,name]=createsig;
  if length(sig)
    ud=get(gcf,'UserData');
    ud.sig=sig; ud.name=name; ud.fs=fs;
    set(gcf,'UserData',ud);
    distortion 'newsig'
  end
  
case 'load'
  ud=get(gcf,'UserData');
  [name,sig,fs]=loadsig(fullfile(madroot,'data','sounds'));
  if length(sig) 
    ud.sig=sig; ud.name=name; ud.fs=fs;
    set(gcf,'UserData',ud);
    distortion 'newsig'
  end

case 'newsig'
	ud=get(gcf,'UserData');
		set(gcf,'Name',['Distorted signals. Loaded: ' ud.name]);
    set([ud.timemenu ud.envmenu ud.carriermenu],'Enable','on');
    set(findobj(gcf,'Tag','pmenu'),'Enable','on');
    axes(ud.spectAxes);
    ud.sig=normalise(ud.sig(:)');
    sg=spectrogram(ud.sig,ud.fs,30,5);
		sg=sg';
		[nc,nf]=size(sg);
		set(ud.spectAxes,'XLim',[1 nf],'YLim',[1 nc]);
    ud.im=imagesc(sg);
    set(ud.im,'ButtonDownFcn','','EraseMode','xor');
		drawnow
		set(ud.spectstatus,'String','Click on spect to listen');
    set(gcf,'UserData',ud);
		distortion 'filter'
    distortion 'distort'

case 'filter'
  ud=get(gcf,'UserData');
  set(ud.im,'ButtonDownFcn','');
  ud.selected=ones(1,ud.numchans);
  noise=rand(size(ud.sig));
  ud.samps=length(ud.sig);
  ud.sampsPer10ms=round(ud.fs/100);
  e=sampleEnv(ud.sig,ud.sampsPer10ms);
  le=length(e);
  ud.highcf=round(ud.fs/2.5);
  ud.cfs=MakeErbCFs(ud.lowcf,ud.highcf,ud.numchans);
  ud.envs=zeros(ud.numchans,ud.samps);
  ud.cars=zeros(ud.numchans,ud.samps);
  ud.noisecars=zeros(ud.numchans,ud.samps);
  ud.distorted=zeros(ud.numchans,ud.samps);
  ud.t=round(1:ud.sampsPer10ms:ud.samps);
  ud.t=ud.t(1:length(e));
  delete(get(ud.wavAxes,'Children'));
  set(ud.wavAxes,'XLim',[1 le], 'YLim',[0 ud.numchans+1],'YTick',1:ud.numchans);
  axes(ud.wavAxes);		
  v=[];
  for c=1:ud.numchans   
    ud.lines(c)=CreateLine(c,le,ud.wavAxes);
    v=strvcat(v,num2str(round(ud.cfs(c))));
  end
  set(ud.wavAxes,'YTickLabel',v);
  ylabel('channel cf');
  drawnow
  for c=1:ud.numchans
    [env,carrier]=gammatoneEnvCarrier(ud.sig,ud.fs,ud.cfs(c));
    [nenv,ncarrier]=gammatoneEnvCarrier(noise,ud.fs,ud.cfs(c));
    ud.envs(c,:) = env;
    ud.cars(c,:) = carrier;
    ud.noisecars(c,:) = ncarrier;
    ud.distorted(c,:) = env.*carrier;
    e=sampleEnv(ud.distorted(c,:),ud.sampsPer10ms);
    ChangeLine(ud.lines(c),c,e); 
    set(ud.wavstatus,'String',['Computing Hilbert envelope for chan ' num2str(c)]); 

    drawnow
  end
  set(ud.wavstatus,'String','Click to select/unselect; shift-click to listen');

  set(gcf,'UserData',ud);
  distortion 'drawspect'
  
case 'playDistortedSignal'
	ud=get(gcf,'UserData');
	soundsc(ud.selected*ud.distorted,ud.fs);
  
case 'selectSignal'
  lud=get(gcbo,'UserData');
  chan=lud.chan;	
	ud=get(gcf,'UserData');
  if strcmp(get(gcf,'SelectionType'),'extend')
    soundsc(ud.distorted(chan,:),ud.fs);
  else
    if ud.selected(chan)
      LowlightLine(ud.lines(chan));
      ud.selected(chan)=0;
    else
      HighlightLine(ud.lines(chan));
      ud.selected(chan)=1;
    end
    set(gcf,'UserData',ud);
    distortion 'drawspect'
  end

case 'drawspect'
  ud=get(gcf,'UserData');	
  sg=spectrogram(ud.selected*ud.distorted,ud.fs,30,5);
  sg=sg';
  set(ud.im,'CData',sg,'ButtonDownFcn','distortion playDistortedSignal');
  set(gcf,'UserData',ud);
  
case 'presets'
  ud=get(gcf,'UserData');
  switch get(gcbo,'Label')	
  case 'low pass'
    ud.selected=ud.cfs < 1500;
  case 'high pass'
    ud.selected=ud.cfs >1500;
  case 'band pass'
    ud.selected=and(ud.cfs >1000,ud.cfs<2000);
  case 'band stop'
    ud.selected=or(ud.cfs < 500,ud.cfs > 3000);     
  case '2 bands'
    ud.selected=zeros(1,ud.numchans);
    ud.selected(1)=1; ud.selected(ud.numchans)=1;
  case 'no bands'
    ud.selected=zeros(1,ud.numchans);     
  case 'all bands'
    ud.selected=ones(1,ud.numchans);
  end	
  set(gcf,'UserData',ud)
  distortion 'showSelected'
  distortion 'drawspect'
  
case 'showSelected'
  ud=get(gcf,'UserData');
  for c=1:ud.numchans
    if ud.selected(c)
      HighlightLine(ud.lines(c));
    else 
      LowlightLine(ud.lines(c));
    end
  end
  %set(ud.lines(1),'YData',sampleEnv(ud.selected*ud.distorted,ud.sampsPer10ms));
  set(gcf,'UserData',ud)
  
case 'changeNumchans'
  ud=get(gcf,'UserData');
  ud.numchans=getuicontrolvalue(ud.chansmenu,'int');
  ud.selected=ones(1,ud.numchans);
  set(gcf,'UserData',ud);
  %if ~isempty(ud.filename)
    distortion 'filter'	
 %end

case 'distortEnvelope'
  ud=get(gcf,'UserData');
  ud.distortenv=getuicontrolvalue(ud.envmenu);
  set(gcf,'UserData',ud);
  distortion 'distort'	
  
case 'distortCarrier'
  ud=get(gcf,'UserData');
  ud.distortcarrier=getuicontrolvalue(ud.carriermenu);
  set(gcf,'UserData',ud);
  distortion 'distort'	
  
case 'distortTime'
  ud=get(gcf,'UserData');
  ud.distorttime=getuicontrolvalue(ud.timemenu,'int');
  set(gcf,'UserData',ud);
  distortion 'distort'	
  
case 'distortShuffle'
  ud=get(gcf,'UserData');
  ud.distortshuffle=getuicontrolvalue(ud.shufflemenu,'int');
  set(gcf,'UserData',ud);
  distortion 'distort'	
  
case 'distort'
  ud=get(gcf,'UserData');
  
  switch ud.distortcarrier
  case 'natural'
    cars=ud.cars;
  case 'noise'
    cars=ud.noisecars;
  case 'tone'
    [c,tm]=size(ud.cars);
    ts=1:tm;
    cars=sin((2*pi/ud.fs)*ts'*ud.cfs)';			
  end
  switch ud.distortenv
  case 'natural'
    envs=ud.envs;
  case 'noise'
    envs=rand(size(ud.envs));  
    %envs=(mean(ud.envs')'*ones(1,ud.samps));
    %envs=envs.*rand(size(envs));
  case 'constant'
    envs=(mean(ud.envs')'*ones(1,ud.samps));
  end

  ud.distorted=envs.*cars;
  if ud.distortshuffle == 0
    if ud.distorttime > 0
      % compute random shuffles
      delays=round((ud.fs/1000)*ud.distorttime*rand(1,ud.numchans));
      for c=1:ud.numchans
        ud.distorted(c,:)=delay(ud.distorted(c,:),delays(c));
      end
    end
  end
  set(gcf,'UserData',ud');
  distortion 'drawspect'
  distortion 'redraw'
  
case 'redraw'
  ud=get(gcf,'UserData');
  for c=1:ud.numchans
    ChangeLine(ud.lines(c),c,sampleEnv(ud.distorted(c,:),ud.sampsPer10ms));
  end

otherwise
  maduievent('distortion',action);
 
end

%-----
function y=delay(x,t)
lx=length(x)-t;
y=[zeros(1,t)  x(1:lx)];

%--------
function [env,carrier]=gammatoneEnvCarrier(x,fshz,cfhz)
% envelope and carrier form of complex gammatone

bwcorrection=1.019;
wcf=2*pi*cfhz;
tpt=(2*pi)/fshz;
bw=erb(cfhz)*bwcorrection;

a=exp(-bw*tpt);
kT=[0:length(x)-1]/fshz;
gain=((bw*tpt)^4)/6;                          % integral of imp resp

q=exp(-j*wcf*kT).*x;                          % shift down to d.c.
p=filter([1 0],[1 -4*a 6*a^2 -4*a^3 a^4],q);	% filter: part 1
u=filter([1 4*a 4*a^2 0],[1 0],p);            % filter: part 2
env=gain*abs(u);
carrier=cos(wcf*kT + angle(u));

%-----------
function e=sampleEnv(env,interval)
pts=round(length(env)/interval)-1;
e=max(abs(reshape(env(1:(interval*pts)),interval,pts)));

%------
function l=CreateLine(p,len,parent)
x=[1:len (len-1):-1:2];
y=p*ones(size(x));
pat=patch(x,y,[0.5 0.2 0.7]);
set(pat,'Erasemode','xor');
l=line(1:len,p*ones(1,len));
set(l,'Color',[0.5 0.2 0.7],'Erasemode','xor','ButtonDownFcn',...
'distortion selectSignal');
lud.patch=pat;
lud.chan=p;
set(l,'UserData',lud);

function ChangeLine(l,c,env)
lud=get(l,'UserData');
lenv=length(env);
set(lud.patch,'YData',[c+3*env c-3*fliplr(env(2:lenv-1))]);

function HighlightLine(l)
lud=get(l,'UserData');
set(lud.patch,'FaceColor',[0.5 0.2 0.7]);
set(l,'Color',[0.5 0.2 0.7]);

function LowlightLine(l)
lud=get(l,'UserData');
set(lud.patch,'FaceColor',[0.9 0.9 0.7]);
set(l,'Color',[0.9 0.9 0.7]);
