function spc01
% spc01.m Spectrogram Viewer スペクトログラムの作成
%    19-Jun-2007 Hasada version 0.01beta
%    20-Jun-2007 Hasada version 0.02beta
%    26-Jun-2007 Hasada version 1.01beta
%    27-Jun-2007 Hasada version 1.02beta
verstr='1.02beta';

%%%% コントロールパネル %%%%
close(findobj(0,'name',['Spectrogram Viewer ' verstr]))
width=650;
height=100;
up=figure('position',[20 550 width height],'units','pixels','tag','panelfig',...
    'menubar','none','numbertitle','off',...
    'DefaultUicontrolUnits','pixels',...
    'Color',get(0,'defaultUicontrolBackgroundColor'),...
    'DefaultUicontrolFontUnits','pixels','DefaultUicontrolFontSize',12,...
    'deletefcn',@delpanel,...
    'name',['Spectrogram Viewer ' verstr]);

%%%% メニュー %%%%
mfile=uimenu(up,'label','File');
uimenu(mfile,'label','Open...','callback',@openfile,...
    'accelerator','O');
uimenu(mfile,'label','Save setting...','callback',@savepanel,...
    'accelerator','S');
if exist('spc01hst.mat')==2
    load('spc01hst.mat')
    for k=1:length(flist)
        fl(k)=uimenu(mfile,'label',flist{k},'tag','filehist',...
            'callback',{@openfile,flist{k}});
    end
    set(fl(1),'separator','on');
end
uimenu(mfile,'label','Quit','accelerator','W','callback','delete(gcf)',...
    'separator','on')
mtool=uimenu(up,'Label','Tool');
uimenu(mtool,'label','Draw','accelerator','D','callback',@plotimg);

%%%% ファイル %%%%
hb=25;
ht=17;
hs=get(up,'DefaultUicontrolFontSize');
y=height-(hb+ht)/2;
ys=height-(hb+hs)/2;
uicontrol(up,'style','text','string','Data file',...
    'horizontalalignment','center',...
    'position',[5 ys 85 hs]);
uicontrol(up,'style','edit','tag','filename',...
    'horizontalalignment','left',...
    'callback',@openfile,...
    'position',[90 y 195 ht]);
uicontrol(up,'style','pushbutton','string','...',...
    'callback',@openfile,...
    'position',[290 y 35 ht]);
uicontrol(up,'style','popupmenu','string','Select data',...
    'callback',@selvar,'tag','selvar',...
    'position',[340 y 95 ht]);
uicontrol(up,'style','popupmenu','string','Select column',...
    'tag','selcol',...
    'position',[440 y 95 ht]);
uicontrol(up,'style','checkbox','string','transpose',...
    'callback',@transp,'tag','trans',...
    'position',[540 y 85 ht]);
%%%% ファイル２ %%%%
y=y-hb;
ys=ys-hb;
uicontrol(up,'style','text','string','Sample frequency(Hz)',...
    'position',[5 ys 125 hs]);
uicontrol(up,'style','edit','string','100',...
    'horizontalalignment','right','tag','samplef',...
    'position',[130 y 45 ht]);
uicontrol(up,'style','text','string','Time win.(s)',...
    'position',[180 ys 75 hs]);
uicontrol(up,'style','edit','string','3',...
    'horizontalalignment','right','tag','timewin',...
    'position',[255 y 45 ht]);
uicontrol(up,'style','text','string','Overlap (s)',...
    'position',[305 ys 85 hs]);
uicontrol(up,'style','edit','string','1',...
    'horizontalalignment','right','tag','overlap',...
    'position',[390 y 45 ht]);
uicontrol(up,'style','text','string','Taper ratio',...
    'position',[440 ys 65 hs]);
uicontrol(up,'style','text','string','0',...
    'position',[505 ys 10 hs]);
uicontrol(up,'style','slider','value',0,...
    'tag','taper',...
    'position',[515 y 100 ht]);
