Mon Mar 20 08:25:42 2006

Asterisk developer's documentation


Main Page | Modules | Alphabetical List | Data Structures | Directories | File List | Data Fields | Globals | Related Pages

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 <unistd.h>
00028 #include <netinet/in.h>
00029 #include <arpa/inet.h>
00030 #include <stdlib.h>
00031 #include <sys/time.h>
00032 #include <stdio.h>
00033 #include <errno.h>
00034 #include <string.h>
00035 
00036 #include "asterisk.h"
00037 
00038 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 7221 $")
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 struct ast_filestream {
00053    void *reserved[AST_RESERVED_POINTERS];
00054    /* This is what a filestream means to us */
00055    FILE *f; /* Descriptor */
00056    int bytes;
00057    int needsgain;
00058    struct ast_frame fr;          /* Frame information */
00059    char waste[AST_FRIENDLY_OFFSET]; /* Buffer for sending frames, etc */
00060    char empty;                   /* Empty character */
00061    short buf[160];   
00062    int foffset;
00063    int lasttimeout;
00064    int maxlen;
00065    struct timeval last;
00066 };
00067 
00068 
00069 AST_MUTEX_DEFINE_STATIC(wav_lock);
00070 static int glistcnt = 0;
00071 
00072 static char *name = "wav";
00073 static char *desc = "Microsoft WAV format (8000hz Signed Linear)";
00074 static char *exts = "wav";
00075 
00076 #define BLOCKSIZE 160
00077 
00078 #define GAIN 2    /* 2^GAIN is the multiple to increase the volume by */
00079 
00080 #if __BYTE_ORDER == __LITTLE_ENDIAN
00081 #define htoll(b) (b)
00082 #define htols(b) (b)
00083 #define ltohl(b) (b)
00084 #define ltohs(b) (b)
00085 #else
00086 #if __BYTE_ORDER == __BIG_ENDIAN
00087 #define htoll(b)  \
00088           (((((b)      ) & 0xFF) << 24) | \
00089           ((((b) >>  8) & 0xFF) << 16) | \
00090          ((((b) >> 16) & 0xFF) <<  8) | \
00091          ((((b) >> 24) & 0xFF)      ))
00092 #define htols(b) \
00093           (((((b)      ) & 0xFF) << 8) | \
00094          ((((b) >> 8) & 0xFF)      ))
00095 #define ltohl(b) htoll(b)
00096 #define ltohs(b) htols(b)
00097 #else
00098 #error "Endianess not defined"
00099 #endif
00100 #endif
00101 
00102 
00103 static int check_header(FILE *f)
00104 {
00105    int type, size, formtype;
00106    int fmt, hsize;
00107    short format, chans, bysam, bisam;
00108    int bysec;
00109    int freq;
00110    int data;
00111    if (fread(&type, 1, 4, f) != 4) {
00112       ast_log(LOG_WARNING, "Read failed (type)\n");
00113       return -1;
00114    }
00115    if (fread(&size, 1, 4, f) != 4) {
00116       ast_log(LOG_WARNING, "Read failed (size)\n");
00117       return -1;
00118    }
00119    size = ltohl(size);
00120    if (fread(&formtype, 1, 4, f) != 4) {
00121       ast_log(LOG_WARNING, "Read failed (formtype)\n");
00122       return -1;
00123    }
00124    if (memcmp(&type, "RIFF", 4)) {
00125       ast_log(LOG_WARNING, "Does not begin with RIFF\n");
00126       return -1;
00127    }
00128    if (memcmp(&formtype, "WAVE", 4)) {
00129       ast_log(LOG_WARNING, "Does not contain WAVE\n");
00130       return -1;
00131    }
00132    if (fread(&fmt, 1, 4, f) != 4) {
00133       ast_log(LOG_WARNING, "Read failed (fmt)\n");
00134       return -1;
00135    }
00136    if (memcmp(&fmt, "fmt ", 4)) {
00137       ast_log(LOG_WARNING, "Does not say fmt\n");
00138       return -1;
00139    }
00140    if (fread(&hsize, 1, 4, f) != 4) {
00141       ast_log(LOG_WARNING, "Read failed (formtype)\n");
00142       return -1;
00143    }
00144    if (ltohl(hsize) < 16) {
00145       ast_log(LOG_WARNING, "Unexpected header size %d\n", ltohl(hsize));
00146       return -1;
00147    }
00148    if (fread(&format, 1, 2, f) != 2) {
00149       ast_log(LOG_WARNING, "Read failed (format)\n");
00150       return -1;
00151    }
00152    if (ltohs(format) != 1) {
00153       ast_log(LOG_WARNING, "Not a wav file %d\n", ltohs(format));
00154       return -1;
00155    }
00156    if (fread(&chans, 1, 2, f) != 2) {
00157       ast_log(LOG_WARNING, "Read failed (format)\n");
00158       return -1;
00159    }
00160    if (ltohs(chans) != 1) {
00161       ast_log(LOG_WARNING, "Not in mono %d\n", ltohs(chans));
00162       return -1;
00163    }
00164    if (fread(&freq, 1, 4, f) != 4) {
00165       ast_log(LOG_WARNING, "Read failed (freq)\n");
00166       return -1;
00167    }
00168    if (ltohl(freq) != 8000) {
00169       ast_log(LOG_WARNING, "Unexpected freqency %d\n", ltohl(freq));
00170       return -1;
00171    }
00172    /* Ignore the byte frequency */
00173    if (fread(&bysec, 1, 4, f) != 4) {
00174       ast_log(LOG_WARNING, "Read failed (BYTES_PER_SECOND)\n");
00175       return -1;
00176    }
00177    /* Check bytes per sample */
00178    if (fread(&bysam, 1, 2, f) != 2) {
00179       ast_log(LOG_WARNING, "Read failed (BYTES_PER_SAMPLE)\n");
00180       return -1;
00181    }
00182    if (ltohs(bysam) != 2) {
00183       ast_log(LOG_WARNING, "Can only handle 16bits per sample: %d\n", ltohs(bysam));
00184       return -1;
00185    }
00186    if (fread(&bisam, 1, 2, f) != 2) {
00187       ast_log(LOG_WARNING, "Read failed (Bits Per Sample): %d\n", ltohs(bisam));
00188       return -1;
00189    }
00190    /* Skip any additional header */
00191    if (fseek(f,ltohl(hsize)-16,SEEK_CUR) == -1 ) {
00192       ast_log(LOG_WARNING, "Failed to skip remaining header bytes: %d\n", ltohl(hsize)-16 );
00193       return -1;
00194    }
00195    /* Skip any facts and get the first data block */
00196    for(;;)
00197    { 
00198       char buf[4];
00199        
00200        /* Begin data chunk */
00201        if (fread(&buf, 1, 4, f) != 4) {
00202          ast_log(LOG_WARNING, "Read failed (data)\n");
00203          return -1;
00204        }
00205        /* Data has the actual length of data in it */
00206        if (fread(&data, 1, 4, f) != 4) {
00207          ast_log(LOG_WARNING, "Read failed (data)\n");
00208          return -1;
00209        }
00210        data = ltohl(data);
00211        if(memcmp(buf, "data", 4) == 0 ) 
00212          break;
00213        if(memcmp(buf, "fact", 4) != 0 ) {
00214          ast_log(LOG_WARNING, "Unknown block - not fact or data\n");
00215          return -1;
00216        }
00217        if (fseek(f,data,SEEK_CUR) == -1 ) {
00218          ast_log(LOG_WARNING, "Failed to skip fact block: %d\n", data );
00219          return -1;
00220        }
00221    }
00222 #if 0
00223    curpos = lseek(fd, 0, SEEK_CUR);
00224    truelength = lseek(fd, 0, SEEK_END);
00225    lseek(fd, curpos, SEEK_SET);
00226    truelength -= curpos;
00227 #endif   
00228    return data;
00229 }
00230 
00231 static int update_header(FILE *f)
00232 {
00233    off_t cur,end;
00234    int datalen,filelen,bytes;
00235    
00236    
00237    cur = ftell(f);
00238    fseek(f, 0, SEEK_END);
00239    end = ftell(f);
00240    /* data starts 44 bytes in */
00241    bytes = end - 44;
00242    datalen = htoll(bytes);
00243    /* chunk size is bytes of data plus 36 bytes of header */
00244    filelen = htoll(36 + bytes);
00245    
00246    if (cur < 0) {
00247       ast_log(LOG_WARNING, "Unable to find our position\n");
00248       return -1;
00249    }
00250    if (fseek(f, 4, SEEK_SET)) {
00251       ast_log(LOG_WARNING, "Unable to set our position\n");
00252       return -1;
00253    }
00254    if (fwrite(&filelen, 1, 4, f) != 4) {
00255       ast_log(LOG_WARNING, "Unable to set write file size\n");
00256       return -1;
00257    }
00258    if (fseek(f, 40, SEEK_SET)) {
00259       ast_log(LOG_WARNING, "Unable to set our position\n");
00260       return -1;
00261    }
00262    if (fwrite(&datalen, 1, 4, f) != 4) {
00263       ast_log(LOG_WARNING, "Unable to set write datalen\n");
00264       return -1;
00265    }
00266    if (fseek(f, cur, SEEK_SET)) {
00267       ast_log(LOG_WARNING, "Unable to return to position\n");
00268       return -1;
00269    }
00270    return 0;
00271 }
00272 
00273 static int write_header(FILE *f)
00274 {
00275    unsigned int hz=htoll(8000);
00276    unsigned int bhz = htoll(16000);
00277    unsigned int hs = htoll(16);
00278    unsigned short fmt = htols(1);
00279    unsigned short chans = htols(1);
00280    unsigned short bysam = htols(2);
00281    unsigned short bisam = htols(16);
00282    unsigned int size = htoll(0);
00283    /* Write a wav header, ignoring sizes which will be filled in later */
00284    fseek(f,0,SEEK_SET);
00285    if (fwrite("RIFF", 1, 4, f) != 4) {
00286       ast_log(LOG_WARNING, "Unable to write header\n");
00287       return -1;
00288    }
00289    if (fwrite(&size, 1, 4, f) != 4) {
00290       ast_log(LOG_WARNING, "Unable to write header\n");
00291       return -1;
00292    }
00293    if (fwrite("WAVEfmt ", 1, 8, f) != 8) {
00294       ast_log(LOG_WARNING, "Unable to write header\n");
00295       return -1;
00296    }
00297    if (fwrite(&hs, 1, 4, f) != 4) {
00298       ast_log(LOG_WARNING, "Unable to write header\n");
00299       return -1;
00300    }
00301    if (fwrite(&fmt, 1, 2, f) != 2) {
00302       ast_log(LOG_WARNING, "Unable to write header\n");
00303       return -1;
00304    }
00305    if (fwrite(&chans, 1, 2, f) != 2) {
00306       ast_log(LOG_WARNING, "Unable to write header\n");
00307       return -1;
00308    }
00309    if (fwrite(&hz, 1, 4, f) != 4) {
00310       ast_log(LOG_WARNING, "Unable to write header\n");
00311       return -1;
00312    }
00313    if (fwrite(&bhz, 1, 4, f) != 4) {
00314       ast_log(LOG_WARNING, "Unable to write header\n");
00315       return -1;
00316    }
00317    if (fwrite(&bysam, 1, 2, f) != 2) {
00318       ast_log(LOG_WARNING, "Unable to write header\n");
00319       return -1;
00320    }
00321    if (fwrite(&bisam, 1, 2, f) != 2) {
00322       ast_log(LOG_WARNING, "Unable to write header\n");
00323       return -1;
00324    }
00325    if (fwrite("data", 1, 4, f) != 4) {
00326       ast_log(LOG_WARNING, "Unable to write header\n");
00327       return -1;
00328    }
00329    if (fwrite(&size, 1, 4, f) != 4) {
00330       ast_log(LOG_WARNING, "Unable to write header\n");
00331       return -1;
00332    }
00333    return 0;
00334 }
00335 
00336 static struct ast_filestream *wav_open(FILE *f)
00337 {
00338    /* We don't have any header to read or anything really, but
00339       if we did, it would go here.  We also might want to check
00340       and be sure it's a valid file.  */
00341    struct ast_filestream *tmp;
00342    if ((tmp = malloc(sizeof(struct ast_filestream)))) {
00343       memset(tmp, 0, sizeof(struct ast_filestream));
00344       if ((tmp->maxlen = check_header(f)) < 0) {
00345          free(tmp);
00346          return NULL;
00347       }
00348       if (ast_mutex_lock(&wav_lock)) {
00349          ast_log(LOG_WARNING, "Unable to lock wav list\n");
00350          free(tmp);
00351          return NULL;
00352       }
00353       tmp->f = f;
00354       tmp->needsgain = 1;
00355       tmp->fr.data = tmp->buf;
00356       tmp->fr.frametype = AST_FRAME_VOICE;
00357       tmp->fr.subclass = AST_FORMAT_SLINEAR;
00358       /* datalen will vary for each frame */
00359       tmp->fr.src = name;
00360       tmp->fr.mallocd = 0;
00361       tmp->bytes = 0;
00362       glistcnt++;
00363       ast_mutex_unlock(&wav_lock);
00364       ast_update_use_count();
00365    }
00366    return tmp;
00367 }
00368 
00369 static struct ast_filestream *wav_rewrite(FILE *f, const char *comment)
00370 {
00371    /* We don't have any header to read or anything really, but
00372       if we did, it would go here.  We also might want to check
00373       and be sure it's a valid file.  */
00374    struct ast_filestream *tmp;
00375    if ((tmp = malloc(sizeof(struct ast_filestream)))) {
00376       memset(tmp, 0, sizeof(struct ast_filestream));
00377       if (write_header(f)) {
00378          free(tmp);
00379          return NULL;
00380       }
00381       if (ast_mutex_lock(&wav_lock)) {
00382          ast_log(LOG_WARNING, "Unable to lock wav list\n");
00383          free(tmp);
00384          return NULL;
00385       }
00386       tmp->f = f;
00387       glistcnt++;
00388       ast_mutex_unlock(&wav_lock);
00389       ast_update_use_count();
00390    } else
00391       ast_log(LOG_WARNING, "Out of memory\n");
00392    return tmp;
00393 }
00394 
00395 static void wav_close(struct ast_filestream *s)
00396 {
00397    char zero = 0;
00398    if (ast_mutex_lock(&wav_lock)) {
00399       ast_log(LOG_WARNING, "Unable to lock wav list\n");
00400       return;
00401    }
00402    glistcnt--;
00403    ast_mutex_unlock(&wav_lock);
00404    ast_update_use_count();
00405    /* Pad to even length */
00406    if (s->bytes & 0x1)
00407       fwrite(&zero, 1, 1, s->f);
00408    fclose(s->f);
00409    free(s);
00410    s = NULL;
00411 }
00412 
00413 static struct ast_frame *wav_read(struct ast_filestream *s, int *whennext)
00414 {
00415    int res;
00416    int delay;
00417    int x;
00418    short tmp[sizeof(s->buf) / 2];
00419    int bytes = sizeof(tmp);
00420    off_t here;
00421    /* Send a frame from the file to the appropriate channel */
00422    here = ftell(s->f);
00423    if ((s->maxlen - here) < bytes)
00424       bytes = s->maxlen - here;
00425    if (bytes < 0)
00426       bytes = 0;
00427 /*    ast_log(LOG_DEBUG, "here: %d, maxlen: %d, bytes: %d\n", here, s->maxlen, bytes); */
00428    
00429    if ( (res = fread(tmp, 1, bytes, s->f)) <= 0 ) {
00430       if (res) {
00431          ast_log(LOG_WARNING, "Short read (%d) (%s)!\n", res, strerror(errno));
00432       }
00433       return NULL;
00434    }
00435 
00436 #if __BYTE_ORDER == __BIG_ENDIAN
00437    for( x = 0; x < sizeof(tmp)/2; x++) tmp[x] = (tmp[x] << 8) | ((tmp[x] & 0xff00) >> 8);
00438 #endif
00439 
00440    if (s->needsgain) {
00441       for (x=0;x<sizeof(tmp)/2;x++)
00442          if (tmp[x] & ((1 << GAIN) - 1)) {
00443             /* If it has data down low, then it's not something we've artificially increased gain
00444                on, so we don't need to gain adjust it */
00445             s->needsgain = 0;
00446          }
00447    }
00448    if (s->needsgain) {
00449       for (x=0;x<sizeof(tmp)/2;x++) {
00450          s->buf[x] = tmp[x] >> GAIN;
00451       }
00452    } else {
00453       memcpy(s->buf, tmp, sizeof(s->buf));
00454    }
00455          
00456    delay = res / 2;
00457    s->fr.frametype = AST_FRAME_VOICE;
00458    s->fr.subclass = AST_FORMAT_SLINEAR;
00459    s->fr.offset = AST_FRIENDLY_OFFSET;
00460    s->fr.datalen = res;
00461    s->fr.data = s->buf;
00462    s->fr.mallocd = 0;
00463    s->fr.samples = delay;
00464    *whennext = delay;
00465    return &s->fr;
00466 }
00467 
00468 static int wav_write(struct ast_filestream *fs, struct ast_frame *f)
00469 {
00470    int res = 0;
00471    int x;
00472    short tmp[8000], *tmpi;
00473    float tmpf;
00474    if (f->frametype != AST_FRAME_VOICE) {
00475       ast_log(LOG_WARNING, "Asked to write non-voice frame!\n");
00476       return -1;
00477    }
00478    if (f->subclass != AST_FORMAT_SLINEAR) {
00479       ast_log(LOG_WARNING, "Asked to write non-SLINEAR frame (%d)!\n", f->subclass);
00480       return -1;
00481    }
00482    if (f->datalen > sizeof(tmp)) {
00483       ast_log(LOG_WARNING, "Data length is too long\n");
00484       return -1;
00485    }
00486    if (!f->datalen)
00487       return -1;
00488 
00489 #if 0
00490    printf("Data Length: %d\n", f->datalen);
00491 #endif   
00492 
00493    if (fs->buf) {
00494       tmpi = f->data;
00495       /* Volume adjust here to accomodate */
00496       for (x=0;x<f->datalen/2;x++) {
00497          tmpf = ((float)tmpi[x]) * ((float)(1 << GAIN));
00498          if (tmpf > 32767.0)
00499             tmpf = 32767.0;
00500          if (tmpf < -32768.0)
00501             tmpf = -32768.0;
00502          tmp[x] = tmpf;
00503          tmp[x] &= ~((1 << GAIN) - 1);
00504 
00505 #if __BYTE_ORDER == __BIG_ENDIAN
00506          tmp[x] = (tmp[x] << 8) | ((tmp[x] & 0xff00) >> 8);
00507 #endif
00508 
00509       }
00510       if ((fwrite(tmp, 1, f->datalen, fs->f) != f->datalen) ) {
00511          ast_log(LOG_WARNING, "Bad write (%d): %s\n", res, strerror(errno));
00512          return -1;
00513       }
00514    } else {
00515       ast_log(LOG_WARNING, "Cannot write data to file.\n");
00516       return -1;
00517    }
00518    
00519    fs->bytes += f->datalen;
00520    update_header(fs->f);
00521       
00522    return 0;
00523 
00524 }
00525 
00526 static int wav_seek(struct ast_filestream *fs, long sample_offset, int whence)
00527 {
00528    off_t min,max,cur;
00529    long offset=0,samples;
00530    
00531    samples = sample_offset * 2; /* SLINEAR is 16 bits mono, so sample_offset * 2 = bytes */
00532    min = 44; /* wav header is 44 bytes */
00533    cur = ftell(fs->f);
00534    fseek(fs->f, 0, SEEK_END);
00535    max = ftell(fs->f);
00536    if (whence == SEEK_SET)
00537       offset = samples + min;
00538    else if (whence == SEEK_CUR || whence == SEEK_FORCECUR)
00539       offset = samples + cur;
00540    else if (whence == SEEK_END)
00541       offset = max - samples;
00542         if (whence != SEEK_FORCECUR) {
00543       offset = (offset > max)?max:offset;
00544    }
00545    /* always protect the header space. */
00546    offset = (offset < min)?min:offset;
00547    return fseek(fs->f,offset,SEEK_SET);
00548 }
00549 
00550 static int wav_trunc(struct ast_filestream *fs)
00551 {
00552    if (ftruncate(fileno(fs->f), ftell(fs->f)))
00553       return -1;
00554    return update_header(fs->f);
00555 }
00556 
00557 static long wav_tell(struct ast_filestream *fs)
00558 {
00559    off_t offset;
00560    offset = ftell(fs->f);
00561    /* subtract header size to get samples, then divide by 2 for 16 bit samples */
00562    return (offset - 44)/2;
00563 }
00564 
00565 static char *wav_getcomment(struct ast_filestream *s)
00566 {
00567    return NULL;
00568 }
00569 
00570 int load_module()
00571 {
00572    return ast_format_register(name, exts, AST_FORMAT_SLINEAR,
00573                         wav_open,
00574                         wav_rewrite,
00575                         wav_write,
00576                         wav_seek,
00577                         wav_trunc,
00578                         wav_tell,
00579                         wav_read,
00580                         wav_close,
00581                         wav_getcomment);
00582                         
00583                         
00584 }
00585 
00586 int unload_module()
00587 {
00588    return ast_format_unregister(name);
00589 }  
00590 
00591 int usecount()
00592 {
00593    return glistcnt;
00594 }
00595 
00596 char *description()
00597 {
00598    return desc;
00599 }
00600 
00601 
00602 char *key()
00603 {
00604    return ASTERISK_GPL_KEY;
00605 }

Generated on Mon Mar 20 08:25:42 2006 for Asterisk - the Open Source PBX by  doxygen 1.3.9.1