1 // Scilab ( http://www.scilab.org/ ) - This file is part of Scilab
2 // Copyright (C) 2008 - DIGITEO
4 // This file must be used under the terms of the CeCILL.
5 // This source file is licensed as described in the file COPYING, which
6 // you should have received as part of this distribution. The terms
7 // are also available at
8 // http://www.cecill.info/licences/Licence_CeCILL_V2-en.txt
10 // =============================================================================
11 // WAVE Audio File Format
12 // http://ccrma.stanford.edu/courses/422/projects/WaveFormat/
13 // http://www.digitalpreservation.gov/formats/fdd/fdd000001.shtml
14 // =============================================================================
15 function savewave(filename,x,rate,nbits)
17 // create_wavheader, write_ckinfo, write_wavedat functions only defined in savewave.sci
19 function [x,fmt] = create_wavheader(x,rate,nbits)
21 [channels, samples] = size(x);
22 if (samples == 1) then
24 [channels, samples] = size(x);
27 // Clip data to normalized range [-1,+1]:
28 i = matrix(find(abs(x)>1),1,-1);
30 // Data clipped during write to file.
34 // # bytes per sample to write
35 bytes_per_sample = ceil(nbits/8);
36 total_samples = samples * channels;
37 total_bytes = total_samples * bytes_per_sample;
39 riff_cksize = 36 + total_bytes;
40 // Don't include 'RIFF' or its size field
42 // Don't include 'fmt' or its size field
43 data_cksize = total_bytes;
44 // Don't include 'data' or its size field
46 // Determine pad bytes:
47 data_pad = data_cksize - fix(data_cksize./2).*2;
48 riff_cksize = riff_cksize + data_pad;
49 // + fmt_pad, always 0
51 ck= tlist(["ck","fid","Size","ID"]) ;
54 ck("Size") = riff_cksize;
64 ck("Size") = fmt_cksize;
66 // Write <wave-format>:
67 fmt = tlist(["fmt","wFormatTag","nChannels","nSamplesPerSec","nAvgBytesPerSec","nBlockAlign","nBitsPerSample"]);
68 fmt("wFormatTag") = 1;
69 // Data encoding format = PCM
70 fmt("nChannels") = channels;
72 fmt("nSamplesPerSec") = rate;
74 fmt("nAvgBytesPerSec") = channels * bytes_per_sample * rate;
76 fmt("nBlockAlign") = channels * bytes_per_sample;
78 fmt("nBitsPerSample") = nbits;
80 // standard <PCM-format-specific> info
81 status = write_wavefmt(fid,fmt);
85 ck("Size") = data_cksize;
89 // =============================================================================
90 function write_ckinfo(ck,sflg)
91 [nargout,nargin] = argn(0)
92 // WRITE_CKINFO: Writes next RIFF chunk, but not the chunk data.
93 // If optional sflg is set to nonzero, write SUBchunk info instead.
94 // Expects an open FID pointing to first byte of chunk header,
95 // and a chunk structure.
96 // ck.fid, ck.ID, ck.Size, ck.Data
97 if length(ck("ID"))<>4 then
98 error(msprintf(gettext("%s: Wrong size for input argument #%d.\n"),"write_ckinfo",1));
101 mput(ascii(ck("ID")),"c",ck("fid"));
104 if (nargin == 1) then
105 // Write chunk size (skip if subchunk):
106 mput(ck("Size"),"ui",ck("fid"));
109 // =============================================================================
110 function [status] = write_wavedat(fid,fmt,data)
112 // WRITE_WAVEDAT: Write WAVE data chunk
113 // Assumes fid points to the wave-data chunk
114 // Requires <wave-format> structure to be passed.
118 if fmt("wFormatTag") == 1 then
120 // Determine # bytes/sample - format requires rounding
121 // to next integer number of bytes:
122 BytesPerSample = ceil(fmt("nBitsPerSample")/8);
124 select BytesPerSample
126 dtype = "uc"; // unsigned 8-bit
127 // Scale data according to bits/samples: [-1,+1] -> [0,255]
128 data = round((data+1)*255/2);
132 // Scale data according to bits/samples: [-1,+1] -> [-32768,+32767]
133 data = round((data+1)*65535/2)-32768;
137 // Scale data according to bits/samples: [-1,+1] -> [-8 388 608,+8 388 607]
138 data = round((data+1)*(2^24-1)/2)-(2^23);
142 // Scale data according to bits/samples: [-1,+1] -> [-2 147 483 648,+2 147 483 647]
143 data = round((data+1)*(2^32-1)/2)-(2^31);
146 error(msprintf(gettext("%s: An error occurred: %s\n"),"savewave",gettext("only 8/16/24/32 bits for the encoding.")));
149 // Write data, one row at a time (one sample from each channel):
150 [channels,samples] = size(data);
151 total_samples = samples * channels;
153 //24-bits needs special treatment
154 if (BytesPerSample == 3) then
155 oct3 = (floor((data)/(2^16)));//msb
156 oct2 = (floor((data-(oct3*2^16))/(2^8)));
157 oct1 = (floor(data-(oct3*2^16)-(oct2*2^8)));//lsb
158 data_line = zeros(3*total_samples,1);
159 data_line(1:6:$) = oct1(1,:)';
160 data_line(2:6:$) = oct2(1,:)';
161 data_line(3:6:$) = oct3(1,:)';
162 data_line(4:6:$) = oct1(2,:)';
163 data_line(5:6:$) = oct2(2,:)';
164 data_line(6:6:$) = oct3(2,:)';
165 data_line = data_line';
172 mput(data_line,dtype,fid);
178 // Determine if a pad-byte is appended to data chunk:
179 %v2_1$1 = total_samples * BytesPerSample;
180 if ( %v2_1$1 - fix(%v2_1$1./2).*2 ) then
184 // Unknown wave-format for data.
185 error(msprintf(gettext("%s: An error occurred: %s\n"),"write_wavedat",gettext("Unknown data format.")));
189 // =============================================================================
190 function [status]=write_wavefmt(fid,fmt)
192 // WRITE_WAVEFMT: Write WAVE format chunk.
193 // Assumes fid points to the wave-format subchunk.
194 // Requires chunk structure to be passed, indicating
195 // the length of the chunk.
197 // Create <wave-format> data:
199 mput(fmt("wFormatTag"),"us",fid);
200 mput(fmt("nChannels"),"us",fid);
201 mput(fmt("nSamplesPerSec"),"ui",fid);
202 mput(fmt("nAvgBytesPerSec"),"ui",fid);
203 mput(fmt("nBlockAlign"),"us",fid);
205 // Write format-specific info:
206 if ( fmt("wFormatTag") == 1 ) then
207 // Write standard <PCM-format-specific> info:
208 mput(fmt("nBitsPerSample"),"us",fid)
210 error("Unknown data format.");
213 // =============================================================================
227 if ~(type(filename) == 10) then
228 error(msprintf(gettext("%s: Wrong type for input argument #%d: A string expected.\n" ),"savewave",1));
231 if strindex(filename,".")==[] then
232 filename = filename+".wav";
235 [fid,%v] = mopen(filename,"wb",1);
241 if ( fid == (-1) ) then
242 error(msprintf(gettext("%s: Cannot open file %s.\n"),"savewave",filename));
245 [x,fmt] = create_wavheader(x,rate,nbits);
246 status = write_wavedat(fid,fmt,x);
250 // =============================================================================