function StimGenFun(varargin)
%Frequency histogram generator

% By default make the GUI
%from the command line with no parameters

if (nargin == 0)
    fcn = 'makeGUI';
else
    fcn=varargin{1};
    state = get(findobj('tag', 'GenFigure'),'userdata');
end

%This is the main decision point of the function.
%The switch statement is executed once-per-fuction call
switch fcn

    %This code is executed ONCE when the function enters with
    %no arguments
    case 'makeGUI'
        
        %store the name of this executing file
        state.myname = mfilename;
        
        fig = figure(...
            'doublebuffer','on',...
            'position',[100 200 900 600],...
            'tag', 'GenFigure',...
            'Name','Stimulus Editor',...
            'toolbar','figure');
        
        %=== interpolation method ============================
        x=0 ; y=0 ; w=60 ; h=60 ; sp=5 ;
        state.setInterp = uicontrol(...
            'style', 'listbox', ...
            'position', [x, y, w, h], ...
            'string','linear|nearest|spline|pchip', ...
            'value', 1, ...  %initially linear
            'callback', [state.myname,' setInterp'],...
            'Interruptible','off'...
            );
        
        %=== control point initialization ============================
        x=0 ; y=70 ; w=60 ; h=60 ; sp=5 ;
        state.setInit = uicontrol(...
            'style', 'listbox', ...
            'position', [x, y, w, h], ...
            'string','zeros|sine|random', ...
            'value', 1, ...  %initially linear
            'callback', [state.myname,' setInit'],...
            'Interruptible','off'...
            );
        
        %=== sample rate ======================================
        y=0;
        x=x+w+sp ; h=15;
        Fs = 10000 ; %samples/sec
        uicontrol(...
            'style', 'text', ...
            'position', [x h w h], ...
            'enable','on',...
            'string','Sample rate');
        state.setFs = uicontrol(...
            'style', 'edit', ...
            'position', [x 0 w h], ...
            'enable','on',...
            'callback', [state.myname,' setNewPlot'],...
            'TooltipString','Sample rate in samples/second',...
            'string',num2str(Fs));
        
        %=== total time ==========================================
         x=x+w+sp ;  
        tmax = 1 ; % seconds.
        uicontrol(...
            'style', 'text', ...
            'position', [x h w h], ...
            'enable','on',...
            'string','Stim time');
        state.setTime = uicontrol(...
            'style', 'edit', ...
            'position', [x 0 w h], ...
            'enable','on',...
            'callback', [state.myname,' setNewPlot'],...
            'TooltipString','Full stimulus time in seconds',...
            'string',num2str(tmax));
        
        %=== number of control points  ==========================================
         x=x+w+sp ; 
        NumControl = 11 ; %number of control points
        uicontrol(...
            'style', 'text', ...
            'position', [x h w h], ...
            'enable','on',...
            'string','N Control');
        state.setNumControl = uicontrol(...
            'style', 'edit', ...
            'position', [x 0 w h], ...
            'enable','on',...
            'callback', [state.myname,' setNewPlot'],...
            'TooltipString','Number of control points',...
            'string',num2str(NumControl));
        
        %=== vertical range  ==========================================
         x=x+w+sp ; 
        range = 1 ; %output range
        uicontrol(...
            'style', 'text', ...
            'position', [x h w h], ...
            'enable','on',...
            'string','Range Out');
        state.setRange = uicontrol(...
            'style', 'edit', ...
            'position', [x 0 w h], ...
            'enable','on',...
            'callback', [state.myname,' setNewPlot'],...
            'TooltipString','Maximum (and min) values for output variable',...
            'string',num2str(range));
        
        %=== T snap  ==========================================
         x=x+w+sp ;  
        state.enableTsnap = uicontrol(...
            'style', 'checkbox', ...
            'position', [x h w h], ...
            'enable','on',...
            'string','T snap');
        state.setTsnap = uicontrol(...
            'style', 'edit', ...
            'position', [x 0 w h], ...
            'enable','on',...
            'TooltipString','Snap to grid increment for time',...
            'string','0.01');
        
        %=== v snap  ==========================================
         x=x+w+sp ; 
        state.enableVsnap = uicontrol(...
            'style', 'checkbox', ...
            'position', [x h w h], ...
            'enable','on',...
            'string','V snap');
        state.setVsnap = uicontrol(...
            'style', 'edit', ...
            'position', [x 0 w h], ...
            'enable','on',...
            'TooltipString','Snap to grid increment for voltage',...
            'string','0.01');
        
        %=== repeat time ==========================================
         x=x+w+sp ;  
        trep = 1 ; % seconds.
        uicontrol(...
            'style', 'text', ...
            'position', [x h w h], ...
            'enable','on',...
            'string','Repeat time');
        state.setRepeatTime = uicontrol(...
            'style', 'edit', ...
            'position', [x 0 w h], ...
            'enable','on',...
            'TooltipString','Full stimulus repeat interval in seconds',...
            'string',num2str(trep));
        
         x=x+w+sp ;  
        nrep = 1 ; 
        uicontrol(...
            'style', 'text', ...
            'position', [x h w h], ...
            'enable','on',...
            'string','N Repeat');
        state.setRepeatCount = uicontrol(...
            'style', 'edit', ...
            'position', [x 0 w h], ...
            'enable','on',...
            'TooltipString','Full stimulus repetition count',...
            'string',num2str(nrep));
        
        %=== file save =================================================
        x=x+w+sp ;  h=20;
        uicontrol(...
            'style', 'pushbutton', ...
            'position', [x y w h], ...
            'enable','on',...
            'tag','wavfile',...
            'callback', [state.myname,' savefile'] ,...
            'TooltipString','Writes a one channel WAV file',...
            'string','Save WAV');
        
        x=x+w+sp ; 
        uicontrol(...
            'style', 'pushbutton', ...
            'position', [x y w h], ...
            'enable','on',...
            'tag','txtfile',...
            'callback', [state.myname,' savefile'] ,...
            'TooltipString','Writes a 2-column text file',...
            'string','Save TXT');
        
        x=x+w+sp ;  
        uicontrol(...
            'style', 'pushbutton', ...
            'position', [x y w h], ...
            'enable','on',...
            'tag','matfile',...
            'callback', [state.myname,' savefile'] ,...
            'TooltipString','Writes a 2-column matlab file',...
            'string','Save MAT');
        
        %=== button functions =====================================
         set(fig,'windowbuttondownfcn',@downfcn);
         set(fig,'windowbuttonupfcn', @upfcn);
         set(fig,'windowbuttonmotionfcn', @motionfcn);
        
        %=== generate the control points ==========================
        state=drawit(state);
        
    case 'setInterp'
        interp = popupstr(state.setInterp) ;
        state.yy = interp1(state.controlX, state.controlY, state.tt, interp);
        set(state.interpLine, 'ydata', state.yy);
        
    case 'setInit'
        state=drawit(state);
        
    case 'setNewPlot'
        state=drawit(state);
        
    case 'savefile'
        Fs = str2num(get(state.setFs,'string'));
        nrep = str2num(get(state.setRepeatCount,'string'));
        trep = str2num(get(state.setRepeatTime,'string'));
        % set up repeats by padding with zeros then duplicating
        stim = [state.yy' ;  zeros(Fs*trep-length(state.yy'),1)];
        stim = repmat(stim,nrep,1);
        a=[linspace(0, nrep*trep, length(stim))' stim];
        if strcmp(get(gco,'tag'), 'wavfile')
            [fname,fpath] = uiputfile('*.wav');
            wavwrite(stim,Fs,16,[fpath,fname]);
        elseif strcmp(get(gco,'tag'), 'txtfile')
            [fname,fpath] = uiputfile('*.txt');
            save([fpath,fname],'a','-ascii');
        elseif strcmp(get(gco,'tag'), 'matfile')
            [fname,fpath] = uiputfile('*.mat');
            save([fpath,fname],'a','-mat');
        end
end

set(findobj('tag', 'GenFigure'),'userdata', state)
end %main fcn

%==========================================================
function downfcn(src,eventdata)
state = get(findobj('tag', 'GenFigure'),'userdata');
%get the mouse position in graph units
mouse = get(gca,'currentpoint');
%calculate the point nearest to the
%mouse click
state.controlX = get(state.controlLine,'xdata');
state.controlY = get(state.controlLine,'ydata');
[val, state.pnt] = min((state.controlX-mouse(1,1)).^2+(state.controlY-mouse(1,2)).^2);
range = str2num(get(state.setRange,'string'));
set(state.pntValue,...
        'position',[state.controlX(state.pnt), state.controlY(state.pnt)+range/20],...
        'string',[num2str(state.controlX(state.pnt)),'  ',num2str(state.controlY(state.pnt))]);
state.mouseinit = 1;
set(findobj('tag', 'GenFigure'),'userdata', state)
end

%==========================================================
function motionfcn(src,eventdata)
state = get(findobj('tag', 'GenFigure'),'userdata');
Fs = str2num(get(state.setFs,'string'));
NumControl = str2num(get(state.setNumControl,'string'));
range = str2num(get(state.setRange,'string'));
interp = popupstr(state.setInterp) ;

if  state.mouseinit==1 ;
    %move the control point horizontally on the plot
    mouse = get(gca,'currentpoint');
    if state.pnt>1 & state.pnt<NumControl
        state.controlX(state.pnt) = mouse(1,1);
        if get(state.enableTsnap,'value')
            snap = str2num(get(state.setTsnap,'string'));
            state.controlX(state.pnt) = snap*round(mouse(1,1)/snap);
        end
    end
    %check for control point overlap
    if (state.pnt>1) & (state.controlX(state.pnt) <= state.controlX(state.pnt-1))
        state.controlX(state.pnt) = state.controlX(state.pnt-1)+ 1/Fs;
    elseif (state.pnt<NumControl) & (state.controlX(state.pnt) >= state.controlX(state.pnt+1))
        state.controlX(state.pnt) = state.controlX(state.pnt+1)- 1/Fs;
    end
    %nove the control point vertically on the plot
    state.controlY(state.pnt) = mouse(1,2);
    if get(state.enableVsnap,'value')
        snap = str2num(get(state.setVsnap,'string'));
        state.controlY(state.pnt) = snap*round(mouse(1,2)/snap);
    end
    %update plots
    set(state.controlLine, 'xdata', state.controlX, 'ydata', state.controlY);
    set(state.pntValue,...
        'position',[state.controlX(state.pnt), state.controlY(state.pnt)+range/20],...
        'string',[num2str(state.controlX(state.pnt)),'  ',num2str(state.controlY(state.pnt))]);

    %make new interpolation
    state.yy = interp1(state.controlX, state.controlY, state.tt, interp);
    set(state.interpLine, 'ydata', state.yy);
end
set(findobj('tag', 'GenFigure'),'userdata', state)
end

%==========================================================
function upfcn(src,eventdata)
state = get(findobj('tag', 'GenFigure'),'userdata');
%wait for the next click
state.mouseinit = 0;
set(state.pntValue,'string','');
set(findobj('tag', 'GenFigure'),'userdata', state)
end

%==========================================================
function state=drawit(state)
cla
beginValue = 0;
endValue = 0;
Fs = str2num(get(state.setFs,'string'));
tmax = str2num(get(state.setTime,'string'));
NumControl = str2num(get(state.setNumControl,'string'));
interp = popupstr(state.setInterp) ;
initType = popupstr(state.setInit) ; 
range = str2num(get(state.setRange,'string'));
state.controlX = linspace(0, tmax, NumControl) ;
%generate the signal point times
state.tt = linspace(0, tmax, tmax*Fs) ;
%state.controlY(1) = beginValue;
%state.controlY(end) = endValue;
switch initType
    case 'zeros'
        %start with all zeros in control points
        state.controlY = zeros(1,length(state.controlX));
        %and interpolate to generate signal
        state.yy = interp1(state.controlX,state.controlY,state.tt,'linear');
    case 'sine'
        state.controlY = sin(2*pi*state.controlX/tmax);
        state.yy = interp1(state.controlX,state.controlY,state.tt,interp);
    case 'random'
        state.controlY = 2*(rand(1,length(state.controlX))-0.5);
        state.yy = interp1(state.controlX,state.controlY,state.tt,interp);
end

%draw the control and signal points
hold on
state.interpLine = plot(state.tt,state.yy,'color','cyan','linewidth',2);
state.controlLine = plot(state.controlX,state.controlY,'ok','markerfacecolor',[1 1 0]);
set(gca, 'ylim', [-range range], 'color',[1 1 1]*.4);
grid on
% string for point 
state.pntValue = text(0,0,' ','fontsize',14,'color','w');
% make repeat time same as stim time
set(state.setRepeatTime,'string',num2str(tmax));
state.mouseinit = 0; %mouse starts unclicked
end