function audioOut = ambiencode(audioIn, encodeMatrix, format)
%ambiencode Ambisonic Encoder for Higher Order Ambisonics (HOA)
%
% inputs
% audioIn       : audio for ambisonic encoding
% encodeMatrix  : encode matrix of size [numDevices x numAmbisonicChannels]
% format        : supported formats are 'acn-sn3d', 'acn-n3d', 'acn-fuma',
%                   'acn-maxn', 'fuma-sn3d', 'fuma-n3d', 'fuma-fuma',
%                   'fuma-maxn'; the default format is 'acn-sn3d' 
% 
% outputs
% audioOut      : encoded ambisonic audio
%
% This function ambiencode is for demonstration purposes. It may
% change in a future release.

% Copyright 2018-2019 The MathWorks, Inc.

%#codegen
% -------------------------------------------------------------------------
% Validate required inputs
% -------------------------------------------------------------------------
validateattributes(audioIn, {'single', 'double'}, ...
                        {'nonempty','2d','real','nonnan','finite'}, ...
                        'ambiencode', 'audioIn', 1); 
validateattributes(encodeMatrix, {'single', 'double'}, ...
                        {'nonempty','2d','real','nonnan','finite'}, ...
                        'ambiencode', 'encodeMatrix', 2); 
% -------------------------------------------------------------------------
% Validate optional inputs
% -------------------------------------------------------------------------
if nargin>2
    format = char(format);  % string support
else
    format = 'acn-sn3d';    % default format 
end
% -------------------------------------------------------------------------
% Validate input data
% -------------------------------------------------------------------------
[~, inDevices] = size(audioIn);
[numDevices, emAmbiChannels] = size(encodeMatrix);
coder.internal.errorIf((mod(sqrt(emAmbiChannels)-1,1) ~= 0 || emAmbiChannels > 64), ...
    'audio:ambisonics:encodeMatrixChannelsInvalid', emAmbiChannels);
coder.internal.errorIf(inDevices ~= numDevices, ...
    'audio:ambisonics:audioMismatchElements', inDevices, numDevices);
% -------------------------------------------------------------------------
order = sqrt(emAmbiChannels)-1;
% -------------------------------------------------------------------------
switch format
    case 'acn-n3d'
        seqVector = 1:order2chan(order);
        normVector = audioexample.ambisonics.ambinormvec('sn3d-n3d', order);
    case 'acn-sn3d'
        seqVector = 1:order2chan(order);
        normVector = ones(1, order2chan(order));
    case 'acn-fuma'
        seqVector = 1:order2chan(order);
        normVector = audioexample.ambisonics.ambinormvec('sn3d-fuma', order);
    case 'acn-maxn'
        seqVector = 1:order2chan(order);
        normVector = audioexample.ambisonics.ambinormvec('sn3d-maxn', order);
    case 'fuma-n3d'
        seqVector = audioexample.ambisonics.ambiseqvec('acn-fuma', order);
        normVector = audioexample.ambisonics.ambinormvec('sn3d-n3d', order);
    case 'fuma-sn3d'
        seqVector = audioexample.ambisonics.ambiseqvec('acn-fuma', order);
        normVector = ones(1, order2chan(order));
    case 'fuma-fuma'
        seqVector = audioexample.ambisonics.ambiseqvec('acn-fuma', order);
        normVector = audioexample.ambisonics.ambinormvec('sn3d-fuma', order);
    case 'fuma-maxn'
        seqVector = audioexample.ambisonics.ambiseqvec('acn-fuma', order);
        normVector = audioexample.ambisonics.ambinormvec('sn3d-maxn', order);
    otherwise
        coder.internal.error('audio:ambisonics:formatInvalid', format);
end
% -------------------------------------------------------------------------
audioEnc = audioIn * encodeMatrix(:, 1:order2chan(order));  
audioEncNorm = bsxfun(@times, audioEnc, normVector);
audioOut = audioEncNorm(:, seqVector);
% -------------------------------------------------------------------------
function channels = order2chan(order)
    channels = (order+1)^2;
end
end

