function spc01
% spc01.m Spectrogram Viewer スペクトログラムの作成
%    19-Jun-2007 Hasada version 0.01beta

close all;

%%%% figure設定 %%%%
width=800;
fheight=500;
fig=figure('position',[20 20 800 500],'units','pixels','tag','mainfig',...
    'menubar','figure','numbertitle','off',...
    'name','Spectrogram Viewer 0.01beta');
colormap jet;

%%%% グラフ領域 %%%%
ax=axes('nextplot','replacechild','box','on','position',[.1 .1 .8 .5],...
    'tag','mainax','parent',fig);
xlabel('time / s')
ylabel('freq. / Hz')
axraw=axes('nextplot','replacechild','box','on','position',[.1 .65 .8 .1],...
    'tag','axraw','parent',fig);
linkaxes([ax axraw],'x');

%%%% コントロールパネル %%%%
height=100;
up=uipanel('parent',fig,'units','pixels','position',[0 fheight-height width height]);

%%%% ファイル %%%%
uicontrol('style','text','string','データファイル',...
    'horizontalalignment','center',...
    'units','pixels','fontunits','pixels','fontsize',12,...
    'parent',up,...
    'position',[5 height-20 90 15]);
uicontrol('style','edit','tag','filename',...
    'horizontalalignment','left',...
    'units','pixels','fontunits','pixels','fontsize',12,...
    'callback',@openfile,...
    'parent',up,...
    'position',[100 height-20 195 18]);
uicontrol('style','pushbutton','string','参照',...
    'units','pixels','fontunits','pixels','fontsize',12,...
    'callback',@openfile,...
    'parent',up,...
    'position',[300 height-20 35 18]);
uicontrol('style','popupmenu','string','データを選択',...
    'units','pixels','fontunits','pixels','fontsize',12,...
    'callback',@selvar,'tag','selvar',...
    'parent',up,...
    'position',[340 height-20 85 18]);
uicontrol('style','popupmenu','string','列を選択',...
    'units','pixels','fontunits','pixels','fontsize',12,...
    'callback','','tag','selcol',...
    'parent',up,...
    'position',[430 height-20 85 18]);
uicontrol('style','checkbox','string','転置',...
    'units','pixels','fontunits','pixels','fontsize',12,...
    'callback',@transpose,'tag','trans',...
    'parent',up,...
    'position',[520 height-20 45 18]);
uicontrol('style','text','string','サンプリング周波数(Hz)',...
    'units','pixels','fontunits','pixels','fontsize',9,...
    'parent',up,...
    'position',[5 height-40 95 15]);
uicontrol('style','edit','string','100',...
    'units','pixels','fontunits','pixels','fontsize',12,...
    'horizontalalignment','right','tag','samplef',...
    'parent',up,...
    'position',[100 height-40 45 18]);
uicontrol('style','text','string','時間窓幅(秒)',...
    'units','pixels','fontunits','pixels','fontsize',12,...
    'parent',up,...
    'position',[150 height-40 75 15]);
uicontrol('style','edit','string','3',...
    'units','pixels','fontunits','pixels','fontsize',12,...
    'horizontalalignment','right','tag','timewin',...
    'parent',up,...
    'position',[235 height-40 45 18]);
uicontrol('style','pushbutton','string','描画',...
    'units','pixels','fontunits','pixels','fontsize',12,...
    'callback',{@plotimg,ax,axraw},'tag','draw',...
    'parent',up,...
    'position',[305 height-40 45 20]);
    
%%%% 表示（範囲） %%%%
uicontrol('style','togglebutton','string','zoom',...
    'tooltip','グラフを拡大',...
    'horizontalalignment','center',...
    'units','pixels','fontunits','pixels','fontsize',15,...
    'callback',@togbtn,...
    'parent',up,...
    'position',[5 height-60 50 18]);
uicontrol('style','togglebutton','string','pan',...
    'tooltip','グラフをスクロール',...
    'horizontalalignment','center',...
    'units','pixels','fontunits','pixels','fontsize',15,...
    'callback',@togbtn,...
    'parent',up,...
    'position',[60 height-60 50 18]);
uicontrol('style','togglebutton','string','off',...
    'horizontalalignment','center',...
    'units','pixels','fontunits','pixels','fontsize',15,...
    'callback',@togbtn,'value',1,...
    'parent',up,...
    'position',[115 height-60 50 18]);
uicontrol('style','pushbutton','string','manual',...
    'tooltip','グラフの範囲を設定',...
    'horizontalalignment','center',...
    'units','pixels','fontunits','pixels','fontsize',15,...
    'callback',@setax,...
    'parent',up,...
    'position',[170 height-60 50 18]);
uicontrol('style','pushbutton','string','reset',...
    'tooltip','グラフの範囲を最初の状態に戻す',...
    'horizontalalignment','center',...
    'units','pixels','fontunits','pixels','fontsize',15,...
    'callback',@resetax,...
    'parent',up,...
    'position',[225 height-60 50 18]);

