diff toolboxes/MIRtoolbox1.3.2/MIRToolbox/mp3read.m @ 0:e9a9cd732c1e tip

first hg version after svn
author wolffd
date Tue, 10 Feb 2015 15:05:51 +0000
parents
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/toolboxes/MIRtoolbox1.3.2/MIRToolbox/mp3read.m	Tue Feb 10 15:05:51 2015 +0000
@@ -0,0 +1,334 @@
+function [Y,FS,NBITS,OPTS] = mp3read(FILE,N,MONO,DOWNSAMP,DELAY)
+% MP3READ   Read MP3 audio file via use of external binaries.
+%   Y = MP3READ(FILE) reads an mp3-encoded audio file into the
+%     vector Y just like wavread reads a wav-encoded file (one channel 
+%     per column).  Extension ".mp3" is added if FILE has none.
+%     Also accepts other formats of wavread, such as
+%   Y = MP3READ(FILE,N) to read just the first N sample frames (N
+%     scalar), or the frames from N(1) to N(2) if N is a two-element vector.  
+%   Y = MP3READ(FILE,FMT) or Y = mp3read(FILE,N,FMT) 
+%     with FMT as 'native' returns int16 samples instead of doubles; 
+%     FMT can be 'double' for default behavior (to exactly mirror the
+%     syntax of wavread).
+%
+%   [Y,FS,NBITS,OPTS] = MP3READ(FILE...) returns extra information:
+%     FS is the sampling rate,  NBITS is the bit depth (always 16), 
+%     OPTS.fmt is a format info string; OPTS has multiple other
+%     fields, see WAVREAD.
+%
+%   SIZ = MP3READ(FILE,'size') returns the size of the audio data contained
+%     in the file in place of the actual audio data, returning the
+%     2-element vector SIZ=[samples channels].
+%
+%   [Y...] = MP3READ(FILE,N,MONO,DOWNSAMP,DELAY) extends the
+%     WAVREAD syntax to allow access to special features of the
+%     mpg123 engine:  MONO = 1 forces output to be mono (by
+%     averaging stereo channels); DOWNSAMP = 2 or 4 downsamples by 
+%     a factor of 2 or 4 (thus FS returns as 22050 or 11025
+%     respectively for a 44 kHz mp3 file); DELAY controls how many
+%     "warm up" samples to drop at the start of the file; the
+%     default value of 2257 makes an mp3write/mp3read loop for a 44
+%     kHz mp3 file be as close as possible to being temporally
+%     aligned; specify as 0 to prevent discard of initial samples.
+%
+%   Example:
+%   To read an mp3 file as doubles at its original width and sampling rate:
+%     [Y,FS] = mp3read('piano.mp3');
+%   To read the first 1 second of the same file, downsampled by a
+%   factor of 4, cast to mono, using the default filename
+%   extension:
+%     [Y,FS4] = mp3read('piano', FS/4, 1, 4);
+%
+%   Note: Because the mp3 format encodes samples in blocks of 26 ms (at
+%   44 kHz), and because of the "warm up" period of the encoder,
+%   the file length may not be exactly what you expect.
+%
+%   Note: requires external binaries mpg123 and mp3info; you
+%   can find binaries for several platforms at:
+%     http://labrosa.ee.columbia.edu/matlab/mp3read.html
+%
+%   See also mp3write, wavread.
+
+% 2003-07-20 dpwe@ee.columbia.edu  This version calls mpg123.
+% 2004-08-31 Fixed to read whole files correctly
+% 2004-09-08 Uses mp3info to get info about mp3 files too
+% 2004-09-18 Reports all mp3info fields in OPTS.fmt; handles MPG2LSF sizes
+%            + added MONO, DOWNSAMP flags, changed default behavior.
+% 2005-09-28 Fixed bug reading full-rate stereo as 1ch (thx bjoerns@vjk.dk)
+% 2006-09-17 Chop off initial 2257 sample delay (for 44.1 kHz mp3)
+%            so read-write loop doesn't get progressively delayed.
+%            You can suppress this with a 5th argument of 0.
+% 2007-02-04 Added support for FMT argument to match wavread
+%            Added automatic selection of binary etc. to allow it
+%            to work cross-platform without editing prior to
+%            submitting to Matlab File Exchange
+% 2007-07-23 Tweaks to 'size' mode so it exactly agrees with read data.
+
+% find our baseline directory
+path = fileparts(which('mp3read'));
+
+% %%%%% Directory for temporary file (if needed)
+% % Try to read from environment, or use /tmp if it exists, or use CWD
+tmpdir = getenv('TMPDIR');
+if isempty(tmpdir) || exist(tmpdir,'file')==0
+  tmpdir = '/tmp';
+end
+if exist(tmpdir,'file')==0
+  tmpdir = '';
+end
+% ensure it exists
+%if length(tmpdir) > 0 && exist(tmpdir,'file')==0
+%  mkdir(tmpdir);
+%end
+
+%%%%%% Command to delete temporary file (if needed)
+rmcmd = 'rm';
+
+%%%%%% Location of the binaries - attempt to choose automatically
+%%%%%% (or edit to be hard-coded for your installation)
+ext = lower(computer);
+if ispc
+  ext = 'exe';
+  rmcmd = 'del';
+end
+mpg123 = fullfile(path,['mpg123.',ext]);
+mp3info = fullfile(path,['mp3info.',ext]);
+
+%%%%% Process input arguments
+if nargin < 2
+  N = 0;
+end
+
+% Check for FMT spec (per wavread)
+FMT = 'double';
+if ischar(N)
+  FMT = lower(N);
+  N = 0;
+end
+
+if length(N) == 1
+  % Specified N was upper limit
+  N = [1 N];
+end
+if nargin < 3
+  forcemono = 0;
+else
+  % Check for 3rd arg as FMT
+  if ischar(MONO)
+    FMT = lower(MONO);
+    MONO = 0;
+  end
+  forcemono = (MONO ~= 0);
+end
+if nargin < 4
+  downsamp = 1;
+else
+  downsamp = DOWNSAMP;
+end
+if downsamp ~= 1 && downsamp ~= 2 && downsamp ~= 4
+  error('DOWNSAMP can only be 1, 2, or 4');
+end
+if nargin < 5
+  mpg123delay44kHz = 2257;  % empirical delay of lame/mpg123 loop
+  delay = round(mpg123delay44kHz/downsamp);
+else
+  delay = DELAY;
+end
+
+if strcmp(FMT,'native') == 0 && strcmp(FMT,'double') == 0 && ...
+      strcmp(FMT,'size') == 0
+  error(['FMT must be ''native'' or ''double'' (or ''size''), not ''',FMT,'''']);
+end
+
+
+%%%%%% Constants
+NBITS=16;
+
+%%%%% add extension if none (like wavread)
+[path,file,ext] = fileparts(FILE);
+if isempty(ext)
+  FILE = [FILE, '.mp3'];
+end
+
+%%%%%% Probe file to find format, size, etc. using "mp3info" utility
+cmd = ['"',mp3info, '" -r m -p "%Q %u %b %r %v * %C %e %E %L %O %o %p" "', FILE,'"'];
+% Q = samprate, u = #frames, b = #badframes (needed to get right answer from %u) 
+% r = bitrate, v = mpeg version (1/2/2.5)
+% C = Copyright, e = emph, E = CRC, L = layer, O = orig, o = mono, p = pad
+w = mysystem(cmd);
+% Break into numerical and ascii parts by finding the delimiter we put in
+starpos = findstr(w,'*');
+nums = str2num(w(1:(starpos - 2)));
+strs = tokenize(w((starpos+2):end));
+
+SR = nums(1);
+nframes = nums(2);
+nchans = 2 - strcmp(strs{6}, 'mono');
+layer = length(strs{4});
+bitrate = nums(4)*1000;
+mpgv = nums(5);
+% Figure samples per frame, after
+% http://board.mp3-tech.org/view.php3?bn=agora_mp3techorg&key=1019510889
+if layer == 1
+  smpspfrm = 384;
+elseif SR < 32000 && layer ==3
+  smpspfrm = 576;
+  if mpgv == 1
+    error('SR < 32000 but mpeg version = 1');
+  end
+else
+  smpspfrm = 1152;
+end
+
+OPTS.fmt.mpgBitrate = bitrate;
+OPTS.fmt.mpgVersion = mpgv;
+% fields from wavread's OPTS
+OPTS.fmt.nAvgBytesPerSec = bitrate/8;
+OPTS.fmt.nSamplesPerSec = SR;
+OPTS.fmt.nChannels = nchans;
+OPTS.fmt.nBlockAlign = smpspfrm/SR*bitrate/8;
+OPTS.fmt.nBitsPerSample = NBITS;
+OPTS.fmt.mpgNFrames = nframes;
+OPTS.fmt.mpgCopyright = strs{1};
+OPTS.fmt.mpgEmphasis = strs{2};
+OPTS.fmt.mpgCRC = strs{3};
+OPTS.fmt.mpgLayer = strs{4};
+OPTS.fmt.mpgOriginal = strs{5};
+OPTS.fmt.mpgChanmode = strs{6};
+OPTS.fmt.mpgPad = strs{7};
+OPTS.fmt.mpgSampsPerFrame = smpspfrm;
+
+if SR == 16000 && downsamp == 4
+  error('mpg123 will not downsample 16 kHz files by 4 (only 2)');
+end
+
+if downsamp == 1
+  downsampstr = '';
+else
+  downsampstr = [' -',num2str(downsamp)];
+end
+FS = SR/downsamp;
+
+if forcemono == 1
+  nchans = 1;
+  chansstr = ' -m';
+else
+  chansstr = '';
+end
+
+% Size-reading version
+if strcmp(FMT,'size') == 1
+   Y = [floor(smpspfrm*nframes/downsamp)-delay, nchans];
+else
+
+  % Temporary file to use
+  tmpfile = fullfile(tmpdir, ['tmp',num2str(round(1000*rand(1))),'.wav']);
+
+  skipx = 0;
+  skipblks = 0;
+  skipstr = '';
+  sttfrm = N(1)-1;
+
+  % chop off transcoding delay?
+  %sttfrm = sttfrm + delay;  % empirically measured
+  % no, we want to *decode* those samples, then drop them
+  % so delay gets added to skipx instead
+  
+  if sttfrm > 0
+    skipblks = floor(sttfrm*downsamp/smpspfrm);
+    skipx = sttfrm - (skipblks*smpspfrm/downsamp);
+    skipstr = [' -k ', num2str(skipblks)];
+  end
+  skipx = skipx + delay;
+  
+  lenstr = '';
+  endfrm = -1;
+  decblk = 0;
+  if length(N) > 1
+    endfrm = N(2);
+    if endfrm > sttfrm
+      decblk = ceil((endfrm+delay)*downsamp/smpspfrm) - skipblks + 10;   
+      % we read 10 extra blks (+10) to cover the case where up to 10 bad 
+      % blocks are included in the part we are trying to read (it happened)
+      lenstr = [' -n ', num2str(decblk)];
+      % This generates a spurious "Warn: requested..." if reading right 
+      % to the last sample by index (or bad blks), but no matter.
+    end
+ end
+
+  % Run the decode
+  cmd=['"',mpg123,'"', downsampstr, chansstr, skipstr, lenstr, ...
+       ' -q -w "', tmpfile,'"  "',FILE,'"'];
+  %w = 
+  mysystem(cmd);
+
+  % Load the data
+  Y = wavread(tmpfile);
+
+%  % pad delay on to end, just in case
+%  Y = [Y; zeros(delay,size(Y,2))];
+%  % no, the saved file is just longer
+  
+  if decblk > 0 && length(Y) < decblk*smpspfrm/downsamp
+    % This will happen if the selected block range includes >1 bad block
+    disp(['Warn: requested ', num2str(decblk*smpspfrm/downsamp),' frames, returned ',num2str(length(Y))]);
+  end
+  
+  % Delete tmp file
+  mysystem([rmcmd,' "', tmpfile,'"']);
+  
+  % debug
+%  disp(['sttfrm=',num2str(sttfrm),' endfrm=',num2str(endfrm),' skipx=',num2str(skipx),' delay=',num2str(delay),' len=',num2str(length(Y))]);
+  
+  % Select the desired part
+  if skipx+endfrm-sttfrm > length(Y)
+      endfrm = length(Y)+sttfrm-skipx;
+  end
+  
+  if endfrm > sttfrm
+    Y = Y(skipx+(1:(endfrm-sttfrm)),:);
+  elseif skipx > 0
+    Y = Y((skipx+1):end,:);
+  end
+  
+  % Convert to int if format = 'native'
+  if strcmp(FMT,'native')
+    Y = int16((2^15)*Y);
+  end
+
+end
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+function w = mysystem(cmd)
+% Run system command; report error; strip all but last line
+[s,w] = system(cmd);
+if s ~= 0 
+  error(['unable to execute ',cmd,' (',w,')']);
+end
+% Keep just final line
+w = w((1+max([0,findstr(w,10)])):end);
+% Debug
+%disp([cmd,' -> ','*',w,'*']);
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+function a = tokenize(s)
+% Break space-separated string into cell array of strings
+% 2004-09-18 dpwe@ee.columbia.edu
+a = [];
+p = 1;
+n = 1;
+l = length(s);
+nss = findstr([s(p:end),' '],' ');
+for ns = nss
+  % Skip initial spaces
+  if ns == p
+    p = p+1;
+  else
+    if p <= l
+      a{n} = s(p:(ns-1));
+      n = n+1;
+      p = ns+1;
+    end
+  end
+end
+