| /*************************************************************************/ |
| /* */ |
| /* Language Technologies Institute */ |
| /* Carnegie Mellon University */ |
| /* Copyright (c) 2000 */ |
| /* All Rights Reserved. */ |
| /* */ |
| /* Permission is hereby granted, free of charge, to use and distribute */ |
| /* this software and its documentation without restriction, including */ |
| /* without limitation the rights to use, copy, modify, merge, publish, */ |
| /* distribute, sublicense, and/or sell copies of this work, and to */ |
| /* permit persons to whom this work is furnished to do so, subject to */ |
| /* the following conditions: */ |
| /* 1. The code must retain the above copyright notice, this list of */ |
| /* conditions and the following disclaimer. */ |
| /* 2. Any modifications must be clearly marked as such. */ |
| /* 3. Original authors' names are not deleted. */ |
| /* 4. The authors' names are not used to endorse or promote products */ |
| /* derived from this software without specific prior written */ |
| /* permission. */ |
| /* */ |
| /* CARNEGIE MELLON UNIVERSITY AND THE CONTRIBUTORS TO THIS WORK */ |
| /* DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING */ |
| /* ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT */ |
| /* SHALL CARNEGIE MELLON UNIVERSITY NOR THE CONTRIBUTORS BE LIABLE */ |
| /* FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES */ |
| /* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN */ |
| /* AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, */ |
| /* ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF */ |
| /* THIS SOFTWARE. */ |
| /* */ |
| /*************************************************************************/ |
| /* Author: Alan W Black (awb@cs.cmu.edu) */ |
| /* Date: August 2000 */ |
| /*************************************************************************/ |
| /* */ |
| /* Waveforms */ |
| /* */ |
| /*************************************************************************/ |
| #include "cst_string.h" |
| #include "cst_wave.h" |
| #include "cst_file.h" |
| |
| void cst_wave_resample(cst_wave *w, int sample_rate) |
| { |
| /* This is here so that it won't necessarily be linked in tight-space */ |
| /* platforms like PalmOS */ |
| |
| cst_rateconv *filt; |
| int up, down; |
| short *in; |
| const short *inptr; |
| short *outptr; |
| int n, insize, outsize; |
| |
| /* Sure, we could take the GCD. In practice, though, this |
| gives us what we want and makes things go faster. */ |
| down = w->sample_rate / 1000; |
| up = sample_rate / 1000; |
| |
| if (up < 1 || down < 1) { |
| cst_errmsg("cst_wave_resample: invalid input/output sample rates (%d, %d)\n", |
| up * 1000, down * 1000); |
| cst_error(); |
| } |
| |
| filt = new_rateconv(up, down, w->num_channels); |
| |
| inptr = in = w->samples; |
| insize = w->num_samples; |
| |
| w->num_samples = w->num_samples * up / down + 2048; |
| w->samples = cst_alloc(short, w->num_samples * w->num_channels); |
| w->sample_rate = sample_rate; |
| |
| outptr = w->samples; |
| outsize = w->num_samples; |
| |
| while ((n = cst_rateconv_in(filt, inptr, insize)) > 0) { |
| inptr += n; |
| insize -= n; |
| |
| while ((n = cst_rateconv_out(filt, outptr, outsize)) > 0) { |
| outptr += n; |
| outsize -= n; |
| } |
| } |
| cst_rateconv_leadout(filt); |
| while ((n = cst_rateconv_out(filt, outptr, outsize)) > 0) { |
| outptr += n; |
| outsize -= n; |
| } |
| |
| cst_free(in); |
| delete_rateconv(filt); |
| |
| } |
| |
| int cst_wave_save(cst_wave *w,const char *filename,const char *type) |
| { |
| if (cst_streq(type,"riff")) |
| return cst_wave_save_riff(w,filename); |
| /* else if (cst_streq(type,"aiff")) |
| return cst_wave_save_aiff(w,filename); |
| else if (cst_streq(type,"snd")) |
| return cst_wave_save_snd(w,filename); */ |
| else if (cst_streq(type,"raw")) |
| return cst_wave_save_raw(w,filename); |
| else |
| { |
| cst_errmsg("cst_wave_save: unsupported wavetype \"%s\"\n", |
| type); |
| return -1; |
| } |
| } |
| |
| int cst_wave_save_raw(cst_wave *w, const char *filename) |
| { |
| cst_file fd; |
| int rv; |
| |
| if ((fd = cst_fopen(filename,CST_OPEN_WRITE|CST_OPEN_BINARY)) == NULL) |
| { |
| cst_errmsg("cst_wave_save: can't open file \"%s\"\n", |
| filename); |
| return -1; |
| } |
| |
| rv = cst_wave_save_raw_fd(w, fd); |
| cst_fclose(fd); |
| |
| return rv; |
| } |
| |
| int cst_wave_save_raw_fd(cst_wave *w, cst_file fd) |
| { |
| if (cst_fwrite(fd, cst_wave_samples(w), |
| sizeof(short), cst_wave_num_samples(w)) == cst_wave_num_samples(w)) |
| return 0; |
| else |
| return -1; |
| } |
| |
| |
| int cst_wave_append_riff(cst_wave *w,const char *filename) |
| { |
| /* Appends to wave in file if it already exists */ |
| cst_file fd; |
| cst_wave_header hdr; |
| char info[4]; |
| int d_int; |
| int rv, num_bytes, n, sample_rate; |
| |
| if ((fd = cst_fopen(filename,CST_OPEN_WRITE|CST_OPEN_READ|CST_OPEN_BINARY)) == NULL) |
| { |
| cst_errmsg("cst_wave_append: can't open file \"%s\"\n", |
| filename); |
| return -1; |
| } |
| |
| rv = cst_wave_load_riff_header(&hdr,fd); |
| if (rv != CST_OK_FORMAT) |
| { |
| cst_fclose(fd); |
| return rv; |
| } |
| /* We will assume this is one of my riff files so it has *ONE* data part */ |
| cst_fread(fd,info,1,4); |
| cst_fread(fd,&d_int,4,1); |
| if (CST_BIG_ENDIAN) d_int = SWAPINT(d_int); |
| hdr.num_samples = d_int/sizeof(short); |
| |
| cst_fseek(fd, |
| cst_ftell(fd)+(hdr.hsize-16)+ |
| (hdr.num_samples*hdr.num_channels*sizeof(short)), |
| CST_SEEK_ABSOLUTE); |
| |
| if (CST_BIG_ENDIAN) |
| { |
| short *xdata = cst_alloc(short,cst_wave_num_channels(w)* |
| cst_wave_num_samples(w)); |
| memmove(xdata,cst_wave_samples(w), |
| sizeof(short)*cst_wave_num_channels(w)* |
| cst_wave_num_samples(w)); |
| swap_bytes_short(xdata, |
| cst_wave_num_channels(w)* |
| cst_wave_num_samples(w)); |
| n = cst_fwrite(fd,xdata,sizeof(short), |
| cst_wave_num_channels(w)*cst_wave_num_samples(w)); |
| cst_free(xdata); |
| } |
| else |
| { |
| n = cst_fwrite(fd,cst_wave_samples(w),sizeof(short), |
| cst_wave_num_channels(w)*cst_wave_num_samples(w)); |
| } |
| |
| cst_fseek(fd,4,CST_SEEK_ABSOLUTE); |
| num_bytes = hdr.num_bytes + (n*sizeof(short)); |
| if (CST_BIG_ENDIAN) num_bytes = SWAPINT(num_bytes); |
| cst_fwrite(fd,&num_bytes,4,1); /* num bytes in whole file */ |
| cst_fseek(fd,4+4+4+4 +4+2+2 ,CST_SEEK_ABSOLUTE); |
| sample_rate = w->sample_rate; |
| if (CST_BIG_ENDIAN) sample_rate = SWAPINT(sample_rate); |
| cst_fwrite(fd,&sample_rate,4,1); /* sample rate */ |
| cst_fseek(fd,4+4+4+4+4+2+2+4+4+2+2+4,CST_SEEK_ABSOLUTE); |
| num_bytes = |
| (sizeof(short) * cst_wave_num_channels(w) * cst_wave_num_samples(w)) + |
| (sizeof(short) * hdr.num_channels * hdr.num_samples); |
| if (CST_BIG_ENDIAN) num_bytes = SWAPINT(num_bytes); |
| cst_fwrite(fd,&num_bytes,4,1); /* num bytes in data */ |
| cst_fclose(fd); |
| |
| return rv; |
| } |
| |
| int cst_wave_save_riff(cst_wave *w,const char *filename) |
| { |
| cst_file fd; |
| int rv; |
| |
| if ((fd = cst_fopen(filename,CST_OPEN_WRITE|CST_OPEN_BINARY)) == NULL) |
| { |
| cst_errmsg("cst_wave_save: can't open file \"%s\"\n", |
| filename); |
| return -1; |
| } |
| |
| rv = cst_wave_save_riff_fd(w, fd); |
| cst_fclose(fd); |
| |
| return rv; |
| } |
| |
| int cst_wave_save_riff_fd(cst_wave *w, cst_file fd) |
| { |
| const char *info; |
| short d_short; |
| int d_int, n; |
| int num_bytes; |
| |
| info = "RIFF"; |
| cst_fwrite(fd,info,4,1); |
| num_bytes = (cst_wave_num_samples(w) |
| * cst_wave_num_channels(w) |
| * sizeof(short)) + 8 + 16 + 12; |
| if (CST_BIG_ENDIAN) num_bytes = SWAPINT(num_bytes); |
| cst_fwrite(fd,&num_bytes,4,1); /* num bytes in whole file */ |
| info = "WAVE"; |
| cst_fwrite(fd,info,1,4); |
| info = "fmt "; |
| cst_fwrite(fd,info,1,4); |
| num_bytes = 16; /* size of header */ |
| if (CST_BIG_ENDIAN) num_bytes = SWAPINT(num_bytes); |
| cst_fwrite(fd,&num_bytes,4,1); |
| d_short = RIFF_FORMAT_PCM; /* sample type */ |
| if (CST_BIG_ENDIAN) d_short = SWAPSHORT(d_short); |
| cst_fwrite(fd,&d_short,2,1); |
| d_short = cst_wave_num_channels(w); /* number of channels */ |
| if (CST_BIG_ENDIAN) d_short = SWAPSHORT(d_short); |
| cst_fwrite(fd,&d_short,2,1); |
| d_int = cst_wave_sample_rate(w); /* sample rate */ |
| if (CST_BIG_ENDIAN) d_int = SWAPINT(d_int); |
| cst_fwrite(fd,&d_int,4,1); |
| d_int = (cst_wave_sample_rate(w) |
| * cst_wave_num_channels(w) |
| * sizeof(short)); /* average bytes per second */ |
| if (CST_BIG_ENDIAN) d_int = SWAPINT(d_int); |
| cst_fwrite(fd,&d_int,4,1); |
| d_short = (cst_wave_num_channels(w) |
| * sizeof(short)); /* block align */ |
| if (CST_BIG_ENDIAN) d_short = SWAPSHORT(d_short); |
| cst_fwrite(fd,&d_short,2,1); |
| d_short = 2 * 8; /* bits per sample */ |
| if (CST_BIG_ENDIAN) d_short = SWAPSHORT(d_short); |
| cst_fwrite(fd,&d_short,2,1); |
| info = "data"; |
| cst_fwrite(fd,info,1,4); |
| d_int = (cst_wave_num_channels(w) |
| * cst_wave_num_samples(w) |
| * sizeof(short)); /* bytes in data */ |
| if (CST_BIG_ENDIAN) d_int = SWAPINT(d_int); |
| cst_fwrite(fd,&d_int,4,1); |
| |
| if (CST_BIG_ENDIAN) |
| { |
| short *xdata = cst_alloc(short,cst_wave_num_channels(w)* |
| cst_wave_num_samples(w)); |
| memmove(xdata,cst_wave_samples(w), |
| sizeof(short)*cst_wave_num_channels(w)* |
| cst_wave_num_samples(w)); |
| swap_bytes_short(xdata, |
| cst_wave_num_channels(w)* |
| cst_wave_num_samples(w)); |
| n = cst_fwrite(fd,xdata,sizeof(short), |
| cst_wave_num_channels(w)*cst_wave_num_samples(w)); |
| cst_free(xdata); |
| } |
| else |
| { |
| n = cst_fwrite(fd,cst_wave_samples(w),sizeof(short), |
| cst_wave_num_channels(w)*cst_wave_num_samples(w)); |
| } |
| |
| if (n != cst_wave_num_channels(w)*cst_wave_num_samples(w)) |
| return -1; |
| else |
| return 0; |
| |
| } |
| |
| int cst_wave_load_raw(cst_wave *w,const char *filename, |
| const char *bo, int sample_rate) |
| { |
| cst_file fd; |
| int r; |
| |
| if ((fd = cst_fopen(filename,CST_OPEN_READ|CST_OPEN_BINARY)) == NULL) |
| { |
| cst_errmsg("cst_wave_load: can't open file \"%s\"\n", |
| filename); |
| return -1; |
| } |
| |
| r = cst_wave_load_raw_fd(w, fd, bo, sample_rate); |
| |
| cst_fclose(fd); |
| |
| return r; |
| } |
| |
| int cst_wave_load_raw_fd(cst_wave *w, cst_file fd, |
| const char *bo, int sample_rate) |
| { |
| long size; |
| |
| /* Won't work on pipes, tough luck... */ |
| size = cst_filesize(fd) / sizeof(short); |
| cst_wave_resize(w, size, 1); |
| if (cst_fread(fd, w->samples, sizeof(short), size) != size) |
| return -1; |
| |
| w->sample_rate = sample_rate; |
| if (bo) /* if it's NULL we don't care */ |
| if ((CST_LITTLE_ENDIAN && cst_streq(bo, BYTE_ORDER_BIG)) |
| || (CST_BIG_ENDIAN && cst_streq(bo, BYTE_ORDER_LITTLE))) |
| swap_bytes_short(w->samples,w->num_samples); |
| |
| return 0; |
| } |
| |
| int cst_wave_load_riff(cst_wave *w,const char *filename) |
| { |
| cst_file fd; |
| int r; |
| |
| if ((fd = cst_fopen(filename,CST_OPEN_READ|CST_OPEN_BINARY)) == NULL) |
| { |
| cst_errmsg("cst_wave_load: can't open file \"%s\"\n", |
| filename); |
| return -1; |
| } |
| |
| r = cst_wave_load_riff_fd(w,fd); |
| |
| cst_fclose(fd); |
| |
| return r; |
| } |
| |
| int cst_wave_load_riff_header(cst_wave_header *header,cst_file fd) |
| { |
| char info[4]; |
| short d_short; |
| int d_int; |
| |
| if (cst_fread(fd,info,1,4) != 4) |
| return CST_WRONG_FORMAT; |
| else if (strncmp(info,"RIFF",4) != 0) |
| return CST_WRONG_FORMAT; |
| |
| cst_fread(fd,&d_int,4,1); |
| if (CST_BIG_ENDIAN) d_int = SWAPINT(d_int); |
| header->num_bytes = d_int; |
| |
| if ((cst_fread(fd,info,1,4) != 4) || |
| (strncmp(info,"WAVE",4) != 0)) |
| return CST_ERROR_FORMAT; |
| |
| if ((cst_fread(fd,info,1,4) != 4) || |
| (strncmp(info,"fmt ",4) != 0)) |
| return CST_ERROR_FORMAT; |
| |
| cst_fread(fd,&d_int,4,1); |
| if (CST_BIG_ENDIAN) d_int = SWAPINT(d_int); |
| header->hsize = d_int; |
| cst_fread(fd,&d_short,2,1); |
| if (CST_BIG_ENDIAN) d_short = SWAPSHORT(d_short); |
| |
| if (d_short != RIFF_FORMAT_PCM) |
| { |
| cst_errmsg("cst_load_wave_riff: unsupported sample format\n"); |
| return CST_ERROR_FORMAT; |
| } |
| cst_fread(fd,&d_short,2,1); |
| if (CST_BIG_ENDIAN) d_short = SWAPSHORT(d_short); |
| |
| header->num_channels = d_short; |
| |
| cst_fread(fd,&d_int,4,1); |
| if (CST_BIG_ENDIAN) d_int = SWAPINT(d_int); |
| header->sample_rate = d_int; |
| cst_fread(fd,&d_int,4,1); /* avg bytes per second */ |
| cst_fread(fd,&d_short,2,1); /* block align */ |
| cst_fread(fd,&d_short,2,1); /* bits per sample */ |
| |
| return CST_OK_FORMAT; |
| } |
| |
| int cst_wave_load_riff_fd(cst_wave *w,cst_file fd) |
| { |
| cst_wave_header hdr; |
| int rv; |
| char info[4]; |
| int d_int, d; |
| int data_length; |
| int samples; |
| |
| rv = cst_wave_load_riff_header(&hdr,fd); |
| if (rv != CST_OK_FORMAT) |
| return rv; |
| |
| cst_fseek(fd,cst_ftell(fd)+(hdr.hsize-16),CST_SEEK_ABSOLUTE); /* skip rest of header */ |
| |
| /* Note there's a bunch of potential random headers */ |
| while (1) |
| { |
| if (cst_fread(fd,info,1,4) != 4) |
| return CST_ERROR_FORMAT; |
| if (strncmp(info,"data",4) == 0) |
| { |
| cst_fread(fd,&d_int,4,1); |
| if (CST_BIG_ENDIAN) d_int = SWAPINT(d_int); |
| samples = d_int/sizeof(short); |
| break; |
| } |
| else if (strncmp(info,"fact",4) == 0) |
| { |
| cst_fread(fd,&d_int,4,1); |
| if (CST_BIG_ENDIAN) d_int = SWAPINT(d_int); |
| cst_fseek(fd,cst_ftell(fd)+d_int,CST_SEEK_ABSOLUTE); |
| } |
| else if (strncmp(info,"clm ",4) == 0) |
| { /* another random chunk type -- resample puts this one in */ |
| cst_fread(fd,&d_int,4,1); |
| if (CST_BIG_ENDIAN) d_int = SWAPINT(d_int); |
| cst_fseek(fd,cst_ftell(fd)+d_int,CST_SEEK_ABSOLUTE); |
| } |
| else |
| { |
| cst_errmsg("cst_wave_load_riff: unsupported chunk type \"%*s\"\n", |
| 4,info); |
| return CST_ERROR_FORMAT; |
| } |
| } |
| |
| /* Now read the data itself */ |
| cst_wave_set_sample_rate(w,hdr.sample_rate); /* sample rate */ |
| data_length = samples; |
| cst_wave_resize(w,samples/hdr.num_channels,hdr.num_channels); |
| |
| if ((d = cst_fread(fd,w->samples,sizeof(short),data_length)) != data_length) |
| { |
| cst_errmsg("cst_wave_load_riff: %d missing samples, resized accordingly\n", |
| data_length-d); |
| w->num_samples = d; |
| } |
| |
| if (CST_BIG_ENDIAN) |
| swap_bytes_short(w->samples,w->num_samples); |
| |
| return CST_OK_FORMAT; |
| } |