% c_IRsVideoMap_ambisonics_image.m Spatial acoustic mapping of an IR.
% This script implements source acoustic mapping using recordings of noise signal 
% got from a microphone array encoded into Ambisonics.
% Background is a static image. It saves video overlay with pseudocolor SPL maps.
%
% INPUT
% - encoded_format.mat -> file containing the ambisonics format generated by b_generateEncodedFormat
% - panoramic_360_image.jpg
%
% OUTPUT
% - acusticVideo.mp4
%
% Author: Daniel Pinardi.
% University of Aalto, Helsinki - Finlan, 2019.
% Last review: 05/2023, Daniel Pinardi

clear variables
close all
clc

addpath( './lib' )                                                          % add to path the library with needed common files
a_configureFileNamesAndDirectories                                          % configure filenames, directories, resources

set( 0, 'DefaultTextInterpreter', 'none' );                                 % setting for map titles
warning off                                                                 % to avoid warnings on image size


%% AUDIO PARAMETERS
% set fullscale value
fullScaleLevel          = 130;                                              % dB

% Signal processing
trimEnable              = 1;        % 1 to enable signal trimming
aWeightingEnable        = 1;        % 1 to enable A filtering
timeSignalEnable        = 1;        % 1 to enable: plot time signal under video maps. Disable for VR
nfft                    = 16384; 
bufferSize              = 256;       % [samples]
stepSize				= 10;		% [samples]
autoselectOverlap       = 0;        % enable for VR video
frameRate               = 10;       % Default 30 (for VR) - min value for VLC is 6

% Bandpass filtering -> 0 (one of the two) to disable
bandpassLowCutFreq      = 0;        % Low Cutoff Frequency [Hz]
bandpassHighCutFreq     = 6000;     % High Cutoff Frequency [Hz]



%% AMBISONICS PARAMETER
fileAlreadyBformat      = 1; % 1 -> enable: use if the file is not A-format recording but already encoded outside
                             % file must be placed in ArrayRecording and filename must be set in
                             % configureFileNamesAndDirectories -> recordedFileName
maxOrderToUse           = 7; % limit the maximum Ambisonics order to use in the processing


%% IMAGE PARAMETER
resolutionMap           = 5; % degree

% pointing correction: + -> turn map to the right / - -> turn map to the left
horizontalTilt          = 0;        % degrees

% image
imageOffsetCorrection   = 0;    	% 1 to enable. Correct vertical offset between acoustical and optical center
radiusCorrection        = 1.5;      % [m]. The radius of the sphere where correction is performed
verticalOffset          = 0.15;     % [m]. The amount of offset to be compensated 
mapWindowWidth          = 2048;     % 0 to autoselect maximum possible, depending on screen resoution. Aspect ratio will be 2:1
outputWindowWidth       = 2048;     % Cannot be 0. Ignored if timeSignalEnable = 1.

outputBlackWhite        = 1;        % 1 -> blackwhite, 0 -> color
alpha                   = 0.3;      % parameter for transparency

% Surf mapping
useAdimensialPlotBounds = 0;        % colormap is in the range 0 - 1
adimensionalLowerLimit  = 0.05;     % upperlimit is always 1
useFixedSplPlotBounds   = 0;        % if 0 each surface plot has color normalization with fixedLowerLimit and fixedUpperLimit
fixedLowerLimit         = 80;       % user defined lower limit
fixedUpperLimit         = 90;       % user defined upper limit
noiseGate               = 25;       % max difference from maxlevel to plot
contourEnable           = 1;        % 1 to enable isolevel lines
contourSpan             = 2;        % [dB] define isoline difference
valueTickEnable         = 1;        % 1 to enable: show SPL value of isolevel lines
tickSize                = 8;		% font dimension
tickBold                = 0;		% 1 -> enable, 0 -> disable
colorbarEnable          = 1;		% 1 -> enable, 0 -> disable
injectMetadata          = 0;        % 1 -> enable, 0 -> disable


%%  OTHER PARAMETERS
% Frequency range order limitation
o2_lowF                 = 20;		% [Hz]
o2_highF                = 20000;    % [Hz]
o3_lowF                 = 20;		% [Hz]
o3_highF                = 20000;    % [Hz]
o4_lowF                 = 20;		% [Hz]
o4_highF                = 20000; 	% [Hz]
o5_lowF                 = 20;		% [Hz]
o5_highF                = 20000;    % [Hz]
o6_lowF                 = 20;		% [Hz]
o6_highF                = 20000;    % [Hz]
o7_lowF                 = 20;		% [Hz]
o7_highF                = 20000; 	% [Hz]



