
% Version 2.2 (Wednesday 25th May 2016)
% Distortion/Convolution Function by Daniel Natoli 
%
% This function loads two audio files (.wav) and and convolves the two using 
% FFT convolution, applying a time-variant distortion curve to the convolved file
% over time. The files are then mixed and summed for output.
%
% v2.0 Updates: This script now offers IR reversal and time stretching! 
% v2.2 Updates: Improved processing speed, fixed various bugs. 
%
% This function calls the following functions (supplied):
%
    % distconv_systemA.m
    % distconv_systemB.m
    % fft_conv.m
    % mix_percentage_check.m
    % my_decay_function.m
    % expdist.m
    % symclip.m
    % tanhdist.m 
    % stretchtest.m (paired with princarg.m)
    
%%

function [output, fs] = distconv_function(input, IR, fs, decayfactor, dist_value, mix_wetdry, mix_distortion, distortion_option, reverse_IR, t_stretch_ans, systemstatus) 
%% Display and Check Inputs

    % Checks mix percentage values with mix_percentage_check.m and returns
    % an error message if values are incorrect or exceed the allocated range.

    [mix_dry, mix_wet, mix_distortion_wet, mix_distortion_dry, decayfactor] = mix_percentage_check(mix_wetdry, mix_distortion, decayfactor); 

    [~, channelsIN] = size(input); 
    [~, channelsIR] = size(IR); 
    
        % Reverses the Impulse Response  
        switch reverse_IR
            case 'Yes' 
            IR = flipud(IR);
            disp('IR Reversed!')
        end
                
        % If "Time-Stretch IR using stretchtest.m" is selected...
        if t_stretch_ans == 1; 
           choice = questdlg('Are you sure you want to time-stretch the Impulse Response? Depending on the length of the file, you might be here for a while...');
                switch choice
                case 'Yes'
                    IR = stretchtest(IR); 
                case 'No'
                    disp('Time Stretching Cancelled...')
                end 
        else disp('Time Stretching Cancelled...')                
        end 
       
        % Displays the chosen distortion function. 
        switch distortion_option  
            case 1
            disp('You have selected the "tanh" distortion function.');
            case 2 
            disp('You have selected the "expdist" distortion function.');
            case 3
            disp('You have selected the "symclip" distortion function.');
        end 
        
        % Displays the chosen system status
        if systemstatus == 'A';
            disp('You have selected SYSTEM A!');
        else
            disp('You have selected SYSTEM B!');
        end
        
%% Run Functions         

% When the input and IR files are both mono: 

    if channelsIN == 1 && channelsIR == 1;  
       disp('The input and IR are both mono.');

            % Runs the corresponding System A/B function. 
            if systemstatus == 'A';
                    convolved_sum = distconv_systemA(input, IR, fs, distortion_option, dist_value, decayfactor, mix_distortion_dry, mix_distortion_wet);
            elseif systemstatus == 'B';
                    convolved_sum = distconv_systemB(input, IR, fs, distortion_option, dist_value, decayfactor, mix_distortion_dry, mix_distortion_wet);
            else error('System Status MUST be set to "A" or "B"') 
            end
   
            % Pad input signal with 0's to ensure matching lengths
            input(numel(convolved_sum)) = 0;

            % Create Output Mix 
            output = (input*mix_dry)+(convolved_sum*mix_wet);

            % Normalise Output 
            output = output./max(abs(output));
                        
% When the input is mono and the IR is stereo: 

    elseif channelsIN == 1 && channelsIR ==2; 
       disp('The input is mono and IR is stereo.');

            % Splits a mono input in to two mono signals
            input_L = input(:,1); 
            input_R = input(:,1);
       
            % Splits a stereo IR in to two mono signals
            IR_L = IR(:,1);
            IR_R = IR(:,2);

            % Runs the corresponding System A/B function. 
            if systemstatus == 'A';
                    convolved_sumL = distconv_systemA(input_L, IR_L, fs, distortion_option, dist_value, decayfactor, mix_distortion_dry, mix_distortion_wet);
                    convolved_sumR = distconv_systemA(input_R, IR_R, fs, distortion_option, dist_value, decayfactor, mix_distortion_dry, mix_distortion_wet);
            elseif systemstatus == 'B';
                    convolved_sumL = distconv_systemB(input_L, IR_L, fs, distortion_option, dist_value, decayfactor, mix_distortion_dry, mix_distortion_wet);
                    convolved_sumR = distconv_systemB(input_R, IR_R, fs, distortion_option, dist_value, decayfactor, mix_distortion_dry, mix_distortion_wet);
            else error('System Status MUST be set to "A" or "B"') 

            end            

                % Determines which file is longest, in order to apply
                % correct zero padding. 
                [s1, ~] = size(convolved_sumL); 
                [s2, ~] = size(convolved_sumR); 

                    if s1 >= s2;
                       x = convolved_sumL ;
                       convolved_sumR(numel(x)) = 0; 
                    else
                       x = convolved_sumR;
                       convolved_sumL(numel(x)) = 0; 
                    end

                % Pad input signal with 0's to ensure matching lengths
                input_L(numel(x)) = 0; 
                input_R(numel(x)) = 0; 

            input = [input_L input_R];

            convolved_sum_total = [convolved_sumL, convolved_sumR];

            % Create Output Mix
            output = (input*mix_dry)+(convolved_sum_total*mix_wet); 
         
            % Normalise Output Signal
            output = output./max(max(abs(output))); 


