% funkce pro změnu základního kmitočtu audio signálu
%
% význam vstupních proměnných:
% ============================
% y vstupní signál určený ke zpracování
% Fs vzorkovací kmitočet zpracovávaného signálu
% pulsy - detekovaná poloha pulsů (výstup z funkce pro analýzu)
% kmitocet - detekovaný průběh základního kmitočtu (výstup z funkce pro analýzu)
% posun - požadovaný posun základního kmitočtu
%   [] - automaticky v závislosti na vstupní proměnné kmitocet
%   ischar - název požadované noty
%   index - udává požadovaný posun v indexu not
% rozsah - [počáteční_vzorek koncový_vzorek] rozsah signálu, na kterém bude probíhat doladění
%
% význam výstupních proměnných:
% =============================
% y2 výstupní signál se změněným základním kmitočtem

function y2 = Doladeni(y, Fs, pulsy, kmitocet, posun, rozsah)

    bar = waitbar(0, 'Probíhá doladění ...', 'WindowStyle', 'modal'); % ukazatel fáze dokončení doladění (bargraf)

    %% Vytovření vektoru kmitocet2 s novým žádaným průběhem kmitočtu
    N = length(y); % počet vzorků v dolaďovaném signálu
    seg = length(kmitocet); % počet segmentů použitý při analýze = počet hodnot kmitočtů pro celý průběh
    
    kmitocet(kmitocet < 20) = 1; % neaktivní hodnoty původního kmitočtu = 1, aby šel vypočítat index noty

    if isempty(posun) % automatické doladění
        n = 12*log2(kmitocet/str2double(get(findobj('Tag', 'Reference'), 'String'))); % převod původního kmitočtu na index
        rozdil = round(n) - n; % výpočet odchylky od přesného kmitočtu
        n2 = n + rozdil; % přičtení odchylky k původnímu kmitočtu
        kmitocet2 = str2double(get(findobj('Tag', 'Reference'), 'String')) * 2.^(n2/12); % doladěný průběh kmitočtu
        
    elseif ischar(posun) % posun ke konkrétní notě
        nazevnot = {'D#' 'E' 'F' 'F#' 'G' 'G#' 'A' 'A#' 'H' ... % název not pro určení indexu noty
                    'c' 'c#' 'd' 'd#' 'e' 'f' 'f#' 'g' 'g#' 'a' 'a#' 'h' ...
                    'c1' 'c#1' 'd1' 'd#1' 'e1' 'f1' 'f#1' 'g1' 'g#1' 'a1' 'a#1' 'h1'...
                    'c2' 'c#2' 'd2' 'd#2' 'e2' 'f2' 'f#2' 'g2' 'g#2' 'a2' 'a#2' 'h2' 'c3' 'c#3'};
        
        n = -30:1:16;
        n = n(strcmp(posun, nazevnot)); % zjištěný index požadované noty
        
        kmitocet2 = zeros(1, length(kmitocet));
        kmitocet2 = kmitocet2 + str2double(get(findobj('Tag', 'Reference'), 'String')) * 2^(n/12); % požadovaný kmitočet
                
    else % posun o určitou hodnotu
        n = 12*log2(kmitocet/str2double(get(findobj('Tag', 'Reference'), 'String'))); % převod původního kmitočtu na index
        n2 = n + posun; % přičtení požadovaného posunu k původní hodnotě
        kmitocet2 = str2double(get(findobj('Tag', 'Reference'), 'String')) * 2.^(n2/12); % požadovaný průběh kmitočtu
        
    end
    
    kmitocet2(kmitocet == 1) = -50; % neaktivní hodnoty původního kmitočtu jsou předány do nového kmitočtu
    
    %% Vytovření vektoru pulsy2 s novými polohami pulsů
    pulsy = pulsy((pulsy>rozsah(1))&(pulsy<rozsah(2))); % původní pozice pulsů jen vybraného úseku signálu
    pulsy2(1:2) = pulsy(1); % do nového vektoru se první pozice okopíruje z původního vektoru
                            % do prvních dvou pozic, aby mohla být vytvořená následující podmínka
    i = 3; % počáteční hodnota indexování
    
    while pulsy2(i-1) < rozsah(2) - (pulsy2(i-1) - pulsy2(i-2)) % dokud je poslední zapsaná pozice v dolaďovaném úseku
        if kmitocet2(ceil(seg*pulsy2(i-1)/N)) > 0
            perioda = round(Fs/kmitocet2(ceil(seg*pulsy2(i-1)/N))); % počet vzorků za předchozím pulsem odpovídající
                                                                    % periodě předcházejícího pulsu                                                                   
            pulsy2(i) = pulsy2(i-1) + perioda; % nová pozice pulsu
            
        elseif kmitocet2(ceil(seg*pulsy2(i-1)/N):end) < 0 % pokud pro zbytek signálu není detekovaný kmitočet
            break
            
        else % pokud v dolaďovaném úseku započala pauza (bez zpěvu) a za chvíli zkončí
            zacatekpauzy = ceil(seg*pulsy2(i-1)/N); % pořadí segmentu v kmitocet2, kde zacina pauza
            
            pauza = kmitocet2(zacatekpauzy:end) < 0; % od začátku pauzy po konec signálu nastaví všechny 
                                                     % segmenty s pauzou na 1, zpěv na 0
            % určení počtu segmentů představující jen první pauzu 
            delkapauzy = 0;
            j = 1;
            
            while (pauza(j) == 1) & (j < length(pauza))
                delkapauzy = delkapauzy + 1; % počet segmentů v kmitocet2, kde je pauza
                j = j + 1; % zvýšení indexu o 1
            end
            
            konecpauzy = length(kmitocet2(1:zacatekpauzy+delkapauzy)); % pořadí segmentu, kde začíná zpěv po pauze
            konecpauzy = round((konecpauzy*N)/seg); % přepočet pořadí segmentu začátku zpěvu na pořadí vzorku v signálu
            
            puls = pulsy(pulsy > konecpauzy); % původní pulsy za začátkem zpěvu po pauze
            
            if ~isempty(puls) & (puls(1) < rozsah(2)) % pokud po pauze nějaké původní pulsy existují a zároveň je menší, 
                pulsy2(i) = puls(1); % než dolaďovaný rozsah dosadí se první pozice pulzu do nových pulzů
            else
                break
            end
  
        end
        
        i = i + 1; % zvýšení indexu
    end
 
    %% Vyjmutí period ze signálu, váhování a uložení do matice
    y2 = y; % pro začátek je výstupní signál stejný jako vstupní
    
    Nvzperiode = ceil(2*(Fs/max(kmitocet(ceil(seg*rozsah(1)/N):ceil(seg*rozsah(2)/N))))); % maximální počet vzorků ve vybírané periodě (perioda s 50% přesahem na každou stranu)
    Nperiod = length(pulsy); % počet dříve nalezených a označených pulsů, tedy počet výsledných period
    periody = zeros(Nvzperiode, Nperiod); % prázdná matice pro naplnění periodama
    
    waitbarkrok = 0.5/Nperiod; % obsluha waitbaru
    aktualnikrok = 0;
    
    for i = 1:Nperiod
       if kmitocet(ceil(seg*pulsy(i)/N)) > 1 % pokud by se chtěla vyjmou perioda z úseku signálu, kde byla detekována pauza
            delkaperiody =  ceil(2*(Fs/kmitocet(ceil(seg*pulsy(i)/N)))); % pocet vzorků v konkrétní periodě + 50% na každou stranu každého pulsu (podle aktuálního kmitočtu
            w = hann(delkaperiody);   % von Hannovo váhovací okno
       
            % meze vzorků signálu pro výběr periody
            if (pulsy(i) - (delkaperiody/2)) <= 1 % pokud by vybírací meze zasahovali před signál
                dolnimez = 1;
                hornimez = pulsy(i) + (delkaperiody/2 - 1);
           
                w = w(end - (hornimez - dolnimez):end); % ořezání váhovacího okna podle hodnot mezí
            
            elseif (pulsy(i) + (delkaperiody/2) - 1) >= N; % pokud by vybírací meze zasahovali za signál
                dolnimez = pulsy(i) - (delkaperiody/2);
                hornimez = N;
           
                w = w(1:1 + (hornimez-dolnimez)); % ořezání váhovacího okna podle hodnot mezí
           
            else
                dolnimez = pulsy(i) - (delkaperiody/2);
                hornimez = pulsy(i) + (delkaperiody/2 - 1);
            end
       
            perioda = y(dolnimez : hornimez) .*w; % vybraná a váhovaná perioda ze vstupního signálu
       
            periody(1:length(perioda), i) = perioda; % zapsání periody do matice s periodama
       
            y2(dolnimez : hornimez) = y2(dolnimez : hornimez) - perioda; % odečtení vyjmuté periody z výstupního signálu
        end
       
        aktualnikrok = aktualnikrok + waitbarkrok; % obsluha waitbaru
        waitbar(aktualnikrok);
    end
    
    %% Dosazení dříve vyjmutých period zpět do výstupního signálu na nové pozice
    
    Nperiod2 = length(pulsy2); % počet nových požadovaných pulsů, tedy počet dosazovaných period
    
    waitbarkrok = 0.5/Nperiod2; % obsluha waitbaru
    
    for i = 1:Nperiod2
        poradi = ceil(i*Nperiod/Nperiod2); % rozhodnutí, která z dříve uložených period se použije
        
        perioda = periody(:,poradi); % výběr vybrané periody z matice
        perioda = perioda(perioda ~= 0); % ořezání periody od nul
        
        delkaperiody = length(perioda); % zjištění délky použité periody
        
       % meze vzorků signálu pro dosazení periody
       if (pulsy2(i) - (delkaperiody/2)) < 1 % pokud by dosazovací meze zasahovali před signál
           dolnimez = 1;
           hornimez = pulsy2(i) + (delkaperiody/2) - 1;
           
           perioda = perioda(end - (hornimez - dolnimez):end); % ořezání dosazované periody podle hodnot mezí
           
       elseif (pulsy2(i) + (delkaperiody/2) - 1) > N; % pokud by dosazovací meze zasahovali za signál
           dolnimez = pulsy2(i) - (delkaperiody/2);
           hornimez = N;
           
           perioda = perioda(1:1 + (hornimez-dolnimez)); % ořezání dosazované periody podle hodnot mezí
           
       else
           dolnimez = pulsy2(i) - (delkaperiody/2);
           hornimez = pulsy2(i) + (delkaperiody/2) - 1;
       end
       
       y2(dolnimez : hornimez) = y2(dolnimez : hornimez) + perioda; % vložení periody na nové místo
       
       aktualnikrok = aktualnikrok + waitbarkrok; % obsluha waitbaru
       waitbar(aktualnikrok);
    end

    %% ukončení waitbaru
    waitbar(1);
    pause(0.5);
    close(bar);
end