1 ## Copyright (C) 1999 Paul Kienzle
3 ## This program is free software; you can redistribute it and/or modify
4 ## it under the terms of the GNU General Public License as published by
5 ## the Free Software Foundation; either version 2 of the License, or
6 ## (at your option) any later version.
8 ## This program is distributed in the hope that it will be useful,
9 ## but WITHOUT ANY WARRANTY; without even the implied warranty of
10 ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 ## GNU General Public License for more details.
13 ## You should have received a copy of the GNU General Public License
14 ## along with this program; If not, see <http://www.gnu.org/licenses/>.
16 ## usage: ausave('filename.ext', x, fs, format)
18 ## Writes an audio file with the appropriate header. The extension on
19 ## the filename determines the layout of the header. Currently supports
20 ## .wav and .au layouts. Data is a matrix of audio samples in the
21 ## range [-1,1] (inclusive), one row per time step, one column per
22 ## channel. Fs defaults to 8000 Hz. Format is one of ulaw, alaw, char,
23 ## short, long, float, double
25 ## Note that translating the symmetric range [-1,1] into the asymmetric
26 ## range [-2^n,2^n-1] requires a DC offset of -2/2^n. The inverse
27 ## process used by auload requires a DC offset of 2/2^n, so loading and
28 ## saving a file will not change the contents. Other applications may
29 ## compensate for the asymmetry in a different way (including previous
30 ## versions of auload/ausave) so you may find small differences in
31 ## calculated DC offsets for the same file.
34 ## 2001-10-23 Paul Kienzle
35 ## * force lin2mu to use [-1:1] regardless of its default
36 ## 2001-12-11 Paul Kienzle <pkienzle@users.sf.net>
37 ## * use closed interval [-1,1] rather than open interval [-1,1) internally
38 ## * rescale data if it exceeds the range
40 function ausave(path, data, rate, sampleformat)
42 if nargin < 2 || nargin>4
43 usage("ausave('filename.ext', x [, fs, sampleformat])");
45 if nargin < 3, rate = 8000; end
46 if nargin < 4, sampleformat = 'short'; end
48 ext = rindex(path, '.');
50 usage("ausave('filename.ext', x [, fs, sampleformat])");
52 ext = tolower(substr(path, ext+1, length(path)-ext));
54 # determine data size and orientation
55 [samples, channels] = size(data);
56 if (samples < channels)
58 [samples, channels] = size(data);
61 ## Microsoft .wav format
64 ## Header format obtained from sox/wav.c
66 ## Copyright 1992 Rick Richardson
67 ## Copyright 1991 Lance Norskog And Sundry Contributors
68 ## This source code is freely redistributable and may be used for
69 ## any purpose. This copyright notice must be maintained.
70 ## Lance Norskog And Sundry Contributors are not responsible for
71 ## the consequences of using this software.
73 if (strcmp(sampleformat,'uchar'))
76 elseif (strcmp(sampleformat,'short'))
79 elseif (strcmp(sampleformat, 'long'))
82 elseif (strcmp(sampleformat, 'float'))
85 elseif (strcmp(sampleformat, 'double'))
88 elseif (strcmp(sampleformat, 'alaw'))
91 elseif (strcmp(sampleformat, 'ulaw'))
95 error("%s is invalid format for .wav file\n", sampleformat);
97 datasize = channels*samplesize*samples;
99 [file, msg] = fopen(path, 'wb');
101 error("%s: %s", msg, path);
104 ## write the magic header
106 fwrite(file, toascii('RIFF'), 'char');
107 fwrite(file, datasize+36, 'long', 0, arch);
108 fwrite(file, toascii('WAVE'), 'char');
110 ## write the "fmt " section
111 fwrite(file, toascii('fmt '), 'char');
112 fwrite(file, 16, 'long', 0, arch);
113 fwrite(file, formatid, 'short', 0, arch);
114 fwrite(file, channels, 'short', 0, arch);
115 fwrite(file, rate, 'long', 0, arch);
116 fwrite(file, rate*channels*samplesize, 'long', 0, arch);
117 fwrite(file, channels*samplesize, 'short', 0, arch);
118 fwrite(file, samplesize*8, 'short', 0, arch);
120 ## write the "data" section
121 fwrite(file, toascii('data'), 'char');
122 fwrite(file, datasize, 'long', 0, arch);
125 elseif strcmp(ext, 'au')
127 ## Header format obtained from sox/au.c
128 ## September 25, 1991
129 ## Copyright 1991 Guido van Rossum And Sundry Contributors
130 ## This source code is freely redistributable and may be used for
131 ## any purpose. This copyright notice must be maintained.
132 ## Guido van Rossum And Sundry Contributors are not responsible for
133 ## the consequences of using this software.
135 if (strcmp(sampleformat, 'ulaw'))
138 elseif (strcmp(sampleformat,'uchar'))
141 elseif (strcmp(sampleformat,'short'))
144 elseif (strcmp(sampleformat, 'long'))
147 elseif (strcmp(sampleformat, 'float'))
150 elseif (strcmp(sampleformat, 'double'))
154 error("%s is invalid format for .au file\n", sampleformat);
156 datasize = channels*samplesize*samples;
158 [file, msg] = fopen(path, 'wb');
160 error("%s: %s", msg, path);
164 fwrite(file, toascii('.snd'), 'char');
165 fwrite(file, 24, 'long', 0, arch);
166 fwrite(file, datasize, 'long', 0, arch);
167 fwrite(file, formatid, 'long', 0, arch);
168 fwrite(file, rate, 'long', 0, arch);
169 fwrite(file, channels, 'long', 0, arch);
171 ## Apple/SGI .aiff format
172 elseif strcmp(ext,'aiff') || strcmp(ext,'aif')
174 ## Header format obtained from sox/aiff.c
175 ## September 25, 1991
176 ## Copyright 1991 Guido van Rossum And Sundry Contributors
177 ## This source code is freely redistributable and may be used for
178 ## any purpose. This copyright notice must be maintained.
179 ## Guido van Rossum And Sundry Contributors are not responsible for
180 ## the consequences of using this software.
182 ## IEEE 80-bit float I/O taken from
183 ## ftp://ftp.mathworks.com/pub/contrib/signal/osprey.tar
184 ## David K. Mellinger
188 ## Monterey Bay Aquarium Research Institute
189 ## 7700 Sandholdt Road
191 if (strcmp(sampleformat,'uchar'))
193 elseif (strcmp(sampleformat,'short'))
195 elseif (strcmp(sampleformat, 'long'))
198 error("%s is invalid format for .aiff file\n", sampleformat);
200 datasize = channels*samplesize*samples;
202 [file, msg] = fopen(path, 'wb');
204 error("%s: %s", msg, path);
207 ## write the magic header
209 fwrite(file, toascii('FORM'), 'char');
210 fwrite(file, datasize+46, 'long', 0, arch);
211 fwrite(file, toascii('AIFF'), 'char');
213 ## write the "COMM" section
214 fwrite(file, toascii('COMM'), 'char');
215 fwrite(file, 18, 'long', 0, arch);
216 fwrite(file, channels, 'short', 0, arch);
217 fwrite(file, samples, 'long', 0, arch);
218 fwrite(file, 8*samplesize, 'short', 0, arch);
219 fwrite(file, 16414, 'ushort', 0, arch); % sample rate exponent
220 fwrite(file, [rate, 0], 'ulong', 0, arch); % sample rate mantissa
222 ## write the "SSND" section
223 fwrite(file, toascii('SSND'), 'char');
224 fwrite(file, datasize+8, 'long', 0, arch); # section length
225 fwrite(file, 0, 'long', 0, arch); # block size
226 fwrite(file, 0, 'long', 0, arch); # offset
228 ## file extension unknown
230 error('ausave(filename.ext,...) understands .wav .au and .aiff only');
233 ## Make sure the data fits into the sample range
234 scale = max(abs(data(:)));
236 warning("ausave: audio data exceeds range [-1,1] --- rescaling");
240 ## convert samples from range [-1, 1]
241 if strcmp(sampleformat, 'alaw')
242 error("FIXME: ausave needs linear to alaw conversion\n");
244 elseif strcmp(sampleformat, 'ulaw')
245 data = lin2mu(data, 0);
247 elseif strcmp(sampleformat, 'uchar')
248 data = round((data+1)*127.5);
250 elseif strcmp(sampleformat, 'short')
251 data = round(data*32767.5 - 0.5);
253 elseif strcmp(sampleformat, 'long')
254 data = round(data*(2^31-0.5) - 0.5);
257 precision = sampleformat;
259 fwrite(file, data', precision, 0, arch);