%% CHECK IF FFMPEG IS PRESENT IN C:\WINDOWS
if exist( 'C:\Windows\ffmpeg.exe', 'file' ) == 0
    fprintf( 'FFMPEG not found in C:\\Windows.\n' )
	fprintf( 'If necessary, download from " https://www.gyan.dev/ffmpeg/builds/ffmpeg-release-full.7z " and copy ffmpeg.exe in C:\\Windows.\n' );
end


%% LOAD DATA FROM FILES
% clear unused variables
clear backgroundVideoFile backgroundVideoFileName invSweepFile invSweepFileName irsFolder irsMatFileName irsWavFileName
clear mapsFolderIrs mapsFolderNoise outputFolder recordedNoiseFolder recordedReferenceSignalFileName recordedReferenceSignalFolder
clear recordedSweepFile recordedSweepFolder spsFilterMatrixFile spsFilterMatrixFileName spsIrsFolder spsIrsMatFileName
clear spsIrsWavFileName spsNoiseFolder spsNoiseWavFileName

% define filename to load and folder output
filename                = encodedIrsMatFileName;
fprintf( '\nNoise elaboration started\n' );

% Ambisonics format
if fileAlreadyBformat == 1
    [ ambixFormat, fs ] = audioread( [ './ArrayRecording/' recordedFileName ] );
else
    disp( [ 'Loading Ambix format file: ' encodedIrsMatFileName] );
    load( encodedIrsMatFileName );
    ambixFormat         = encodedFormat;
    clear encodedFormat
end

ambixVmic               = size( ambixFormat, 2 );                           % number of virtual microphones
ambixOrder              = sqrt( ambixVmic ) - 1;                            % ambisonics order available
if maxOrderToUse < ambixOrder
    ambixVmic           = ( maxOrderToUse + 1 ) ^ 2;
    ambixFormat         = ambixFormat( :, 1 : ambixVmic );
    ambixOrder          = maxOrderToUse;
end


% Background 360° image
disp( [ 'Loading background image file: ' backgroundImageFile ] );
backgroundImage         = imread( backgroundImageFile );


%% IMAGE PROCESSING
% fix width taking into account screen resolution scaling 
videoWindow				= figure( 'units', 'pixel', 'innerposition', [ 0 0 1000 500 ], 'menubar', 'none' );
F						= getframe( videoWindow );
widthsize				= size( F.cdata );
close( videoWindow );
if widthsize(2) ~= 1000
   ScreenScaleFactor	= widthsize( 2 ) / 1000;
%    outputWindowWidth	= outputWindowWidth / ScreenScaleFactor;
   mapWindowWidth		= mapWindowWidth / ScreenScaleFactor;
end

% check monitor dimension and resize if necessary
[ mapWindowWidth ]      = screenSize( mapWindowWidth, timeSignalEnable );   % clear unused variables

% check image aspect ratio and perform correction if needed ( output format is 2x1 ) and resize image
[ backgroundImage, backgroundHeight, backgroundWidth, ~ ] = image2x1resize( backgroundImage, mapWindowWidth );

% correction of offset between acoustic center and image center
if imageOffsetCorrection == 1
    fprintf( 'Perform image correction\n\n' );
    [ backgroundImage ] = imageVertOffsetCorr( backgroundImage, radiusCorrection, verticalOffset );
end

% if outputBlackWhite == 1
%     background          = (1+alpha) * flipud( backgroundImage( :, :, [ 1 1 1 ] ) ); % flip and conversion to black white
% else
%     background          = (1+alpha) * flipud( backgroundImage );                    % flip
% end

if outputBlackWhite == 1
    background          = flipud( backgroundImage( :, :, [ 1 1 1 ] ) ); % flip and conversion to black white
else
    background          = flipud( backgroundImage );                    % flip
end

clear radiusCorrection verticalOffset


%% A-weight filtering
if aWeightingEnable == 1
    fprintf( 'Generating A-weight filter.\n' );
    AWeighting          = weightingFilter( 'A-weighting', fs );             % use Matlab built-in A_weigth filter
    fprintf( 'A-weight filtering started.\n' );
    for i = 1 : ambixVmic
        ambixFormat( :, i ) = AWeighting( ambixFormat( :, i ) );
    end
    fprintf( 'A-weighting ended.\n' );
