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