%%%% 表示（色） %%%%
uicontrol('style','text','string','色の設定',...
    'horizontalalignment','center',...
    'units','pixels','fontunits','pixels','fontsize',12,...
    'parent',up,...
    'position',[5 height-80 55 15]);
uicontrol('style','radio','string','linear',...
    'value',1,...
    'callback',@linlog,...
    'parent',up,...
    'position',[63 height-80 50 18]);
uicontrol('style','radio','string','log',...
    'value',0,...
    'callback',@linlog,...
    'parent',up,...
    'position',[115 height-80 50 18]);
uicontrol('style','popupmenu',...
    'string',{'青→赤','白→黒','カラーマップエディタ'},...
    'callback',@changecolmap,...
    'parent',up,...
    'position',[170 height-80 115 18]);
uicontrol('style','text','string','色の閾値',...
    'horizontalalignment','center',...
    'units','pixels','fontunits','pixels','fontsize',12,...
    'parent',up,...
    'position',[290 height-80 55 15]);
uicontrol('style','slider','value',1,...
    'tooltip','色の閾値を調整',...
    'callback',@changeclim,...
    'parent',up,...
    'position',[350 height-80 100 15]);

%%%% リサイズ設定 %%%%
set(fig,'resizefcn',@resizfig);

return

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

function resizfig(src,ev)
fig=gcbo;
fpos=get(fig,'position');
up=findobj('type','uipanel');
ppos=get(up,'position');
set(up,'position',[0 fpos(4)-ppos(4) fpos(3) ppos(4)])
return

