/audio_coding/m4aread.m

http://github.com/simon-r/Audio-Tools · Objective C · 338 lines · 299 code · 39 blank · 0 comment · 59 complexity · 05da59803f26ef14068c98ffc6f7462d MD5 · raw file

  1. function [Y SR NBITS OPTS] = m4aread(FILE,N,MONO,DOWNSAMP,DELAY)
  2. % M4AREAD Read mpeg 4 audio (AAC) file via use of external binaries.
  3. % Y = M4AREAD(FILE) reads an m4a-encoded audio file into the
  4. % vector Y just like wavread reads a wav-encoded file (one channel
  5. % per column). Extension ".m4a" is added if FILE has none.
  6. % Also accepts other formats of wavread, such as
  7. % Y = M4AREAD(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 = M4AREAD(FILE,FMT) or Y = m4aread(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] = M4AREAD(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 = M4AREAD(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...] = M4AREAD(FILE,N,MONO,DOWNSAMP) extends the
  24. % WAVREAD syntax to emulate 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 m4a file). (A final DELAY argument
  29. % is also supported for full compatibility with mp3read, but
  30. % has no use). In this case, N is interpreted in terms of the
  31. % post-downsampling samples
  32. %
  33. % Example:
  34. % To read an m4a file as doubles at its original width and sampling rate:
  35. % [Y,FS] = m4aread('piano.m4a');
  36. % To read the first 1 second of the same file, downsampled by a
  37. % factor of 4, cast to mono, using the default filename
  38. % extension:
  39. % [Y,FS4] = m4aread('piano', FS/4, 1, 4);
  40. %
  41. % Note: requires external binary faad
  42. % http://labrosa.ee.columbia.edu/matlab/mp3read.html
  43. %
  44. % See also wavread.
  45. % $Header: /Users/dpwe/matlab/columbiafns/m4aread/RCS/m4aread.m,v 1.1 2010/09/17 16:07:46 dpwe Exp dpwe $
  46. % 2009-10-14 m4aread created from mp3read
  47. % Copyright (c) 2010, Dan Ellis
  48. % All rights reserved.
  49. %
  50. % Redistribution and use in source and binary forms, with or without
  51. % modification, are permitted provided that the following conditions are
  52. % met:
  53. %
  54. % * Redistributions of source code must retain the above copyright
  55. % notice, this list of conditions and the following disclaimer.
  56. % * Redistributions in binary form must reproduce the above copyright
  57. % notice, this list of conditions and the following disclaimer in
  58. % the documentation and/or other materials provided with the distribution
  59. %
  60. % THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  61. % AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  62. % IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  63. % ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
  64. % LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
  65. % CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
  66. % SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
  67. % INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
  68. % CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  69. % ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  70. % POSSIBILITY OF SUCH DAMAGE.
  71. % find our baseline directory
  72. %path = fileparts(which('m4aread'));
  73. if ispc()
  74. path = fileparts(which('m4aread')) ;
  75. ext = 'exe';
  76. faad = fullfile(path,['faad.',ext]);
  77. elseif isunix()
  78. path = locate_unix_cmd( 'faad' ) ;
  79. faad = path ;
  80. elseif ismac()
  81. fooo = 1 ;
  82. end
  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. %%%%%% Command to delete temporary file (if needed)
  97. rmcmd = 'rm';
  98. %%%%%% Location of the binaries - attempt to choose automatically
  99. %%%%%% (or edit to be hard-coded for your installation)
  100. % ext = lower(computer);
  101. % if ispc
  102. %
  103. % rmcmd = 'del';
  104. % end
  105. %faad = '/opt/local/bin/faad';
  106. %%%%% Process input arguments
  107. if nargin < 2
  108. N = 0;
  109. end
  110. % Check for FMT spec (per wavread)
  111. FMT = 'double';
  112. if ischar(N)
  113. FMT = lower(N);
  114. N = 0;
  115. end
  116. if length(N) == 0
  117. N = [1 0];
  118. elseif length(N) == 1
  119. % Specified N was upper limit
  120. N = [1 N];
  121. end
  122. if nargin < 3
  123. forcemono = 0;
  124. else
  125. % Check for 3rd arg as FMT
  126. if ischar(MONO)
  127. FMT = lower(MONO);
  128. MONO = 0;
  129. end
  130. forcemono = (MONO ~= 0);
  131. end
  132. if nargin < 4
  133. downsamp = 1;
  134. else
  135. downsamp = DOWNSAMP;
  136. end
  137. if downsamp ~= 1 && downsamp ~= 2 && downsamp ~= 4
  138. error('DOWNSAMP can only be 1, 2, or 4');
  139. end
  140. % process DELAY option (nargin 5) after we've read the SR
  141. if strcmp(FMT,'native') == 0 && strcmp(FMT,'double') == 0 && ...
  142. strcmp(FMT,'size') == 0
  143. error(['FMT must be ''native'' or ''double'' (or ''size''), not ''',FMT,'''']);
  144. end
  145. %%%%%% Constants
  146. NBITS=16;
  147. %%%%% add extension if none (like wavread)
  148. [path,file,ext] = fileparts(FILE);
  149. if isempty(ext)
  150. FILE = [FILE, '.m4a'];
  151. end
  152. %%%%%% Probe file to find format, size, etc.
  153. cmd = ['"',faad, '" -i "', FILE,'"'];
  154. [s,w] = system(cmd);
  155. % Break into lines
  156. rp = [0,find(w==10)];
  157. for i =1:length(rp)-1;
  158. ll{i} = w(rp(i)+1:rp(i+1)-1);
  159. end
  160. % Find the line that ends "file info:"
  161. r = strfind(ll,'file info');
  162. for i = 1:length(r);
  163. if length(r{i}) ==0;
  164. rr(i) = 0;
  165. else
  166. rr(i) = r{i};
  167. end
  168. end
  169. fil = find(rr);
  170. if length(fil) ~= 1
  171. display(['m4aread: Unexpected result from "',cmd,'": ',w]);
  172. %error('unexpected result');
  173. Y = [];
  174. SR = 0;
  175. return;
  176. end
  177. % line after gives info e.g.
  178. % LC AAC 318.870 secs, 2 ch, 44100 Hz
  179. % or
  180. % ADTS, 100.008 sec, 128 kbps, 44100 Hz
  181. infoline = '';
  182. while length(infoline) == 0
  183. fil = fil +1;
  184. infoline = ll{fil};
  185. end
  186. infoline(infoline == 9) = ' ';
  187. strs = tokenize(infoline);
  188. % defaults
  189. SR = 44100; dur = 0; nchans = 2;
  190. % try to find stuff
  191. ps = strmatch('Hz', strs);
  192. if length(ps) > 0
  193. SR = str2num(strs{ps(1)-1});
  194. else
  195. disp(['Warn: using default SR = ',num2str(SR)]);
  196. end
  197. ps = strmatch('sec',strs); % will match 'sec' or 'secs'
  198. if length(ps) > 0
  199. dur = str2num(strs{ps(1)-1});
  200. else
  201. disp(['Warn: no duration found, using ', num2str(dur)]);
  202. end
  203. ps = strmatch('ch',strs); % will match 'ch,'
  204. if length(ps) > 0
  205. nchans = str2num(strs{ps(1)-1});
  206. else
  207. disp(['Warn: no channel count found, using ', num2str(nchans)]);
  208. end
  209. nframes = round(SR*dur); % well, approximately
  210. smpsperfrm = nchans;
  211. % fields from wavread's OPTS
  212. OPTS.fmt.nAvgBytesPerSec = 0; % bitrate/8;
  213. OPTS.fmt.nSamplesPerSec = SR;
  214. OPTS.fmt.nChannels = nchans;
  215. OPTS.fmt.nBlockAlign = 0; %smpspfrm/SR*bitrate/8;
  216. OPTS.fmt.nBitsPerSample = NBITS;
  217. OPTS.fmt.mpgLayer = strs{1}; % [strs{1},' ',strs{2}];
  218. % process or set delay
  219. if nargin < 5
  220. delay = 0;
  221. else
  222. delay = DELAY;
  223. end
  224. % Size-reading version
  225. if strcmp(FMT,'size') == 1
  226. Y = [floor(nframes/downsamp), nchans];
  227. else
  228. % Temporary file to use
  229. %tmpfile = fullfile(tmpdir, ['tmp',num2str(round(1000*rand(1))),'.wav']);
  230. [s,upid] = system('echo $$');
  231. % remove final CR
  232. upid = upid(1:end-1);
  233. tmpfile = fullfile(tmpdir, ['m4atmp',upid,'.wav']);
  234. sttfrm = max(0,N(1)-1);
  235. lenstr = '';
  236. endfrm = -1;
  237. decblk = 0;
  238. if length(N) > 1
  239. endfrm = N(2);
  240. end
  241. % Run the decode
  242. cmd=['"',faad,'" -o "', tmpfile,'" "',FILE,'"'];
  243. %w =
  244. mysystem(cmd);
  245. sttfrm = sttfrm + delay;
  246. endfrm = endfrm + delay;
  247. if endfrm > sttfrm
  248. % don't load more data than we want
  249. [Y,SR] = wavread_downsamp(tmpfile, [sttfrm+1 endfrm], forcemono, downsamp);
  250. else
  251. % Load all the data
  252. [Y,SR] = wavread_downsamp(tmpfile, [sttfrm+1 Inf], forcemono, downsamp);
  253. end
  254. % % pad delay on to end, just in case
  255. % Y = [Y; zeros(delay,size(Y,2))];
  256. % % no, the saved file is just longer
  257. % Delete tmp file
  258. mysystem([rmcmd,' "', tmpfile,'"']);
  259. % debug
  260. % disp(['sttfrm=',num2str(sttfrm),' endfrm=',num2str(endfrm),' delay=',num2str(delay),' len=',num2str(length(Y))]);
  261. % Convert to int if format = 'native'
  262. if strcmp(FMT,'native')
  263. Y = int16((2^15)*Y);
  264. end
  265. end
  266. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  267. function w = mysystem(cmd)
  268. % Run system command; report error; strip all but last line
  269. [s,w] = system(cmd);
  270. if s ~= 0
  271. error(['unable to execute ',cmd,' (',w,')']);
  272. end
  273. % Keep just final line
  274. w = w((1+max([0,findstr(w,10)])):end);
  275. % Debug
  276. %disp([cmd,' -> ','*',w,'*']);
  277. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  278. function a = tokenize(s,t)
  279. % Break space-separated string into cell array of strings.
  280. % Optional second arg gives alternate separator (default ' ')
  281. % 2004-09-18 dpwe@ee.columbia.edu
  282. if nargin < 2; t = ' '; end
  283. a = [];
  284. p = 1;
  285. n = 1;
  286. l = length(s);
  287. nss = findstr([s(p:end),t],t);
  288. for ns = nss
  289. % Skip initial spaces (separators)
  290. if ns == p
  291. p = p+1;
  292. else
  293. if p <= l
  294. a{n} = s(p:(ns-1));
  295. n = n+1;
  296. p = ns+1;
  297. end
  298. end
  299. end