summaryrefslogtreecommitdiff
path: root/snd/sndheader.c
diff options
context:
space:
mode:
Diffstat (limited to 'snd/sndheader.c')
-rw-r--r--snd/sndheader.c614
1 files changed, 614 insertions, 0 deletions
diff --git a/snd/sndheader.c b/snd/sndheader.c
new file mode 100644
index 0000000..2e38b14
--- /dev/null
+++ b/snd/sndheader.c
@@ -0,0 +1,614 @@
+/* sndheader.c -- nice new empty */
+
+/* functions to write or delete
+
+references from nyqsrc/sndread.c/snd_make_read()
+
+snd_seek()
+snd_bytes_per_frame()
+cvt_from_8
+cvt_from_16
+cvt_from_24
+cvt_from_32
+cvt_from_unknown
+
+
+references from nyqsrc/sndwritepa.c/find_cvt_to_fn
+
+cvt_from_8
+cvt_from_16
+cvt_from_24
+cvt_from_32
+cvt_from_unknown
+
+snd/audiooss.o: In function `audio_poll':
+snd_bytes_per_frame()
+
+snd/snd.o: In function `file_close':
+write_sndheader_finish()
+
+snd/snd.o: In function `snd_read':
+snd_bytes_per_frame()
+
+snd/snd.o: In function `snd_write':
+snd_bytes_per_frame()
+
+snd/snd.o:
+snd_open_file() in the file_dictionary snd_fns_node.
+
+
+*/
+
+
+/* sndheader.c -- low-level sound I/O
+ *
+ *
+ * Judy Hawkins
+ * Feb 2008
+ *
+ * Rewritten to use libsndfile 1.0.17.
+ *
+ * Based on sndheader.c from Nyquist 3.01, which had
+ * a long history courtesy of Jim Zelenka, CMU/ITC, 9 Jun 1992
+ * and Roger Dannenberg, CMU, Mar 1993
+ *
+ *
+ */
+
+/* Standard includes */
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "snd.h" /* probably going to modify this one */
+
+/* jlh leaving a whole bunch of includes out; as needed I'll put them
+ back in. */
+
+#ifdef LINUX
+# ifdef WIN32
+# error LINUX and WIN32 both?
+# endif
+#endif
+
+/* jlh leaving out a bunch of Macintosh specific stuff on the theory
+ that libsndfile does all that now. */
+
+/* jlh leaving out some AIFF marker and instrument structure
+ definitions on the theory that if anyone is using them I'll figure
+ out how to use libsndfile to access them, and write structures as
+ needed to support libsndfile. */
+
+/* jlh leaving out convert functions; libsndfile handles all that. */
+
+/* jlh leaving out a bunch of little bitty file and data handling
+ support functions; libsndfile... */
+
+/* Local buffer for constructing useful messages */
+#define STRBUFF_LEN 1024
+static char strBuffer [STRBUFF_LEN] ;
+
+
+/* =====
+ * lifted verbatim from sndheader.c v301
+ */
+void snd_open_fail(snd_type snd)
+{
+ /* char msg[250]; */
+
+ snd->device = SND_DEVICE_NONE;
+ snd->dictionary = &snd_none_dictionary;
+
+ /* It's not fatal if the file does not exist...
+ sprintf(msg, "snd_open: could not open file %s\n",
+ snd->u.file.filename);
+ snd_fail(msg); */
+
+ return;
+}
+
+/*=====================
+** Convert libsndfile's minor format to nyquist's SND_MODE specifier.
+*/
+
+int get_ny_mode (SF_INFO *sfinfo)
+{
+
+ int mode;
+ mode = 0;
+
+ switch (sfinfo->format & SF_FORMAT_SUBMASK )
+ {
+ case SF_FORMAT_IMA_ADPCM:
+ case SF_FORMAT_MS_ADPCM:
+ case SF_FORMAT_VOX_ADPCM:
+ case SF_FORMAT_G721_32:
+ case SF_FORMAT_G723_24:
+ case SF_FORMAT_G723_40:
+ mode = SND_MODE_ADPCM;
+ break;
+
+ case SF_FORMAT_RAW:
+ case SF_FORMAT_PCM_S8:
+ case SF_FORMAT_PCM_16:
+ case SF_FORMAT_PCM_24:
+ case SF_FORMAT_PCM_32:
+
+ case SF_FORMAT_DPCM_8: /* jlh or are these two the UPCM, see below? */
+ case SF_FORMAT_DPCM_16:
+
+ mode = SND_MODE_PCM;
+ break;
+
+ case SF_FORMAT_ULAW:
+ mode = SND_MODE_ULAW;
+ break;
+
+ case SF_FORMAT_ALAW:
+ mode = SND_MODE_ALAW;
+ break;
+
+ case SF_FORMAT_FLOAT:
+ mode = SND_MODE_FLOAT;
+ break;
+
+ /* jlh libsndfile doesn't do UPCM? or is that the same as DPCM,
+ see above? And what about the special case in the
+ sndheader.c-orig, about how 8 bit PCM WAV files are actually
+ UPCM? JLH */
+
+ case SF_FORMAT_PCM_U8: /* jlh looks more like UPCM than anything else */
+ mode = SND_MODE_UPCM;
+ break;
+
+ default:
+#ifdef DEBUG_LSF
+ printf(" at the default case in get_ny_mode -- check mask...\n");
+#endif
+ mode = SND_MODE_UNKNOWN;
+ break;
+ }
+
+ return mode;
+}
+
+/*==============
+ * Convert the libsndfile extension to what nyquist needs.
+ */
+
+int get_ny_head ( SF_INFO *sfinfo )
+{
+
+ int head;
+ head = 0;
+ /* this would already have been put in by snd_open_fail if the file
+ open failed: SND_HEAD_NONE */
+
+ switch (sfinfo->format & SF_FORMAT_TYPEMASK)
+ {
+ case SF_FORMAT_AIFF:
+ head = SND_HEAD_AIFF;
+ break;
+
+ case SF_FORMAT_IRCAM:
+ head = SND_HEAD_IRCAM;
+ break;
+
+ case SF_FORMAT_AU:
+ head = SND_HEAD_NEXT;
+ break;
+
+ case SF_FORMAT_WAV:
+ head = SND_HEAD_WAVE;
+ break;
+
+ default:
+ head = SND_HEAD_NONE;
+ break;
+ }
+
+ return head;
+}
+
+
+int get_ny_bits(SF_INFO *sfinfo)
+{
+
+ int bits;
+ bits = 0;
+
+ /* does libsdnfile give me any help with this? making a lot of
+ guesses I'll have to verify later... jlh*/
+
+ switch (sfinfo->format & SF_FORMAT_SUBMASK )
+ {
+ /* 8 bit */
+ case SF_FORMAT_PCM_S8:
+ case SF_FORMAT_DPCM_8:
+ case SF_FORMAT_PCM_U8:
+ case SF_FORMAT_RAW:
+ bits = 8;
+ break;
+
+ /* 16 bit */
+ case SF_FORMAT_IMA_ADPCM:
+ case SF_FORMAT_MS_ADPCM:
+ case SF_FORMAT_VOX_ADPCM:
+ case SF_FORMAT_PCM_16:
+ case SF_FORMAT_DPCM_16:
+ bits = 16;
+ break;
+
+ /* 24 bit */
+ case SF_FORMAT_G723_24:
+ case SF_FORMAT_PCM_24:
+ bits = 24;
+ break;
+
+ /* 32 bit */
+ case SF_FORMAT_G721_32:
+ case SF_FORMAT_PCM_32:
+ bits = 32;
+ break;
+
+ /* 40 bit */
+ case SF_FORMAT_G723_40:
+ bits = 40;
+ break;
+
+ /* wild guess at 4 bytes = 32 bits */
+ case SF_FORMAT_ULAW:
+ case SF_FORMAT_ALAW:
+ case SF_FORMAT_FLOAT:
+ bits = 32;
+ break;
+
+
+
+ default:
+#ifdef DEBUG_LSF
+ printf(" at the default case in get_ny_bits -- check mask...\n");
+#endif
+ bits = 8;
+ break;
+ }
+ return bits;
+}
+
+
+/*==================
+ * Fill in the SF_INFO structure for opening a file for write or read/write.
+ *
+ * I am attempting to make this do double duty for SND_WRITE and
+ * SND_OVERWRITE. Wish me luck.
+ */
+
+int make_sfinfo(snd_type snd)
+{
+ int format;
+ int subformat;
+
+ subformat = format = 0;
+
+ /* Translate nyquist/snd header, mode and bits into libsndfile values. */
+
+ switch(snd->u.file.header)
+ {
+ case SND_HEAD_AIFF:
+ format = SF_FORMAT_AIFF;
+ break;
+
+ case SND_HEAD_IRCAM:
+ format = SF_FORMAT_IRCAM;
+ break;
+
+ case SND_HEAD_NEXT:
+ format = SF_FORMAT_AU;
+ break;
+
+ case SND_HEAD_WAVE:
+ format = SF_FORMAT_WAV;
+ break;
+
+ default:
+ format = SF_FORMAT_WAV;
+ break;
+ }
+
+ switch(snd->format.mode)
+ {
+ case SND_MODE_ADPCM:
+ subformat = SF_FORMAT_IMA_ADPCM;
+ break;
+
+ case SND_MODE_PCM:
+ switch(snd->format.bits)
+ {
+ case 8:
+ subformat = SF_FORMAT_PCM_S8;
+ break;
+
+ case 16:
+ subformat = SF_FORMAT_PCM_16;
+ break;
+
+ case 24:
+ subformat = SF_FORMAT_PCM_24;
+ break;
+
+ case 32:
+ subformat = SF_FORMAT_PCM_32;
+ break;
+
+ default:
+ subformat = SF_FORMAT_PCM_16;
+ break;
+ }
+ break;
+
+ case SND_MODE_ULAW:
+ subformat = SF_FORMAT_ULAW;
+ break;
+
+ case SND_MODE_ALAW:
+ subformat = SF_FORMAT_ALAW;
+ break;
+
+ case SND_MODE_FLOAT:
+ subformat = SF_FORMAT_FLOAT;
+ break;
+
+ case SND_MODE_UPCM:
+ subformat = SF_FORMAT_PCM_U8;
+ break;
+
+ default:
+
+ /* I think for simplicity in this here not very limited first
+ cut, I'll go for a default of WAV, 16 bit PCM. I could get a
+ lot more complicated, but that can happen later. jlh.*/
+
+ format = SF_FORMAT_WAV;
+ subformat = SF_FORMAT_PCM_16;
+ break;
+ }
+
+ snd->u.file.sfinfo.format = format | subformat;
+
+ if (snd->format.srate > 0)
+ snd->u.file.sfinfo.samplerate = snd->format.srate;
+ else
+ snd->u.file.sfinfo.samplerate = 41000;
+
+ if (snd->format.channels > 0)
+ snd->u.file.sfinfo.channels = snd->format.channels;
+ else
+ snd->u.file.sfinfo.channels = 1;
+
+
+ snd->u.file.sfinfo.frames = 0;
+
+ return sf_format_check(&snd->u.file.sfinfo);
+
+ }
+
+/*==============================================================
+ * The beating heart of this libsndfile project.
+ *
+ * Code from the original sndheader.c, v301, is mostly gone; I tried
+ * to keep error handling the same.
+ *
+ * Bits and pieces from libsndfile examples/
+ */
+
+int snd_open_file ( snd_type snd, long *flags)
+{
+
+
+ SNDFILE *sffile;
+
+ (*flags) = 0; /* No file has successfully opened; at the end, when
+ all checks have passed, flags will get set to
+ values that say the file open worked and fields
+ have valid values. */
+ if (snd->write_flag == SND_READ)
+ {
+ snd->u.file.loop_info = FALSE;
+
+ /* jlh -- somewhere previous to here I have to make sure format
+ is 0, except if this is a RAW file, in which case I have to
+ set samplerate, channels and format fields. */
+
+ snd->u.file.sfinfo.format = 0;
+ /* if raw... */
+
+ if ((sffile = sf_open (snd->u.file.filename, SFM_READ, &snd->u.file.sfinfo)) == NULL)
+ {
+ printf ("Error : Not able to open input file %s.\n",
+ snd->u.file.filename) ;
+ fflush (stdout) ;
+ memset (strBuffer, 0, sizeof (strBuffer)) ;
+ sf_command (sffile, SFC_GET_LOG_INFO, strBuffer, STRBUFF_LEN) ;
+ puts (strBuffer) ;
+ puts (sf_strerror (NULL)) ;
+ snd_open_fail(snd);
+ return SND_FILE_FAILURE;
+ }
+
+ /* snd_read_header OUTTA DA WINDOW!!! (sound of splintering code
+ fragments) jlh */
+
+ /* jlh -- instead, I have put sfinfo into snd. Now I will make
+ sure it is a good happy little structure. */
+
+ if ((sf_format_check(&snd->u.file.sfinfo)) != TRUE)
+ {
+ printf ("Error : sfinfo not ok on file open: %s.\n",
+ snd->u.file.filename) ;
+ fflush (stdout) ;
+ memset (strBuffer, 0, sizeof (strBuffer)) ;
+ sf_command (sffile, SFC_GET_LOG_INFO, strBuffer, STRBUFF_LEN) ;
+ puts (strBuffer) ;
+ puts (sf_strerror (NULL)) ;
+ snd_open_fail(snd);
+ return SND_FILE_FAILURE;
+ }
+
+ /* YEAH. cooking with hydrogen. */
+
+ /* Fill out the nyquist format structure. */
+ snd->format.mode = get_ny_mode(&snd->u.file.sfinfo);
+ snd->format.channels = snd->u.file.sfinfo.channels;
+ snd->format.srate = snd->u.file.sfinfo.samplerate;
+ snd->format.bits = get_ny_bits(&snd->u.file.sfinfo);
+
+ /* nyquist header value, used in lisp code (I believe. jlh )*/
+ snd->u.file.header = get_ny_head(&snd->u.file.sfinfo);
+
+ } /* if (snd->write_flag == SND_READ) */
+
+ else if (snd->write_flag == SND_WRITE)
+ {
+ if (snd->u.file.header < 0 || snd->u.file.header >= SND_NUM_HEADS)
+ {
+ sprintf(strBuffer,
+ "snd_open_file: can't write file %s: invalid snd header value\n",
+ snd->u.file.filename);
+ snd_fail (strBuffer);
+ snd_open_fail(snd);
+ return !SND_SUCCESS;
+ }
+
+ /* Writing a new file: clear the libsndfile structures for action */
+ memset (&snd->u.file.sfinfo, 0, sizeof(snd->u.file.sfinfo));
+ snd->u.file.sffile = NULL;
+
+ if ( make_sfinfo(snd) == SF_FALSE )
+ {
+ sprintf(strBuffer,
+ "snd_open_file: Header information invalid for writing %s\n",
+ snd->u.file.filename);
+ snd_fail (strBuffer);
+ snd_open_fail(snd);
+ return !SND_SUCCESS;
+ }
+
+ /* Headers and info structures all filled out; now create the
+ file */
+
+ if ((sffile = sf_open (snd->u.file.filename,
+ SFM_WRITE,
+ &snd->u.file.sfinfo)) == NULL)
+ {
+ sprintf(strBuffer,
+ "snd_open_file: failed to open \"%s\" for write;\nLIBSNDFILE error: %s\n",
+ snd->u.file.filename,
+ sf_strerror(sffile));
+ snd_fail (strBuffer);
+ snd_open_fail(snd);
+ return !SND_SUCCESS;
+ }
+
+ snd->u.file.file = 0; /* Ok, this is going to make for noise,
+ but I do want to know when .file is
+ being accessed so I can fix it. jlh */
+ snd->u.file.sffile = sffile; /* prob redundant... but I may
+ get rid of file.file. jlh */
+
+ /* JLH gee... is this all I need to do? that was easy. */
+
+ } /* if snd->write_flag == SND_WRITE */
+
+ else if (snd->write_flag == SND_OVERWRITE)
+ {
+
+ if (snd->u.file.header < 0 || snd->u.file.header >= SND_NUM_HEADS)
+ {
+ sprintf(strBuffer,
+ "snd_open_file: can't write file %s: invalid snd header value\n",
+ snd->u.file.filename);
+ snd_fail (strBuffer);
+ snd_open_fail(snd);
+ return !SND_SUCCESS;
+ }
+
+ /* If sfinfo is valid, keep it; if not, make a new one. */
+ if (sf_format_check(&snd->u.file.sfinfo) == SF_FALSE)
+ {
+ memset (&snd->u.file.sfinfo, 0, sizeof(snd->u.file.sfinfo));
+ snd->u.file.sffile = NULL;
+
+ if ( make_sfinfo(snd) == SF_FALSE )
+ {
+ sprintf(strBuffer,
+ "snd_open_file: Header information invalid for writing %s\n",
+ snd->u.file.filename);
+ snd_fail (strBuffer);
+ snd_open_fail(snd);
+ return !SND_SUCCESS;
+ }
+ }
+
+ /* Headers and info structures all filled out; now create the
+ file */
+
+ if ((sffile = sf_open (snd->u.file.filename,
+ SFM_RDWR,
+ &snd->u.file.sfinfo)) == NULL)
+ {
+ sprintf(strBuffer,
+ "snd_open_file: failed to open \"%s\" for read/write;\nLIBSNDFILE error: %s\n",
+ snd->u.file.filename,
+ sf_strerror(sffile));
+ snd_fail (strBuffer);
+ snd_open_fail(snd);
+ return !SND_SUCCESS;
+ }
+
+ /* Don't need to find the beginning of the data, libsndfile
+ handles that. */
+ }
+ /* JLH I'm not doing anything with swap, I'm assuming libsndfile
+ handles all that. */
+
+ snd->u.file.file = 0; /* jlh and fix problems as they occur */
+ snd->u.file.sffile = sffile; /* prob redundant... but I may
+ get rid of file.file. jlh */
+
+ /* jlh The file must have opened successfully; make flags reflect that,
+ using old nyquist/snd values; lots of things use these??? */
+ (*flags) = SND_HEAD_SRATE | SND_HEAD_CHANNELS | SND_HEAD_BITS | SND_HEAD_MODE | SND_HEAD_LEN;
+
+ return SND_SUCCESS;
+}
+
+/*==================
+ * used once, in sndread.c
+ *
+ */
+/* FIX -- need to test this. I think offset is in seconds, so this
+ function is not going to be compatible with previous one. */
+ int snd_seek(snd_type snd, double offset)
+ {
+ sf_count_t frames;
+ sf_count_t tsk;
+ int whence;
+
+ frames = offset; /* Once again, the question of what units offset
+ is in.... I'm going to assume frames until
+ proven wrong. jlh */
+
+ whence = (int)snd->u.file.current_offset; /* jlh and what if.... */
+
+ tsk = sf_seek( snd->u.file.sffile, frames, whence);
+
+ if (tsk < 0)
+ {
+ return !SND_SUCCESS;
+ }
+ else
+ {
+ snd->u.file.current_offset = (long)tsk;
+ }
+ return SND_SUCCESS;
+ }