%%%% ファイル %%%%
function openfile(src,ev)
try
    switch get(src,'style')
        case 'edit'
            fpath=get(src,'string');
        case 'pushbutton'
            [fname,pname]=uigetfile({...
                '*.*','*.* テキストファイル';...
                '*.mat','*.mat MATファイル';...
                '*.wav','*.wav WAVファイル'},'ファイルを開く');
            if fname==0
                return
            end
            fpath=[pname fname];
            set(findobj('tag','filename'),'string',fpath)
    end
    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
    vnames=fieldnames(x);
    n=size(vnames,1);
    for k=1:n
        v=getfield(x,vnames{k});
        if ~isnumeric(v)|prod(size(v))==1
            x=rmfield(x,vnames{k});
        end
    end
    vnames=fieldnames(x);
    if size(vnames,1)~=1
        set(findobj('tag','selvar'),'string',{'データを選択' vnames{:}},...
            'value',1)
        set(findobj('tag','selcol'),'string','列を選択','value',1)
        set(findobj('tag','trans'),'value',0)
    else
        set(findobj('tag','selvar'),'string',{'データを選択' vnames{1}},...
            'value',2)
        set(findobj('tag','trans'),'value',0)
        sz=size(getfield(x,vnames{1}));
        if sz(2)==1
            set(findobj('tag','selcol'),'string',{'列を選択';'1'},...
                'value',2)
        elseif sz(1)==1
            set(findobj('tag','trans'),'value',1)
            set(findobj('tag','selcol'),'string',{'列を選択';'1'},...
                'value',2)
        else
            set(findobj('tag','selcol'),'string',{'列を選択';num2str([1:sz(2)]')},...
                'value',1)
        end
    end
    ud=get(findobj('tag','mainfig'),'userdata');
    ud.data=x;
    set(findobj('tag','mainfig'),'userdata',ud);
    if exist('fs')==1&size(fs)==[1 1]
        set(findobj('tag','samplef'),'string',num2str(fs))
    end
    if exist('tlen')==1&size(fs)==[1 1]
        set(findobj('tag','timewin'),'string',num2str(tlen/20))
    end
    
catch
    errordlg('ファイルを読み込めません');
end
return

function selvar(src,ev)
if get(src,'value')==1
    return
end
vnames=get(src,'string');
data=getfield(get(findobj('tag','mainfig'),'userdata'),'data');
sz=size(getfield(data,vnames{get(src,'value')}));
set(findobj('tag','trans'),'value',0)
if sz(2)==1
    set(findobj('tag','selcol'),'string',{'列を選択';'1'},...
        'value',2)
elseif sz(1)==1
    set(findobj('tag','trans'),'value',1)
    set(findobj('tag','selcol'),'string',{'列を選択';'1'},...
        'value',2)
else
    set(findobj('tag','selcol'),'string',{'列を選択';num2str([1:sz(2)]')},...
        'value',1)
end
return

function transpose(src,ev)
if size(get(findobj('tag','selcol'),'string'),1)<=2
    set(src,'value',~get(src,'value'))
    return
end
v=get(findobj('tag','selvar'),'value');
vs=get(findobj('tag','selvar'),'string');
sz=size(getfield(getfield(get(findobj('tag','mainfig'),'userdata'),'data'),vs{v}));
if sz(2)==1
    set(findobj('tag','selcol'),'string',{'列を選択';'1'},...
        'value',2)
    set(src,'value',0)
elseif sz(1)==1
    set(src,'value',1)
    set(findobj('tag','selcol'),'string',{'列を選択';'1'},...
        'value',2)
else
    switch get(src,'value')
        case 1
            set(findobj('tag','selcol'),'string',{'列を選択';num2str([1:sz(1)]')},...
                'value',1)
        case 0
            set(findobj('tag','selcol'),'string',{'列を選択';num2str([1:sz(2)]')},...
                'value',1)
    end
end
return

%%%% 表示（範囲） %%%%
function xrange(src,ev,lim)
set(findobj('type','axes'),'xlim',[0 lim]);
return

function togbtn(src,ev)
switch get(src,'string')
    case 'zoom'
        set(src,'value',1)
        set(findobj('string','pan'),'value',0)
        set(findobj('string','off'),'value',0)
        zoom on
    case 'pan'
        set(src,'value',1)
        set(findobj('string','zoom'),'value',0)
        set(findobj('string','off'),'value',0)
        pan on
    case 'off'
        set(findobj('string','pan'),'value',0)
        set(findobj('string','zoom'),'value',0)
        set(src,'value',1)
        pan off
        zoom off
end
return

function setax(src,ev)
answer=inputdlg('軸範囲を入力してください [tmin tmax fmin fmax]',...
    'Set axis');
if isempty(answer)
    return
end
try
    ar=str2num(cell2mat(answer));
    axis(findobj('tag','mainax'),ar);
catch
    errordlg('軸範囲の指定が正しくありません。')
end
return

function resetax(src,ev)
axis(findobj('type','axes'),'tight')
return

function linlog(src,ev)
s=get(src,'string');
set(src,'value',1)
obj=findobj('type','image');
P=get(obj,'userdata');
P=P/max(max(P));
sli=findobj('style','slider');
set(findobj('tag','mainax'),'climmode','auto')
switch s
    case 'linear'
        set(obj,'cdata',P);
        set(findobj('string','log'),'value',0);
        set(sli,'value',1)
    case 'log'
        set(obj,'cdata',log(P),'cdatamap','scaled');
        set(findobj('string','linear'),'value',0);

        set(sli,'value',0)
end
return

function changecolmap(src,ev)
axes(findobj('tag','mainax'))
sli=findobj('style','slider');
switch get(src,'value')
    case 1
        colormap jet
        set(sli,'enable','on')
    case 2
        colormap(flipud(gray))
        set(sli,'enable','on')
    case 3
        set(sli,'enable','off')
        colormapeditor
end
return

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


%%%% 描画 %%%%
function plotimg(src,ev,ax,axraw)

ud=get(findobj('tag','mainfig'),'userdata');
if ~isfield(ud,'data')
    return
end
vn=get(findobj('tag','selvar'),'value');
c=get(findobj('tag','selcol'),'value');
if vn<=1|c<=1
    errordlg('変数・列を選択してください');
    return
end
vnames=get(findobj('tag','selvar'),'string');
data=getfield(ud.data,vnames{vn});
if get(findobj('tag','trans'),'value')==1
    data=data.';
end
data=data(:,c-1);

try
    dt=1/str2num(get(findobj('tag','samplef'),'string'));
catch
    h=errordlg('サンプリング周波数が正しくありません');
    uiwait(h);
    set(findobj('tag','samplef'),'string','');
    return
end
if ~isfinite(dt)|dt<0
        h=errordlg('サンプリング周波数が正しくありません');
    uiwait(h);
    set(findobj('tag','samplef'),'string','');
    return
end

try
    tw=str2num(get(findobj('tag','timewin'),'string'));
    seg=round(tw/dt);
catch
    h=errordlg('時間窓幅が正しくありません');
    uiwait(h);
    set(findobj('tag','timewin'),'string','');
    return
end
nseg=floor(length(data)/seg);
if nseg<=1
    h=errordlg('時間窓幅が正しくありません');
    uiwait(h);
    set(findobj('tag','timewin'),'string','');
    return
end

axes(axraw);
if ~isreal(data)
    hw=warndlg('複素数データは実部のみ扱います');
    uiwait(hw)
    data=real(data);
end
plot([0:length(data)-1]*dt,data)
axis tight
yl=ylim;

x=reshape(data(1:seg*nseg),seg,nseg);

X=fft(x)/seg;
P=abs(X(2:floor(seg/2),:));

taxis=([1 nseg]*seg-seg/2)*dt;
faxis=[1 floor(seg/2)-1]/seg/dt;
if get(findobj('string','linear'),'value')==1
    image(taxis,faxis,P/max(max(P)),'cdatamap','scaled','parent',ax,'userdata',P)
else
    image(taxis,faxis,log(P/max(max(P))),'cdatamap','scaled','parent',ax,'userdata',P)
end
ylim(yl);

ud.tmax=(length(data)-1)*dt;
set(findobj('tag','mainfig'),'userdata',ud);

set(findobj('string','zoom'),'value',0)
zoom off
set(findobj('string','pan'),'value',0)
pan off
set(findobj('string','off'),'value',1)
return
