8a4678662f09cf14abd3d6ea0f3219ad3dd03691
[scilab.git] / scilab / modules / sound / macros / wavread.sci
1 // Scilab ( http://www.scilab.org/ ) - This file is part of Scilab
2 // Copyright (C) ???? - INRIA - Scilab
3 // Copyright (C) ???? - ENPC
4 // Copyright (C) 2008-2011 - DIGITEO - Allan CORNET
5 //
6 // This file must be used under the terms of the CeCILL.
7 // This source file is licensed as described in the file COPYING, which
8 // you should have received as part of this distribution.  The terms
9 // are also available at
10 // http://www.cecill.info/licences/Licence_CeCILL_V2.1-en.txt
11
12 // =============================================================================
13 function [y, Fs, bits] = wavread(wavfile, ext)
14
15     y = [];
16     Fs = [];
17     bits = [];
18
19     // Read Microsoft .wav sound file.
20     // y=wavread(wavfile) reads a .wav file specified by the string wavfile,
21     // returning the sampled data in y. The .wav extension is appended
22     // if no extension is given.
23     // Amplitude values are in the range [-1,+1].
24     // [y,fs,bits]=wavread(wavfile) returns the sample rate (fs) in Hertz
25     // and the number of bits per sample (bits) used to encode the
26     // data in the file.
27     // [...]=wavread(wavfile,n) returns only the first n samples from each
28     //       channel in the file.
29     // [...]=wavread(wavfile,[n1 n2]) returns only samples n1 through n2 from
30     //      each channel in the file.
31     // siz=wavread(wavfile,"size") returns the size of the audio data contained
32     //     in the file in place of the actual audio data, returning the
33     //      vector siz=[samples channels].
34     // infos=wavread(wavfile,"size") returns a vector with the information about actual
35     // audio data.
36     // vector infos = [wFormatTag, nChannels, nSamplesPerSec,
37     //                 nAvgBytesPerSec, nBlockAlign,
38     //                 nBitsPerSample, cbSize, nChannels, samples]
39
40
41     // Append .wav extension if necessary
42     if ( strindex(wavfile, ".") == [] ) then
43         wavfile = wavfile + ".wav";
44     end
45
46     // Open the file
47     [fid, err] = mopen(wavfile, "rb", 1);    // Little-endian
48     if (err < 0) then
49         error(msprintf(gettext("%s: Cannot open file %s.\n"), "wavread", wavfile));
50     end
51
52     // Handle ext optional argument
53     if (argn(2) < 2) then
54         ext = [];
55     end
56
57     if (type(ext) == 10) then
58         ext = convstr(ext);
59         if (ext <> "size") & (ext <> "info") then
60             error(msprintf(gettext("%s: Wrong value for input argument #%d: Must be ""%s"" or ""%s"", an integer or a vector of %d integers.\n"), "wavread", 2, "size", "info", 2));
61         end
62     elseif (type(ext) == 1) then
63         exts = size(ext, "*");
64         if (exts > 2) then
65             error(msprintf(gettext("%s: Wrong value for input argument: Index range must be specified as a scalar or %d-element vector.\n"), "wavread", 2));
66         end
67         if (exts == 1) then
68             if (ext == 0) then
69                 ext = "size";  // synonym for size
70             else
71                 ext = [1, ext];
72             end
73         end
74     else
75         error(msprintf(gettext("%s: Wrong value for input argument #%d: Must be ""%s"" or ""%s"", an integer or a vector of %d integers.\n"), "wavread", 2, "size", "info", 2));
76     end
77
78     Data = [];
79     ID = stripblanks(ascii(mget(4, "c", fid)));
80     Size = mget(1, "ui", fid);
81     if (convstr(ID) ~= "riff") then
82         error(msprintf(gettext("%s: An error occurred: %s\n"), "wavread", gettext(".wav file does not contain the RIFF identifier.")));
83     end
84     rifftype = mget(4, "c", fid);
85     dtype = convstr(ascii(rifftype)');
86     if (dtype ~= "wave") then
87         error(msprintf(gettext("%s: An error occurred: %s\n"), "wavread", gettext(".wav file does not contain the wave identifier.")));
88     end
89
90     // Find optional chunks
91     found_fmt = 0;
92     found_data = 0;
93     while ~found_data then
94         [ID, Size] = find_cktype(fid);
95         select ID
96         case "fact" then
97             total_bytes = Size;
98             orig_pos = mtell(fid);
99             nbytes = 4;
100             // # of required bytes in <fact-ck> header
101             if total_bytes < nbytes then
102                 error(msprintf(gettext("%s: An error occurred: %s\n"), "wavread", gettext("Error reading .wav file.")));
103             end
104             factdata = mget(1, "ui", fid); // Samples per second
105             rbytes = total_bytes - (mtell(fid) - orig_pos);
106             if rbytes then
107                 mseek(rbytes, fid, "cur");
108                 if (merror(fid) <> 0) then
109                     error(msprintf(gettext("%s: An error occurred: %s\n"), "wavread", gettext("Error reading <fact-ck> chunk.")));
110                 end
111             end
112
113             // bug 4037
114         case "bext" then
115             error(msprintf(gettext("%s: An error occurred: %s is not supported.\n"), "wavread", "Broadcast Wave Format"));
116             return
117
118             // bug 4832 - Sampler Chunk
119         case "smpl" then
120             error(msprintf(gettext("%s: An error occurred: invalid file format. Error reading <%s> chunk.\n"), "wavread", ID));
121             return
122
123         case "fmt" then
124             found_fmt = 1;
125             [wFormatTag, nChannels, nSamplesPerSec, nAvgBytesPerSec, nBlockAlign, nBitsPerSample, cbSize] = read_wavefmt(fid, Size);
126
127         case "data" then
128
129             found_data = 1;
130             if ~found_fmt then
131                 error(msprintf(gettext("%s: An error occurred: %s\n"), "wavread", gettext("Invalid .wav file: found data before format information.")));
132             end
133             if (ext == "size") | (ext == "info") | (~(ext == [])) & and(ext == 0) then
134                 // Caller just wants data size:
135                 samples = read_wavedat(fid, Size ,wFormatTag, nChannels, nBitsPerSample, -1);
136                 mclose(fid);
137                 if (ext == "info") then
138                     y = [wFormatTag, nChannels, nSamplesPerSec, nAvgBytesPerSec, nBlockAlign, nBitsPerSample, cbSize, nChannels, samples];
139                 else // "size"
140                     y = [nChannels, samples];
141                 end
142             else
143                 y = read_wavedat(fid, Size ,wFormatTag, nChannels, nBitsPerSample, ext);
144                 mclose(fid);
145             end
146
147         else
148             mseek(Size, fid, "cur")
149             if (merror(fid) <> 0) then
150                 error(msprintf(gettext("%s: An error occurred: %s\n"), "wavread", gettext("Incorrect chunk size information in RIFF file.")))
151             end
152         end
153     end
154     Fs = nSamplesPerSec;
155     if ( wFormatTag == 1 | wFormatTag == 3) then
156         bits = (nBlockAlign / nChannels) * 8;
157     else
158         // unknown
159         bits = [];
160     end
161 endfunction
162 // =============================================================================
163 function [ID, Size] = find_cktype(fid)
164     ID = stripblanks(ascii(mget(4, "c", fid)));
165     Size = mget(1, "ui", fid);
166 endfunction
167 // =============================================================================
168 function [wFormatTag, nChannels, nSamplesPerSec, nAvgBytesPerSec, nBlockAlign, nBitsPerSample, cbSize] = read_wavefmt(fid, total_bytes)
169     orig_pos = mtell(fid);
170     nbytes = 14; // # of required bytes in  header
171
172     if total_bytes < nbytes then
173         error(msprintf(gettext("%s: An error occurred: %s\n"), "read_wavefmt", gettext("Error reading .wav file.")));
174     end
175
176     // Read wav data:
177     wFormatTag = mget(1, "us", fid);  // Data encoding format
178     nChannels = mget(1, "us", fid); // Number of channels
179     nSamplesPerSec = mget(1, "ui", fid); // Samples per second
180     nAvgBytesPerSec = mget(1, "ui", fid); // Avg transfer rate
181     nBlockAlign = mget(1, "us", fid); // Block alignment
182     if (and(wFormatTag ~= [1 3])) then
183         error(msprintf(gettext("%s: An error occurred: %s\n"), "find_cktype", gettext("Invalid wav format.")));
184     else
185         [cbSize, nBitsPerSample] = read_fmt_pcm(fid, total_bytes);
186     end
187
188     rbytes = total_bytes - (mtell(fid) - orig_pos);
189     if rbytes then
190         mseek(rbytes, fid, "cur")
191         if (merror(fid) <> 0) then
192             error(msprintf(gettext("%s: An error occurred: %s\n"), "read_wavefmt", gettext("Error reading .wav file.")));
193         end
194     end
195 endfunction
196 // =============================================================================
197 function [cbSize, nBitsPerSample] = read_fmt_pcm(fid, total_bytes)
198     nbytes = 14;
199     cbSize = [];
200     nBitsPerSample = [];
201     // # of bytes already read
202     if (total_bytes < nbytes + 2) then
203         error(msprintf(gettext("%s: An error occurred: %s\n"), "find_cktype", gettext("Error reading wav file.")));
204     end
205     nBitsPerSample = mget(1, "us", fid);
206     nbytes = nbytes + 2;
207     if (total_bytes > nbytes) then
208         if (total_bytes >= nbytes + 2) then
209             cbSize = mget(1, "us", fid);
210             nbytes = nbytes + 2;
211         end
212         if (total_bytes > nbytes) then
213             mseek(total_bytes - nbytes, fid, "cur")
214             if (merror(fid) <> 0) then
215                 error(msprintf(gettext("%s: An error occurred: %s\n"), "find_cktype", gettext("Error reading wav file.")));
216             end
217         end
218     end
219 endfunction
220 // =============================================================================
221 function Data = read_wavedat(fid, Size, wFormatTag, nChannels, nBitsPerSample, ext)
222     fmt_msg = [];
223     select wFormatTag
224     case 1 then
225         // PCM Format:
226         Data = read_dat_pcm(fid, Size, nChannels, nBitsPerSample, ext, wFormatTag);
227     case 2 then
228         fmt_msg = "Microsoft ADPCM";
229     case 3 then
230         // normalized floating-point
231         Data = read_dat_pcm(fid, Size, nChannels, nBitsPerSample, ext, wFormatTag);
232     case 6 then
233         fmt_msg = "CCITT a-law";
234     case 7 then
235         fmt_msg = "CCITT mu-law";
236     case 17 then
237         fmt_msg = "IMA ADPCM";
238     case 34 then
239         fmt_msg = "DSP Group TrueSpeech TM";
240     case 49 then
241         fmt_msg = "GSM 6.10";
242     case 50 then
243         fmt_msg = "MSN Audio";
244     case 257 then
245         fmt_msg = "IBM Mu-law";
246     case 258 then
247         fmt_msg = "IBM A-law";
248     case 259 then
249         fmt_msg = "IBM AVC Adaptive Differential";
250     else
251         fmt_msg = "Format #" + string(wFormatTag);
252     end
253     if ~(fmt_msg == []) then
254         error(msprintf(gettext("%s: An error occurred: Data compression format %s is not supported.\n"), "read_wavedat", fmt_msg));
255     end
256 endfunction
257 // =============================================================================
258 function Data = read_dat_pcm(fid,total_bytes , nChannels, nBitsPerSample, ext, wFormatTag)
259     // Determine # bytes/sample - format requires rounding
260     //  to next integer number of bytes:
261     BytesPerSample = ceil(nBitsPerSample / 8);
262
263     select BytesPerSample
264     case 1 then  // unsigned 8-bit
265         dtype = "uc";
266     case 2 then // signed 16-bit
267         dtype = "s";
268     case 3 then // signed 24-bit
269         dtype = "c";
270     case 4 then // signed 32-bit (long)
271         if wFormatTag == 3 then
272             dtype = "f";
273         else
274             dtype = "l";
275         end
276     else
277         error(msprintf(gettext("%s: An error occurred: %s\n"), "read_dat_pcm", gettext("Cannot read .wav file  with more than 16 bits per sample.")));
278     end//  select BytesPerSample
279
280     // # bytes in this chunk
281     total_samples = total_bytes / BytesPerSample;
282     SamplesPerChannel = total_samples / nChannels;
283     if ((~(ext == [])) & (ext == -1)) then
284         // Just return the samples per channel, and seek past data:
285         Data = SamplesPerChannel;
286         mseek(total_bytes, fid, "cur");
287         return
288     end
289     if (ext == []) then
290         ext = [1, SamplesPerChannel];
291     else
292         if (prod(size(ext)) ~= 2) then
293             error(msprintf(gettext("%s: An error occurred: %s\n"), "read_dat_pcm", gettext("Sample limit vector must have 2 entries.")));
294             return
295         end
296         if (ext(1) < 1) | (ext(2) > SamplesPerChannel) then
297             error(msprintf(gettext("%s: An error occurred: %s\n"), "read_dat_pcm", gettext("Sample limits out of range.")));
298         end
299         if (ext(1) > ext(2)) then
300             error(msprintf(gettext("%s: An error occurred: %s\n"), "read_dat_pcm", gettext("Invalid sample limits (use ascending order).")));
301         end
302     end
303
304     if (ext(1) > 1) then
305         // Skip if specified:
306         mseek(BytesPerSample * (ext(1) - 1) * nChannels, fid, "cur");
307     end
308
309     // Read data:
310     nSPCext = ext(2) - ext(1) + 1;
311     // # samples per channel in extraction range
312     extSamples = nChannels * nSPCext;
313
314     //24-bits files need special treatment
315     if ( BytesPerSample == 3 ) then
316         Data_tmp = [];
317         Data_tmp = (mget(3 * nChannels * nSPCext, dtype, fid));
318         oct1 = uint8(Data_tmp(1:3:$-1));
319         oct2 = uint8(Data_tmp(2:3:$));
320         oct3 = Data_tmp(3:3:$);
321         Data_tmp2 = (double(oct1) * (2^0) + double(oct2) * (2^8) + double(oct3) * (2^16));
322         Data = matrix(Data_tmp2, [nChannels, nSPCext]);
323     else
324         Data = matrix(mget(nChannels * nSPCext, dtype, fid), [nChannels, nSPCext]);
325     end;
326
327     // Skip trailing samples:
328
329     mseek(BytesPerSample * (SamplesPerChannel - ext(2)) * nChannels, fid, "cur");
330
331     // Determine if a pad-byte is appended and skip if present:
332     junk = Size;
333     if ( junk - fix(junk./2).*2 ) then
334         mseek(1, fid, "cur");
335     end
336     // Normalize data range in [-1 1] (min will hit -1)
337     select BytesPerSample
338     case 1 then
339         Data = (Data - 128) / 128;
340     case 2 then
341         Data = Data / 32768;
342     case 3 then
343         Data = Data / (2^23);
344     case 4 then
345         //  wFormatTag == 3 already normalized
346         if wFormatTag <> 3 then
347             Data = Data / (2^31);
348         end
349     end; //normalization in range [-1 +1]
350 endfunction
351 // =============================================================================