function strands(action)
%STRANDS Demonstration of synchrony strands and their synthesis.
%

% Martin Cooke, January 1998
%   Revised June 1998 for MAD release
%   and again July 1999 for v2.1

if nargin < 1
  action='init';
end

switch action

case 'init'
  % does main figure window exist? If so, bring it to front
  f=findobj('Tag','strands_fig');
  if ~isempty(f)
    figure(f);
  else  	
    savedir=pwd;  
    strands_gui;
    ud=get(gcf,'UserData');
    ud.fs=8012;  
    ud.savedir=savedir;
    ud.path=fullfile(madroot,'strands','');   
    set(gcf,'UserData',ud);
  end

case 'load'
	ud=get(gcf,'UserData');
  [sd,f]=getstrandsfile(fullfile(madroot,'data','strands','*.strands'));
  if f
    % compute properties here like freq range and time range
    set(ud.status,'String','Data loaded');
    strs=sd.strands;
    sfs=sd.strandsfs;
    ud.numstrands=length(strs);
    ud.selected=ones(1,ud.numstrands);
    maxen=max([strs.meanen]);
		set(gcf,'Name',['Strands. Loaded: ' f]);
    axes(ud.strandsAxes);
    cla
    set(ud.strandsAxes,'XLim',[min([strs.start]) max([strs.fin])],'YLim',[2 30]);                      
    for c=1:ud.numstrands
      set(ud.status,'String',['Synthesising strand ' num2str(c) ' of ' num2str(ud.numstrands)] );
      [strs(c).wav,strs(c).wavstart,strs(c).wavfin]=synthStrand(strs(c),ud.fs,sfs);
      strs(c).line=CreateStrand(c,strs(c),maxen);
    end
    ud.maxwavfin=max([strs.wavfin]);
    sd.strands=strs;
    ud.strandsData=sd;
    set(ud.toggleAll,'Enable','on');  
    if sd.sigloaded
      set(ud.playOrig,'Enable','on');  
    else  
      set(ud.playOrig,'Enable','off');
    end
    set(ud.status,'String','click strand to (un)select, shift-click to play; click elsewhere plays all' );

    set(gcf,'UserData',ud);
  end  

case 'selectStrand'
  lud=get(gcbo,'UserData');
  sid=lud.sid;	
	ud=get(gcf,'UserData');
  if strcmp(get(gcf,'SelectionType'),'extend')
    soundsc(ud.strandsData.strands(sid).wav,ud.fs);
  else
    if ud.selected(sid)
      LowlightStrands(gcbo);
      ud.selected(sid)=0;
    else
      HighlightStrands(gcbo);
      ud.selected(sid)=1;
    end
    set(gcf,'UserData',ud);
  end
  
case 'toggleSelect'
  ud=get(gcf,'UserData');
  if strcmp(get(gcbo,'String'),'select all')
    set(gcbo,'String','unselect all');
    ud.selected=ones(1,ud.numstrands);
    HighlightStrands([ud.strandsData.strands.line]);
  else
    set(gcbo,'String','select all');
    ud.selected=zeros(1,ud.numstrands);
    LowlightStrands([ud.strandsData.strands.line]);
  end   
  set(gcf,'UserData',ud);
  
case 'playStrands'
  ud=get(gcf,'UserData');
  synth=zeros(1,ud.maxwavfin);
  strs=ud.strandsData.strands;
  for c=1:ud.numstrands
    if ud.selected(c)
      seg=strs(c).wavstart:strs(c).wavfin;
      synth(seg)=synth(seg)+strs(c).wav;
    end
  end
  ud.synth=synth./max(abs(synth));
  set(gcf,'UserData',ud);
  soundsc(ud.synth,ud.fs);  


case 'playOriginal'
  ud=get(gcf,'UserData');
  soundsc(ud.strandsData.sig,ud.strandsData.sigfs);
    
case 'reallyquit'
  ud=get(gcf,'UserData');
  cd(ud.savedir);
  delete(get(0,'CurrentFigure'));  

otherwise
  maduievent('strands',action);
 
end


%-------------------  
function [x,start,fin]=synthStrand(str,fs,sfs)

  resample=fs/sfs;
  step=1/resample;
  twopii=(2*pi)/fs;
  freqs=str.freqs;
  amps=str.amps;
  l=length(amps);
  ti=1:step:l;
  x=interp1(1:l,amps,ti).*sin(twopii.*cumsum(interp1(1:l,freqs,ti)));
  start=round(str.start*resample);
  fin=start+length(x)-1;  