end


%% Bandpass filtering
if bandpassLowCutFreq ~= 0 && bandpassHighCutFreq ~= 0
    fprintf( 'Generating band-pass filter.\n' );
    hBandPass           = fdesign.bandpass( 'N,F3dB1,F3dB2', 10, bandpassLowCutFreq, bandpassHighCutFreq, fs );
    HBandPass           = design( hBandPass, 'butter' );
    fprintf( 'Band-pass filtering started.\n' );
    ambixFormat         = filter( HBandPass, ambixFormat );
    fprintf( 'Band-pass filtering ended.\n' );
end


%% Evaluate stepSize and overlap
if autoselectOverlap == 1
    stepSize            = floor( fs / frameRate );							% overwrite
    overlap             = ( 1 - stepSize / bufferSize ) * 100;    
else
	overlap             = ( 1 - stepSize / bufferSize ) * 100;   
% 	stepSize            = floor( bufferSize * ( 1 - overlap ) );                % calculate stepSize
end
fprintf( [ '\nFrame rate is ', num2str( frameRate ) ' fps, fs is ' num2str( fs ) ' Hz and stepsize is ' num2str( stepSize ) ' samples. \n' ] );
fprintf( [ '\nBuffer size is ' num2str( bufferSize ) ' samples and video overlap is ', num2str( overlap ) '%%. \n' ] );


%% TRIM
if trimEnable == 1
    
    fig                 = figure( 'windowstate', 'maximized' );
    hax                 = axes;
    plot( ambixFormat( :, 1 ) )
    xlim( [ 0 size( ambixFormat, 1 ) ] )
    grid on
    xlabel( 'Samples' )
    title( 'Audio recorded' )

    prompt              = 'AUDIO samples to skip ( 0 is accepted ): ';
    noiseStart          = input( prompt );
    line( [ noiseStart noiseStart ], get( hax, 'YLim' ), 'Color', [ 1 0 0 ] )
    prompt              = 'last AUDIO sample: ';
    noiseEnd            = input( prompt );

    close all
    ambixFormat         = ambixFormat( 1 + noiseStart : noiseEnd, : );      % trim noise
	
end

ambixLength             = size( ambixFormat, 1 );
numbersFrame            = floor( ambixLength / stepSize );                  % integer number of frames
noiseEnd                = stepSize * numbersFrame;                          % update value
ambixFormat             = ambixFormat( 1 : noiseEnd, : );                   % trim noise


%% FULL SCALE LEVEL
fullScale               = fullScaleLevel + 20 * log10( rms( ambixFormat( :, 1 ) ) );


%% ZERO PADDING
zeroPadMatrix           = zeros( bufferSize/2, ambixVmic );                 % center of buffer must be at 0
ambixFormat             = [ zeroPadMatrix; ambixFormat; zeroPadMatrix ];    % apply zero padding


%% GRID CREATION
aziElev2aziPolar        = @(dirs) [ dirs( :, 1 ) pi/2-dirs( :, 2 ) ];       
grid_dirs               = grid2dirs( resolutionMap, resolutionMap, 0, 0 ); % Grid of directions to evaluate DoA estimation
grid_dirs( :, 1 )       = - grid_dirs( :, 1 );
gridMap                 = Fdirs2grid( grid_dirs( :, 1 ), resolutionMap, resolutionMap, 1 );
gridMapEl               = size( gridMap, 1 );
gridMapAz               = size( gridMap, 2 );


%% DATA PROCESSING
ambixOmni               = ambixFormat( :, 1 );

fprintf( [ 'Processing ' num2str( numbersFrame ) ' frames.\n' ] );
splMatrix               = zeros ( gridMapEl, gridMapAz, numbersFrame );     % preallocation

% Define window
hannWin                 = hann( bufferSize );
energyWinFactor         = ( rms( hannWin ) ) ^-1;
hannWin                 = repmat( hannWin, [ 1 ambixVmic ] );

% set frequency limit for orders > 1
freq                    = linspace( 0, fs/2, nfft/2 + 1 );

[ ~, index_o2_lowF ]    = min( abs( freq - o2_lowF ) );
[ ~, index_o2_highF ]   = min( abs( freq - o2_highF ) );

