2 * Asterisk -- An open source telephony toolkit.
4 * Copyright (C) 1999 - 2005, Digium, Inc.
6 * Mark Spencer <markster@digium.com>
8 * See http://www.asterisk.org for more information about
9 * the Asterisk project. Please do not directly contact
10 * any of the maintainers of this project for assistance;
11 * the project provides a web site, mailing lists and IRC
12 * channels for your use.
14 * This program is free software, distributed under the terms of
15 * the GNU General Public License Version 2. See the LICENSE file
16 * at the top of the source tree.
21 * \brief Work with WAV in the proprietary Microsoft format.
22 * Microsoft WAV format (8000hz Signed Linear)
23 * \arg File name extension: wav (lower case)
29 ASTERISK_FILE_VERSION(__FILE__
, "$Revision$")
31 #include "asterisk/mod_format.h"
32 #include "asterisk/module.h"
33 #include "asterisk/endian.h"
35 /* Some Ideas for this code came from makewave.c by Jeffrey Chilton */
37 /* Portions of the conversion code are by guido@sienanet.it */
39 #define WAV_BUF_SIZE 320
41 struct wav_desc
{ /* format-specific parameters */
50 #if __BYTE_ORDER == __LITTLE_ENDIAN
56 #if __BYTE_ORDER == __BIG_ENDIAN
58 (((((b) ) & 0xFF) << 24) | \
59 ((((b) >> 8) & 0xFF) << 16) | \
60 ((((b) >> 16) & 0xFF) << 8) | \
61 ((((b) >> 24) & 0xFF) ))
63 (((((b) ) & 0xFF) << 8) | \
64 ((((b) >> 8) & 0xFF) ))
65 #define ltohl(b) htoll(b)
66 #define ltohs(b) htols(b)
68 #error "Endianess not defined"
73 static int check_header(FILE *f
)
75 int type
, size
, formtype
;
77 short format
, chans
, bysam
, bisam
;
81 if (fread(&type
, 1, 4, f
) != 4) {
82 ast_log(LOG_WARNING
, "Read failed (type)\n");
85 if (fread(&size
, 1, 4, f
) != 4) {
86 ast_log(LOG_WARNING
, "Read failed (size)\n");
90 if (fread(&formtype
, 1, 4, f
) != 4) {
91 ast_log(LOG_WARNING
, "Read failed (formtype)\n");
94 if (memcmp(&type
, "RIFF", 4)) {
95 ast_log(LOG_WARNING
, "Does not begin with RIFF\n");
98 if (memcmp(&formtype
, "WAVE", 4)) {
99 ast_log(LOG_WARNING
, "Does not contain WAVE\n");
102 if (fread(&fmt
, 1, 4, f
) != 4) {
103 ast_log(LOG_WARNING
, "Read failed (fmt)\n");
106 if (memcmp(&fmt
, "fmt ", 4)) {
107 ast_log(LOG_WARNING
, "Does not say fmt\n");
110 if (fread(&hsize
, 1, 4, f
) != 4) {
111 ast_log(LOG_WARNING
, "Read failed (formtype)\n");
114 if (ltohl(hsize
) < 16) {
115 ast_log(LOG_WARNING
, "Unexpected header size %d\n", ltohl(hsize
));
118 if (fread(&format
, 1, 2, f
) != 2) {
119 ast_log(LOG_WARNING
, "Read failed (format)\n");
122 if (ltohs(format
) != 1) {
123 ast_log(LOG_WARNING
, "Not a wav file %d\n", ltohs(format
));
126 if (fread(&chans
, 1, 2, f
) != 2) {
127 ast_log(LOG_WARNING
, "Read failed (format)\n");
130 if (ltohs(chans
) != 1) {
131 ast_log(LOG_WARNING
, "Not in mono %d\n", ltohs(chans
));
134 if (fread(&freq
, 1, 4, f
) != 4) {
135 ast_log(LOG_WARNING
, "Read failed (freq)\n");
138 if (ltohl(freq
) != DEFAULT_SAMPLE_RATE
) {
139 ast_log(LOG_WARNING
, "Unexpected freqency %d\n", ltohl(freq
));
142 /* Ignore the byte frequency */
143 if (fread(&bysec
, 1, 4, f
) != 4) {
144 ast_log(LOG_WARNING
, "Read failed (BYTES_PER_SECOND)\n");
147 /* Check bytes per sample */
148 if (fread(&bysam
, 1, 2, f
) != 2) {
149 ast_log(LOG_WARNING
, "Read failed (BYTES_PER_SAMPLE)\n");
152 if (ltohs(bysam
) != 2) {
153 ast_log(LOG_WARNING
, "Can only handle 16bits per sample: %d\n", ltohs(bysam
));
156 if (fread(&bisam
, 1, 2, f
) != 2) {
157 ast_log(LOG_WARNING
, "Read failed (Bits Per Sample): %d\n", ltohs(bisam
));
160 /* Skip any additional header */
161 if (fseek(f
,ltohl(hsize
)-16,SEEK_CUR
) == -1 ) {
162 ast_log(LOG_WARNING
, "Failed to skip remaining header bytes: %d\n", ltohl(hsize
)-16 );
165 /* Skip any facts and get the first data block */
170 /* Begin data chunk */
171 if (fread(&buf
, 1, 4, f
) != 4) {
172 ast_log(LOG_WARNING
, "Read failed (data)\n");
175 /* Data has the actual length of data in it */
176 if (fread(&data
, 1, 4, f
) != 4) {
177 ast_log(LOG_WARNING
, "Read failed (data)\n");
181 if(memcmp(buf
, "data", 4) == 0 )
183 if(memcmp(buf
, "fact", 4) != 0 ) {
184 ast_log(LOG_WARNING
, "Unknown block - not fact or data\n");
187 if (fseek(f
,data
,SEEK_CUR
) == -1 ) {
188 ast_log(LOG_WARNING
, "Failed to skip fact block: %d\n", data
);
193 curpos
= lseek(fd
, 0, SEEK_CUR
);
194 truelength
= lseek(fd
, 0, SEEK_END
);
195 lseek(fd
, curpos
, SEEK_SET
);
196 truelength
-= curpos
;
201 static int update_header(FILE *f
)
204 int datalen
,filelen
,bytes
;
207 fseek(f
, 0, SEEK_END
);
209 /* data starts 44 bytes in */
211 datalen
= htoll(bytes
);
212 /* chunk size is bytes of data plus 36 bytes of header */
213 filelen
= htoll(36 + bytes
);
216 ast_log(LOG_WARNING
, "Unable to find our position\n");
219 if (fseek(f
, 4, SEEK_SET
)) {
220 ast_log(LOG_WARNING
, "Unable to set our position\n");
223 if (fwrite(&filelen
, 1, 4, f
) != 4) {
224 ast_log(LOG_WARNING
, "Unable to set write file size\n");
227 if (fseek(f
, 40, SEEK_SET
)) {
228 ast_log(LOG_WARNING
, "Unable to set our position\n");
231 if (fwrite(&datalen
, 1, 4, f
) != 4) {
232 ast_log(LOG_WARNING
, "Unable to set write datalen\n");
235 if (fseeko(f
, cur
, SEEK_SET
)) {
236 ast_log(LOG_WARNING
, "Unable to return to position\n");
242 static int write_header(FILE *f
)
244 unsigned int hz
=htoll(8000);
245 unsigned int bhz
= htoll(16000);
246 unsigned int hs
= htoll(16);
247 unsigned short fmt
= htols(1);
248 unsigned short chans
= htols(1);
249 unsigned short bysam
= htols(2);
250 unsigned short bisam
= htols(16);
251 unsigned int size
= htoll(0);
252 /* Write a wav header, ignoring sizes which will be filled in later */
254 if (fwrite("RIFF", 1, 4, f
) != 4) {
255 ast_log(LOG_WARNING
, "Unable to write header\n");
258 if (fwrite(&size
, 1, 4, f
) != 4) {
259 ast_log(LOG_WARNING
, "Unable to write header\n");
262 if (fwrite("WAVEfmt ", 1, 8, f
) != 8) {
263 ast_log(LOG_WARNING
, "Unable to write header\n");
266 if (fwrite(&hs
, 1, 4, f
) != 4) {
267 ast_log(LOG_WARNING
, "Unable to write header\n");
270 if (fwrite(&fmt
, 1, 2, f
) != 2) {
271 ast_log(LOG_WARNING
, "Unable to write header\n");
274 if (fwrite(&chans
, 1, 2, f
) != 2) {
275 ast_log(LOG_WARNING
, "Unable to write header\n");
278 if (fwrite(&hz
, 1, 4, f
) != 4) {
279 ast_log(LOG_WARNING
, "Unable to write header\n");
282 if (fwrite(&bhz
, 1, 4, f
) != 4) {
283 ast_log(LOG_WARNING
, "Unable to write header\n");
286 if (fwrite(&bysam
, 1, 2, f
) != 2) {
287 ast_log(LOG_WARNING
, "Unable to write header\n");
290 if (fwrite(&bisam
, 1, 2, f
) != 2) {
291 ast_log(LOG_WARNING
, "Unable to write header\n");
294 if (fwrite("data", 1, 4, f
) != 4) {
295 ast_log(LOG_WARNING
, "Unable to write header\n");
298 if (fwrite(&size
, 1, 4, f
) != 4) {
299 ast_log(LOG_WARNING
, "Unable to write header\n");
305 static int wav_open(struct ast_filestream
*s
)
307 /* We don't have any header to read or anything really, but
308 if we did, it would go here. We also might want to check
309 and be sure it's a valid file. */
310 struct wav_desc
*tmp
= (struct wav_desc
*)s
->_private
;
311 if ((tmp
->maxlen
= check_header(s
->f
)) < 0)
316 static int wav_rewrite(struct ast_filestream
*s
, const char *comment
)
318 /* We don't have any header to read or anything really, but
319 if we did, it would go here. We also might want to check
320 and be sure it's a valid file. */
322 if (write_header(s
->f
))
327 static void wav_close(struct ast_filestream
*s
)
330 struct wav_desc
*fs
= (struct wav_desc
*)s
->_private
;
336 /* Pad to even length */
338 fwrite(&zero
, 1, 1, s
->f
);
341 static struct ast_frame
*wav_read(struct ast_filestream
*s
, int *whennext
)
344 int samples
; /* actual samples read */
345 #if __BYTE_ORDER == __BIG_ENDIAN
349 int bytes
= WAV_BUF_SIZE
; /* in bytes */
351 /* Send a frame from the file to the appropriate channel */
352 struct wav_desc
*fs
= (struct wav_desc
*)s
->_private
;
355 if (fs
->maxlen
- here
< bytes
) /* truncate if necessary */
356 bytes
= fs
->maxlen
- here
;
359 /* ast_debug(1, "here: %d, maxlen: %d, bytes: %d\n", here, s->maxlen, bytes); */
360 s
->fr
.frametype
= AST_FRAME_VOICE
;
361 s
->fr
.subclass
= AST_FORMAT_SLINEAR
;
363 AST_FRAME_SET_BUFFER(&s
->fr
, s
->buf
, AST_FRIENDLY_OFFSET
, bytes
);
365 if ( (res
= fread(s
->fr
.data
.ptr
, 1, s
->fr
.datalen
, s
->f
)) <= 0 ) {
367 ast_log(LOG_WARNING
, "Short read (%d) (%s)!\n", res
, strerror(errno
));
371 s
->fr
.samples
= samples
= res
/ 2;
373 tmp
= (short *)(s
->fr
.data
.ptr
);
374 #if __BYTE_ORDER == __BIG_ENDIAN
375 /* file format is little endian so we need to swap */
376 for( x
= 0; x
< samples
; x
++)
377 tmp
[x
] = (tmp
[x
] << 8) | ((tmp
[x
] & 0xff00) >> 8);
384 static int wav_write(struct ast_filestream
*fs
, struct ast_frame
*f
)
386 #if __BYTE_ORDER == __BIG_ENDIAN
388 short tmp
[8000], *tmpi
;
390 struct wav_desc
*s
= (struct wav_desc
*)fs
->_private
;
393 if (f
->frametype
!= AST_FRAME_VOICE
) {
394 ast_log(LOG_WARNING
, "Asked to write non-voice frame!\n");
397 if (f
->subclass
!= AST_FORMAT_SLINEAR
) {
398 ast_log(LOG_WARNING
, "Asked to write non-SLINEAR frame (%d)!\n", f
->subclass
);
404 #if __BYTE_ORDER == __BIG_ENDIAN
406 if (f
->datalen
> sizeof(tmp
)) {
407 ast_log(LOG_WARNING
, "Data length is too long\n");
411 for (x
=0; x
< f
->datalen
/2; x
++)
412 tmp
[x
] = (tmpi
[x
] << 8) | ((tmpi
[x
] & 0xff00) >> 8);
414 if ((res
= fwrite(tmp
, 1, f
->datalen
, fs
->f
)) != f
->datalen
) {
417 if ((res
= fwrite(f
->data
.ptr
, 1, f
->datalen
, fs
->f
)) != f
->datalen
) {
419 ast_log(LOG_WARNING
, "Bad write (%d): %s\n", res
, strerror(errno
));
423 s
->bytes
+= f
->datalen
;
429 static int wav_seek(struct ast_filestream
*fs
, off_t sample_offset
, int whence
)
431 off_t min
, max
, cur
, offset
= 0, samples
;
433 samples
= sample_offset
* 2; /* SLINEAR is 16 bits mono, so sample_offset * 2 = bytes */
434 min
= 44; /* wav header is 44 bytes */
436 fseeko(fs
->f
, 0, SEEK_END
);
438 if (whence
== SEEK_SET
)
439 offset
= samples
+ min
;
440 else if (whence
== SEEK_CUR
|| whence
== SEEK_FORCECUR
)
441 offset
= samples
+ cur
;
442 else if (whence
== SEEK_END
)
443 offset
= max
- samples
;
444 if (whence
!= SEEK_FORCECUR
) {
445 offset
= (offset
> max
)?max
:offset
;
447 /* always protect the header space. */
448 offset
= (offset
< min
)?min
:offset
;
449 return fseeko(fs
->f
, offset
, SEEK_SET
);
452 static int wav_trunc(struct ast_filestream
*fs
)
454 if (ftruncate(fileno(fs
->f
), ftello(fs
->f
)))
456 return update_header(fs
->f
);
459 static off_t
wav_tell(struct ast_filestream
*fs
)
462 offset
= ftello(fs
->f
);
463 /* subtract header size to get samples, then divide by 2 for 16 bit samples */
464 return (offset
- 44)/2;
467 static const struct ast_format wav_f
= {
470 .format
= AST_FORMAT_SLINEAR
,
472 .rewrite
= wav_rewrite
,
479 .buf_size
= WAV_BUF_SIZE
+ AST_FRIENDLY_OFFSET
,
480 .desc_size
= sizeof(struct wav_desc
),
483 static int load_module(void)
485 if (ast_format_register(&wav_f
))
486 return AST_MODULE_LOAD_FAILURE
;
487 return AST_MODULE_LOAD_SUCCESS
;
490 static int unload_module(void)
492 return ast_format_unregister(wav_f
.name
);
495 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY
, "Microsoft WAV format (8000Hz Signed Linear)");