function audioOut = ambidecode(audioIn, decodeMatrix, format)
%ambidecode Ambisonic Decoder for Higher Order Ambisonics (HOA)
%
% inputs
% audioIn       : ambisonic audio for decoding
% decodeMatrix  : decode matrix of size [numAmbisonicChannels x numDevices]
% 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      : decoded ambisonic audio
%
% This function ambidecode 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'}, ...
                        'ambidecode', 'audioIn', 1); 
validateattributes(decodeMatrix, {'single', 'double'}, ...
                        {'nonempty','2d','real','nonnan','finite'}, ...
                        'ambidecode', 'decodeMatrix', 2); 
% -------------------------------------------------------------------------
% Validate optional inputs
% -------------------------------------------------------------------------
if nargin>2
    format = char(format);  % string support
else
    format = 'acn-sn3d';    % default format 
end
% -------------------------------------------------------------------------
% Validate input data
% -------------------------------------------------------------------------
[~, inAmbiChannels] = size(audioIn);
[dmAmbiChannels, ~] = size(decodeMatrix);
coder.internal.errorIf((mod(sqrt(inAmbiChannels)-1,1) ~= 0 || inAmbiChannels > 64), ...
    'audio:ambisonics:ambisonicChannelsInvalid', inAmbiChannels);
coder.internal.errorIf((mod(sqrt(dmAmbiChannels)-1,1) ~= 0 || dmAmbiChannels > 64), ...
    'audio:ambisonics:decodeMatrixChannelsInvalid', dmAmbiChannels);
% -------------------------------------------------------------------------
order= min(sqrt(inAmbiChannels)-1, sqrt(dmAmbiChannels)-1);
% -------------------------------------------------------------------------
switch format
    case 'acn-n3d'
        seqVector = 1:order2chan(order);
        normVector = audioexample.ambisonics.ambinormvec('n3d-sn3d', order);
    case 'acn-sn3d'
        seqVector = 1:order2chan(order);
        normVector = ones(1, order2chan(order));
    case 'acn-fuma'
        seqVector = 1:order2chan(order);
        normVector = audioexample.ambisonics.ambinormvec('fuma-sn3d', order);
    case 'acn-maxn'
        seqVector = 1:order2chan(order);
        normVector = audioexample.ambisonics.ambinormvec('maxn-sn3d', order);
    case 'fuma-n3d'
        seqVector = audioexample.ambisonics.ambiseqvec('fuma-acn', order);
        normVector = audioexample.ambisonics.ambinormvec('n3d-sn3d', order);
    case 'fuma-sn3d'
        seqVector = audioexample.ambisonics.ambiseqvec('fuma-acn', order);
        normVector = ones(1, order2chan(order));
    case 'fuma-fuma'
        seqVector = audioexample.ambisonics.ambiseqvec('fuma-acn', order);
        normVector = audioexample.ambisonics.ambinormvec('fuma-sn3d', order);
    case 'fuma-maxn'
        seqVector = audioexample.ambisonics.ambiseqvec('fuma-acn', order);
        normVector = audioexample.ambisonics.ambinormvec('maxn-sn3d', order);
    otherwise
        coder.internal.error('audio:ambisonics:formatInvalid', format);
end
% -------------------------------------------------------------------------
audioSeq = audioIn(:, seqVector);
audioSeqNorm = bsxfun(@times, audioSeq, normVector);    % replace when coder supports compatible sizes
audioOut = audioSeqNorm * decodeMatrix(1:order2chan(order), :);
% -------------------------------------------------------------------------
function channels = order2chan(order)
    channels = (order+1)^2;
end
end