%--------------
function [s,fs]=readStrands(f)
% read strands from file f, returning array of strand structures

if nargin==1
  lengthThresh=3;
end

fid = fopen(f,'r');
if fid == -1
  error(['readStrands: file ' f ' not found']);
end

fs=fscanf(fid,'%d',1);
s(1).start=0; s(1).freqs=[]; s(1).amps=[]; s(1).selected=1;
s(1).ams=[];  s(1).en=0;     s(1).id=0;    s(1).line=0;
s(1).maxf=0;  s(1).minf=0;   s(1).len=0;   s(1).fin=0;
s(1).meanen=0; s(1).wav=[];  s(1).line=[]; s(1).wavstart=1;
s(1).wavfin=1;
str=0;

a=fscanf(fid,'%d', 4);
while ~isempty(a)
  data=fscanf(fid,'%d',[3,a(3)]);
  if a(3) > lengthThresh
    str=str+1;
    s(str)=s(1);
    s(str).start=a(2);
    s(str).id=str;
    s(str).freqs=data(1,:);
    s(str).ams=data(2,:);
    s(str).amps=data(3,:);
    s(str).en=sum(data(3,:));
    s(str).minf=min(data(1,:));
    s(str).maxf=max(data(1,:));
    s(str).meanen=mean(data(3,:));
    s(str).len=length(s(str).freqs);
    s(str).fin=s(str).start+s(str).len-1;
  end	
  a=fscanf(fid,'%d', 4);
end

fclose(fid);

%--------------------
function highlight(s)
l=s.line;
ud=get(l,'UserData');
set(ud.p,'FaceColor',[0.0 0.0 1]);
set(l,'Color',[0.0 0.0 1]);

function lowlight(s)
l=s.line;
ud=get(l,'UserData');
set(ud.p,'FaceColor',[0.9 0.9 1]);
set(l,'Color',[0.9 0.9 1]);


%--------------------------------------------
function [strandsData,f]=getstrandsfile(strandspath)

[f,p]=uigetfile(strandspath,'choose a strands file');
if f
  % first, read strands file
  [strandsData.strands,strandsData.strandsfs]=readStrands([p f]);
  % now look in same directory for .snd or .au file of same name
  strandsData.sigloaded=0;
  sf=strrep([p f],'.strands','');
 if exist([sf '.au'],'file') 
    [sig,fs,bits]=auread([sf '.au']);
    strandsData.sig=sig;
    strandsData.sigfs=fs;
    strandsData.sigloaded=1;
  elseif exist([sf '.snd'],'file') 
    [sig,fs,bits]=auread([sf '.snd']);
    strandsData.sig=sig;
    strandsData.sigfs=fs;
    strandsData.sigloaded=1;
  end
else
 strandsData=[];
end 

function l=CreateStrand(p,str,maxen)
    
  freqs=HzToErbRate(str.freqs);
  amps=str.amps.*(0.6/maxen);
  lf=freqs-amps;
  hf=freqs+amps;
  stt=str.start; 
  x=str.start:str.fin;
  x=[x fliplr(x(2:length(x)-1))];
  y=[lf fliplr(hf(2:length(hf)-1))];
  pat=patch(x,y,[0.5 0.2 0.7]);
	set(pat,'EraseMode','background');
  l=line(str.start:str.fin,freqs);
  set(l,'Color',[0.5 0.2 0.7],'Erasemode','xor','ButtonDownFcn',...
        'strands selectStrand');
  lud.patch=pat;
  lud.sid=p;
  set(l,'UserData',lud);


function HighlightStrands(ss)
  for s=1:length(ss)
    l=ss(s);
    lud=get(l,'UserData');
    set(lud.patch,'FaceColor',[0.5 0.2 0.7]);
    set(l,'Color',[0.5 0.2 0.7]);
  end
  
function LowlightStrands(ss)
  for s=1:length(ss)
    l=ss(s);
    lud=get(l,'UserData');
    set(lud.patch,'FaceColor',[0.9 0.9 0.7]);
    set(l,'Color',[0.9 0.9 0.7]);
  end