% When the input is stereo and the IR is mono:

    elseif channelsIN == 2 && channelsIR ==1; 
       disp('The input is stereo and IR is mono.'); 
            
            % Splits a mono input in to two mono signals
            input_L = input(:,1); 
            input_R = input(:,2);
       
            % Splits a stereo IR in to two mono signals
            IR_L = IR(:,1);
            IR_R = IR(:,1);

            if systemstatus == 'A';
                    convolved_sumL = distconv_systemA(input_L, IR_L, fs, distortion_option, dist_value, decayfactor, mix_distortion_dry, mix_distortion_wet);
                    convolved_sumR = distconv_systemA(input_R, IR_R, fs, distortion_option, dist_value, decayfactor, mix_distortion_dry, mix_distortion_wet);
            elseif systemstatus == 'B'; 
                    convolved_sumL = distconv_systemB(input_L, IR_L, fs, distortion_option, dist_value, decayfactor, mix_distortion_dry, mix_distortion_wet);
                    convolved_sumR = distconv_systemB(input_R, IR_R, fs, distortion_option, dist_value, decayfactor, mix_distortion_dry, mix_distortion_wet);
            else error('System Status MUST be set to "A" or "B"') 
            end  
            
            % Determines which file is longest, in order to apply
            % correct zero padding. 
            
            [s1, ~] = size(convolved_sumL); 
            [s2, ~] = size(convolved_sumR); 

                if s1 >= s2;
                   x = convolved_sumL ;
                   convolved_sumR(numel(x)) = 0; 
                else
                   x = convolved_sumR;
                   convolved_sumL(numel(x)) = 0; 
                end

            input_L(numel(x)) = 0; 
            input_R(numel(x)) = 0; 

            input = [input_L input_R];

            convolved_sum_total = [convolved_sumL, convolved_sumR];

            output = (input*mix_dry)+(convolved_sum_total*mix_wet); 
         
            % Normalise Output Signal
            output = output./max(max(abs(output)));

% When the input is stereo and the IR is stereo: 

    elseif channelsIN == 2 && channelsIR ==2; 
    disp('The input is stereo and IR is stereo.');  

            % Splits a mono input in to two mono signals
            input_L = input(:,1); 
            input_R = input(:,2);
       
            % Splits a stereo IR in to two mono signals
            IR_L = IR(:,1);
            IR_R = IR(:,2);


            if systemstatus == 'A';
                    convolved_sumL = distconv_systemA(input_L, IR_L, fs, distortion_option, dist_value, decayfactor, mix_distortion_dry, mix_distortion_wet);
                    convolved_sumR = distconv_systemA(input_R, IR_R, fs, distortion_option, dist_value, decayfactor, mix_distortion_dry, mix_distortion_wet);
            elseif systemstatus == 'B';
                    convolved_sumL = distconv_systemB(input_L, IR_L, fs, distortion_option, dist_value, decayfactor, mix_distortion_dry, mix_distortion_wet);
                    convolved_sumR = distconv_systemB(input_R, IR_R, fs, distortion_option, dist_value, decayfactor, mix_distortion_dry, mix_distortion_wet);
            else error('System Status MUST be set to "A" or "B"') 
            end  
        
            % Determines which file is longest, in order to apply
            % correct zero padding. 
            
            [s1, ~] = size(convolved_sumL); 
            [s2, ~] = size(convolved_sumR); 

                if s1 >= s2;
                   x = convolved_sumL ;
                   convolved_sumR(numel(x)) = 0; 
                else
                   x = convolved_sumR;
                   convolved_sumL(numel(x)) = 0; 
                end

            input_L(numel(x)) = 0; 
            input_R(numel(x)) = 0; 

            input = [input_L input_R];

            convolved_sum_total = [convolved_sumL, convolved_sumR];

            output = (input*mix_dry)+(convolved_sum_total*mix_wet); 
         
            % Normalise Output Signal
            output = output./max(max(abs(output)));

% If either channels are not stereo or mono: 

    else disp('ERROR - Incorrect format of input/output files...')
        
    end
    
end 