[ ~, index_o3_lowF ]    = min( abs( freq - o3_lowF ) );
[ ~, index_o3_highF ]   = min( abs( freq - o3_highF ) );

[ ~, index_o4_lowF ]    = min( abs( freq - o4_lowF ) );
[ ~, index_o4_highF ]   = min( abs( freq - o4_highF ) );

[ ~, index_o5_lowF ]    = min( abs( freq - o5_lowF ) );
[ ~, index_o5_highF ]   = min( abs( freq - o5_highF ) );

[ ~, index_o6_lowF ]    = min( abs( freq - o6_lowF ) );
[ ~, index_o6_highF ]   = min( abs( freq - o6_highF ) );

[ ~, index_o7_lowF ]    = min( abs( freq - o7_lowF ) );
[ ~, index_o7_highF ]   = min( abs( freq - o7_highF ) );

% Percentage initializing
oldCompletedPercentage  = -1;
fprintf( '\nFrames processing: ' )
varStr                  = '0%%';
varStrLen               = length( varStr );
fprintf( '%s', varStr )

for blockIndex = 1 : numbersFrame
    
    %%%%%%%%%%% PERCENTAGE PROCESSING %%%%%%%%%%%  
    completedPercentage = round( blockIndex/numbersFrame*100 );
    if oldCompletedPercentage ~= completedPercentage
        for i = 1 : varStrLen
            fprintf( '\b' )
        end
        varStr          = sprintf( '%d%%', completedPercentage );
        varStrLen       = length( varStr );
        fprintf( '%s', varStr )
    end
    oldCompletedPercentage = completedPercentage;
    
    [ DoA ]         = frameProcessing_ambisonics_fd( ambixFormat, blockIndex, stepSize, bufferSize, ...
                                                     grid_dirs, hannWin, energyWinFactor, ...
                                                     index_o2_lowF, index_o3_lowF, index_o4_lowF, ...
													 index_o5_lowF, index_o6_lowF, index_o7_lowF, ...
													 index_o2_highF, index_o3_highF, index_o4_highF, ...
                                                     index_o5_highF, index_o6_highF, index_o7_highF, ambixOrder );

    
    splMatrix( :, :, blockIndex ) = Fdirs2grid( DoA, resolutionMap, resolutionMap, 1 );

end

splMatrix               = single( splMatrix );

% set colormap limits
maxDoA                  = max( max( max( abs( splMatrix ) ) ) );

if useAdimensialPlotBounds == 1
    
    splMatrix           = splMatrix ./ maxDoA;
    upperLimit          = 1;
    lowerLimit          = adimensionalLowerLimit;
    v                   = lowerLimit : 0.1 : upperLimit;                    % iso-line levels
    
