Sat Aug 6 00:39:28 2011

Asterisk developer's documentation


format_wav.c

Go to the documentation of this file.
00001 /*
00002  * Asterisk -- An open source telephony toolkit.
00003  *
00004  * Copyright (C) 1999 - 2005, Digium, Inc.
00005  *
00006  * Mark Spencer <markster@digium.com>
00007  *
00008  * See http://www.asterisk.org for more information about
00009  * the Asterisk project. Please do not directly contact
00010  * any of the maintainers of this project for assistance;
00011  * the project provides a web site, mailing lists and IRC
00012  * channels for your use.
00013  *
00014  * This program is free software, distributed under the terms of
00015  * the GNU General Public License Version 2. See the LICENSE file
00016  * at the top of the source tree.
00017  */
00018 
00019 /*! \file
00020  *
00021  * \brief Work with WAV in the proprietary Microsoft format.
00022  * Microsoft WAV format (8000hz Signed Linear)
00023  * \arg File name extension: wav (lower case)
00024  * \ingroup formats
00025  */
00026  
00027 #include "asterisk.h"
00028 
00029 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 315257 $")
00030 
00031 #include <unistd.h>
00032 #include <netinet/in.h>
00033 #include <arpa/inet.h>
00034 #include <stdlib.h>
00035 #include <sys/time.h>
00036 #include <stdio.h>
00037 #include <errno.h>
00038 #include <string.h>
00039 
00040 #include "asterisk/lock.h"
00041 #include "asterisk/channel.h"
00042 #include "asterisk/file.h"
00043 #include "asterisk/logger.h"
00044 #include "asterisk/sched.h"
00045 #include "asterisk/module.h"
00046 #include "asterisk/endian.h"
00047 
00048 /* Some Ideas for this code came from makewave.c by Jeffrey Chilton */
00049 
00050 /* Portions of the conversion code are by guido@sienanet.it */
00051 
00052 #define  WAV_BUF_SIZE   320
00053 
00054 struct wav_desc { /* format-specific parameters */
00055    int bytes;
00056    int needsgain;
00057    int lasttimeout;
00058    int maxlen;
00059    struct timeval last;
00060 };
00061 
00062 #define BLOCKSIZE 160
00063 
00064 #define GAIN 0    /* 2^GAIN is the multiple to increase the volume by.  The original value of GAIN was 2, or 4x (12 dB),
00065           * but there were many reports of the clipping of loud signal peaks (issue 5823 for example). */
00066 
00067 #if __BYTE_ORDER == __LITTLE_ENDIAN
00068 #define htoll(b) (b)
00069 #define htols(b) (b)
00070 #define ltohl(b) (b)
00071 #define ltohs(b) (b)
00072 #else
00073 #if __BYTE_ORDER == __BIG_ENDIAN
00074 #define htoll(b)  \
00075           (((((b)      ) & 0xFF) << 24) | \
00076           ((((b) >>  8) & 0xFF) << 16) | \
00077          ((((b) >> 16) & 0xFF) <<  8) | \
00078          ((((b) >> 24) & 0xFF)      ))
00079 #define htols(b) \
00080           (((((b)      ) & 0xFF) << 8) | \
00081          ((((b) >> 8) & 0xFF)      ))
00082 #define ltohl(b) htoll(b)
00083 #define ltohs(b) htols(b)
00084 #else
00085 #error "Endianess not defined"
00086 #endif
00087 #endif
00088 
00089 
00090 static int check_header_fmt(FILE *f, int hsize)
00091 {
00092    short format, chans, bysam, bisam;
00093    int bysec;
00094    int freq;
00095    if (hsize < 16) {
00096       ast_log(LOG_WARNING, "Unexpected header size %d\n", ltohl(hsize));
00097       return -1;
00098    }
00099    if (fread(&format, 1, 2, f) != 2) {
00100       ast_log(LOG_WARNING, "Read failed (format)\n");
00101       return -1;
00102    }
00103    if (ltohs(format) != 1) {
00104       ast_log(LOG_WARNING, "Not a wav file %d\n", ltohs(format));
00105       return -1;
00106    }
00107    if (fread(&chans, 1, 2, f) != 2) {
00108       ast_log(LOG_WARNING, "Read failed (format)\n");
00109       return -1;
00110    }
00111    if (ltohs(chans) != 1) {
00112       ast_log(LOG_WARNING, "Not in mono %d\n", ltohs(chans));
00113       return -1;
00114    }
00115    if (fread(&freq, 1, 4, f) != 4) {
00116       ast_log(LOG_WARNING, "Read failed (freq)\n");
00117       return -1;
00118    }
00119    if (ltohl(freq) != DEFAULT_SAMPLE_RATE) {
00120       ast_log(LOG_WARNING, "Unexpected frequency %d\n", ltohl(freq));
00121       return -1;
00122    }
00123    /* Ignore the byte frequency */
00124    if (fread(&bysec, 1, 4, f) != 4) {
00125       ast_log(LOG_WARNING, "Read failed (BYTES_PER_SECOND)\n");
00126       return -1;
00127    }
00128    /* Check bytes per sample */
00129    if (fread(&bysam, 1, 2, f) != 2) {
00130       ast_log(LOG_WARNING, "Read failed (BYTES_PER_SAMPLE)\n");
00131       return -1;
00132    }
00133    if (ltohs(bysam) != 2) {
00134       ast_log(LOG_WARNING, "Can only handle 16bits per sample: %d\n", ltohs(bysam));
00135       return -1;
00136    }
00137    if (fread(&bisam, 1, 2, f) != 2) {
00138       ast_log(LOG_WARNING, "Read failed (Bits Per Sample): %d\n", ltohs(bisam));
00139       return -1;
00140    }
00141    /* Skip any additional header */
00142    if (fseek(f,ltohl(hsize)-16,SEEK_CUR) == -1 ) {
00143       ast_log(LOG_WARNING, "Failed to skip remaining header bytes: %d\n", ltohl(hsize)-16 );
00144       return -1;
00145    }
00146    return 0;
00147 }
00148 
00149 static int check_header(FILE *f)
00150 {
00151    int type, size, formtype;
00152    int data;
00153    if (fread(&type, 1, 4, f) != 4) {
00154       ast_log(LOG_WARNING, "Read failed (type)\n");
00155       return -1;
00156    }
00157    if (fread(&size, 1, 4, f) != 4) {
00158       ast_log(LOG_WARNING, "Read failed (size)\n");
00159       return -1;
00160    }
00161    size = ltohl(size);
00162    if (fread(&formtype, 1, 4, f) != 4) {
00163       ast_log(LOG_WARNING, "Read failed (formtype)\n");
00164       return -1;
00165    }
00166    if (memcmp(&type, "RIFF", 4)) {
00167       ast_log(LOG_WARNING, "Does not begin with RIFF\n");
00168       return -1;
00169    }
00170    if (memcmp(&formtype, "WAVE", 4)) {
00171       ast_log(LOG_WARNING, "Does not contain WAVE\n");
00172       return -1;
00173    }
00174    /* Skip any facts and get the first data block */
00175    for(;;)
00176    { 
00177       char buf[4];
00178        
00179        /* Begin data chunk */
00180        if (fread(&buf, 1, 4, f) != 4) {
00181          ast_log(LOG_WARNING, "Read failed (block header format)\n");
00182          return -1;
00183        }
00184        /* Data has the actual length of data in it */
00185        if (fread(&data, 1, 4, f) != 4) {
00186          ast_log(LOG_WARNING, "Read failed (block '%.4s' header length)\n", buf);
00187          return -1;
00188        }
00189        data = ltohl(data);
00190       if (memcmp(&buf, "fmt ", 4) == 0) {
00191          if (check_header_fmt(f, data))
00192             return -1;
00193          continue;
00194       }
00195        if(memcmp(buf, "data", 4) == 0 ) 
00196          break;
00197       ast_log(LOG_DEBUG, "Skipping unknown block '%.4s'\n", buf);
00198        if (fseek(f,data,SEEK_CUR) == -1 ) {
00199          ast_log(LOG_WARNING, "Failed to skip '%.4s' block: %d\n", buf, data);
00200          return -1;
00201        }
00202    }
00203 #if 0
00204    curpos = lseek(fd, 0, SEEK_CUR);
00205    truelength = lseek(fd, 0, SEEK_END);
00206    lseek(fd, curpos, SEEK_SET);
00207    truelength -= curpos;
00208 #endif   
00209    return data;
00210 }
00211 
00212 static int update_header(FILE *f)
00213 {
00214    off_t cur,end;
00215    int datalen,filelen,bytes;
00216    
00217    cur = ftello(f);
00218    fseek(f, 0, SEEK_END);
00219    end = ftello(f);
00220    /* data starts 44 bytes in */
00221    bytes = end - 44;
00222    datalen = htoll(bytes);
00223    /* chunk size is bytes of data plus 36 bytes of header */
00224    filelen = htoll(36 + bytes);
00225    
00226    if (cur < 0) {
00227       ast_log(LOG_WARNING, "Unable to find our position: %s\n", strerror(errno));
00228       return -1;
00229    }
00230    if (fseek(f, 4, SEEK_SET)) {
00231       ast_log(LOG_WARNING, "Unable to set our position: %s\n", strerror(errno));
00232       return -1;
00233    }
00234    if (fwrite(&filelen, 1, 4, f) != 4) {
00235       ast_log(LOG_WARNING, "Unable to set write file size: %s\n", strerror(errno));
00236       return -1;
00237    }
00238    if (fseek(f, 40, SEEK_SET)) {
00239       ast_log(LOG_WARNING, "Unable to set our position: %s\n", strerror(errno));
00240       return -1;
00241    }
00242    if (fwrite(&datalen, 1, 4, f) != 4) {
00243       ast_log(LOG_WARNING, "Unable to set write datalen: %s\n", strerror(errno));
00244       return -1;
00245    }
00246    if (fseeko(f, cur, SEEK_SET)) {
00247       ast_log(LOG_WARNING, "Unable to return to position: %s\n", strerror(errno));
00248       return -1;
00249    }
00250    return 0;
00251 }
00252 
00253 static int write_header(FILE *f)
00254 {
00255    unsigned int hz=htoll(8000);
00256    unsigned int bhz = htoll(16000);
00257    unsigned int hs = htoll(16);
00258    unsigned short fmt = htols(1);
00259    unsigned short chans = htols(1);
00260    unsigned short bysam = htols(2);
00261    unsigned short bisam = htols(16);
00262    unsigned int size = htoll(0);
00263    /* Write a wav header, ignoring sizes which will be filled in later */
00264    fseek(f,0,SEEK_SET);
00265    if (fwrite("RIFF", 1, 4, f) != 4) {
00266       ast_log(LOG_WARNING, "Unable to write header\n");
00267       return -1;
00268    }
00269    if (fwrite(&size, 1, 4, f) != 4) {
00270       ast_log(LOG_WARNING, "Unable to write header\n");
00271       return -1;
00272    }
00273    if (fwrite("WAVEfmt ", 1, 8, f) != 8) {
00274       ast_log(LOG_WARNING, "Unable to write header\n");
00275       return -1;
00276    }
00277    if (fwrite(&hs, 1, 4, f) != 4) {
00278       ast_log(LOG_WARNING, "Unable to write header\n");
00279       return -1;
00280    }
00281    if (fwrite(&fmt, 1, 2, f) != 2) {
00282       ast_log(LOG_WARNING, "Unable to write header\n");
00283       return -1;
00284    }
00285    if (fwrite(&chans, 1, 2, f) != 2) {
00286       ast_log(LOG_WARNING, "Unable to write header\n");
00287       return -1;
00288    }
00289    if (fwrite(&hz, 1, 4, f) != 4) {
00290       ast_log(LOG_WARNING, "Unable to write header\n");
00291       return -1;
00292    }
00293    if (fwrite(&bhz, 1, 4, f) != 4) {
00294       ast_log(LOG_WARNING, "Unable to write header\n");
00295       return -1;
00296    }
00297    if (fwrite(&bysam, 1, 2, f) != 2) {
00298       ast_log(LOG_WARNING, "Unable to write header\n");
00299       return -1;
00300    }
00301    if (fwrite(&bisam, 1, 2, f) != 2) {
00302       ast_log(LOG_WARNING, "Unable to write header\n");
00303       return -1;
00304    }
00305    if (fwrite("data", 1, 4, f) != 4) {
00306       ast_log(LOG_WARNING, "Unable to write header\n");
00307       return -1;
00308    }
00309    if (fwrite(&size, 1, 4, f) != 4) {
00310       ast_log(LOG_WARNING, "Unable to write header\n");
00311       return -1;
00312    }
00313    return 0;
00314 }
00315 
00316 static int wav_open(struct ast_filestream *s)
00317 {
00318    /* We don't have any header to read or anything really, but
00319       if we did, it would go here.  We also might want to check
00320       and be sure it's a valid file.  */
00321    struct wav_desc *tmp = (struct wav_desc *)s->_private;
00322    if ((tmp->maxlen = check_header(s->f)) < 0)
00323       return -1;
00324    return 0;
00325 }
00326 
00327 static int wav_rewrite(struct ast_filestream *s, const char *comment)
00328 {
00329    /* We don't have any header to read or anything really, but
00330       if we did, it would go here.  We also might want to check
00331       and be sure it's a valid file.  */
00332 
00333    if (write_header(s->f))
00334       return -1;
00335    return 0;
00336 }
00337 
00338 static void wav_close(struct ast_filestream *s)
00339 {
00340    char zero = 0;
00341    struct wav_desc *fs = (struct wav_desc *)s->_private;
00342    /* Pad to even length */
00343    if (fs->bytes & 0x1) {
00344       if (!fwrite(&zero, 1, 1, s->f)) {
00345          ast_log(LOG_WARNING, "fwrite() failed: %s\n", strerror(errno));
00346       }
00347    }
00348 }
00349 
00350 static struct ast_frame *wav_read(struct ast_filestream *s, int *whennext)
00351 {
00352    int res;
00353    int samples;   /* actual samples read */
00354    int x;
00355    short *tmp;
00356    int bytes = WAV_BUF_SIZE;  /* in bytes */
00357    off_t here;
00358    /* Send a frame from the file to the appropriate channel */
00359    struct wav_desc *fs = (struct wav_desc *)s->_private;
00360 
00361    here = ftello(s->f);
00362    if (fs->maxlen - here < bytes)      /* truncate if necessary */
00363       bytes = fs->maxlen - here;
00364    if (bytes < 0)
00365       bytes = 0;
00366 /*    ast_log(LOG_DEBUG, "here: %d, maxlen: %d, bytes: %d\n", here, s->maxlen, bytes); */
00367    s->fr.frametype = AST_FRAME_VOICE;
00368    s->fr.subclass = AST_FORMAT_SLINEAR;
00369    s->fr.mallocd = 0;
00370    AST_FRAME_SET_BUFFER(&s->fr, s->buf, AST_FRIENDLY_OFFSET, bytes);
00371    
00372    if ( (res = fread(s->fr.data, 1, s->fr.datalen, s->f)) <= 0 ) {
00373       if (res)
00374          ast_log(LOG_WARNING, "Short read (%d) (%s)!\n", res, strerror(errno));
00375       return NULL;
00376    }
00377    s->fr.datalen = res;
00378    s->fr.samples = samples = res / 2;
00379 
00380    tmp = (short *)(s->fr.data);
00381 #if __BYTE_ORDER == __BIG_ENDIAN
00382    /* file format is little endian so we need to swap */
00383    for( x = 0; x < samples; x++)
00384       tmp[x] = (tmp[x] << 8) | ((tmp[x] & 0xff00) >> 8);
00385 #endif
00386 
00387    if (fs->needsgain) {
00388       for (x=0; x < samples; x++) {
00389          if (tmp[x] & ((1 << GAIN) - 1)) {
00390             /* If it has data down low, then it's not something we've artificially increased gain
00391                on, so we don't need to gain adjust it */
00392             fs->needsgain = 0;
00393             break;
00394          }
00395       }
00396       if (fs->needsgain) {
00397          for (x=0; x < samples; x++)
00398             tmp[x] = tmp[x] >> GAIN;
00399       }
00400    }
00401          
00402    *whennext = samples;
00403    return &s->fr;
00404 }
00405 
00406 static int wav_write(struct ast_filestream *fs, struct ast_frame *f)
00407 {
00408    int x;
00409    short tmp[8000], *tmpi;
00410    float tmpf;
00411    struct wav_desc *s = (struct wav_desc *)fs->_private;
00412    int res;
00413 
00414    if (f->frametype != AST_FRAME_VOICE) {
00415       ast_log(LOG_WARNING, "Asked to write non-voice frame!\n");
00416       return -1;
00417    }
00418    if (f->subclass != AST_FORMAT_SLINEAR) {
00419       ast_log(LOG_WARNING, "Asked to write non-SLINEAR frame (%d)!\n", f->subclass);
00420       return -1;
00421    }
00422    if (f->datalen > sizeof(tmp)) {
00423       ast_log(LOG_WARNING, "Data length is too long\n");
00424       return -1;
00425    }
00426    if (!f->datalen)
00427       return -1;
00428 
00429 #if 0
00430    printf("Data Length: %d\n", f->datalen);
00431 #endif   
00432 
00433    tmpi = f->data;
00434    /* Volume adjust here to accomodate */
00435    for (x=0;x<f->datalen/2;x++) {
00436       tmpf = ((float)tmpi[x]) * ((float)(1 << GAIN));
00437       if (tmpf > 32767.0)
00438          tmpf = 32767.0;
00439       if (tmpf < -32768.0)
00440          tmpf = -32768.0;
00441       tmp[x] = tmpf;
00442       tmp[x] &= ~((1 << GAIN) - 1);
00443 
00444 #if __BYTE_ORDER == __BIG_ENDIAN
00445       tmp[x] = (tmp[x] << 8) | ((tmp[x] & 0xff00) >> 8);
00446 #endif
00447 
00448    }
00449    if ((res = fwrite(tmp, 1, f->datalen, fs->f)) != f->datalen ) {
00450       ast_log(LOG_WARNING, "Bad write (%d): %s\n", res, strerror(errno));
00451       return -1;
00452    }
00453 
00454    s->bytes += f->datalen;
00455    update_header(fs->f);
00456       
00457    return 0;
00458 
00459 }
00460 
00461 static int wav_seek(struct ast_filestream *fs, off_t sample_offset, int whence)
00462 {
00463    off_t min, max, cur, offset = 0, samples;
00464 
00465    samples = sample_offset * 2; /* SLINEAR is 16 bits mono, so sample_offset * 2 = bytes */
00466    min = 44; /* wav header is 44 bytes */
00467    cur = ftello(fs->f);
00468    fseeko(fs->f, 0, SEEK_END);
00469    max = ftello(fs->f);
00470    if (whence == SEEK_SET)
00471       offset = samples + min;
00472    else if (whence == SEEK_CUR || whence == SEEK_FORCECUR)
00473       offset = samples + cur;
00474    else if (whence == SEEK_END)
00475       offset = max - samples;
00476         if (whence != SEEK_FORCECUR) {
00477       offset = (offset > max)?max:offset;
00478    }
00479    /* always protect the header space. */
00480    offset = (offset < min)?min:offset;
00481    return fseeko(fs->f, offset, SEEK_SET);
00482 }
00483 
00484 static int wav_trunc(struct ast_filestream *fs)
00485 {
00486    if (ftruncate(fileno(fs->f), ftello(fs->f)))
00487       return -1;
00488    return update_header(fs->f);
00489 }
00490 
00491 static off_t wav_tell(struct ast_filestream *fs)
00492 {
00493    off_t offset;
00494    offset = ftello(fs->f);
00495    /* subtract header size to get samples, then divide by 2 for 16 bit samples */
00496    return (offset - 44)/2;
00497 }
00498 
00499 static const struct ast_format wav_f = {
00500    .name = "wav",
00501    .exts = "wav",
00502    .format = AST_FORMAT_SLINEAR,
00503    .open =  wav_open,
00504    .rewrite = wav_rewrite,
00505    .write = wav_write,
00506    .seek = wav_seek,
00507    .trunc = wav_trunc,
00508    .tell =  wav_tell,
00509    .read = wav_read,
00510    .close = wav_close,
00511    .buf_size = WAV_BUF_SIZE + AST_FRIENDLY_OFFSET,
00512    .desc_size = sizeof(struct wav_desc),
00513 };
00514 
00515 static int load_module(void)
00516 {
00517    return ast_format_register(&wav_f);
00518 }
00519 
00520 static int unload_module(void)
00521 {
00522    return ast_format_unregister(wav_f.name);
00523 }  
00524 
00525 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_FIRST, "Microsoft WAV format (8000Hz Signed Linear)",
00526    .load = load_module,
00527    .unload = unload_module,
00528 );

Generated on Sat Aug 6 00:39:28 2011 for Asterisk - the Open Source PBX by  doxygen 1.4.7