Mercurial > octave-antonio
comparison scripts/audio/wavread.m @ 19814:71770cf07c30
wavread, wavwrite: Overhaul functions.
* wavread.m, wavwrite.m: Rewrite as wrappers around the new audioread
and audiowrite functions. Redo docstrings and add seealso links to
audioread and audiowrite. Improve input validation. Fix %!tests and
make conditional on HAVE_SNDFILE.
author | Mike Miller <mtmiller@ieee.org> |
---|---|
date | Sat, 21 Feb 2015 11:58:20 -0500 |
parents | 4197fc428c7d |
children | 9fc020886ae9 |
comparison
equal
deleted
inserted
replaced
19813:879cff0c05dc | 19814:71770cf07c30 |
---|---|
1 ## Copyright (C) 2015 Mike Miller | |
1 ## Copyright (C) 2005-2015 Michael Zeising | 2 ## Copyright (C) 2005-2015 Michael Zeising |
2 ## | 3 ## |
3 ## This file is part of Octave. | 4 ## This file is part of Octave. |
4 ## | 5 ## |
5 ## Octave is free software; you can redistribute it and/or modify it | 6 ## Octave is free software; you can redistribute it and/or modify it |
16 ## along with Octave; see the file COPYING. If not, see | 17 ## along with Octave; see the file COPYING. If not, see |
17 ## <http://www.gnu.org/licenses/>. | 18 ## <http://www.gnu.org/licenses/>. |
18 | 19 |
19 ## -*- texinfo -*- | 20 ## -*- texinfo -*- |
20 ## @deftypefn {Function File} {@var{y} =} wavread (@var{filename}) | 21 ## @deftypefn {Function File} {@var{y} =} wavread (@var{filename}) |
21 ## @deftypefnx {Function File} {[@var{y}, @var{Fs}, @var{bps}] =} wavread (@var{filename}) | 22 ## @deftypefnx {Function File} {[@var{y}, @var{fs}, @var{nbits}] =} wavread (@var{filename}) |
22 ## @deftypefnx {Function File} {[@dots{}] =} wavread (@var{filename}, @var{n}) | 23 ## @deftypefnx {Function File} {[@dots{}] =} wavread (@var{filename}, @var{n}) |
23 ## @deftypefnx {Function File} {[@dots{}] =} wavread (@var{filename}, [@var{n1} @var{n2}]) | 24 ## @deftypefnx {Function File} {[@dots{}] =} wavread (@var{filename}, [@var{n1} @var{n2}]) |
24 ## @deftypefnx {Function File} {[@var{samples}, @var{channels}] =} wavread (@var{filename}, "size") | 25 ## @deftypefnx {Function File} {[@dots{}] =} wavread (@dots{}, @var{datatype}) |
26 ## @deftypefnx {Function File} {@var{sz} =} wavread (@var{filename}, "size") | |
27 ## Read the audio signal @var{y} from the RIFF/WAVE sound file @var{filename}. | |
28 ## If the file contains multichannel data, then @var{y} is a matrix with the | |
29 ## channels represented as columns. | |
25 ## | 30 ## |
26 ## Load the RIFF/WAVE sound file @var{filename}, and return the samples | 31 ## The optional return value @var{fs} is the sample rate of the audio file in |
27 ## in vector @var{y}. If the file contains multichannel data, then | 32 ## Hz. The optional return value @var{nbits} is the number of bits per sample |
28 ## @var{y} is a matrix with the channels represented as columns. | 33 ## as encoded in the file. |
29 ## | 34 ## |
30 ## @code{[@var{y}, @var{Fs}, @var{bps}] = wavread (@var{filename})} | 35 ## If @var{n} is specified, only the first @var{n} samples of the file are |
36 ## returned. If [@var{n1} @var{n2}] is specified, only the range of samples | |
37 ## from @var{n1} to @var{n2} is returned. A value of @code{Inf} can be used | |
38 ## to represent the total number of samples in the file. | |
31 ## | 39 ## |
32 ## Additionally return the sample rate (@var{fs}) in Hz and the number of bits | 40 ## If the option @qcode{"size"} is given, then the size of the audio signal |
33 ## per sample (@var{bps}). | 41 ## is returned instead of the data. The size is returned in a row vector of |
34 ## | 42 ## the form [@var{samples} @var{channels}]. If there are two output arguments, |
35 ## @code{[@dots{}] = wavread (@var{filename}, @var{n})} | 43 ## the number of samples is assigned to the first and the number of channels |
36 ## | 44 ## is assigned to the second. |
37 ## Read only the first @var{n} samples from each channel. | 45 ## @seealso{audioread, audiowrite, wavwrite} |
38 ## | |
39 ## @code{wavread (@var{filename}, [@var{n1} @var{n2}])} | |
40 ## | |
41 ## Read only samples @var{n1} through @var{n2} from each channel. | |
42 ## | |
43 ## @code{[@var{samples}, @var{channels}] = wavread (@var{filename}, "size")} | |
44 ## | |
45 ## Return the number of samples (@var{n}) and number of channels (@var{ch}) | |
46 ## instead of the audio data. | |
47 ## @seealso{wavwrite} | |
48 ## @end deftypefn | 46 ## @end deftypefn |
49 | 47 |
50 ## Author: Michael Zeising <michael@michaels-website.de> | 48 function [y, fs, nbits] = wavread (filename, varargin) |
51 ## Created: 06 December 2005 | |
52 | 49 |
53 function [y, samples_per_sec, bits_per_sample] = wavread (filename, param) | 50 if (nargin < 1 || nargin > 3) |
54 | |
55 FORMAT_PCM = 0x0001; # PCM (8/16/32 bit) | |
56 FORMAT_IEEE_FLOAT = 0x0003; # IEEE float (32/64 bit) | |
57 BYTEORDER = "ieee-le"; | |
58 | |
59 if (nargin < 1 || nargin > 2) | |
60 print_usage (); | 51 print_usage (); |
61 endif | 52 endif |
62 | 53 |
63 if (! ischar (filename)) | 54 if (! ischar (filename)) |
64 error ("wavread: FILENAME must be a character string"); | 55 error ("wavread: FILENAME must be a character string"); |
65 endif | 56 endif |
66 | 57 |
67 fid = -1; | 58 datatype = "double"; |
59 samples = [1, Inf]; | |
60 do_file_size = false; | |
68 | 61 |
69 unwind_protect | 62 if (nargin == 3) |
70 | 63 samples = varargin{1}; |
71 [fid, msg] = fopen (filename, "rb"); | 64 datatype = varargin{2}; |
72 | 65 elseif (nargin == 2) |
73 if (fid < 0) | 66 if (strcmp (varargin{1}, "size")) |
74 error ("wavread: %s", msg); | 67 do_file_size = true; |
68 elseif (ischar (varargin{1})) | |
69 datatype = varargin{1}; | |
70 else | |
71 samples = varargin{1}; | |
75 endif | 72 endif |
76 | |
77 ## Get file size. | |
78 fseek (fid, 0, "eof"); | |
79 file_size = ftell (fid); | |
80 fseek (fid, 0, "bof"); | |
81 | |
82 ## Find RIFF chunk. | |
83 riff_size = find_chunk (fid, "RIFF", file_size); | |
84 riff_pos = ftell (fid); | |
85 if (riff_size == -1) | |
86 error ("wavread: file contains no RIFF chunk"); | |
87 endif | |
88 riff_size = min (riff_size, file_size - riff_pos); | |
89 | |
90 riff_type = char (fread (fid, 4))'; | |
91 if (! strcmp (riff_type, "WAVE")) | |
92 error ("wavread: file contains no WAVE signature"); | |
93 endif | |
94 riff_pos = riff_pos + 4; | |
95 riff_size = riff_size - 4; | |
96 | |
97 ## Find format chunk inside the RIFF chunk. | |
98 fseek (fid, riff_pos, "bof"); | |
99 fmt_size = find_chunk (fid, "fmt ", riff_size); | |
100 fmt_pos = ftell (fid); | |
101 if (fmt_size == -1) | |
102 error ("wavread: file contains no format chunk"); | |
103 endif | |
104 | |
105 ## Find data chunk inside the RIFF chunk. | |
106 ## We don't assume that it comes after the format chunk. | |
107 fseek (fid, riff_pos, "bof"); | |
108 data_size = find_chunk (fid, "data", riff_size); | |
109 data_pos = ftell (fid); | |
110 if (data_size == -1) | |
111 error ("wavread: file contains no data chunk"); | |
112 endif | |
113 data_size = min (data_size, file_size - data_pos); | |
114 | |
115 ### Read format chunk. | |
116 fseek (fid, fmt_pos, "bof"); | |
117 | |
118 ## Sample format code. | |
119 format_tag = fread (fid, 1, "uint16", 0, BYTEORDER); | |
120 if (format_tag != FORMAT_PCM && format_tag != FORMAT_IEEE_FLOAT) | |
121 error ("wavread: sample format %#x is not supported", format_tag); | |
122 endif | |
123 | |
124 ## Number of interleaved channels. | |
125 channels = fread (fid, 1, "uint16", 0, BYTEORDER); | |
126 | |
127 ## Sample rate. | |
128 samples_per_sec = fread (fid, 1, "uint32", 0, BYTEORDER); | |
129 | |
130 ## Bits per sample. | |
131 fseek (fid, 6, "cof"); | |
132 bits_per_sample = fread (fid, 1, "uint16", 0, BYTEORDER); | |
133 | |
134 ### Read data chunk. | |
135 fseek (fid, data_pos, "bof"); | |
136 | |
137 ## Determine sample data type. | |
138 if (format_tag == FORMAT_PCM) | |
139 switch (bits_per_sample) | |
140 case 8 | |
141 format = "uint8"; | |
142 case 16 | |
143 format = "int16"; | |
144 case 24 | |
145 format = "uint8"; | |
146 case 32 | |
147 format = "int32"; | |
148 otherwise | |
149 error ("wavread: %d bits sample resolution is not supported with PCM", | |
150 bits_per_sample); | |
151 endswitch | |
152 else | |
153 switch (bits_per_sample) | |
154 case 32 | |
155 format = "float32"; | |
156 case 64 | |
157 format = "float64"; | |
158 otherwise | |
159 error ("wavread: %d bits sample resolution is not supported with IEEE float", | |
160 bits_per_sample); | |
161 endswitch | |
162 endif | |
163 | |
164 ## Parse arguments. | |
165 if (nargin == 1) | |
166 length = idivide (8 * data_size, bits_per_sample); | |
167 else | |
168 nparams = numel (param); | |
169 if (nparams == 1) | |
170 ## Number of samples is given. | |
171 length = param * channels; | |
172 elseif (nparams == 2) | |
173 ## Sample range is given. | |
174 if (fseek (fid, (param(1)-1) * channels * (bits_per_sample/8), "cof") < 0) | |
175 warning ("wavread: seeking failed"); | |
176 endif | |
177 length = (param(2)-param(1)+1) * channels; | |
178 elseif (nparams == 4 && char (param) == "size") | |
179 ## Size of the file is requested. | |
180 y = idivide (8 * data_size, channels * bits_per_sample); | |
181 samples_per_sec = channels; | |
182 return; | |
183 else | |
184 error ("wavread: invalid PARAM argument"); | |
185 endif | |
186 endif | |
187 | |
188 ## Read samples and close file. | |
189 if (bits_per_sample == 24) | |
190 length *= 3; | |
191 endif | |
192 | |
193 [yi, n] = fread (fid, length, format, 0, BYTEORDER); | |
194 | |
195 unwind_protect_cleanup | |
196 | |
197 if (fid >= 0) | |
198 fclose (fid); | |
199 endif | |
200 | |
201 end_unwind_protect | |
202 | |
203 ## Check data. | |
204 if (mod (numel (yi), channels) != 0) | |
205 error ("wavread: data in %s doesn't match the number of channels", | |
206 filename); | |
207 endif | 73 endif |
208 | 74 |
209 if (bits_per_sample == 24) | 75 if (isscalar (samples)) |
210 yi = reshape (yi, 3, rows (yi) / 3)'; | 76 samples = [1, samples]; |
211 yi(yi(:,3) >= 128, 3) -= 256; | |
212 yi = yi * [1; 256; 65536]; | |
213 endif | 77 endif |
214 | 78 |
215 if (format_tag == FORMAT_PCM) | 79 if (! (isrow (samples) && numel (samples) == 2 && all (samples > 0) |
216 ## Normalize samples. | 80 && all (fix (samples) == samples))) |
217 switch (bits_per_sample) | 81 error ("wavread: SAMPLES must be a 1- or 2-element integer row vector"); |
218 case 8 | |
219 yi = (yi - 128)/128; | |
220 case 16 | |
221 yi /= 32768; | |
222 case 24 | |
223 yi /= 8388608; | |
224 case 32 | |
225 yi /= 2147483648; | |
226 endswitch | |
227 endif | 82 endif |
228 | 83 |
229 ## Deinterleave. | 84 if (! (ischar (datatype) && any (strcmp (datatype, {"double", "native"})))) |
230 nr = numel (yi) / channels; | 85 error ('wavread: DATATYPE must be either "double" or "native"'); |
231 y = reshape (yi, channels, nr)'; | 86 endif |
87 | |
88 info = audioinfo (filename); | |
89 | |
90 if (do_file_size) | |
91 if (nargout > 1) | |
92 [y, fs] = deal (info.TotalSamples, info.NumChannels); | |
93 else | |
94 y = [info.TotalSamples, info.NumChannels]; | |
95 endif | |
96 else | |
97 [y, fs] = audioread (filename, samples, datatype); | |
98 nbits = info.BitsPerSample; | |
99 endif | |
232 | 100 |
233 endfunction | 101 endfunction |
234 | 102 |
235 ## Given a chunk_id, scan through chunks from the current file position | |
236 ## though at most size bytes. Return the size of the found chunk, with | |
237 ## file position pointing to the start of the chunk data. Return -1 for | |
238 ## size if chunk is not found. | |
239 | 103 |
240 function chunk_size = find_chunk (fid, chunk_id, size) | 104 ## Functional tests for wavread/wavwrite pair are in wavwrite.m. |
241 id = ""; | |
242 offset = 8; | |
243 chunk_size = 0; | |
244 | 105 |
245 while (! strcmp (id, chunk_id) && (offset < size)) | 106 %% Test input validation |
246 fseek (fid, chunk_size, "cof"); | 107 %!error wavread () |
247 id = char (fread (fid, 4))'; | 108 %!error wavread (1) |
248 chunk_size = fread (fid, 1, "uint32", 0, "ieee-le"); | 109 %!error wavread ("foo.wav", 2, 3, 4) |
249 ## Chunk sizes must be word-aligned (2 byte) | 110 %!error wavread ("foo.wav", "foo") |
250 chunk_size += rem (chunk_size, 2); | 111 %!error wavread ("foo.wav", -1) |
251 offset = offset + 8 + chunk_size; | 112 %!error wavread ("foo.wav", [1, Inf], "foo"); |
252 endwhile | |
253 if (! strcmp (id, chunk_id)) | |
254 chunk_size = -1; | |
255 endif | |
256 endfunction | |
257 | 113 |
258 | |
259 ## Mark file as tested. Tests for wavread/wavwrite pair are in wavwrite.m. | |
260 %!assert (1) | |
261 |