else
    
	ambixFrf			= avgFft( ambixFormat, nfft, 0.5 );
	complexSum			= real( ambixFrf ).^2 + imag( ambixFrf ).^2;
	ambixFrfReal		= mean( complexSum, 3 );
	rmsOmniUnfiltered	= ( sum( ambixFrfReal( :, 1 ), 1 )' ) ./ 2;
	splMatrix			= splMatrix ./ maxDoA * rmsOmniUnfiltered;
	splMatrix			= 10 * log10( splMatrix ) + fullScaleLevel;

    if useFixedSplPlotBounds == 1
        upperLimit      = fixedUpperLimit;
        lowerLimit      = fixedLowerLimit;
    else
        upperLimit      = ceil( max( max( max( splMatrix ) ) ) );           % find absolute max level
        lowerLimit      = upperLimit - noiseGate;                           % range to plot
    end
    
    v                   = lowerLimit+1 : contourSpan : upperLimit;          % iso-line levels
    splMatrix( splMatrix < lowerLimit ) = 0;                                % set to 0 SPL values < lowerLimit
        
end

% pointing correction
correctionIndex     = round( horizontalTilt * gridMapAz / 360 );
if correctionIndex > 0  
    splMatrix       = splMatrix( :, [ correctionIndex+1 : gridMapAz, 1:correctionIndex ], : );
elseif correctionIndex < 0
    splMatrix       = splMatrix( :, [ gridMapAz+correctionIndex+1 : gridMapAz, 1:gridMapAz+correctionIndex ], : );
end

fprintf( '\nData processing ended correctly.\n' )


%% VIDEO PROCESSING
fprintf( '\nInitializing video processing.\n' );
videoTitle          = recordedFileName( 1 : end-4 );                        % name of file video
videoFile           = VideoWriter( videoTitle, 'MPEG-4' );                  % initializes video
videoFile.FrameRate = frameRate;                                            % set frame rate
videoFile.Quality   = 100;                                                  % set quality
open( videoFile );
cmap                = jet( 64 );
cmap( 1, : )        = [ 0 0 0 ]; % black full scale
duration            = numbersFrame / frameRate;

% Percentage initializing
oldCompletedPercentage  = -1;
fprintf( '\nVideo processing: ' )
varStr              = '0%%';
varStrLen           = length( varStr );
fprintf( '%s', varStr )

if timeSignalEnable == 1

    verticalLimit   = [ min( ambixOmni ) max( ambixOmni ) ];
    videoHeigth     = mapWindowWidth / ( 2 * 0.8 );                         % define video window
    videoWindow     = figure( 'units', 'pixel', 'innerposition', [ 0 0 mapWindowWidth videoHeigth  ], 'menubar', 'none' );
    imageHeight     = mapWindowWidth / 2;
    
    % reshape diretions for colormap generation
    elev            = -imageHeight/2 : resolutionMap/90*imageHeight/2 : imageHeight/2;
    azi             = -mapWindowWidth/2 : resolutionMap/180*mapWindowWidth/2 : mapWindowWidth/2;
    aziMat          = repmat( azi, size( elev, 2 ), 1 );
    elevMat         = repmat( elev, size( azi, 2 ), 1 )';

    for blockIndex = 1 : numbersFrame
        
        %%%%%%%%%%% PERCENTAGE PROCESSING %%%%%%%%%%%  
        completedPercentage     = round( blockIndex/numbersFrame*100 );
        if oldCompletedPercentage ~= completedPercentage
            for i = 1 : varStrLen
                fprintf( '\b' )
            end
            varStr              = sprintf( '%d%%', completedPercentage );
            varStrLen           = length( varStr );
            fprintf( '%s', varStr )
        end
        oldCompletedPercentage  = completedPercentage;
        
        ZI          = splMatrix( :, :, blockIndex );                        % extract current value to plot

        % subplot colormap
        plotBG      = subplot( 'Position', [ 0 0.2 1 0.8 ] );
        ambisonicsVideoColorMap( background, backgroundWidth, backgroundHeight, azi, elev, ZI, lowerLimit, upperLimit, ...
                                 plotBG, v, alpha, contourEnable, valueTickEnable, cmap, aziMat, elevMat, tickSize, tickBold, colorbarEnable )
        
        % subplot time signal
        plotTime   	= subplot( 'Position', [ 0 0 1 0.2 ] );                 % subplot with time signal
        timeSignalPlot( ambixOmni, verticalLimit, blockIndex, stepSize, bufferSize );

        writeVideo( videoFile, getframe( videoWindow ) );
        
    end
    
elseif outputWindowWidth ~= 0
    
    videoHeigth     = mapWindowWidth / 2;                                % set video window dimension to 2:1 for VR
    videoWindow   	= figure( 'units', 'pixel', 'innerposition', [ 0 0 mapWindowWidth videoHeigth  ], 'menubar', 'none' );

    % reshape diretions for colormap generation
    elev            = -videoHeigth/2 : resolutionMap/90*videoHeigth/2 : videoHeigth/2;
    azi             = -mapWindowWidth/2 : resolutionMap/180*mapWindowWidth/2 : mapWindowWidth/2;
    aziMat          = repmat( azi, size( elev, 2 ), 1 );
    elevMat         = repmat( elev, size( azi, 2 ), 1 )';
    
    for blockIndex = 1 : numbersFrame

        %%%%%%%%%%% PERCENTAGE PROCESSING %%%%%%%%%%%  
        completedPercentage     = round( blockIndex/numbersFrame*100 );
        if oldCompletedPercentage ~= completedPercentage
            for i = 1 : varStrLen
                fprintf( '\b' )
            end
            varStr              = sprintf( '%d%%', completedPercentage );
            varStrLen           = length( varStr );
            fprintf( '%s', varStr )
        end
        oldCompletedPercentage  = completedPercentage;

        ZI              = splMatrix(:,:,blockIndex);                    % extract current value to plot

        plotBG          = subplot( 'Position', [ 0 0 1 1 ] );
        set( plotBG, 'YDir', 'normal' );
        ambisonicsVideoColorMapFFMPEG( azi, elev, ZI, lowerLimit, upperLimit, v, contourEnable, ...
                                       valueTickEnable, cmap, aziMat, elevMat, tickSize, tickBold )

        writeVideo( videoFile, getframe( videoWindow ) );
        
    end
    
    close( videoFile )
	close all

    % copy background image to root folder and convert to requested format
    copyfile( backgroundImageFile, './', 'f' )

    if outputBlackWhite == 1
        system( [ 'ffmpeg -y -i ' backgroundImageFileName ' -vf hue=s=0 ' backgroundImageFileName ] )
    end
    
    system( [ 'ffmpeg -i ' backgroundImageFileName ' -vf scale=' num2str( outputWindowWidth ) ':' num2str( outputWindowWidth/2 ) ' temp.png ' ] );
    delete( backgroundImageFileName )
    
    % create background video from image
    mapVideoFile            = [ videoTitle '.mp4' ];
    backgroundVideoOutput   = [ 'overlay_' mapVideoFile ];
    system( [ 'ffmpeg -loop 1 -i temp.png -c:v libx264 -t ' num2str( duration ) ' -pix_fmt yuv420p tempBackground.mp4' ] );
	delete( 'temp.png' )
    clear mapVideo 
    
    % generate overlay
    system( [ 'ffmpeg -y -i ' mapVideoFile ' -i tempBackground.mp4 -filter_complex " '...
    '[0:v]setpts=PTS-STARTPTS, scale=' num2str( outputWindowWidth ) 'x' num2str( outputWindowWidth/2 ) '[top]; '...
    '[1:v]setpts=PTS-STARTPTS, scale=' num2str( outputWindowWidth ) 'x' num2str( outputWindowWidth/2 ) '[bottom]; '...
    '[top]split[m][a]; [m][a]alphamerge[keyed]; [bottom][keyed]overlay=eof_action=endall" ' backgroundVideoOutput ] );        
    delete( 'tempBackground.mp4' )
    delete( mapVideoFile )
    movefile( backgroundVideoOutput, mapVideoFile );
        
else
    
    videoHeigth     = mapWindowWidth / 2;                                % set video window dimension to 2:1 for VR
    videoWindow   	= figure( 'units', 'pixel', 'innerposition', [ 0 0 mapWindowWidth videoHeigth  ], 'menubar', 'none' );
    
    % reshape diretions for colormap generation
    elev            = -videoHeigth/2 : resolutionMap/90*videoHeigth/2 : videoHeigth/2;
    azi             = -mapWindowWidth/2 : resolutionMap/180*mapWindowWidth/2 : mapWindowWidth/2;
    aziMat          = repmat( azi, size( elev, 2 ), 1 );
    elevMat         = repmat( elev, size( azi, 2 ), 1 )';
    
    for blockIndex = 1 : numbersFrame
        
        %%%%%%%%%%% PERCENTAGE PROCESSING %%%%%%%%%%%  
        completedPercentage     = round( blockIndex/numbersFrame*100 );
        if oldCompletedPercentage ~= completedPercentage
            for i = 1 : varStrLen
                fprintf( '\b' )
            end
            varStr              = sprintf( '%d%%', completedPercentage );
            varStrLen           = length( varStr );
            fprintf( '%s', varStr )
        end
        oldCompletedPercentage  = completedPercentage;
        
        ZI          = splMatrix( :, :, blockIndex );                        % extract current value to plot

        % subplot colormap
        plotBG      = subplot( 'Position', [ 0 0 1 1 ] );
        ambisonicsVideoColorMap( background, backgroundWidth, backgroundHeight, azi, elev, ZI, lowerLimit, upperLimit, ...
                                 plotBG, v, alpha, contourEnable, valueTickEnable, cmap, aziMat, elevMat, tickSize, tickBold, colorbarEnable )
                        
        writeVideo( videoFile, getframe( videoWindow ) );
        
    end
    
end

close( videoFile );                                                         % finalizes video 
close all


%% METADATA
if injectMetadata == 1
    source              = './lib/SpatialMediaMetadataInjector.exe';
    copyfile( source, './', 'f' )
    system( 'SpatialMediaMetadataInjector.exe' )
end

delete( 'SpatialMediaMetadataInjector.exe' )
rmpath( './lib' )
fprintf( '\nEnded correctly.\n\n' )
