2 * Asterisk -- A telephony toolkit for Linux.
4 * Work with WAV in the proprietary Microsoft format.
6 * Copyright (C) 1999, Mark Spencer
8 * Mark Spencer <markster@linux-support.net>
10 * This program is free software, distributed under the terms of
11 * the GNU General Public License
14 #include <asterisk/lock.h>
15 #include <asterisk/channel.h>
16 #include <asterisk/file.h>
17 #include <asterisk/logger.h>
18 #include <asterisk/sched.h>
19 #include <asterisk/module.h>
20 #include <asterisk/endian.h>
21 #include <netinet/in.h>
22 #include <arpa/inet.h>
30 /* Some Ideas for this code came from makewave.c by Jeffrey Chilton */
32 /* Portions of the conversion code are by guido@sienanet.it */
34 struct ast_filestream {
35 void *reserved[AST_RESERVED_POINTERS];
36 /* This is what a filestream means to us */
37 int fd; /* Descriptor */
40 struct ast_frame fr; /* Frame information */
41 char waste[AST_FRIENDLY_OFFSET]; /* Buffer for sending frames, etc */
42 char empty; /* Empty character */
51 AST_MUTEX_DEFINE_STATIC(wav_lock);
52 static int glistcnt = 0;
54 static char *name = "wav";
55 static char *desc = "Microsoft WAV format (8000hz Signed Linear)";
56 static char *exts = "wav";
60 #define GAIN 2 /* 2^GAIN is the multiple to increase the volume by */
62 #if __BYTE_ORDER == __LITTLE_ENDIAN
68 #if __BYTE_ORDER == __BIG_ENDIAN
70 (((((b) ) & 0xFF) << 24) | \
71 ((((b) >> 8) & 0xFF) << 16) | \
72 ((((b) >> 16) & 0xFF) << 8) | \
73 ((((b) >> 24) & 0xFF) ))
75 (((((b) ) & 0xFF) << 8) | \
76 ((((b) >> 8) & 0xFF) ))
77 #define ltohl(b) htoll(b)
78 #define ltohs(b) htols(b)
80 #error "Endianess not defined"
85 static int check_header(int fd)
87 int type, size, formtype;
89 short format, chans, bysam, bisam;
93 if (read(fd, &type, 4) != 4) {
94 ast_log(LOG_WARNING, "Read failed (type)\n");
97 if (read(fd, &size, 4) != 4) {
98 ast_log(LOG_WARNING, "Read failed (size)\n");
102 if (read(fd, &formtype, 4) != 4) {
103 ast_log(LOG_WARNING, "Read failed (formtype)\n");
106 if (memcmp(&type, "RIFF", 4)) {
107 ast_log(LOG_WARNING, "Does not begin with RIFF\n");
110 if (memcmp(&formtype, "WAVE", 4)) {
111 ast_log(LOG_WARNING, "Does not contain WAVE\n");
114 if (read(fd, &fmt, 4) != 4) {
115 ast_log(LOG_WARNING, "Read failed (fmt)\n");
118 if (memcmp(&fmt, "fmt ", 4)) {
119 ast_log(LOG_WARNING, "Does not say fmt\n");
122 if (read(fd, &hsize, 4) != 4) {
123 ast_log(LOG_WARNING, "Read failed (formtype)\n");
126 if (ltohl(hsize) < 16) {
127 ast_log(LOG_WARNING, "Unexpected header size %d\n", ltohl(hsize));
130 if (read(fd, &format, 2) != 2) {
131 ast_log(LOG_WARNING, "Read failed (format)\n");
134 if (ltohs(format) != 1) {
135 ast_log(LOG_WARNING, "Not a wav file %d\n", ltohs(format));
138 if (read(fd, &chans, 2) != 2) {
139 ast_log(LOG_WARNING, "Read failed (format)\n");
142 if (ltohs(chans) != 1) {
143 ast_log(LOG_WARNING, "Not in mono %d\n", ltohs(chans));
146 if (read(fd, &freq, 4) != 4) {
147 ast_log(LOG_WARNING, "Read failed (freq)\n");
150 if (ltohl(freq) != 8000) {
151 ast_log(LOG_WARNING, "Unexpected freqency %d\n", ltohl(freq));
154 /* Ignore the byte frequency */
155 if (read(fd, &bysec, 4) != 4) {
156 ast_log(LOG_WARNING, "Read failed (BYTES_PER_SECOND)\n");
159 /* Check bytes per sample */
160 if (read(fd, &bysam, 2) != 2) {
161 ast_log(LOG_WARNING, "Read failed (BYTES_PER_SAMPLE)\n");
164 if (ltohs(bysam) != 2) {
165 ast_log(LOG_WARNING, "Can only handle 16bits per sample: %d\n", ltohs(bysam));
168 if (read(fd, &bisam, 2) != 2) {
169 ast_log(LOG_WARNING, "Read failed (Bits Per Sample): %d\n", ltohs(bisam));
172 /* Skip any additional header */
173 if ( lseek(fd,ltohl(hsize)-16,SEEK_CUR) == -1 ) {
174 ast_log(LOG_WARNING, "Failed to skip remaining header bytes: %d\n", ltohl(hsize)-16 );
177 /* Skip any facts and get the first data block */
182 /* Begin data chunk */
183 if (read(fd, &buf, 4) != 4) {
184 ast_log(LOG_WARNING, "Read failed (data)\n");
187 /* Data has the actual length of data in it */
188 if (read(fd, &data, 4) != 4) {
189 ast_log(LOG_WARNING, "Read failed (data)\n");
193 if( memcmp(buf, "data", 4) == 0 ) break;
194 if( memcmp(buf, "fact", 4) != 0 ) {
195 ast_log(LOG_WARNING, "Unknown block - not fact or data\n");
198 if ( lseek(fd,data,SEEK_CUR) == -1 ) {
199 ast_log(LOG_WARNING, "Failed to skip fact block: %d\n", data );
204 curpos = lseek(fd, 0, SEEK_CUR);
205 truelength = lseek(fd, 0, SEEK_END);
206 lseek(fd, curpos, SEEK_SET);
207 truelength -= curpos;
212 static int update_header(int fd)
215 int datalen,filelen,bytes;
218 cur = lseek(fd, 0, SEEK_CUR);
219 end = lseek(fd, 0, SEEK_END);
220 /* data starts 44 bytes in */
222 datalen = htoll(bytes);
223 /* chunk size is bytes of data plus 36 bytes of header */
224 filelen = htoll(36 + bytes);
227 ast_log(LOG_WARNING, "Unable to find our position\n");
230 if (lseek(fd, 4, SEEK_SET) != 4) {
231 ast_log(LOG_WARNING, "Unable to set our position\n");
234 if (write(fd, &filelen, 4) != 4) {
235 ast_log(LOG_WARNING, "Unable to set write file size\n");
238 if (lseek(fd, 40, SEEK_SET) != 40) {
239 ast_log(LOG_WARNING, "Unable to set our position\n");
242 if (write(fd, &datalen, 4) != 4) {
243 ast_log(LOG_WARNING, "Unable to set write datalen\n");
246 if (lseek(fd, cur, SEEK_SET) != cur) {
247 ast_log(LOG_WARNING, "Unable to return to position\n");
253 static int write_header(int fd)
255 unsigned int hz=htoll(8000);
256 unsigned int bhz = htoll(16000);
257 unsigned int hs = htoll(16);
258 unsigned short fmt = htols(1);
259 unsigned short chans = htols(1);
260 unsigned short bysam = htols(2);
261 unsigned short bisam = htols(16);
262 unsigned int size = htoll(0);
263 /* Write a wav header, ignoring sizes which will be filled in later */
264 lseek(fd,0,SEEK_SET);
265 if (write(fd, "RIFF", 4) != 4) {
266 ast_log(LOG_WARNING, "Unable to write header\n");
269 if (write(fd, &size, 4) != 4) {
270 ast_log(LOG_WARNING, "Unable to write header\n");
273 if (write(fd, "WAVEfmt ", 8) != 8) {
274 ast_log(LOG_WARNING, "Unable to write header\n");
277 if (write(fd, &hs, 4) != 4) {
278 ast_log(LOG_WARNING, "Unable to write header\n");
281 if (write(fd, &fmt, 2) != 2) {
282 ast_log(LOG_WARNING, "Unable to write header\n");
285 if (write(fd, &chans, 2) != 2) {
286 ast_log(LOG_WARNING, "Unable to write header\n");
289 if (write(fd, &hz, 4) != 4) {
290 ast_log(LOG_WARNING, "Unable to write header\n");
293 if (write(fd, &bhz, 4) != 4) {
294 ast_log(LOG_WARNING, "Unable to write header\n");
297 if (write(fd, &bysam, 2) != 2) {
298 ast_log(LOG_WARNING, "Unable to write header\n");
301 if (write(fd, &bisam, 2) != 2) {
302 ast_log(LOG_WARNING, "Unable to write header\n");
305 if (write(fd, "data", 4) != 4) {
306 ast_log(LOG_WARNING, "Unable to write header\n");
309 if (write(fd, &size, 4) != 4) {
310 ast_log(LOG_WARNING, "Unable to write header\n");
316 static struct ast_filestream *wav_open(int fd)
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. */
321 struct ast_filestream *tmp;
322 if ((tmp = malloc(sizeof(struct ast_filestream)))) {
323 memset(tmp, 0, sizeof(struct ast_filestream));
324 if ((tmp->maxlen = check_header(fd)) < 0) {
328 if (ast_mutex_lock(&wav_lock)) {
329 ast_log(LOG_WARNING, "Unable to lock wav list\n");
335 tmp->fr.data = tmp->buf;
336 tmp->fr.frametype = AST_FRAME_VOICE;
337 tmp->fr.subclass = AST_FORMAT_SLINEAR;
338 /* datalen will vary for each frame */
343 ast_mutex_unlock(&wav_lock);
344 ast_update_use_count();
349 static struct ast_filestream *wav_rewrite(int fd, const char *comment)
351 /* We don't have any header to read or anything really, but
352 if we did, it would go here. We also might want to check
353 and be sure it's a valid file. */
354 struct ast_filestream *tmp;
355 if ((tmp = malloc(sizeof(struct ast_filestream)))) {
356 memset(tmp, 0, sizeof(struct ast_filestream));
357 if (write_header(fd)) {
361 if (ast_mutex_lock(&wav_lock)) {
362 ast_log(LOG_WARNING, "Unable to lock wav list\n");
368 ast_mutex_unlock(&wav_lock);
369 ast_update_use_count();
371 ast_log(LOG_WARNING, "Out of memory\n");
375 static void wav_close(struct ast_filestream *s)
378 if (ast_mutex_lock(&wav_lock)) {
379 ast_log(LOG_WARNING, "Unable to lock wav list\n");
383 ast_mutex_unlock(&wav_lock);
384 ast_update_use_count();
385 /* Pad to even length */
387 write(s->fd, &zero, 1);
393 static struct ast_frame *wav_read(struct ast_filestream *s, int *whennext)
398 short tmp[sizeof(s->buf) / 2];
399 int bytes = sizeof(tmp);
401 /* Send a frame from the file to the appropriate channel */
402 here = lseek(s->fd, 0, SEEK_CUR);
403 if ((s->maxlen - here) < bytes)
404 bytes = s->maxlen - here;
407 /* ast_log(LOG_DEBUG, "here: %d, maxlen: %d, bytes: %d\n", here, s->maxlen, bytes); */
409 if ( (res = read(s->fd, tmp, bytes)) <= 0 ) {
411 ast_log(LOG_WARNING, "Short read (%d) (%s)!\n", res, strerror(errno));
416 #if __BYTE_ORDER == __BIG_ENDIAN
417 for( x = 0; x < sizeof(tmp)/2; x++) tmp[x] = (tmp[x] << 8) | ((tmp[x] & 0xff00) >> 8);
421 for (x=0;x<sizeof(tmp)/2;x++)
422 if (tmp[x] & ((1 << GAIN) - 1)) {
423 /* If it has data down low, then it's not something we've artificially increased gain
424 on, so we don't need to gain adjust it */
429 for (x=0;x<sizeof(tmp)/2;x++) {
430 s->buf[x] = tmp[x] >> GAIN;
433 memcpy(s->buf, tmp, sizeof(s->buf));
437 s->fr.frametype = AST_FRAME_VOICE;
438 s->fr.subclass = AST_FORMAT_SLINEAR;
439 s->fr.offset = AST_FRIENDLY_OFFSET;
443 s->fr.samples = delay;
448 static int wav_write(struct ast_filestream *fs, struct ast_frame *f)
452 short tmp[8000], *tmpi;
454 if (f->frametype != AST_FRAME_VOICE) {
455 ast_log(LOG_WARNING, "Asked to write non-voice frame!\n");
458 if (f->subclass != AST_FORMAT_SLINEAR) {
459 ast_log(LOG_WARNING, "Asked to write non-SLINEAR frame (%d)!\n", f->subclass);
462 if (f->datalen > sizeof(tmp)) {
463 ast_log(LOG_WARNING, "Data length is too long\n");
470 printf("Data Length: %d\n", f->datalen);
475 /* Volume adjust here to accomodate */
476 for (x=0;x<f->datalen/2;x++) {
477 tmpf = ((float)tmpi[x]) * ((float)(1 << GAIN));
483 tmp[x] &= ~((1 << GAIN) - 1);
485 #if __BYTE_ORDER == __BIG_ENDIAN
486 tmp[x] = (tmp[x] << 8) | ((tmp[x] & 0xff00) >> 8);
490 if ((write (fs->fd, tmp, f->datalen) != f->datalen) ) {
491 ast_log(LOG_WARNING, "Bad write (%d): %s\n", res, strerror(errno));
495 ast_log(LOG_WARNING, "Cannot write data to file.\n");
499 fs->bytes += f->datalen;
500 update_header(fs->fd);
506 static int wav_seek(struct ast_filestream *fs, long sample_offset, int whence)
509 long offset=0,samples;
511 samples = sample_offset * 2; /* SLINEAR is 16 bits mono, so sample_offset * 2 = bytes */
512 min = 44; /* wav header is 44 bytes */
513 cur = lseek(fs->fd, 0, SEEK_CUR);
514 max = lseek(fs->fd, 0, SEEK_END);
515 if (whence == SEEK_SET)
516 offset = samples + min;
517 else if (whence == SEEK_CUR || whence == SEEK_FORCECUR)
518 offset = samples + cur;
519 else if (whence == SEEK_END)
520 offset = max - samples;
521 if (whence != SEEK_FORCECUR) {
522 offset = (offset > max)?max:offset;
524 /* always protect the header space. */
525 offset = (offset < min)?min:offset;
526 return lseek(fs->fd,offset,SEEK_SET);
529 static int wav_trunc(struct ast_filestream *fs)
531 if(ftruncate(fs->fd, lseek(fs->fd,0,SEEK_CUR)))
533 return update_header(fs->fd);
536 static long wav_tell(struct ast_filestream *fs)
539 offset = lseek(fs->fd, 0, SEEK_CUR);
540 /* subtract header size to get samples, then divide by 2 for 16 bit samples */
541 return (offset - 44)/2;
544 static char *wav_getcomment(struct ast_filestream *s)
551 return ast_format_register(name, exts, AST_FORMAT_SLINEAR,
567 return ast_format_unregister(name);
573 if (ast_mutex_lock(&wav_lock)) {
574 ast_log(LOG_WARNING, "Unable to lock wav list\n");
578 ast_mutex_unlock(&wav_lock);
590 return ASTERISK_GPL_KEY;