uicontrol(up,'style','text','string','1',...
    'position',[615 ys 10 hs]);

%%%% 描画 %%%%
y=y-hb;
ys=ys-hb;
uicontrol(up,'style','pushbutton','string','Draw new',...
    'callback',@plotimg,'tag','drawnew',...
    'position',[5 y-2 75 ht+4]);
uicontrol(up,'style','pushbutton','string','Redraw',...
    'callback',@plotimg,'tag','draw',...
    'position',[85 y-2 75 ht+4]);
    
%%%% 表示（範囲） %%%%
uicontrol(up,'style','text','string','Axis range',...
    'position',[205 ys 70 hs]);
uicontrol(up,'style','pushbutton','string','Manual',...
    'tooltip','グラフの範囲を設定',...
    'horizontalalignment','center',...
    'callback',@setax,...
    'position',[280 y-2 50 ht+4]);
uicontrol(up,'style','pushbutton','string','Auto',...
    'tooltip','グラフの範囲を最初の状態に戻す',...
    'horizontalalignment','center',...
    'callback',@resetax,...
    'position',[335 y-2 50 ht+4]);

%%%% 表示（色） %%%%
y=y-hb;
ys=ys-hb;
uicontrol(up,'style','text','string','Colormap',...
    'horizontalalignment','center',...
    'position',[5 ys 55 hs]);
uicontrol(up,'style','radio','string','linear',...
    'callback',@linlog,'value',1,...
    'tag','ccon',...
    'position',[63 y 50 ht]);
uicontrol(up,'style','radio','string','log',...
    'callback',@linlog,'value',0,...
    'tag','ccon',...
    'position',[115 y 50 ht]);
uicontrol(up,'style','popupmenu',...
    'string',{'Blue->Red','White->Black','Reverse hot','Colormap editor'},...
    'callback',@changecolmap,...
    'tag','ccon',...
    'position',[170 y 115 ht]);
uicontrol(up,'style','text','string','Color limit',...
    'horizontalalignment','center',...
    'position',[290 ys 55 hs]);
uicontrol(up,'style','slider','value',1,...
    'tooltip','色の閾値を調整',...
    'callback',@changeclim,'tag','clim',...
    'position',[350 y 100 ht]);
set(findobj(up,'tag','draw'),'enable','off')
set(findobj(up,'tag','drawnew'),'enable','off')
set(findobj(up,'tag','ccon'),'enable','off')
set(findobj(up,'tag','clim'),'enable','off')
return

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%% グラフウィンドウ作成 %%%%
function [fig,ax,axraw,colax]=makegraphwin(position)
% nfig=length(findobj('tag','graphfigs'))+1;
nms=get(findobj('tag','graphfigs'),'name');
if iscell(nms)
    nms=cell2mat(nms);
end
if ~isempty(nms)
    nfig=max(str2num(nms(:,end)))+1;
else
    nfig=1;
end
fig=figure('position',position,'units','pixels','tag','graphfig',...
    'menubar','figure','numbertitle','off',...
    'DefaultAxesNextplot','replacechildren',...
    'DefaultAxesBox','on',...
    'colormap',jet(64),'name',['Spectrogram-' num2str(nfig)]);
orient landscape;
ax=axes('position',[.1 .1 .75 .65],...
    'tag','mainax','parent',fig);
xlabel('time / s')
ylabel('freq. / Hz')
axraw=axes('position',[.1 .8 .75 .1],...
    'tag','axraw','parent',fig);
colax=axes('position',[.88 .1 .02 .65],...
    'yaxislocation','right','xtick',[],...
    'tag','colax');
return

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%% コールバック関数 %%%%

%%%% 終了時 %%%%
function delpanel(src,ev)
return

%%%% 設定を保存 %%%%
function savepanel(src,ev,path)
[fname,pname]=uiputfile('*.fig');
if fname==0
    return
