Code covered by the BSD License  

Highlights from
Nero AAC Codec Wrapper

Nero AAC Codec Wrapper

by

Kesh Ikuma (view profile)

 

MATLAB wrapper functions for Nero AAC Codec (v1.0)

neroaactag(aacfile,cmdstr,varargin)
function varargout = neroaactag(aacfile,cmdstr,varargin)
%NEROAACTAG Edit Tags in Advanced Audio Coding sound file.
%   NEROAACTAG is a MATLAB wrapper function for Nero AAC Tagger, which is
%   downloadable for free at
%   http://www.nero.com/eng/technologies-aac-codec.html
%
%   NEROAACTAG(AACFILE)
%   NEROAACTAG(AACFILE,'disp'), and
%   NEROAACTAG(AACFILE,'get') display all the stored tag information in an
%   AAC sound file specified by the string AACFILE. The ".m4a" extension is
%   appended if no extension is given.
%
%   V = NEROAACTAG(AACFILE)
%   V = NEROAACTAG(AACFILE,'disp'), and 
%   V = NEROAACTAG(AACFILE,'get') return a structure where each field name
%   is the name of a tag and each field contains the value of that tag.
%
%   V = NEROAACTAG(AACFILE,'get','TagName') returns the value of the
%   specified tag. If 'TagName' is replaced by a cell array of strings
%   containing tag names, then NEROAACTAG will return a matching cell array
%   of values.
%  
%   A = NEROAACTAG(AACFILE,'set') and NEROAACTAG(AACFILE,'set') returns or
%   displays all the user-editable tags. The returned array is a cell array
%   of the names of the editable tags.
%
%   NEROAACTAG(AACFILE,'set','TagName',TagValue) sets the value of the
%   specified tag for the AAC sound file with file name AACFILE. The value
%   must be a string of characters or a numeric array. If a numeric array
%   is given, it is converted to string by NUM2STR function.
%  
%   NEROAACTAG(AACFILE,'set',A) where A is a structure whose field names
%   are tag names, sets the tag named in each tag name with the values
%   contained in the structure.
%  
%   NEROAACTAG(AACFILE,'set',PN,PV) sets the named tags specified in
%   the cell array of strings PN to the corresponding values in the cell
%   array PV.  The cell arrays PN and PV must be vectors with matching
%   dimension.
%  
%   NEROAACTAG(AACFILE,'set','TagName1',TagValue1,'TagName2',TagValue2,...)
%   sets multiple tag values with a single statement.
%
%   See also NEROAACREAD and NEROAACWRITE.

% Copyright 2012 Takeshi Ikuma
% History:
% rev. - : (10-03-2012) original release

% Check input arguments (must have at least 2 arguments)
error(nargchk(1,inf,nargin)); %#ok

% Check file name
if ~ischar(aacfile) || size(aacfile,1)~=1
   error('FILE must be a string of characters.');
end

aacfile = which(aacfile); % expand to full path
if isempty(aacfile) % if file does not exist, try appending .wav extension
   if isempty(regexp(aacfile,'.m4a$','once'))
      aacfile = which(strcat(aacfile,'.m4a'));
      if isempty(aacfile)
         error('Invalid FILE: Cannot open file.');
      end
   end
end

% Check command
if nargin==1
   cmd = 1;
else
   if ~ischar(cmdstr) || size(cmdstr,1)~=1
      error('Invalid CMDSTR: Must be a string of characters.');
   end
   
   cmd = find(strcmpi(cmdstr,{'disp','set','get'}));
   if isempty(cmd)
      error('Invalid CMDSTR: Must be one of ''disp'',''set'', and ''get''.');
   end
end

% initialize the output argument cell
varargout = cell(1,nargout);

try
   % run the command
   switch cmd
      case 1 % disp
         [varargout{:}] = disptag(aacfile,varargin{:});
      case 2 % set
         [varargout{:}] = settag(aacfile,varargin{:});
      case 3 % get
         varargout{1} = gettag(aacfile,varargin{:});
      otherwise       % should never reach here
         error('Invalid CMDSTR: Must be one of ''disp'',''set'', and ''get''.');
   end
catch ME
   rethrow(ME);
end

end

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

function varargout = settag(aacfile,varargin)

% Get valid meta data names
metanames = liststandardmeta();

if nargin==1 % return the list of standard meta field names
   if nargout==0 % display
      fprintf('\n');
      fprintf('    %s\n',metanames{:});
      fprintf('\n');
   else
      varargout{1} = metanames;
   end
   return;
end

% Check input
if iscell(varargin{1}) % if cell input given
   error(nargchk(3,3,nargin)); %#ok
   if ~iscell(varargin{2})
      error('Invalid PV argument: Must be an cell array.');
   end
   fnames = varargin{1};
   fvalues = varargin{2};
   if numel(fnames)~=numel(fvalues)
      error('Invalid PN & PV arguments: Mismatched number of elements.');
   end
elseif isstruct(varargin{1})
   error(nargchk(2,2,nargin)); %#ok
   fnames = fieldnames(varargin{1});
   fvalues = struct2cell(varargin{1});
   N = numel(fnames);
   if numel(fvalues)>N
      error('Invalid A argument: Struct must be scalar.');
   end
else
   error(nargchk(3,inf,nargin)); %#ok
   Narghalf = nargin/2;
   N = floor(Narghalf);
   if N == Narghalf
      error('FieldValue arguments must be given for all PropertyName arguments.')
   end
   fnames = varargin(1:2:end);
   fvalues = varargin(2:2:end);
end

if ~iscellstr(fnames)
   error('Field names must be given in strings of characters.');
end
if ~all(cellfun(@(x)ischar(x)||isnumeric(x),fvalues))
   error('A field value must be given either as a number or as a string of characters.');
end

% Make sure the field names are all supported
[tf,I] = ismember(fnames(:),metanames);
if ~all(tf)
   errmsg = sprintf('%s ',fnames{~tf});
   if sum(~tf)==1
      error('Invalid FieldName: %s',errmsg);
   else
      error('Invalid FieldNames: %s',errmsg);
   end
end

% Retrieve the NEROAACTAG.EXE path
[exefile,msg] = getexepath();
error(msg);

% Form AAC tagger command line string
% Usage: 
% neroaactag <file.mp4> <command> [<command> [<command> ...]] 
% Available commands: 
% -meta:<name>=<value>            : Sets specified metadata field to specified 
%                                   value. Eg. -meta:artist="Pink Floyd" 
cmd = sprintf('"%s" "%s"',exefile,aacfile);
for n = 1:N
   if ischar(fvalues{n})
      val = fvalues{n};
   else
      val = num2str(fvalues{n});
   end
   cmd = sprintf('%s -meta:%s=%s',cmd,fnames{I(n)},val);
end

[status,result] = system(cmd);
if status==1
   error(result);
end

end

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

function varargout = gettag(aacfile,varargin)

% if no meta data name given, perform 'disp' command instead
if nargin==1
   varargout = cell(1,nargout);
   [varargout{:}] = disptag(aacfile);
   return;
end

% check input
error(nargchk(2,2,nargin)); %#ok

switch class(varargin{1})
   case 'char'
      IsChar = true;
      fnames = lower(varargin(1));
   case 'cell'
      IsChar = false;
      fnames = lower(varargin{1});
   otherwise
      error('Invalid Object for PropertyName');
end

% Get meta data
metadata = listmeta(aacfile);

% Lookfor the metadata name
[tf,I] = ismember(fnames(:),metadata(1,:));
if ~all(tf)
   errmsg = sprintf('%s ',fnames{~tf});
   if sum(~tf)==1
      error('Invalid PropertyName: %s',errmsg);
   else
      error('Invalid PropertyNames: %s',errmsg);
   end
end

% Return the property values
if IsChar
   varargout(1) = metadata(2,I);
else
   varargout{1} = reshape(metadata(2,I),size(fnames));
end

end

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

function varargout = disptag(aacfile,varargin)

% Get meta data
metadata = listmeta(aacfile);

if nargout==0 % display
   Nfn = cellfun(@numel,metadata(1,:));
   Nmax = max(Nfn);
   fprintf('\n');
   for n = 1:size(metadata,2)
      fprintf('    %s%s: %s\n',repmat(' ',1,Nmax-Nfn(n)),metadata{1,n},metadata{2,n});
   end
   fprintf('\n');
else % output struct
   varargout{1} = struct(metadata{:});
end

end

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

function metadata = listmeta(aacfile)
% returns metadata in a cell array of strings

% Retrieve the path
[exefile,msg] = getexepath();
error(msg);

% Prepare AAC tagger command
% Usage: 
% neroaactag <file.mp4> <command>
% Available commands: 
% -list-meta                      : Lists existing metadata entries. 
% -list-covers                    : Lists cover art entries. 

[status,result] = system(sprintf('"%s" "%s" -list-meta',exefile,aacfile));
if status==1
   error(result);
end

% Get meta data

I = regexp(result,'Metadata list:\s+','end','once');
result(1:I) = [];

I = regexp(result,'\s+End of metadata.','start','once');
result(I:end) = [];

data = textscan(result,'%s','Delimiter',char(10));
metadata = regexp(data{1},'\s*(\S+) = (.*)','tokens','once');
metadata = reshape([metadata{:}],2,numel(metadata));

end

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

function metanames = liststandardmeta()
% returns metadata in a cell array of strings

% Retrieve the path
[exefile,msg] = getexepath();
error(msg);

% Prepare AAC tagger command
% Usage: 
% neroaactag <file.mp4> <command>
% Available commands: 
% -list-standard-meta             : Displays a list of field names usable with 
%                                   -meta command. 
[status,result] = system(sprintf('"%s" -list-standard-meta',exefile));
if status==1
   error(result);
end

% Get meta data

I = regexp(result,'List of standard Nero Digital metadata field names:\s+','end','once');
result(1:I) = [];

I = regexp(result,'\s+End of metadata field name list.','start','once');
result(I:end) = [];

metanames = subsref(textscan(result,'%s'),struct('type','{}','subs',{{1,1}}));

end

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

function [exefile,msg] = getexepath()

msg = '';
if ispref('neroaac','tagpath')
   exefile = getpref('neroaac','tagpath');
   if ~(ischar(exefile) && exist(exefile,'file')) % check to make sure the file exists
      exefile = '';
   end
else
   exefile = '';
end

if isempty(exefile)
   [filename,pathname] = uigetfile({'neroaactag.exe','Nero AAC Tagger EXE file'},'Locate Nero AAC Tagger EXE file',which('neroaactag.exe'));
   if (isnumeric(filename) && filename==0) || (isnumeric(pathname) && pathname==0)
      msg = 'User canceled to specify the Nero AAC tagger executable.';
      return;
   end
   
   exefile = [pathname filename];
   
   setpref('neroaac','tagpath',exefile);
end
end

Contact us