annotate toolboxes/MIRtoolbox1.3.2/MIRToolbox/mp3read.m @ 0:cc4b1211e677 tip

initial commit to HG from Changeset: 646 (e263d8a21543) added further path and more save "camirversion.m"
author Daniel Wolff
date Fri, 19 Aug 2016 13:07:06 +0200
parents
children
rev   line source
Daniel@0 1 function [Y,FS,NBITS,OPTS] = mp3read(FILE,N,MONO,DOWNSAMP,DELAY)
Daniel@0 2 % MP3READ Read MP3 audio file via use of external binaries.
Daniel@0 3 % Y = MP3READ(FILE) reads an mp3-encoded audio file into the
Daniel@0 4 % vector Y just like wavread reads a wav-encoded file (one channel
Daniel@0 5 % per column). Extension ".mp3" is added if FILE has none.
Daniel@0 6 % Also accepts other formats of wavread, such as
Daniel@0 7 % Y = MP3READ(FILE,N) to read just the first N sample frames (N
Daniel@0 8 % scalar), or the frames from N(1) to N(2) if N is a two-element vector.
Daniel@0 9 % Y = MP3READ(FILE,FMT) or Y = mp3read(FILE,N,FMT)
Daniel@0 10 % with FMT as 'native' returns int16 samples instead of doubles;
Daniel@0 11 % FMT can be 'double' for default behavior (to exactly mirror the
Daniel@0 12 % syntax of wavread).
Daniel@0 13 %
Daniel@0 14 % [Y,FS,NBITS,OPTS] = MP3READ(FILE...) returns extra information:
Daniel@0 15 % FS is the sampling rate, NBITS is the bit depth (always 16),
Daniel@0 16 % OPTS.fmt is a format info string; OPTS has multiple other
Daniel@0 17 % fields, see WAVREAD.
Daniel@0 18 %
Daniel@0 19 % SIZ = MP3READ(FILE,'size') returns the size of the audio data contained
Daniel@0 20 % in the file in place of the actual audio data, returning the
Daniel@0 21 % 2-element vector SIZ=[samples channels].
Daniel@0 22 %
Daniel@0 23 % [Y...] = MP3READ(FILE,N,MONO,DOWNSAMP,DELAY) extends the
Daniel@0 24 % WAVREAD syntax to allow access to special features of the
Daniel@0 25 % mpg123 engine: MONO = 1 forces output to be mono (by
Daniel@0 26 % averaging stereo channels); DOWNSAMP = 2 or 4 downsamples by
Daniel@0 27 % a factor of 2 or 4 (thus FS returns as 22050 or 11025
Daniel@0 28 % respectively for a 44 kHz mp3 file); DELAY controls how many
Daniel@0 29 % "warm up" samples to drop at the start of the file; the
Daniel@0 30 % default value of 2257 makes an mp3write/mp3read loop for a 44
Daniel@0 31 % kHz mp3 file be as close as possible to being temporally
Daniel@0 32 % aligned; specify as 0 to prevent discard of initial samples.
Daniel@0 33 %
Daniel@0 34 % Example:
Daniel@0 35 % To read an mp3 file as doubles at its original width and sampling rate:
Daniel@0 36 % [Y,FS] = mp3read('piano.mp3');
Daniel@0 37 % To read the first 1 second of the same file, downsampled by a
Daniel@0 38 % factor of 4, cast to mono, using the default filename
Daniel@0 39 % extension:
Daniel@0 40 % [Y,FS4] = mp3read('piano', FS/4, 1, 4);
Daniel@0 41 %
Daniel@0 42 % Note: Because the mp3 format encodes samples in blocks of 26 ms (at
Daniel@0 43 % 44 kHz), and because of the "warm up" period of the encoder,
Daniel@0 44 % the file length may not be exactly what you expect.
Daniel@0 45 %
Daniel@0 46 % Note: requires external binaries mpg123 and mp3info; you
Daniel@0 47 % can find binaries for several platforms at:
Daniel@0 48 % http://labrosa.ee.columbia.edu/matlab/mp3read.html
Daniel@0 49 %
Daniel@0 50 % See also mp3write, wavread.
Daniel@0 51
Daniel@0 52 % 2003-07-20 dpwe@ee.columbia.edu This version calls mpg123.
Daniel@0 53 % 2004-08-31 Fixed to read whole files correctly
Daniel@0 54 % 2004-09-08 Uses mp3info to get info about mp3 files too
Daniel@0 55 % 2004-09-18 Reports all mp3info fields in OPTS.fmt; handles MPG2LSF sizes
Daniel@0 56 % + added MONO, DOWNSAMP flags, changed default behavior.
Daniel@0 57 % 2005-09-28 Fixed bug reading full-rate stereo as 1ch (thx bjoerns@vjk.dk)
Daniel@0 58 % 2006-09-17 Chop off initial 2257 sample delay (for 44.1 kHz mp3)
Daniel@0 59 % so read-write loop doesn't get progressively delayed.
Daniel@0 60 % You can suppress this with a 5th argument of 0.
Daniel@0 61 % 2007-02-04 Added support for FMT argument to match wavread
Daniel@0 62 % Added automatic selection of binary etc. to allow it
Daniel@0 63 % to work cross-platform without editing prior to
Daniel@0 64 % submitting to Matlab File Exchange
Daniel@0 65 % 2007-07-23 Tweaks to 'size' mode so it exactly agrees with read data.
Daniel@0 66
Daniel@0 67 % find our baseline directory
Daniel@0 68 path = fileparts(which('mp3read'));
Daniel@0 69
Daniel@0 70 % %%%%% Directory for temporary file (if needed)
Daniel@0 71 % % Try to read from environment, or use /tmp if it exists, or use CWD
Daniel@0 72 tmpdir = getenv('TMPDIR');
Daniel@0 73 if isempty(tmpdir) || exist(tmpdir,'file')==0
Daniel@0 74 tmpdir = '/tmp';
Daniel@0 75 end
Daniel@0 76 if exist(tmpdir,'file')==0
Daniel@0 77 tmpdir = '';
Daniel@0 78 end
Daniel@0 79 % ensure it exists
Daniel@0 80 %if length(tmpdir) > 0 && exist(tmpdir,'file')==0
Daniel@0 81 % mkdir(tmpdir);
Daniel@0 82 %end
Daniel@0 83
Daniel@0 84 %%%%%% Command to delete temporary file (if needed)
Daniel@0 85 rmcmd = 'rm';
Daniel@0 86
Daniel@0 87 %%%%%% Location of the binaries - attempt to choose automatically
Daniel@0 88 %%%%%% (or edit to be hard-coded for your installation)
Daniel@0 89 ext = lower(computer);
Daniel@0 90 if ispc
Daniel@0 91 ext = 'exe';
Daniel@0 92 rmcmd = 'del';
Daniel@0 93 end
Daniel@0 94 mpg123 = fullfile(path,['mpg123.',ext]);
Daniel@0 95 mp3info = fullfile(path,['mp3info.',ext]);
Daniel@0 96
Daniel@0 97 %%%%% Process input arguments
Daniel@0 98 if nargin < 2
Daniel@0 99 N = 0;
Daniel@0 100 end
Daniel@0 101
Daniel@0 102 % Check for FMT spec (per wavread)
Daniel@0 103 FMT = 'double';
Daniel@0 104 if ischar(N)
Daniel@0 105 FMT = lower(N);
Daniel@0 106 N = 0;
Daniel@0 107 end
Daniel@0 108
Daniel@0 109 if length(N) == 1
Daniel@0 110 % Specified N was upper limit
Daniel@0 111 N = [1 N];
Daniel@0 112 end
Daniel@0 113 if nargin < 3
Daniel@0 114 forcemono = 0;
Daniel@0 115 else
Daniel@0 116 % Check for 3rd arg as FMT
Daniel@0 117 if ischar(MONO)
Daniel@0 118 FMT = lower(MONO);
Daniel@0 119 MONO = 0;
Daniel@0 120 end
Daniel@0 121 forcemono = (MONO ~= 0);
Daniel@0 122 end
Daniel@0 123 if nargin < 4
Daniel@0 124 downsamp = 1;
Daniel@0 125 else
Daniel@0 126 downsamp = DOWNSAMP;
Daniel@0 127 end
Daniel@0 128 if downsamp ~= 1 && downsamp ~= 2 && downsamp ~= 4
Daniel@0 129 error('DOWNSAMP can only be 1, 2, or 4');
Daniel@0 130 end
Daniel@0 131 if nargin < 5
Daniel@0 132 mpg123delay44kHz = 2257; % empirical delay of lame/mpg123 loop
Daniel@0 133 delay = round(mpg123delay44kHz/downsamp);
Daniel@0 134 else
Daniel@0 135 delay = DELAY;
Daniel@0 136 end
Daniel@0 137
Daniel@0 138 if strcmp(FMT,'native') == 0 && strcmp(FMT,'double') == 0 && ...
Daniel@0 139 strcmp(FMT,'size') == 0
Daniel@0 140 error(['FMT must be ''native'' or ''double'' (or ''size''), not ''',FMT,'''']);
Daniel@0 141 end
Daniel@0 142
Daniel@0 143
Daniel@0 144 %%%%%% Constants
Daniel@0 145 NBITS=16;
Daniel@0 146
Daniel@0 147 %%%%% add extension if none (like wavread)
Daniel@0 148 [path,file,ext] = fileparts(FILE);
Daniel@0 149 if isempty(ext)
Daniel@0 150 FILE = [FILE, '.mp3'];
Daniel@0 151 end
Daniel@0 152
Daniel@0 153 %%%%%% Probe file to find format, size, etc. using "mp3info" utility
Daniel@0 154 cmd = ['"',mp3info, '" -r m -p "%Q %u %b %r %v * %C %e %E %L %O %o %p" "', FILE,'"'];
Daniel@0 155 % Q = samprate, u = #frames, b = #badframes (needed to get right answer from %u)
Daniel@0 156 % r = bitrate, v = mpeg version (1/2/2.5)
Daniel@0 157 % C = Copyright, e = emph, E = CRC, L = layer, O = orig, o = mono, p = pad
Daniel@0 158 w = mysystem(cmd);
Daniel@0 159 % Break into numerical and ascii parts by finding the delimiter we put in
Daniel@0 160 starpos = findstr(w,'*');
Daniel@0 161 nums = str2num(w(1:(starpos - 2)));
Daniel@0 162 strs = tokenize(w((starpos+2):end));
Daniel@0 163
Daniel@0 164 SR = nums(1);
Daniel@0 165 nframes = nums(2);
Daniel@0 166 nchans = 2 - strcmp(strs{6}, 'mono');
Daniel@0 167 layer = length(strs{4});
Daniel@0 168 bitrate = nums(4)*1000;
Daniel@0 169 mpgv = nums(5);
Daniel@0 170 % Figure samples per frame, after
Daniel@0 171 % http://board.mp3-tech.org/view.php3?bn=agora_mp3techorg&key=1019510889
Daniel@0 172 if layer == 1
Daniel@0 173 smpspfrm = 384;
Daniel@0 174 elseif SR < 32000 && layer ==3
Daniel@0 175 smpspfrm = 576;
Daniel@0 176 if mpgv == 1
Daniel@0 177 error('SR < 32000 but mpeg version = 1');
Daniel@0 178 end
Daniel@0 179 else
Daniel@0 180 smpspfrm = 1152;
Daniel@0 181 end
Daniel@0 182
Daniel@0 183 OPTS.fmt.mpgBitrate = bitrate;
Daniel@0 184 OPTS.fmt.mpgVersion = mpgv;
Daniel@0 185 % fields from wavread's OPTS
Daniel@0 186 OPTS.fmt.nAvgBytesPerSec = bitrate/8;
Daniel@0 187 OPTS.fmt.nSamplesPerSec = SR;
Daniel@0 188 OPTS.fmt.nChannels = nchans;
Daniel@0 189 OPTS.fmt.nBlockAlign = smpspfrm/SR*bitrate/8;
Daniel@0 190 OPTS.fmt.nBitsPerSample = NBITS;
Daniel@0 191 OPTS.fmt.mpgNFrames = nframes;
Daniel@0 192 OPTS.fmt.mpgCopyright = strs{1};
Daniel@0 193 OPTS.fmt.mpgEmphasis = strs{2};
Daniel@0 194 OPTS.fmt.mpgCRC = strs{3};
Daniel@0 195 OPTS.fmt.mpgLayer = strs{4};
Daniel@0 196 OPTS.fmt.mpgOriginal = strs{5};
Daniel@0 197 OPTS.fmt.mpgChanmode = strs{6};
Daniel@0 198 OPTS.fmt.mpgPad = strs{7};
Daniel@0 199 OPTS.fmt.mpgSampsPerFrame = smpspfrm;
Daniel@0 200
Daniel@0 201 if SR == 16000 && downsamp == 4
Daniel@0 202 error('mpg123 will not downsample 16 kHz files by 4 (only 2)');
Daniel@0 203 end
Daniel@0 204
Daniel@0 205 if downsamp == 1
Daniel@0 206 downsampstr = '';
Daniel@0 207 else
Daniel@0 208 downsampstr = [' -',num2str(downsamp)];
Daniel@0 209 end
Daniel@0 210 FS = SR/downsamp;
Daniel@0 211
Daniel@0 212 if forcemono == 1
Daniel@0 213 nchans = 1;
Daniel@0 214 chansstr = ' -m';
Daniel@0 215 else
Daniel@0 216 chansstr = '';
Daniel@0 217 end
Daniel@0 218
Daniel@0 219 % Size-reading version
Daniel@0 220 if strcmp(FMT,'size') == 1
Daniel@0 221 Y = [floor(smpspfrm*nframes/downsamp)-delay, nchans];
Daniel@0 222 else
Daniel@0 223
Daniel@0 224 % Temporary file to use
Daniel@0 225 tmpfile = fullfile(tmpdir, ['tmp',num2str(round(1000*rand(1))),'.wav']);
Daniel@0 226
Daniel@0 227 skipx = 0;
Daniel@0 228 skipblks = 0;
Daniel@0 229 skipstr = '';
Daniel@0 230 sttfrm = N(1)-1;
Daniel@0 231
Daniel@0 232 % chop off transcoding delay?
Daniel@0 233 %sttfrm = sttfrm + delay; % empirically measured
Daniel@0 234 % no, we want to *decode* those samples, then drop them
Daniel@0 235 % so delay gets added to skipx instead
Daniel@0 236
Daniel@0 237 if sttfrm > 0
Daniel@0 238 skipblks = floor(sttfrm*downsamp/smpspfrm);
Daniel@0 239 skipx = sttfrm - (skipblks*smpspfrm/downsamp);
Daniel@0 240 skipstr = [' -k ', num2str(skipblks)];
Daniel@0 241 end
Daniel@0 242 skipx = skipx + delay;
Daniel@0 243
Daniel@0 244 lenstr = '';
Daniel@0 245 endfrm = -1;
Daniel@0 246 decblk = 0;
Daniel@0 247 if length(N) > 1
Daniel@0 248 endfrm = N(2);
Daniel@0 249 if endfrm > sttfrm
Daniel@0 250 decblk = ceil((endfrm+delay)*downsamp/smpspfrm) - skipblks + 10;
Daniel@0 251 % we read 10 extra blks (+10) to cover the case where up to 10 bad
Daniel@0 252 % blocks are included in the part we are trying to read (it happened)
Daniel@0 253 lenstr = [' -n ', num2str(decblk)];
Daniel@0 254 % This generates a spurious "Warn: requested..." if reading right
Daniel@0 255 % to the last sample by index (or bad blks), but no matter.
Daniel@0 256 end
Daniel@0 257 end
Daniel@0 258
Daniel@0 259 % Run the decode
Daniel@0 260 cmd=['"',mpg123,'"', downsampstr, chansstr, skipstr, lenstr, ...
Daniel@0 261 ' -q -w "', tmpfile,'" "',FILE,'"'];
Daniel@0 262 %w =
Daniel@0 263 mysystem(cmd);
Daniel@0 264
Daniel@0 265 % Load the data
Daniel@0 266 Y = wavread(tmpfile);
Daniel@0 267
Daniel@0 268 % % pad delay on to end, just in case
Daniel@0 269 % Y = [Y; zeros(delay,size(Y,2))];
Daniel@0 270 % % no, the saved file is just longer
Daniel@0 271
Daniel@0 272 if decblk > 0 && length(Y) < decblk*smpspfrm/downsamp
Daniel@0 273 % This will happen if the selected block range includes >1 bad block
Daniel@0 274 disp(['Warn: requested ', num2str(decblk*smpspfrm/downsamp),' frames, returned ',num2str(length(Y))]);
Daniel@0 275 end
Daniel@0 276
Daniel@0 277 % Delete tmp file
Daniel@0 278 mysystem([rmcmd,' "', tmpfile,'"']);
Daniel@0 279
Daniel@0 280 % debug
Daniel@0 281 % disp(['sttfrm=',num2str(sttfrm),' endfrm=',num2str(endfrm),' skipx=',num2str(skipx),' delay=',num2str(delay),' len=',num2str(length(Y))]);
Daniel@0 282
Daniel@0 283 % Select the desired part
Daniel@0 284 if skipx+endfrm-sttfrm > length(Y)
Daniel@0 285 endfrm = length(Y)+sttfrm-skipx;
Daniel@0 286 end
Daniel@0 287
Daniel@0 288 if endfrm > sttfrm
Daniel@0 289 Y = Y(skipx+(1:(endfrm-sttfrm)),:);
Daniel@0 290 elseif skipx > 0
Daniel@0 291 Y = Y((skipx+1):end,:);
Daniel@0 292 end
Daniel@0 293
Daniel@0 294 % Convert to int if format = 'native'
Daniel@0 295 if strcmp(FMT,'native')
Daniel@0 296 Y = int16((2^15)*Y);
Daniel@0 297 end
Daniel@0 298
Daniel@0 299 end
Daniel@0 300
Daniel@0 301 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
Daniel@0 302 function w = mysystem(cmd)
Daniel@0 303 % Run system command; report error; strip all but last line
Daniel@0 304 [s,w] = system(cmd);
Daniel@0 305 if s ~= 0
Daniel@0 306 error(['unable to execute ',cmd,' (',w,')']);
Daniel@0 307 end
Daniel@0 308 % Keep just final line
Daniel@0 309 w = w((1+max([0,findstr(w,10)])):end);
Daniel@0 310 % Debug
Daniel@0 311 %disp([cmd,' -> ','*',w,'*']);
Daniel@0 312
Daniel@0 313 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
Daniel@0 314 function a = tokenize(s)
Daniel@0 315 % Break space-separated string into cell array of strings
Daniel@0 316 % 2004-09-18 dpwe@ee.columbia.edu
Daniel@0 317 a = [];
Daniel@0 318 p = 1;
Daniel@0 319 n = 1;
Daniel@0 320 l = length(s);
Daniel@0 321 nss = findstr([s(p:end),' '],' ');
Daniel@0 322 for ns = nss
Daniel@0 323 % Skip initial spaces
Daniel@0 324 if ns == p
Daniel@0 325 p = p+1;
Daniel@0 326 else
Daniel@0 327 if p <= l
Daniel@0 328 a{n} = s(p:(ns-1));
Daniel@0 329 n = n+1;
Daniel@0 330 p = ns+1;
Daniel@0 331 end
Daniel@0 332 end
Daniel@0 333 end
Daniel@0 334