end
fpath=[pname fname];
saveas(gcf,fpath,'fig');

%%%% ファイル %%%%
function openfile(src,ev,fpath)
nmin=10; % 許可する最小のデータ長
g=guihandles(src);
try
    if strcmp(get(src,'type'),'uicontrol') & strcmp(get(src,'style'),'edit')
        fpath=get(src,'string');
    elseif exist('fpath')==1
        set(g.filename,'string',fpath)
    else
        [fname,pname]=uigetfile({...
            '*.*','Text file (*.*)';...
            '*.mat','MAT file (*.mat)';...
            '*.wav','WAV file (*.wav)';...
            '*.fig','Setting file (*.fig)'});
        if fname==0
            return
        end
        fpath=[pname fname];
        set(g.filename,'string',fpath)
    end
    % FIGファイル
    if strcmp(fpath(end-3:end),'.fig')|strcmp(fpath(end-3:end),'.FIG')
        h=openfig(fpath,'new','invisible');
        if strcmp(get(h,'tag'),'panelfig')
            set(h,'visible','on','position',get(g.panelfig,'position'))
            close(g.panelfig)
        else
            errordlg('Cannot open setting file.')
        end
        return
    end
    % WAVファイル
    if strcmp(fpath(end-3:end),'.wav')|strcmp(fpath(end-3:end),'.WAV')
        [x.data,fs]=wavread(fpath);
        tlen=size(x.data,1)/fs;
    else % その他のファイル
        data=load(fpath);
        if ~isstruct(data)
            x.data=data;
        else
            x=data;
        end
    end
    ud=get(g.panelfig,'userdata');
    ud.data=x;
    set(g.panelfig,'userdata',ud);
    if exist('fs')==1&size(fs)==[1 1]&isnumeric(fs)&fs>0&isreal(fs)&isfinite(fs)
        set(g.samplef,'string',num2str(fs))
    else
        set(g.samplef,'string','100')
    end
    if exist('tlen')==1&size(fs)==[1 1]
        set(g.timewin,'string',num2str(tlen/20))
        set(g.overlap,'string',num2str(tlen/40))
    else
        set(g.timewin,'string','3')
    end
    vnames=fieldnames(x);
    n=size(vnames,1);
    for k=1:n
        v=getfield(x,vnames{k});
        if ~isnumeric(v)|max(size(v))<nmin
            x=rmfield(x,vnames{k});
        end
    end
    vnames=fieldnames(x);
    if size(vnames,1)~=1
        set(g.selvar,'string',{'Select data' vnames{:}},'value',1,'enable','on')
        set(g.selcol,'string','Select column','value',1)
        set(g.trans,'value',0)
        uicontrol(g.selvar);
    else
        set(g.selvar,'string',{'Select data' vnames{1}},'value',2,'enable','off')
        set(g.trans,'value',0)
        selvar(g.selvar);
    end
    set([g.draw;g.drawnew],'enable','on')
    % 履歴ファイルに書き込み
    s=flipud(get(g.filehist,'label'));
    s1=get(g.filename,'string');
    if exist(s1)==2
        s=s(setdiff(1:length(s),strmatch(s1,s)));
        flist={s1,s{1:min(length(s),5)}}';
        save spc01hst.mat flist
    end
catch
    uiwait(errordlg('Cannot load data file'));
    uicontrol(g.filename);
end
return

%%%% 変数を選択 %%%%
function selvar(src,ev)
if get(src,'value')==1
    return
end
g=guihandles(src);
vnames=get(src,'string');
data=getfield(get(g.panelfig,'userdata'),'data');
sz=size(getfield(data,vnames{get(src,'value')}));
set(g.trans,'value',0,'enable','off')
nmin=10; % 許可する最小のデータ長
msz=min(sz);
if msz==1
    set(g.selcol,'string',{'Select column';'1'},'value',2,'enable','off')
    uicontrol(g.samplef)
