Index: formats/format_wav.c =================================================================== --- formats/format_wav.c (revision 401703) +++ formats/format_wav.c (working copy) @@ -20,7 +20,10 @@ * * \brief Work with WAV in the proprietary Microsoft format. * Microsoft WAV format (8000hz Signed Linear) - * \arg File name extension: wav (lower case) + * Microsoft WAV format (16000hz Signed Linear) + * Microsoft WAV format (8000hz u-Law) + * Microsoft WAV format (8000hz A-Law) + * \arg File name extension: wav (lower case), wavs, wava, wavu, wav16 * \ingroup formats */ @@ -35,17 +38,25 @@ #include "asterisk/mod_format.h" #include "asterisk/module.h" #include "asterisk/endian.h" +#include "asterisk/alaw.h" +#include "asterisk/ulaw.h" /* Some Ideas for this code came from makewave.c by Jeffrey Chilton */ /* Portions of the conversion code are by guido@sienanet.it */ +/* A-law and u-law capability added by Michael Walton */ + #define WAV_BUF_SIZE 320 #define WAV_HEADER_SIZE 44 +#define FWAV_XLAW_ULAW 1 +#define FWAV_XLAW_ALAW 2 + struct wav_desc { /* format-specific parameters */ int hz; + int xlaw; /* is the wav file in a/u-law format? */ int bytes; int lasttimeout; int maxlen; @@ -77,11 +88,12 @@ #endif -static int check_header_fmt(FILE *f, int hsize, int hz) +static int check_header_fmt(FILE *f, int hsize, int hz, int *xlaw) { short format, chans, bysam, bisam; int bysec; int freq; + *xlaw = 0; if (hsize < 16) { ast_log(LOG_WARNING, "Unexpected header size %d\n", hsize); return -1; @@ -90,7 +102,17 @@ ast_log(LOG_WARNING, "Read failed (format)\n"); return -1; } - if (ltohs(format) != 1) { + /* Linear PCM is 1, A-law is 6, u-law is 7 */ + switch (ltohs(format)) { + case 1: /* Linear PCM */ + break; + case 6: /* A-law */ + *xlaw = FWAV_XLAW_ALAW; + break; + case 7: /* u-law */ + *xlaw = FWAV_XLAW_ULAW; + break; + default: ast_log(LOG_WARNING, "Not a wav file %d\n", ltohs(format)); return -1; } @@ -122,8 +144,8 @@ ast_log(LOG_WARNING, "Read failed (BYTES_PER_SAMPLE)\n"); return -1; } - if (ltohs(bysam) != 2) { - ast_log(LOG_WARNING, "Can only handle 16bits per sample: %d\n", ltohs(bysam)); + if (ltohs(bysam) != (*xlaw ? 1 : 2)) { + ast_log(LOG_WARNING, "Wrong bytes per sample: %d\n", ltohs(bysam)); return -1; } if (fread(&bisam, 1, 2, f) != 2) { @@ -138,10 +160,11 @@ return 0; } -static int check_header(FILE *f, int hz) +static int check_header(FILE *f, int hz, int *xlaw) { int type, size, formtype; int data; + *xlaw = 0; if (fread(&type, 1, 4, f) != 4) { ast_log(LOG_WARNING, "Read failed (type)\n"); return -1; @@ -180,7 +203,7 @@ } data = ltohl(data); if (memcmp(&buf, "fmt ", 4) == 0) { - if (check_header_fmt(f, data, hz)) + if (check_header_fmt(f, data, hz, xlaw)) return -1; continue; } @@ -201,7 +224,7 @@ return data; } -static int update_header(FILE *f) +static int update_header(FILE *f, int xlaw) { off_t cur,end; int datalen,filelen,bytes; @@ -209,11 +232,11 @@ cur = ftello(f); fseek(f, 0, SEEK_END); end = ftello(f); - /* data starts 44 bytes in */ - bytes = end - 44; + /* data starts 44 bytes in (46 for xlaw) */ + bytes = end - (xlaw ? 46 : 44); datalen = htoll(bytes); - /* chunk size is bytes of data plus 36 bytes of header */ - filelen = htoll(36 + bytes); + /* chunk size is bytes of data plus 36 (38) bytes of header */ + filelen = htoll((xlaw ? 38 : 36) + bytes); if (cur < 0) { ast_log(LOG_WARNING, "Unable to find our position\n"); @@ -227,7 +250,7 @@ ast_log(LOG_WARNING, "Unable to set write file size\n"); return -1; } - if (fseek(f, 40, SEEK_SET)) { + if (fseek(f, xlaw ? 42 : 40, SEEK_SET)) { ast_log(LOG_WARNING, "Unable to set our position\n"); return -1; } @@ -242,23 +265,34 @@ return 0; } -static int write_header(FILE *f, int writehz) +static int write_header(FILE *f, int writehz, int xlaw) { unsigned int hz; unsigned int bhz; - unsigned int hs = htoll(16); + unsigned int hs = xlaw ? htoll(18) : htoll(16); unsigned short fmt = htols(1); unsigned short chans = htols(1); - unsigned short bysam = htols(2); - unsigned short bisam = htols(16); + unsigned short bysam = xlaw ? htols(1) : htols(2); + unsigned short bisam = xlaw ? htols(8) : htols(16); unsigned int size = htoll(0); + unsigned short xtra = 0; + switch (xlaw) { + case FWAV_XLAW_ALAW: + fmt = htols(6); + break; + case FWAV_XLAW_ULAW: + fmt = htols(7); + break; + default: + break; + } if (writehz == 16000) { hz = htoll(16000); bhz = htoll(32000); } else { hz = htoll(8000); - bhz = htoll(16000); + bhz = xlaw ? htoll(8000) : htoll(16000); } /* Write a wav header, ignoring sizes which will be filled in later */ fseek(f,0,SEEK_SET); @@ -302,6 +336,13 @@ ast_log(LOG_WARNING, "Unable to write header\n"); return -1; } + if (xlaw) { + /* Extra 2 bytes 0000 for compressed formats */ + if (fwrite(&xtra, 1, 2, f) != 2) { + ast_log(LOG_WARNING, "Unable to write header\n"); + return -1; + } + } if (fwrite("data", 1, 4, f) != 4) { ast_log(LOG_WARNING, "Unable to write header\n"); return -1; @@ -319,7 +360,7 @@ if we did, it would go here. We also might want to check and be sure it's a valid file. */ struct wav_desc *tmp = (struct wav_desc *)s->_private; - if ((tmp->maxlen = check_header(s->f, (s->fmt->format.id == AST_FORMAT_SLINEAR16 ? 16000 : 8000))) < 0) + if ((tmp->maxlen = check_header(s->f, (s->fmt->format.id == AST_FORMAT_SLINEAR16 ? 16000 : 8000), &tmp->xlaw)) < 0) return -1; return 0; } @@ -331,8 +372,18 @@ and be sure it's a valid file. */ struct wav_desc *tmp = (struct wav_desc *)s->_private; + switch (s->fmt->format.id) { + case AST_FORMAT_ULAW: + tmp->xlaw = FWAV_XLAW_ULAW; + break; + case AST_FORMAT_ALAW: + tmp->xlaw = FWAV_XLAW_ALAW; + break; + default: + tmp->xlaw = 0; + } tmp->hz = (s->fmt->format.id == AST_FORMAT_SLINEAR16 ? 16000 : 8000); - if (write_header(s->f,tmp->hz)) + if (write_header(s->f,tmp->hz,tmp->xlaw)) return -1; return 0; } @@ -347,7 +398,7 @@ } if (s->filename) { - update_header(s->f); + update_header(s->f, fs->xlaw); } /* Pad to even length */ @@ -372,6 +423,8 @@ struct wav_desc *fs = (struct wav_desc *)s->_private; bytes = (fs->hz == 16000 ? (WAV_BUF_SIZE * 2) : WAV_BUF_SIZE); + if (fs->xlaw) + bytes = WAV_BUF_SIZE/2; /* 160 */ here = ftello(s->f); if (fs->maxlen - here < bytes) /* truncate if necessary */ @@ -380,7 +433,16 @@ bytes = 0; /* ast_debug(1, "here: %d, maxlen: %d, bytes: %d\n", here, s->maxlen, bytes); */ s->fr.frametype = AST_FRAME_VOICE; - ast_format_set(&s->fr.subclass.format, (fs->hz == 16000 ? AST_FORMAT_SLINEAR16 : AST_FORMAT_SLINEAR), 0); + switch (fs->xlaw) { + case FWAV_XLAW_ULAW: + ast_format_set(&s->fr.subclass.format, AST_FORMAT_ULAW, 0); + break; + case FWAV_XLAW_ALAW: + ast_format_set(&s->fr.subclass.format, AST_FORMAT_ALAW, 0); + break; + default: + ast_format_set(&s->fr.subclass.format, (fs->hz == 16000 ? AST_FORMAT_SLINEAR16 : AST_FORMAT_SLINEAR), 0); + } s->fr.mallocd = 0; AST_FRAME_SET_BUFFER(&s->fr, s->buf, AST_FRIENDLY_OFFSET, bytes); @@ -390,13 +452,16 @@ return NULL; } s->fr.datalen = res; - s->fr.samples = samples = res / 2; + samples = fs->xlaw ? res : res / 2; + s->fr.samples = samples; #if __BYTE_ORDER == __BIG_ENDIAN - tmp = (short *)(s->fr.data.ptr); - /* file format is little endian so we need to swap */ - for( x = 0; x < samples; x++) - tmp[x] = (tmp[x] << 8) | ((tmp[x] & 0xff00) >> 8); + if (!fs->xlaw) { + tmp = (short *)(s->fr.data.ptr); + /* file format is little endian so we need to swap */ + for( x = 0; x < samples; x++) + tmp[x] = (tmp[x] << 8) | ((tmp[x] & 0xff00) >> 8); + } #endif *whennext = samples; @@ -411,37 +476,51 @@ #endif struct wav_desc *s = (struct wav_desc *)fs->_private; int res; + struct ast_format codec; + switch (s->xlaw) { + case FWAV_XLAW_ULAW: + ast_format_set(&codec, AST_FORMAT_ULAW, 0); + break; + case FWAV_XLAW_ALAW: + ast_format_set(&codec, AST_FORMAT_ALAW, 0); + break; + default: + ast_format_set(&codec, (s->hz == 16000 ? AST_FORMAT_SLINEAR16 : AST_FORMAT_SLINEAR), 0); + } if (f->frametype != AST_FRAME_VOICE) { ast_log(LOG_WARNING, "Asked to write non-voice frame!\n"); return -1; } - if ((f->subclass.format.id != AST_FORMAT_SLINEAR) && (f->subclass.format.id != AST_FORMAT_SLINEAR16)) { - ast_log(LOG_WARNING, "Asked to write non-SLINEAR%s frame (%s)!\n", s->hz == 16000 ? "16" : "", ast_getformatname(&f->subclass.format)); + if (f->subclass.format.id != codec.id) { + ast_log(LOG_WARNING, "Asked to write non-%s frame (%s)!\n", ast_getformatname(&codec), ast_getformatname(&f->subclass.format)); return -1; } if (ast_format_cmp(&f->subclass.format, &fs->fmt->format) == AST_FORMAT_CMP_NOT_EQUAL) { - ast_log(LOG_WARNING, "Can't change SLINEAR frequency during write\n"); + ast_log(LOG_WARNING, "Can't change format during write\n"); return -1; } if (!f->datalen) return -1; #if __BYTE_ORDER == __BIG_ENDIAN - /* swap and write */ - if (f->datalen > sizeof(tmp)) { - ast_log(LOG_WARNING, "Data length is too long\n"); - return -1; - } - tmpi = f->data.ptr; - for (x=0; x < f->datalen/2; x++) - tmp[x] = (tmpi[x] << 8) | ((tmpi[x] & 0xff00) >> 8); - - if ((res = fwrite(tmp, 1, f->datalen, fs->f)) != f->datalen ) { -#else + if (!s->alaw) { + /* swap and write */ + if (f->datalen > sizeof(tmp)) { + ast_log(LOG_WARNING, "Data length is too long\n"); + return -1; + } + tmpi = f->data.ptr; + for (x=0; x < f->datalen/2; x++) + tmp[x] = (tmpi[x] << 8) | ((tmpi[x] & 0xff00) >> 8); + if ((res = fwrite(tmp, 1, f->datalen, fs->f)) != f->datalen ) { + ast_log(LOG_WARNING, "Bad write (%d): %s\n", res, strerror(errno)); + return -1; + } + } else +#endif /* just write */ if ((res = fwrite(f->data.ptr, 1, f->datalen, fs->f)) != f->datalen ) { -#endif ast_log(LOG_WARNING, "Bad write (%d): %s\n", res, strerror(errno)); return -1; } @@ -455,8 +534,10 @@ static int wav_seek(struct ast_filestream *fs, off_t sample_offset, int whence) { off_t min = WAV_HEADER_SIZE, max, cur, offset = 0, samples; + struct wav_desc *s = (struct wav_desc *)fs->_private; - samples = sample_offset * 2; /* SLINEAR is 16 bits mono, so sample_offset * 2 = bytes */ + samples = s->xlaw ? sample_offset : sample_offset * 2; /* SLINEAR is 16 bits mono, so sample_offset * 2 = bytes */ + min = s->xlaw ? 46 : 44; /* wav header is 44 (46) bytes */ if ((cur = ftello(fs->f)) < 0) { ast_log(AST_LOG_WARNING, "Unable to determine current position in wav filestream %p: %s\n", fs, strerror(errno)); @@ -489,6 +570,7 @@ static int wav_trunc(struct ast_filestream *fs) { + struct wav_desc *s = (struct wav_desc *)fs->_private; int fd; off_t cur; @@ -504,15 +586,16 @@ if (ftruncate(fd, cur)) { return -1; } - return update_header(fs->f); + return update_header(fs->f, s->xlaw); } static off_t wav_tell(struct ast_filestream *fs) { + struct wav_desc *s = (struct wav_desc *)fs->_private; off_t offset; offset = ftello(fs->f); /* subtract header size to get samples, then divide by 2 for 16 bit samples */ - return (offset - 44)/2; + return s->xlaw ? (offset - 46) : ((offset - 44)/2); } static struct ast_format_def wav16_f = { @@ -532,7 +615,49 @@ static struct ast_format_def wav_f = { .name = "wav", - .exts = "wav", +#ifdef FORMAT_WAV_IS_LINEAR + .exts = "wav|wavs", +#else + .exts = "wavs", +#endif + .open = wav_open, + .rewrite = wav_rewrite, + .write = wav_write, + .seek = wav_seek, + .trunc = wav_trunc, + .tell = wav_tell, + .read = wav_read, + .close = wav_close, + .buf_size = WAV_BUF_SIZE + AST_FRIENDLY_OFFSET, + .desc_size = sizeof(struct wav_desc), +}; + +static struct ast_format_def wava_f = { + .name = "wava", +#ifdef FORMAT_WAV_IS_LINEAR + .exts = "wava", +#else + .exts = "wav|wava", +#endif + .open = wav_open, + .rewrite = wav_rewrite, + .write = wav_write, + .seek = wav_seek, + .trunc = wav_trunc, + .tell = wav_tell, + .read = wav_read, + .close = wav_close, + .buf_size = WAV_BUF_SIZE + AST_FRIENDLY_OFFSET, + .desc_size = sizeof(struct wav_desc), +}; + +static struct ast_format_def wavu_f = { + .name = "wavu", +#ifdef FORMAT_WAV_IS_LINEAR + .exts = "wavu", +#else + .exts = "wav|wavu", +#endif .open = wav_open, .rewrite = wav_rewrite, .write = wav_write, @@ -549,8 +674,12 @@ { ast_format_set(&wav_f.format, AST_FORMAT_SLINEAR, 0); ast_format_set(&wav16_f.format, AST_FORMAT_SLINEAR16, 0); + ast_format_set(&wava_f.format, AST_FORMAT_ALAW, 0); + ast_format_set(&wavu_f.format, AST_FORMAT_ULAW, 0); if (ast_format_def_register(&wav_f) - || ast_format_def_register(&wav16_f)) + || ast_format_def_register(&wav16_f) + || ast_format_def_register(&wava_f) + || ast_format_def_register(&wavu_f)) return AST_MODULE_LOAD_FAILURE; return AST_MODULE_LOAD_SUCCESS; } @@ -558,10 +687,12 @@ static int unload_module(void) { return ast_format_def_unregister(wav_f.name) - || ast_format_def_unregister(wav16_f.name); + || ast_format_def_unregister(wav16_f.name) + || ast_format_def_unregister(wava_f.name) + || ast_format_def_unregister(wavu_f.name); } -AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "Microsoft WAV/WAV16 format (8kHz/16kHz Signed Linear)", +AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "Microsoft WAV format (8kHz/16kHz Signed Linear/alaw/ulaw)", .load = load_module, .unload = unload_module, .load_pri = AST_MODPRI_APP_DEPEND