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