Mercurial > hg > camir-aes2014
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 +