📄 pc_wav.c
字号:
/* * This material contains unpublished, proprietary software of * Entropic Research Laboratory, Inc. Any reproduction, distribution, * or publication of this work must be authorized in writing by Entropic * Research Laboratory, Inc., and must bear the notice: * * "Copyright (c) 1998 Entropic Research Laboratory, Inc. * All rights reserved." * * The copyright notice above does not evidence any actual or intended * publication of this source code. * * Written by: Rodney Johnson * Checked by: * Revised by: * * Brief description: Functions for use by ESPS I/O routines in dealing * with sampled-data files in RIFF WAVE format. * See "Multimedia Programming Interface and Data Specifications 1.0", * IBM Corp. and Microsoft Corp., 1991, * http://www.users.lith.com/~matts/riffmci/riffmci.rtf. See also * http://www.users.lith.com/~matts/riffnew/riffnew.htm. */static char *sccs_id = "@(#)pc_wav.c 1.1 5/1/98 ERL";#include <esps/esps.h>#include <esps/fea.h>#include <esps/unix.h>#include <esps/pc_wav.h>#define WAVE_FORMAT_PCM (0x0001)long get_genhd_val_l(char *, struct header *, long); /* See genhd.c */extern int EspsDebug; /* set in headers.c for header debugging */int PcWavPrefixSize = 12; /* "RIFF", <size>, "WAVE": 4 bytes each *//* * LOCAL TYPEDEFS AND STRUCTURES */typedef struct { unsigned short BitsPerSample;} PCM_specific;struct pc_wav_hd { unsigned short FormatTag; unsigned short Channels; unsigned long SamplesPerSec; unsigned long AvgBytesPerSec; unsigned short BlockAlign; union { PCM_specific pcm; } spec; unsigned long data_len;};/* * LOCAL FUNCTION DECLARATIONS */static pc_wav_hd *get_hd(char *prefix, int num_read, FILE *file);static int get_ck_hd(char *ckID, unsigned long *ckSize, char *end_pref, int *num_read, FILE *file);static int skip_ck(unsigned long ckSize, char *end_pref, int *num_read, FILE *file);static int get_lsbf_ushort(unsigned short *item, char *end_pref, int *num_read, FILE *file);static int get_lsbf_ulong(unsigned long *item, char *end_pref, int *num_read, FILE *file);static int next_char(char *end_pref, int *num_read, FILE *file);static long read_lsbf_short(short *ptr, long len, FILE *file);static void error_msg(char *func_name, char *msg);/* * PUBLIC FUNCTION DEFINITIONS *//* * Check whether the characters in "prefix" look like the beginning of * a PC WAVE file: the four characters "RIFF" followed by four bytes of * binary data and the four characters "WAVE". Return TRUE if so and * FALSE otherwise. */intpc_wav_check_prefix(char *prefix){ return (prefix != NULL && strncmp(prefix, "RIFF", 4) == 0 && strncmp(prefix + 8, "WAVE", 4) == 0);}/* * Read a PC WAVE "header" and construct a FEA_SD header that can be * passed to ESPS data input routines to allow them to read the PC WAVE * data samples. * * Assume "prefix" contains "num_read" bytes read from the beginning of * file "file" and that "file" is positioned for reading the following * characters. If the catenation of "prefix" with the remainder of * "file" begins with a PC WAVE header, this function reads in the rest * of the header material, creates a corresponding ESPS FEA_SD header, * and leaves the file positioned at the beginning of the data, ready for * reading of the binary samples that follow. * * In more detail, this means reading the "RIFF" chunk ID, the * four-byte chunk size, and the form type "WAVE"; skipping any * irrelevant chunks; extracting the information from the "fmt" chunk; * and reading the initial chunk ID "data" and the four-byte chunk size * of the data chunk. No "LIST" data chunks are supported, only a simple * "data" chunk, and the format must be 16-bit PCM. Multiple channels * are allowed, however. * * The items in the "fmt" chunk are saved in generic header items in the * FEA_SD header. They are also saved in a "pc_wav_hd" structure, and a * pointer to the structure is saves as a long in the generic header item * "pc_wav_hd_ptr". The generic header items "start_time" and * "record_freq" are initialized respectively to zero and to the value of * the WAVE "fmt" item "SamplesPerSec". * * The return value is a pointer to the FEA_SD header, or NULL in case * of error. * * This function is intended for use when it has been necessary to read * a number of characters at the beginning of a file to determine whether * it is in PC WAVE format or some other (such as FEA, Esignal, or NIST). * On a seekable input stream, it would be possible to simply rewind and * read all the header information directly from the file; however, the * function is intended to work on pipes. */struct header *pc_wav_to_feasd(char *prefix, int num_read, FILE *file){ pc_wav_hd *wav_hd; double start_time; double sf; struct header *ohd; wav_hd = get_hd(prefix, num_read, file); if (wav_hd == NULL) { error_msg("pc_wav_to_feasd", "read header failed."); return NULL; } switch (wav_hd->FormatTag) { case WAVE_FORMAT_PCM: if (wav_hd->spec.pcm.BitsPerSample != 16) { error_msg("pc_wav_to_feasd", "only 16-bit audio supported."); return NULL; } break; default: error_msg("pc_wav_to_feasd", "only WAVE_FORMAT_PCM supported."); return NULL; } ohd = new_header(FT_FEA); if (ohd == NULL) { error_msg("pc_wav_to_feasd", "couldn't create FEA header."); return NULL; } strcpy(ohd->common.prog, "pc_wav_to_feasd"); add_comment(ohd, "Converted from PC WAVE header\n"); ohd->common.tag = NO; ohd->common.edr = NO; start_time = 0.0; sf = (double) wav_hd->SamplesPerSec; init_feasd_hd(ohd, SHORT, (int) wav_hd->Channels, &start_time, NO, sf); *add_genhd_l("FormatTag", (long *) NULL, 1, ohd) = (long) wav_hd->FormatTag; *add_genhd_l("Channels", (long *) NULL, 1, ohd) = (long) wav_hd->Channels; *add_genhd_l("SamplesPerSec", (long *) NULL, 1, ohd) = (long) wav_hd->SamplesPerSec; *add_genhd_l("AvgBytesPerSec", (long *) NULL, 1, ohd) = (long) wav_hd->AvgBytesPerSec; *add_genhd_l("BlockAlign", (long *) NULL, 1, ohd) = (long) wav_hd->BlockAlign; *add_genhd_l("BitsPerSample", (long *) NULL, 1, ohd) = (long) wav_hd->spec.pcm.BitsPerSample; *add_genhd_l("pc_wav_hd_ptr", (long *) NULL, 1, ohd) = (long) wav_hd; return ohd;}/* * From a FEA_SD header structure created "pc_wav_to_feasd", recover a * pointer to the original PC WAVE header information, stored by * "pc_wav_to_feasd" in the generic header item "pc_wav_hd_ptr". */pc_wav_hd *get_pc_wav_hdr(struct header *hdr){ long gen; if (hdr == NULL) return NULL; else { gen = get_genhd_val_l("pc_wav_hd_ptr", hdr, 0L); if (gen == 0) return NULL; else return (pc_wav_hd *) gen; }}/* * Obtain the record size (in bytes) from PC WAVE header data stored in a * "pc_wav_hd" structure. */longpc_wav_rec_size(pc_wav_hd *wav_hd){ if (wav_hd == NULL) { error_msg("pc_wav_rec_size", "PC WAVE header pointer is NULL."); return -1; } switch (wav_hd->FormatTag) { case WAVE_FORMAT_PCM: return wav_hd->BlockAlign; break; default: error_msg("pc_wav_rec_size", "only WAVE_FORMAT_PCM supported."); return -1; }}/* * Advance the file position in a PC WAVE input file by a given number * of records, given a pointer to a "pc_wav_hd" structure describing the * file data format. */voidpc_wav_skip_recs(FILE *file, long nrec, pc_wav_hd *wav_hd){ long size; if (wav_hd == NULL) { error_msg("pc_wav_skip_recs", "PC WAVE header pointer is NULL."); return; } if (nrec == 0) return; size = pc_wav_rec_size(wav_hd); if (size > 0) { skiprec(file, nrec, (int) size); } else { error_msg("pc_wav_skip_recs", "bad record size"); }}/* * Read one "record" (i.e. a sample, possibly multi-channel) from a * PC WAVE file into a FEA record structure, given a "pc_wav_hd" * structure describing the file data format and an ESPS FEA header * structure describing the FEA record format. */intpc_wav_get_rec(struct fea_data *rec, pc_wav_hd *wav_hd, struct header *fea_hd, FILE *file){ long num_read; if (rec == NULL || wav_hd == NULL || fea_hd == NULL || file == NULL) { error_msg("pc_wav_get_rec", "NULL argument."); return EOF; } /*!*//* ASSUME: format is WAVE_FORMAT_PCM; BitsPerSample is 16; * data type of field "samples" is SHORT. */ num_read = read_lsbf_short(rec->s_data, (long) wav_hd->Channels, file); return (num_read == wav_hd->Channels) ? 1 : EOF;}/* * Read a given number of records from a PC WAVE file into a data * buffer (as shorts) given a "pc_wav_hd" structure describing the * format of the data in the file. */longpc_wav_getsd_recs(char *buffer, long num_records, pc_wav_hd *wav_hd, FILE *file){ if (buffer == NULL || wav_hd == NULL || file == NULL) { error_msg("pc_wav_getsd_recs", "NULL arguments."); return 0; } if (num_records == 0) return 0; /*!*//* ASSUME: format is WAVE_FORMAT_PCM; BitsPerSample is 16; * data type of field "samples" is SHORT. */ return read_lsbf_short((short *) buffer, num_records * wav_hd->Channels, file);}/* * LOCAL FUNCTION DEFINITIONS *//* * Function used by "pc_wav_to_feasd" to "read" PC WAVE "header" * information and create a pc_wav_hd structure containing it. The * return value is a pointer to the structure, or NULL in case of error. * * The arguments are as for "pc_wav_to_feasd": the string "prefix" * contains a number "num_read" of bytes previously read from "file", so * that the complete PC WAVE file consists of the contents of "prefix" * followed by the remaining bytes in "file". This function, and * functions used by it, keep track of progress through "prefix" by means * of a fixed pointer "end_pref" to the _end_ of the sequence of pre-read * bytes, and by decrementing "num_read". When that count reaches zero, * the functions switch over from obtaining characters from "prefix" to * reading characters from "file". Data-reading functions such as * "pc_wav_get_rec" and "pc_wav_getsd_recs" are not expected to go * through such contortions, so this function returns an error indication * if "num_read" has not become zero by the time the beginning of the * data samples has been reached. * * It is assumed that "prefix" contains at least the initial chunk ID * "RIFF", the RIFF chunk size, and the form type "WAVE", and that these * have been checked by "pc_wav_check_prefix". Within the RIFF WAVE * chunk, any chunks with chunk IDs other than "fmt " or "data" are * skipped, and a "fmt " chunk is required before the "data" chunk. The * function reads the items FormatTag, Channels, SamplesPerSec, * AvgBytesPerSec, and BlockAlign and for WAVE_FORMAT_PCM (the only * format currently supported) the format-specific item BitsPerSample. * It stores these items in the pc_wav_hd structure. It exits after * reading the chunk ID and size of the "data" chunk. */static pc_wav_hd *get_hd(char *prefix, int num_read, FILE *file){ char *end_pref; pc_wav_hd *wav_hd; int got_ck_hd; char ckID[4]; unsigned long ckSize; end_pref = prefix + num_read; num_read -= PcWavPrefixSize; /* skip "RIFF", <size>, "WAVE" */ spsassert(num_read >= 0, "get_hd: error reading PC WAVE header"); wav_hd = malloc(sizeof(pc_wav_hd)); if (wav_hd == NULL)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -