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)

neroaacwrite.m
function neroaacwrite(varargin)
%NEROAACWRITE Write Advanced Audio Coding sound file.
%   NEROAACWRITE is a MATLAB wrapper function for Nero AAC Encoder, which is
%   downloadable for free at http://www.nero.com/eng/technologies-aac-codec.html
%
%   NEROAACWRITE(Y,AACFILE) writes the data stored in the variable Y to a
%   AAC file called AACFILE. The AACFILE input is a string enclosed in
%   single quotes. If AACFILE does not have a file extension, NEROAACWRITE
%   appends ".m4a". The sample rate is set to the default 8000 Hz with 16
%   bits per sample. Each column of Y representsa separate channel.
%   Therefore, stereo data should be specified as a matrix with two
%   columns. The AAC encoder is configured in one-pass mode with quality
%   rating of 0.5.
%
%   NEROAACWRITE(Y,FS,AACFILE) specifies the sampling rate Fs. Supported
%   sampling rates are 8000, 11025, 16000, 22050, 32000, 44100, 48000,
%   88200, 96000, and 192000 Hz. If the specified FS is not supported,
%   NEROAACWRITE resamples Y to the nearest next sampling rate.
%
%   NEROAACWRITE(Y,FS,NBITS,AACFILE) sets the bit depth to NBITS. NBITS
%   must be 8, 16, 24, or 32.
%
%   NEROAACWRITE(Y,FS,NBITS,QMODE,QVAL,AACFILE) specifies quality/bitrate
%   setting. QMODE may be one of 'q' (quality), 'br' (bitrate), 'cbr'
%   (constant bitrate). For the 'quality' mode, QVAL must be a value
%   between 0.0 to 1.0 (larger the better). For the two bitrate modes, QVAL
%   specifies the bitrate in kilobits per second. The supported bitrates
%   are between 8 and 400 kbps.
%
%   NEROAACWRITE(Y,FS,NBITS,QMODE,QVAL,NUMPASS,AACFILE) sets the AAC
%   encoder to run in one-pass (NUMPASS='1pass') or two-pass
%   (NUMPASS='2pass').
%
%   Input Data Ranges
%   The range of values in Y depends on the number of bits specified by
%   NBITS and the data type of Y.  Some examples of valid input ranges
%   based on the value of NBITS and Y's data type are listed in the tables
%   below.
%
%   If Y contains integer data:
%
%       NBITS   Y's data type    Y's Data range          Output Format
%      -------  ---------------- ---------------------   -------------
%         8         uint8             0 <= Y <= 255         uint8
%        16         int16        -32768 <= Y <= +32767      int16
%        24         int32         -2^23 <= Y <= 2^23-1      int32
%
%   If Y contains floating point data:
%
%       NBITS   Y's data type    Y's Data range          Output Format
%      -------  ---------------- ---------------------   -------------
%         8     single or double   -1.0 <= Y <  +1.0        uint8
%        16     single or double   -1.0 <= Y <  +1.0        int16
%        24     single or double   -1.0 <= Y <  +1.0        int32
%        32     single or double   -1.0 <= Y <= +1.0        single
%
%   Note that for floating point data where NBITS < 32, amplitude values
%   are clipped to the range -1.0 <= Y < +1.0.
%
%   NEROAACWRITE(WAVFILE,QMODE,QVAL,NUMPASS,AACFILE) converts the WAVE file
%   specified by WAVFILE, a string enclosed in single quotes. The sampling
%   rate and bit depth of the WAVE file is retained in the AAC file.
%   Similar to other data, 
%
%   See also NEROAACREAD, NEROAACTAG, WAVWRITE.

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

% Parse & check input arguments
[y,fs,nbits,qmode,qval,twopass,wavfile,aacfile,msg] = parse_input(nargin,varargin);
error(msg);

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

% Set the output file path
if ~ismember(aacfile(1),['.' filesep]) || aacfile(2)==':'
   aacfile = strcat(pwd,filesep,aacfile);
end

genwav = isempty(wavfile);

% resample data if needed
[req2resample,fsnew] = checkfs(fs);
if req2resample
   if ~genwav % wavfile name given, retrieve data first
      [y,fs,nbits] = wavread(wavfile);
      genwav = true; % must create new wave file
   end
   
   y = resample_data(y,fs,fsnew);
   fs = fsnew;
end

% generate wave file name
if genwav
   wavfile = strcat(tempname,'.wav'); % to be deleted
end

% Prepare AAC encoder command
cmd = sprintf('"%s" %s %s %s -if "%s" -of "%s"',exefile,qmode,qval,twopass,wavfile,aacfile);
% neroaacenc [options] -if name of source file -of name of target file
% Quality/bitrate control:
% -q <number>   : Enables "target quality" mode.
%                 <number> is a floating-point number in 0...1 range.
% -br <number>  : Specifies "target bitrate" mode.
%                 <number> is target bitrate in bits per second.
% -cbr <number> : Specifies "target bitrate (streaming)" mode.
%                 <number> is target bitrate in bits per second.
%                 When neither of above quality/bitrate options is used,
%                 the encoder defaults to equivalent of -q 0.5
%
% Multipass encoding:
% -2pass        : Enables two-pass encoding mode.
%                 Note that two-pass more requires a physical file as input,
%                 rather than stdin.
% -2passperiod  : Overrides two-pass encoding bitrate averaging period,
%   <number>    : in milliseconds.
%               : Specify zero to use least restrictive value possible (default).

if genwav % generate wave file first
   wavwrite(y,fs,nbits,wavfile);
end

try
   [status,result] = system(cmd);
   if genwav, delete(wavfile); end % Delete temporary wave file
catch ME
   delete(wavfile);
   rethrow(ME);
end

if status==1
   error(result);
end

end

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

function [tf,fsnew] = checkfs(fs)
% resample the data if needed

fs_supported = [8000  11025 16000 22050 32000 44100 48000 88200 96000 192000];

I = find(fs_supported>=fs,1,'first'); % determine the nearest next supported sampling rate
if isempty(I) % specified sampling rate too high
   fsnew = fs_supported(end);
else
   fsnew = fs_supported(I);
end

tf = fs~=fsnew; % true if data needs to be resampled

end

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

function y = resample_data(y,fs,fsnew)

% resample
infactors = factor(fs);
outfactors = factor(fsnew);
cmnfactors = intersect(infactors,outfactors);
for n = 1:numel(cmnfactors)
   Iin = find(infactors==cmnfactors(n));
   Iout = find(outfactors==cmnfactors(n));
   Ncmn = min(numel(Iin),numel(Iout));
   infactors(Iin(1:Ncmn)) = [];
   outfactors(Iout(1:Ncmn)) = [];
end

while ~isempty(outfactors) || ~isempty(infactors)
   % set upsampling factor
   P = 1;
   if ~isempty(outfactors)
      Pnext = outfactors(end);
      while Pnext<10
         P = Pnext;
         outfactors(end) = [];
         if isempty(outfactors)
            break;
         end
         
         Pnext = P*outfactors(end);
      end
      fsnew = fs*P;
   end
   
   % set downsampling factor
   Q = 1;
   if ~isempty(infactors)
      Qnext = infactors(1);
      while (P>1 && fsnew/Qnext>fs) || (P==1 && Qnext<10)
         Q = Qnext;
         infactors(1) = [];
         if isempty(infactors)
            break;
         end
         Qnext = Q*infactors(1);
      end
      fsnew = fsnew/Q;
   end
   
   % resample
   y = resample(y,P,Q);
end
end

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function [exefile,msg] = getexepath()

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

if isempty(exefile)
   [filename,pathname] = uigetfile({'neroaacenc.exe','Nero AAC Encoder EXE file'},'Locate Nero AAC Encoder EXE file',which('neroaacenc.exe'));
   if (isnumeric(filename) && filename==0) || (isnumeric(pathname) && pathname==0)
      msg = 'User canceled to specify the Nero AAC Encoder executable.';
      return;
   end
   
   exefile = [pathname filename];
   
   setpref('neroaac','encpath',exefile);
end
end
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function [y,fs,nbits,qmode,qval,twopass,wavfile,aacfile,msg] = parse_input(N,argin)

y = [];
fs = 8000;
nbits = 16;
qmode = '-q';
qval = '0.5';
twopass = '';
wavfile = '';
aacfile = '';

msg = nargchk(2,7,N); %#ok
if ~isempty(msg), return; end

% Check source data arguments
if isnumeric(argin{1}) % sample matrix given
   y = argin{1};
   if N>2 && ~isempty(argin{2}) % fs defined
      fs = argin{2};
   end
   if N>3 && ~isempty(argin{3}) % nbits defined
      nbits = argin{3};
   end
   n0 = 4;
elseif ischar(argin{1}) % wavfile given
   if ~ischar(argin{1}) || size(argin{1},1)>1
      msg = 'WAVFILE must be a string of characters.';
      return;
   end
   wavfile = which(argin{1});
   if isempty(wavfile) % if file does not exist, try appending .wav extension
      if isempty(regexp(wavfile,'.wav$','once'))
         wavfile = which(strcat(argin{1},'.wav'));
         if isempty(wavfile)
            msg = 'Invalid Wave File: Cannot open file.';
            return;
         end
      end
   end
   info = audioinfo(wavfile);
   fs = info.SampleRate;
   nbits = info.BitsPerSample;
   n0 = 2;
else
   msg = 'Invalid first argument: neither data matrix nor WAVE file name.';
end

% Check AAC Encoding configuration arguments
if N>n0 && ~isempty(argin{n0}) % qmode defined
   if ~ischar(argin{n0}) || size(argin{n0},1)>1
      msg = 'QMODE must be a string of characters.';
      return;
   end
   qmode = ['-' lower(argin{n0})];
   I = strcmp(qmode,{'-q','-br','-cbr'});
   if ~any(I)
      msg = sprintf('Invalid QMODE given (%s)',qmode);
      return;
   elseif ~I(1) % change default qval to that for bitrates
      qval = '192';
   end
end
n0 = n0+1;
if N>n0 && ~isempty(argin{n0}) % qval defined
   if ~isfloat(argin{n0}) || numel(argin{n0})>1 || isnan(argin{n0}) || isinf(argin{n0})
      msg = ('QVAL must be a finite scalar value.');
      return;
   end
   qval = argin{n0};
   if qmode==1 % quality mode
      if qval<0 || qval>1
         msg = 'QVAL must be between 0 and 1.';
         return;
      end
      qval = sprintf('%0.2f',qval);
   else % bitrate mode
      if qval<8 || qval>400
         msg = 'QVAL must be between 8 and 400.';
         return;
      end
      qval = sprintf('%d',qval * 1000); % convert to bps
   end
end
n0 = n0+1;
if N>n0 && ~isempty(argin{n0}) % twopass defined
   if ~ischar(argin{n0}) || size(argin{n0},1)>1
      msg = 'NUMPASS must be a string of characters.';
      return;
   end
   numpass = strcmpi(argin{n0},{'1pass','2pass'});
   if ~any(numpass)
      msg = sprintf('Invalid NUMPASS given (%s)',numpass);
      return;
   elseif numpass(2)
      twopass = '-2pass';
   end
end

% Check Outputfile
if ~ischar(argin{N}) || size(argin{N},1)>1
   msg = 'AACFILE must be a string of characters.';
   return;
end
aacfile = argin{N};

% if extension not assigned, append '.aac'
if isempty(regexp(aacfile,'[^\.]+\.','once'))
   aacfile = strcat(aacfile,'.m4a');
end

end

Contact us