else
    set(g.selcol,'string',{'Select column';num2str([1:msz]')},'value',1,'enable','on')
    uicontrol(g.selcol)
end
set(g.trans,'value',sz(1)<sz(2))
if msz>=nmin
    set(g.trans,'enable','on')
end
return

%%%% 転置 %%%%
function transp(src,ev)
g=guihandles(src);
if size(get(g.selcol,'string'),1)<=2
    set(src,'value',~get(src,'value'))
    return
end
v=get(g.selvar,'value');
vs=get(g.selvar,'string');
sz=size(getfield(getfield(get(g.panelfig,'userdata'),'data'),vs{v}));
switch get(src,'value')
    case 1
        set(g.selcol,'string',{'Select column';num2str([1:sz(1)]')},'value',1)
    case 0
        set(g.selcol,'string',{'Select column';num2str([1:sz(2)]')},'value',1)
end
uicontrol(g.selcol)
return

%%%% 表示（範囲） %%%%
function setax(src,ev)
h=findobj('tag','graphfig');
if isempty(h)
    return
end
h=h(1);
answer=inputdlg('Enter axis range [tmin tmax fmin fmax]',...
    'Set axis');
if isempty(answer)
    return
end
try
    ar=str2num(cell2mat(answer));
    axis(findobj(h,'tag','mainax'),ar);
catch
    errordlg('Invalid axis range.')
end
return

function resetax(src,ev)
h=findobj('tag','graphfig');
if ~isempty(h)
    h=h(1);
    axis(findobj(h,'type','axes'),'tight')
end
return

%%%% 色の設定 %%%%
function linlog(src,ev)
h=findobj('tag','graphfig');
up=get(src,'parent');
s=get(src,'string');
set(src,'value',1);
sli=findobj(up,'tag','clim');
switch s
    case 'linear'
        set(findobj(up,'string','log'),'value',0);
        set(sli,'value',1)
    case 'log'
        set(findobj(up,'string','linear'),'value',0);
        set(sli,'value',0)
end
if ~isempty(h)
    h=h(1);
    set(findobj(h,'tag','mainax'),'climmode','auto')
    plotimg(src);
end
return

function changecolmap(src,ev)
h=findobj('tag','graphfig');
if ~isempty(h)
    h=h(1);
    switch get(src,'value')
        case 1
            set(h,'colormap',jet(64));
        case 2
            set(h,'colormap',flipud(gray(64)))
        case 3
            set(h,'colormap',flipud(hot(64)))
        otherwise
            colormapeditor(h);
    end
end
return

function changeclim(src,sv)
h=findobj('tag','graphfig');
if ~isempty(h)
    h=h(1);
    v=get(src,'value');
    if get(findobj(get(src,'parent'),'string','linear'),'value')==1
        set(findobj(h,'tag','mainax'),'clim',[0 max(v,1e-3)])
        set(findobj(h,'tag','colax'),'clim',[0 max(v,1e-3)])
    else
        cmin=min(min(get(findobj(findobj(h,'tag','colax'),'type','image'),'cdata')));
        set(findobj(h,'tag','mainax'),'clim',[cmin*(1-v) 0])
        set(findobj(h,'tag','colax'),'clim',[cmin*(1-v) 0])
    end
end
return

%%%% 描画 %%%%
function plotimg(src,ev)
err=0;
fig=findobj('tag','graphfig');
if exist('src')==1&strcmp(get(src,'tag'),'drawnew')
    set(fig,'tag','graphfigs')
    fig=[];
end
g=guihandles(src);
ud=get(g.panelfig,'userdata');

% エラーチェック
if ~isfield(ud,'data')
    errordlg('No data.')
    set([g.draw;g.drawnew],'enable','off')
    return
end
vn=get(g.selvar,'value');
c=get(g.selcol,'value');
if vn<=1
    uiwait(errordlg('Select data.'));
    uicontrol(g.selvar);
    return
end
if c<=1
    uiwait(errordlg('Select column.'));
    uicontrol(g.selcol);
    return
end
vnames=get(g.selvar,'string');
data=getfield(ud.data,vnames{vn});
if get(g.trans,'value')==1
    data=data.';
end
data=data(:,c-1);
try
    dt=1/str2num(get(g.samplef,'string'));
catch
    err=1;
end
if ~isfinite(dt)|dt<0|err==1
    uiwait(errordlg('Invalid sampling frequency.'));
    set(g.samplef,'string','');
    uicontrol(g.samplef);
    return
end
try
    tw=str2num(get(g.timewin,'string'));
    if isempty(tw)
        error;
    end
    seg=round(tw/dt);
    if seg<1
        error;
    end
catch
    uiwait(errordlg('Invalid time window.'));
    set(g.timewin,'string','');
    uicontrol(g.timewin);
    return
end
try
    overlap=str2num(get(g.overlap,'string'));
    if isempty(overlap),err=1;end
    inc=round((tw-overlap)/dt);
catch
    err=1;
end
if err==1|overlap<0|inc<1|~isfinite(inc)
    uiwait(errordlg('Invalid overlap.'));
    set(g.overlap,'string','');
    uicontrol(g.overlap);
    return
end
nseg=floor((length(data)-seg)/inc)+1;
if nseg<=1
    uiwait(errordlg('Invalid time window.'));
    set(g.timewin,'string','');
    uicontrol(g.timewin);
    return
end
if ~isreal(data)
    uiwait(warndlg('Using real part of complex numbers.'));
    data=real(data);
end

% ウィンドウ作成/グラフ選択
ppos=get(g.panelfig,'position');
height=450;
pos=[ppos(1) ppos(2)-height-80 ppos(3) height];
if isempty(fig)
    [fig,ax,axraw,colax]=makegraphwin(pos);
else
    fig=fig(1);
    ax=findobj(fig,'tag','mainax');
    axraw=findobj(fig,'tag','axraw');
    colax=findobj(fig,'tag','colax');
end

axes(axraw);
plot([0:length(data)-1]*dt,data)

for k=1:nseg
    x(1:seg,k)=data((k-1)*inc+(1:seg));
end

% cosine taper
taperratio=get(g.taper,'value');
ntap=round(seg*taperratio/2);
w=ones(seg,1);
w(1:ntap)=(1-cos(pi*(0:ntap-1)/ntap))/2;
w(end-ntap+1:end)=flipud(w(1:ntap));

X=fft(x.*repmat(w,1,nseg))/seg;
P=abs(X(2:floor(seg/2),:));

taxis=([1 nseg]*inc-inc/2)*dt;
faxis=[1 floor(seg/2)-1]/seg/dt;
Pn=max(max(P));
nc=size(colormap,1);
if get(findobj(g.panelfig,'string','linear'),'value')==1
    image(taxis,faxis,P/Pn,'cdatamap','scaled','parent',ax,'userdata',P)
    axes(colax)
    image(1,[0,Pn],[0:nc-1]'/(nc-1),'cdatamap','scaled');
    set(colax,'clim',get(ax,'clim'),'ylim',[0,Pn]);
    ylabel('amplitude')
else
    image(taxis,faxis,log10(P/Pn),'cdatamap','scaled','parent',ax,'userdata',P)
    axes(colax)
    minp=min(min(P/Pn));
    image(1,log10([minp 1]*Pn),[log10(minp):-log10(minp)/(nc-1):0]','cdatamap','scaled');
    set(colax,'clim',get(ax,'clim'),'ylim',log10([minp 1]*Pn));
    ylabel('log_{10} amplitude')
end
try
    linkaxes([ax axraw],'x');
end
ud.tmax=(length(data)-1)*dt;
set(g.panelfig,'userdata',ud);
set([g.ccon g.clim],'enable','on')
return

