]> Creatis software - CreaPhase.git/blob - octave_packages/audio-1.1.4/ausave.m
Add a useful package (from Source forge) for octave
[CreaPhase.git] / octave_packages / audio-1.1.4 / ausave.m
1 ## Copyright (C) 1999 Paul Kienzle
2 ##
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.
7 ##
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.
12 ##
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/>.
15
16 ## usage: ausave('filename.ext', x, fs, format)
17 ##
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
24 ##
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.
32
33
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
39
40 function ausave(path, data, rate, sampleformat)
41
42   if nargin < 2 || nargin>4
43     usage("ausave('filename.ext', x [, fs, sampleformat])");
44   end
45   if nargin < 3, rate = 8000; end
46   if nargin < 4, sampleformat = 'short'; end
47
48   ext = rindex(path, '.');
49   if (ext == 0)
50     usage("ausave('filename.ext', x [, fs, sampleformat])");
51   end
52   ext = tolower(substr(path, ext+1, length(path)-ext));
53
54   # determine data size and orientation
55   [samples, channels] = size(data);
56   if (samples < channels)
57     data = data.';
58     [samples, channels] = size(data);
59   endif
60
61   ## Microsoft .wav format
62   if strcmp(ext,'wav') 
63
64     ## Header format obtained from sox/wav.c
65     ## April 15, 1992
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.
72
73     if (strcmp(sampleformat,'uchar'))
74       formatid = 1;
75       samplesize = 1;
76     elseif (strcmp(sampleformat,'short'))
77       formatid = 1;
78       samplesize = 2;
79     elseif (strcmp(sampleformat, 'long'))
80       formatid = 1;
81       samplesize = 4;
82     elseif (strcmp(sampleformat, 'float'))
83       formatid = 3;
84       samplesize = 4;
85     elseif (strcmp(sampleformat, 'double'))
86       formatid = 3;
87       samplesize = 8;
88     elseif (strcmp(sampleformat, 'alaw'))
89       formatid = 6;
90       samplesize = 1;
91     elseif (strcmp(sampleformat, 'ulaw'))
92       formatid = 7;
93       samplesize = 1;
94     else
95       error("%s is invalid format for .wav file\n", sampleformat);
96     end
97     datasize = channels*samplesize*samples;
98
99     [file, msg] = fopen(path, 'wb');
100     if (file == -1)
101       error("%s: %s", msg, path);
102     end
103
104     ## write the magic header
105     arch = 'ieee-le';
106     fwrite(file, toascii('RIFF'), 'char');
107     fwrite(file, datasize+36, 'long', 0, arch);
108     fwrite(file, toascii('WAVE'), 'char');
109
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);
119
120     ## write the "data" section
121     fwrite(file, toascii('data'), 'char');
122     fwrite(file, datasize, 'long', 0, arch);
123
124   ## Sun .au format
125   elseif strcmp(ext, 'au')
126
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.
134
135     if (strcmp(sampleformat, 'ulaw'))
136       formatid = 1;
137       samplesize = 1;
138     elseif (strcmp(sampleformat,'uchar'))
139       formatid = 2;
140       samplesize = 1;
141     elseif (strcmp(sampleformat,'short'))
142       formatid = 3;
143       samplesize = 2;
144     elseif (strcmp(sampleformat, 'long'))
145       formatid = 5;
146       samplesize = 4;
147     elseif (strcmp(sampleformat, 'float'))
148       formatid = 6;
149       samplesize = 4;
150     elseif (strcmp(sampleformat, 'double'))
151       formatid = 7;
152       samplesize = 8;
153     else
154       error("%s is invalid format for .au file\n", sampleformat);
155     end
156     datasize = channels*samplesize*samples;
157
158     [file, msg] = fopen(path, 'wb');
159     if (file == -1)
160       error("%s: %s", msg, path);
161     end
162
163     arch = 'ieee-be';
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);
170
171   ## Apple/SGI .aiff format
172   elseif strcmp(ext,'aiff') || strcmp(ext,'aif')
173
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.
181     ##
182     ## IEEE 80-bit float I/O taken from
183     ##        ftp://ftp.mathworks.com/pub/contrib/signal/osprey.tar
184     ##        David K. Mellinger
185     ##        dave@mbari.org
186     ##        +1-831-775-1805
187     ##        fax       -1620
188     ##        Monterey Bay Aquarium Research Institute
189     ##        7700 Sandholdt Road
190
191     if (strcmp(sampleformat,'uchar'))
192       samplesize = 1;
193     elseif (strcmp(sampleformat,'short'))
194       samplesize = 2;
195     elseif (strcmp(sampleformat, 'long'))
196       samplesize = 4;
197     else
198       error("%s is invalid format for .aiff file\n", sampleformat);
199     end
200     datasize = channels*samplesize*samples;
201
202     [file, msg] = fopen(path, 'wb');
203     if (file == -1)
204       error("%s: %s", msg, path);
205     end
206
207     ## write the magic header
208     arch = 'ieee-be';
209     fwrite(file, toascii('FORM'), 'char');
210     fwrite(file, datasize+46, 'long', 0, arch);
211     fwrite(file, toascii('AIFF'), 'char');
212
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
221
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
227
228   ## file extension unknown
229   else
230     error('ausave(filename.ext,...) understands .wav .au and .aiff only');
231   end
232
233   ## Make sure the data fits into the sample range
234   scale = max(abs(data(:)));
235   if (scale > 1.0)
236     warning("ausave: audio data exceeds range [-1,1] --- rescaling");
237     data = data / scale;
238   endif
239
240   ## convert samples from range [-1, 1]
241   if strcmp(sampleformat, 'alaw')
242     error("FIXME: ausave needs linear to alaw conversion\n");
243     precision = 'uchar';
244   elseif strcmp(sampleformat, 'ulaw')
245     data = lin2mu(data, 0);
246     precision = 'uchar'
247   elseif strcmp(sampleformat, 'uchar')
248     data = round((data+1)*127.5);
249     precision = 'uchar';
250   elseif strcmp(sampleformat, 'short')
251     data = round(data*32767.5 - 0.5);
252     precision = 'short';
253   elseif strcmp(sampleformat, 'long')
254     data = round(data*(2^31-0.5) - 0.5);
255     precision = 'long';
256   else
257     precision = sampleformat;
258   end
259   fwrite(file, data', precision, 0, arch);
260   fclose(file);
261
262 endfunction