diff options
Diffstat (limited to 'libaudiofile')
84 files changed, 20754 insertions, 0 deletions
diff --git a/libaudiofile/Makefile.am b/libaudiofile/Makefile.am new file mode 100644 index 0000000..dc20efb --- /dev/null +++ b/libaudiofile/Makefile.am @@ -0,0 +1,41 @@ +## Process this file with automake to produce Makefile.in + +SUBDIRS = modules + +lib_LTLIBRARIES = libaudiofile.la + +EXTRA_DIST = audiofile.exports + +libaudiofile_la_SOURCES = \ + openclose.c setup.c format.c data.c pcm.c \ + error.c byteorder.c af_vfs.c \ + util.c debug.c aupv.c units.c compression.c \ + aes.c instrument.c loop.c marker.c misc.c track.c query.c \ + raw.c raw.h \ + aiff.c aiffwrite.c extended.c aiff.h \ + next.c nextwrite.c next.h \ + wave.c wavewrite.c wave.h \ + ircam.c ircamwrite.c ircam.h \ + avr.c avrwrite.c avr.h \ + iff.c iffwrite.c iff.h \ + nist.c nistwrite.c nist.h \ + g711.c g711.h \ + afinternal.h aupvinternal.h aupvlist.h byteorder.h \ + compression.h error.h extended.h instrument.h marker.h \ + pcm.h setup.h track.h units.h \ + print.h util.h debug.h \ + modules.c modules.h + +libaudiofile_la_LIBADD = modules/libmodules.la + +libaudiofile_la_LDFLAGS = -version-info 0:2:0 -no-undefined \ + -export-symbols audiofile.exports + +include_HEADERS = audiofile.h aupvlist.h af_vfs.h + +# GNU gcc +# AM_CFLAGS = -Wall -g +# SGI MIPSpro cc +# AM_CFLAGS = -fullwarn -g +# No debugging. +AM_CFLAGS = -DNDEBUG diff --git a/libaudiofile/Makefile.in b/libaudiofile/Makefile.in new file mode 100644 index 0000000..8faf2db --- /dev/null +++ b/libaudiofile/Makefile.in @@ -0,0 +1,536 @@ +# Makefile.in generated automatically by automake 1.5 from Makefile.am. + +# Copyright 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001 +# Free Software Foundation, Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +SHELL = @SHELL@ + +srcdir = @srcdir@ +top_srcdir = @top_srcdir@ +VPATH = @srcdir@ +prefix = @prefix@ +exec_prefix = @exec_prefix@ + +bindir = @bindir@ +sbindir = @sbindir@ +libexecdir = @libexecdir@ +datadir = @datadir@ +sysconfdir = @sysconfdir@ +sharedstatedir = @sharedstatedir@ +localstatedir = @localstatedir@ +libdir = @libdir@ +infodir = @infodir@ +mandir = @mandir@ +includedir = @includedir@ +oldincludedir = /usr/include +pkgdatadir = $(datadir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +top_builddir = .. + +ACLOCAL = @ACLOCAL@ +AUTOCONF = @AUTOCONF@ +AUTOMAKE = @AUTOMAKE@ +AUTOHEADER = @AUTOHEADER@ + +INSTALL = @INSTALL@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_HEADER = $(INSTALL_DATA) +transform = @program_transform_name@ +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +host_alias = @host_alias@ +host_triplet = @host@ +AMTAR = @AMTAR@ +AS = @AS@ +AUDIOFILE_MAJOR_VERSION = @AUDIOFILE_MAJOR_VERSION@ +AUDIOFILE_MICRO_VERSION = @AUDIOFILE_MICRO_VERSION@ +AUDIOFILE_MINOR_VERSION = @AUDIOFILE_MINOR_VERSION@ +AUDIOFILE_VERSION = @AUDIOFILE_VERSION@ +AUDIOFILE_VERSION_INFO = @AUDIOFILE_VERSION_INFO@ +AUDIO_LIB = @AUDIO_LIB@ +AWK = @AWK@ +CC = @CC@ +DEPDIR = @DEPDIR@ +DLLTOOL = @DLLTOOL@ +ECHO = @ECHO@ +EXEEXT = @EXEEXT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +LIBTOOL = @LIBTOOL@ +LN_S = @LN_S@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +PACKAGE = @PACKAGE@ +RANLIB = @RANLIB@ +STRIP = @STRIP@ +TEST_BIN = @TEST_BIN@ +VERSION = @VERSION@ +am__include = @am__include@ +am__quote = @am__quote@ +install_sh = @install_sh@ + +SUBDIRS = modules + +lib_LTLIBRARIES = libaudiofile.la + +EXTRA_DIST = audiofile.exports + +libaudiofile_la_SOURCES = \ + openclose.c setup.c format.c data.c pcm.c \ + error.c byteorder.c af_vfs.c \ + util.c debug.c aupv.c units.c compression.c \ + aes.c instrument.c loop.c marker.c misc.c track.c query.c \ + raw.c raw.h \ + aiff.c aiffwrite.c extended.c aiff.h \ + next.c nextwrite.c next.h \ + wave.c wavewrite.c wave.h \ + ircam.c ircamwrite.c ircam.h \ + avr.c avrwrite.c avr.h \ + iff.c iffwrite.c iff.h \ + nist.c nistwrite.c nist.h \ + g711.c g711.h \ + afinternal.h aupvinternal.h aupvlist.h byteorder.h \ + compression.h error.h extended.h instrument.h marker.h \ + pcm.h setup.h track.h units.h \ + print.h util.h debug.h \ + modules.c modules.h + + +libaudiofile_la_LIBADD = modules/libmodules.la + +libaudiofile_la_LDFLAGS = -version-info 0:2:0 -no-undefined \ + -export-symbols audiofile.exports + + +include_HEADERS = audiofile.h aupvlist.h af_vfs.h + +# GNU gcc +# AM_CFLAGS = -Wall -g +# SGI MIPSpro cc +# AM_CFLAGS = -fullwarn -g +# No debugging. +AM_CFLAGS = -DNDEBUG +subdir = libaudiofile +mkinstalldirs = $(SHELL) $(top_srcdir)/mkinstalldirs +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +LTLIBRARIES = $(lib_LTLIBRARIES) + +libaudiofile_la_DEPENDENCIES = modules/libmodules.la +am_libaudiofile_la_OBJECTS = openclose.lo setup.lo format.lo data.lo \ + pcm.lo error.lo byteorder.lo af_vfs.lo util.lo debug.lo aupv.lo \ + units.lo compression.lo aes.lo instrument.lo loop.lo marker.lo \ + misc.lo track.lo query.lo raw.lo aiff.lo aiffwrite.lo \ + extended.lo next.lo nextwrite.lo wave.lo wavewrite.lo ircam.lo \ + ircamwrite.lo avr.lo avrwrite.lo iff.lo iffwrite.lo nist.lo \ + nistwrite.lo g711.lo modules.lo +libaudiofile_la_OBJECTS = $(am_libaudiofile_la_OBJECTS) + +DEFS = @DEFS@ +DEFAULT_INCLUDES = -I. -I$(srcdir) -I$(top_builddir) +CPPFLAGS = @CPPFLAGS@ +LDFLAGS = @LDFLAGS@ +LIBS = @LIBS@ +depcomp = $(SHELL) $(top_srcdir)/depcomp +@AMDEP_TRUE@DEP_FILES = $(DEPDIR)/aes.Plo $(DEPDIR)/af_vfs.Plo \ +@AMDEP_TRUE@ $(DEPDIR)/aiff.Plo $(DEPDIR)/aiffwrite.Plo \ +@AMDEP_TRUE@ $(DEPDIR)/aupv.Plo $(DEPDIR)/avr.Plo \ +@AMDEP_TRUE@ $(DEPDIR)/avrwrite.Plo $(DEPDIR)/byteorder.Plo \ +@AMDEP_TRUE@ $(DEPDIR)/compression.Plo $(DEPDIR)/data.Plo \ +@AMDEP_TRUE@ $(DEPDIR)/debug.Plo $(DEPDIR)/error.Plo \ +@AMDEP_TRUE@ $(DEPDIR)/extended.Plo $(DEPDIR)/format.Plo \ +@AMDEP_TRUE@ $(DEPDIR)/g711.Plo $(DEPDIR)/iff.Plo \ +@AMDEP_TRUE@ $(DEPDIR)/iffwrite.Plo $(DEPDIR)/instrument.Plo \ +@AMDEP_TRUE@ $(DEPDIR)/ircam.Plo $(DEPDIR)/ircamwrite.Plo \ +@AMDEP_TRUE@ $(DEPDIR)/loop.Plo $(DEPDIR)/marker.Plo \ +@AMDEP_TRUE@ $(DEPDIR)/misc.Plo $(DEPDIR)/modules.Plo \ +@AMDEP_TRUE@ $(DEPDIR)/next.Plo $(DEPDIR)/nextwrite.Plo \ +@AMDEP_TRUE@ $(DEPDIR)/nist.Plo $(DEPDIR)/nistwrite.Plo \ +@AMDEP_TRUE@ $(DEPDIR)/openclose.Plo $(DEPDIR)/pcm.Plo \ +@AMDEP_TRUE@ $(DEPDIR)/query.Plo $(DEPDIR)/raw.Plo \ +@AMDEP_TRUE@ $(DEPDIR)/setup.Plo $(DEPDIR)/track.Plo \ +@AMDEP_TRUE@ $(DEPDIR)/units.Plo $(DEPDIR)/util.Plo \ +@AMDEP_TRUE@ $(DEPDIR)/wave.Plo $(DEPDIR)/wavewrite.Plo +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +LTCOMPILE = $(LIBTOOL) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) \ + $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +CCLD = $(CC) +LINK = $(LIBTOOL) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(AM_LDFLAGS) $(LDFLAGS) -o $@ +CFLAGS = @CFLAGS@ +DIST_SOURCES = $(libaudiofile_la_SOURCES) +HEADERS = $(include_HEADERS) + + +RECURSIVE_TARGETS = info-recursive dvi-recursive install-info-recursive \ + uninstall-info-recursive all-recursive install-data-recursive \ + install-exec-recursive installdirs-recursive install-recursive \ + uninstall-recursive check-recursive installcheck-recursive +DIST_COMMON = $(include_HEADERS) Makefile.am Makefile.in +DIST_SUBDIRS = $(SUBDIRS) +SOURCES = $(libaudiofile_la_SOURCES) + +all: all-recursive + +.SUFFIXES: +.SUFFIXES: .c .lo .o .obj + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs + +distclean-libtool: + -rm -f libtool +$(srcdir)/Makefile.in: Makefile.am $(top_srcdir)/configure.in $(ACLOCAL_M4) + cd $(top_srcdir) && \ + $(AUTOMAKE) --gnu libaudiofile/Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + cd $(top_builddir) && \ + CONFIG_HEADERS= CONFIG_LINKS= \ + CONFIG_FILES=$(subdir)/$@ $(SHELL) ./config.status +install-libLTLIBRARIES: $(lib_LTLIBRARIES) + @$(NORMAL_INSTALL) + $(mkinstalldirs) $(DESTDIR)$(libdir) + @list='$(lib_LTLIBRARIES)'; for p in $$list; do \ + if test -f $$p; then \ + echo " $(LIBTOOL) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$p $(DESTDIR)$(libdir)/$$p"; \ + $(LIBTOOL) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$p $(DESTDIR)$(libdir)/$$p; \ + else :; fi; \ + done + +uninstall-libLTLIBRARIES: + @$(NORMAL_UNINSTALL) + @list='$(lib_LTLIBRARIES)'; for p in $$list; do \ + echo " $(LIBTOOL) --mode=uninstall rm -f $(DESTDIR)$(libdir)/$$p"; \ + $(LIBTOOL) --mode=uninstall rm -f $(DESTDIR)$(libdir)/$$p; \ + done + +clean-libLTLIBRARIES: + -test -z "$(lib_LTLIBRARIES)" || rm -f $(lib_LTLIBRARIES) +libaudiofile.la: $(libaudiofile_la_OBJECTS) $(libaudiofile_la_DEPENDENCIES) + $(LINK) -rpath $(libdir) $(libaudiofile_la_LDFLAGS) $(libaudiofile_la_OBJECTS) $(libaudiofile_la_LIBADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) core *.core + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/aes.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/af_vfs.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/aiff.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/aiffwrite.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/aupv.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/avr.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/avrwrite.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/byteorder.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/compression.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/data.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/debug.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/error.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/extended.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/format.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/g711.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/iff.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/iffwrite.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/instrument.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/ircam.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/ircamwrite.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/loop.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/marker.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/misc.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/modules.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/next.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/nextwrite.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/nist.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/nistwrite.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/openclose.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/pcm.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/query.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/raw.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/setup.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/track.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/units.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/util.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/wave.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/wavewrite.Plo@am__quote@ + +distclean-depend: + -rm -rf $(DEPDIR) + +.c.o: +@AMDEP_TRUE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@ depfile='$(DEPDIR)/$*.Po' tmpdepfile='$(DEPDIR)/$*.TPo' @AMDEPBACKSLASH@ +@AMDEP_TRUE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ + $(COMPILE) -c `test -f $< || echo '$(srcdir)/'`$< + +.c.obj: +@AMDEP_TRUE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@ depfile='$(DEPDIR)/$*.Po' tmpdepfile='$(DEPDIR)/$*.TPo' @AMDEPBACKSLASH@ +@AMDEP_TRUE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ + $(COMPILE) -c `cygpath -w $<` + +.c.lo: +@AMDEP_TRUE@ source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@ depfile='$(DEPDIR)/$*.Plo' tmpdepfile='$(DEPDIR)/$*.TPlo' @AMDEPBACKSLASH@ +@AMDEP_TRUE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ + $(LTCOMPILE) -c -o $@ `test -f $< || echo '$(srcdir)/'`$< +CCDEPMODE = @CCDEPMODE@ +uninstall-info-am: +install-includeHEADERS: $(include_HEADERS) + @$(NORMAL_INSTALL) + $(mkinstalldirs) $(DESTDIR)$(includedir) + @list='$(include_HEADERS)'; for p in $$list; do \ + if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ + f="`echo $$p | sed -e 's|^.*/||'`"; \ + echo " $(INSTALL_HEADER) $$d$$p $(DESTDIR)$(includedir)/$$f"; \ + $(INSTALL_HEADER) $$d$$p $(DESTDIR)$(includedir)/$$f; \ + done + +uninstall-includeHEADERS: + @$(NORMAL_UNINSTALL) + @list='$(include_HEADERS)'; for p in $$list; do \ + f="`echo $$p | sed -e 's|^.*/||'`"; \ + echo " rm -f $(DESTDIR)$(includedir)/$$f"; \ + rm -f $(DESTDIR)$(includedir)/$$f; \ + done + +# This directory's subdirectories are mostly independent; you can cd +# into them and run `make' without going through this Makefile. +# To change the values of `make' variables: instead of editing Makefiles, +# (1) if the variable is set in `config.status', edit `config.status' +# (which will cause the Makefiles to be regenerated when you run `make'); +# (2) otherwise, pass the desired values on the `make' command line. +$(RECURSIVE_TARGETS): + @set fnord $(MAKEFLAGS); amf=$$2; \ + dot_seen=no; \ + target=`echo $@ | sed s/-recursive//`; \ + list='$(SUBDIRS)'; for subdir in $$list; do \ + echo "Making $$target in $$subdir"; \ + if test "$$subdir" = "."; then \ + dot_seen=yes; \ + local_target="$$target-am"; \ + else \ + local_target="$$target"; \ + fi; \ + (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ + || case "$$amf" in *=*) exit 1;; *k*) fail=yes;; *) exit 1;; esac; \ + done; \ + if test "$$dot_seen" = "no"; then \ + $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \ + fi; test -z "$$fail" + +mostlyclean-recursive clean-recursive distclean-recursive \ +maintainer-clean-recursive: + @set fnord $(MAKEFLAGS); amf=$$2; \ + dot_seen=no; \ + case "$@" in \ + distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \ + *) list='$(SUBDIRS)' ;; \ + esac; \ + rev=''; for subdir in $$list; do \ + if test "$$subdir" = "."; then :; else \ + rev="$$subdir $$rev"; \ + fi; \ + done; \ + rev="$$rev ."; \ + target=`echo $@ | sed s/-recursive//`; \ + for subdir in $$rev; do \ + echo "Making $$target in $$subdir"; \ + if test "$$subdir" = "."; then \ + local_target="$$target-am"; \ + else \ + local_target="$$target"; \ + fi; \ + (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ + || case "$$amf" in *=*) exit 1;; *k*) fail=yes;; *) exit 1;; esac; \ + done && test -z "$$fail" +tags-recursive: + list='$(SUBDIRS)'; for subdir in $$list; do \ + test "$$subdir" = . || (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) tags); \ + done + +tags: TAGS + +ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) + list='$(SOURCES) $(HEADERS) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + mkid -fID $$unique $(LISP) + +TAGS: tags-recursive $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + tags=; \ + here=`pwd`; \ + list='$(SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + test -f $$subdir/TAGS && tags="$$tags -i $$here/$$subdir/TAGS"; \ + fi; \ + done; \ + list='$(SOURCES) $(HEADERS) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + test -z "$(ETAGS_ARGS)$$unique$(LISP)$$tags" \ + || etags $(ETAGS_ARGS) $$tags $$unique $(LISP) + +GTAGS: + here=`CDPATH=: && cd $(top_builddir) && pwd` \ + && cd $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) $$here + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH + +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) + +top_distdir = .. +distdir = $(top_distdir)/$(PACKAGE)-$(VERSION) + +distdir: $(DISTFILES) + @for file in $(DISTFILES); do \ + if test -f $$file; then d=.; else d=$(srcdir); fi; \ + dir=`echo "$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test "$$dir" != "$$file" && test "$$dir" != "."; then \ + $(mkinstalldirs) "$(distdir)/$$dir"; \ + fi; \ + if test -d $$d/$$file; then \ + cp -pR $$d/$$file $(distdir) \ + || exit 1; \ + else \ + test -f $(distdir)/$$file \ + || cp -p $$d/$$file $(distdir)/$$file \ + || exit 1; \ + fi; \ + done + for subdir in $(SUBDIRS); do \ + if test "$$subdir" = .; then :; else \ + test -d $(distdir)/$$subdir \ + || mkdir $(distdir)/$$subdir \ + || exit 1; \ + (cd $$subdir && \ + $(MAKE) $(AM_MAKEFLAGS) \ + top_distdir="$(top_distdir)" \ + distdir=../$(distdir)/$$subdir \ + distdir) \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-recursive +all-am: Makefile $(LTLIBRARIES) $(HEADERS) +installdirs: installdirs-recursive +installdirs-am: + $(mkinstalldirs) $(DESTDIR)$(libdir) $(DESTDIR)$(includedir) + +install: install-recursive +install-exec: install-exec-recursive +install-data: install-data-recursive +uninstall: uninstall-recursive + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-recursive +install-strip: + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + `test -z '$(STRIP)' || \ + echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -rm -f Makefile $(CONFIG_CLEAN_FILES) stamp-h stamp-h[0-9]* + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-recursive + +clean-am: clean-generic clean-libLTLIBRARIES clean-libtool \ + mostlyclean-am + +distclean: distclean-recursive + +distclean-am: clean-am distclean-compile distclean-depend \ + distclean-generic distclean-libtool distclean-tags + +dvi: dvi-recursive + +dvi-am: + +info: info-recursive + +info-am: + +install-data-am: install-includeHEADERS + +install-exec-am: install-libLTLIBRARIES + +install-info: install-info-recursive + +install-man: + +installcheck-am: + +maintainer-clean: maintainer-clean-recursive + +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-recursive + +mostlyclean-am: mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool + +uninstall-am: uninstall-includeHEADERS uninstall-info-am \ + uninstall-libLTLIBRARIES + +uninstall-info: uninstall-info-recursive + +.PHONY: $(RECURSIVE_TARGETS) GTAGS all all-am check check-am clean \ + clean-generic clean-libLTLIBRARIES clean-libtool \ + clean-recursive distclean distclean-compile distclean-depend \ + distclean-generic distclean-libtool distclean-recursive \ + distclean-tags distdir dvi dvi-am dvi-recursive info info-am \ + info-recursive install install-am install-data install-data-am \ + install-data-recursive install-exec install-exec-am \ + install-exec-recursive install-includeHEADERS install-info \ + install-info-am install-info-recursive install-libLTLIBRARIES \ + install-man install-recursive install-strip installcheck \ + installcheck-am installdirs installdirs-am \ + installdirs-recursive maintainer-clean maintainer-clean-generic \ + maintainer-clean-recursive mostlyclean mostlyclean-compile \ + mostlyclean-generic mostlyclean-libtool mostlyclean-recursive \ + tags tags-recursive uninstall uninstall-am \ + uninstall-includeHEADERS uninstall-info-am \ + uninstall-info-recursive uninstall-libLTLIBRARIES \ + uninstall-recursive + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/libaudiofile/aes.c b/libaudiofile/aes.c new file mode 100644 index 0000000..c6b09a8 --- /dev/null +++ b/libaudiofile/aes.c @@ -0,0 +1,108 @@ +/* + Audio File Library + Copyright (C) 1998-1999, Michael Pruett <michael@68k.org> + Copyright (C) 2000, Silicon Graphics, Inc. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307 USA. +*/ + +/* + aes.c + + This file contains routines for dealing with AES recording data. +*/ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <string.h> +#include <assert.h> + +#include "audiofile.h" +#include "afinternal.h" +#include "util.h" + +void afInitAESChannelData (AFfilesetup setup, int trackid) +{ + _TrackSetup *track; + + if (!_af_filesetup_ok(setup)) + return; + + if ((track = _af_filesetup_get_tracksetup(setup, trackid)) == NULL) + return; + + track->aesDataSet = AF_TRUE; +} + +void afInitAESChannelDataTo (AFfilesetup setup, int trackid, int willBeData) +{ + _TrackSetup *track; + + if (!_af_filesetup_ok(setup)) + return; + + if ((track = _af_filesetup_get_tracksetup(setup, trackid)) == NULL) + return; + + track->aesDataSet = willBeData; +} + +/* + What is with these return values? +*/ +int afGetAESChannelData (AFfilehandle file, int trackid, unsigned char buf[24]) +{ + _Track *track; + + if ((track = _af_filehandle_get_track(file, trackid)) == NULL) + return -1; + + if (track->hasAESData == AF_FALSE) + { + if (buf) + memset(buf, 0, 24); + return 0; + } + + if (buf) + memcpy(buf, track->aesData, 24); + + return 1; +} + +void afSetAESChannelData (AFfilehandle file, int trackid, unsigned char buf[24]) +{ + _Track *track; + + if ((track = _af_filehandle_get_track(file, trackid)) == NULL) + return; + + if (!_af_filehandle_can_write(file)) + return; + + if (track->hasAESData) + { + memcpy(track->aesData, buf, 24); + } + else + { + _af_error(AF_BAD_NOAESDATA, + "unable to store AES channel status data for track %d", + trackid); + } +} diff --git a/libaudiofile/af_vfs.c b/libaudiofile/af_vfs.c new file mode 100644 index 0000000..2c32d8f --- /dev/null +++ b/libaudiofile/af_vfs.c @@ -0,0 +1,190 @@ +/* + Audio File Library + Copyright (C) 1999, Elliot Lee <sopwith@redhat.com> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307 USA. +*/ + +/* + af_vfs.c + + Virtual file operations for the Audio File Library. +*/ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include "afinternal.h" +#include "af_vfs.h" + +#include <stdlib.h> + +AFvirtualfile * +af_virtual_file_new(void) +{ + return (AFvirtualfile *) calloc(sizeof (AFvirtualfile), 1); +} + +void +af_virtual_file_destroy(AFvirtualfile *vfile) +{ + vfile->destroy(vfile); + + free(vfile); +} + +size_t af_fread (void *data, size_t size, size_t nmemb, AFvirtualfile *vfile) +{ + if (size == 0 || nmemb == 0) + return 0; + + if (vfile->read) { + int retval; + + retval = (* vfile->read) (vfile, data, size * nmemb); + + return retval/size; + } else + return 0; +} + +size_t af_fwrite (const void *data, size_t size, size_t nmemb, + AFvirtualfile *vfile) +{ + if (size == 0 || nmemb == 0) + return 0; + + if (vfile->write) { + int retval; + + retval = (* vfile->write) (vfile, data, size * nmemb); + + return retval/size; + } else + return 0; +} + +int +af_fclose(AFvirtualfile *vfile) +{ + af_virtual_file_destroy(vfile); + + return 0; +} + +long +af_flength(AFvirtualfile *vfile) +{ + if(vfile->length) + return (* vfile->length)(vfile); + else + return 0; +} + +int +af_fseek(AFvirtualfile *vfile, long offset, int whence) +{ + if(whence == SEEK_CUR) + (* vfile->seek) (vfile, offset, 1); + else if(whence == SEEK_SET) + (* vfile->seek) (vfile, offset, 0); + else + return -1; + + return 0; +} + +long +af_ftell(AFvirtualfile *vfile) +{ + if(vfile->tell) + return (* vfile->tell)(vfile); + else + return 0; +} + +static ssize_t af_file_read (AFvirtualfile *vfile, void *data, size_t nbytes); +static long af_file_length (AFvirtualfile *vfile); +static ssize_t af_file_write (AFvirtualfile *vfile, const void *data, + size_t nbytes); +static void af_file_destroy(AFvirtualfile *vfile); +static long af_file_seek(AFvirtualfile *vfile, long offset, int is_relative); +static long af_file_tell(AFvirtualfile *vfile); + +AFvirtualfile * +af_virtual_file_new_for_file(FILE *fh) +{ + AFvirtualfile *vf; + + if(!fh) + return NULL; + + vf = af_virtual_file_new(); + vf->closure = fh; + vf->read = af_file_read; + vf->write = af_file_write; + vf->length = af_file_length; + vf->destroy = af_file_destroy; + vf->seek = af_file_seek; + vf->tell = af_file_tell; + + return vf; +} + +static ssize_t af_file_read(AFvirtualfile *vfile, void *data, size_t nbytes) +{ + return fread(data, 1, nbytes, vfile->closure); +} + +static long +af_file_length(AFvirtualfile *vfile) +{ + long curpos, retval; + + curpos = ftell(vfile->closure); + fseek(vfile->closure, 0, SEEK_END); + retval = ftell(vfile->closure); + fseek(vfile->closure, curpos, SEEK_SET); + + return retval; +} + +static ssize_t af_file_write (AFvirtualfile *vfile, const void *data, + size_t nbytes) +{ + return fwrite(data, 1, nbytes, vfile->closure); +} + +static void +af_file_destroy(AFvirtualfile *vfile) +{ + fclose(vfile->closure); vfile->closure = NULL; +} + +static long +af_file_seek(AFvirtualfile *vfile, long offset, int is_relative) +{ + fseek(vfile->closure, offset, is_relative?SEEK_CUR:SEEK_SET); + + return ftell(vfile->closure); +} + +static long +af_file_tell(AFvirtualfile *vfile) +{ + return ftell(vfile->closure); +} diff --git a/libaudiofile/af_vfs.h b/libaudiofile/af_vfs.h new file mode 100644 index 0000000..93985dc --- /dev/null +++ b/libaudiofile/af_vfs.h @@ -0,0 +1,55 @@ +/* + Audio File Library + Copyright (C) 1999, Elliot Lee <sopwith@redhat.com> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307 USA. +*/ + +/* + af_vfs.h + + Virtual file operations for the Audio File Library. +*/ + +#ifndef AUDIOFILE_VFS_H +#define AUDIOFILE_VFS_H 1 + +#include <stdio.h> + +struct _AFvirtualfile +{ + ssize_t (*read) (AFvirtualfile *vfile, void *data, size_t nbytes); + long (*length) (AFvirtualfile *vfile); + ssize_t (*write) (AFvirtualfile *vfile, const void *data, size_t nbytes); + void (*destroy)(AFvirtualfile *vfile); + long (*seek) (AFvirtualfile *vfile, long offset, int is_relative); + long (*tell) (AFvirtualfile *vfile); + + void *closure; +}; + +AFvirtualfile *af_virtual_file_new (void); +AFvirtualfile *af_virtual_file_new_for_file (FILE *fh); +void af_virtual_file_destroy (AFvirtualfile *vfile); + +size_t af_fread (void *data, size_t size, size_t nmemb, AFvirtualfile *vfile); +size_t af_fwrite (const void *data, size_t size, size_t nmemb, AFvirtualfile *vfile); +int af_fclose (AFvirtualfile *vfile); +long af_flength (AFvirtualfile *vfile); +int af_fseek (AFvirtualfile *vfile, long offset, int whence); +long af_ftell (AFvirtualfile *vfile); + +#endif diff --git a/libaudiofile/afinternal.h b/libaudiofile/afinternal.h new file mode 100644 index 0000000..6e964b1 --- /dev/null +++ b/libaudiofile/afinternal.h @@ -0,0 +1,355 @@ +/* + Audio File Library + Copyright (C) 1998-2000, Michael Pruett <michael@68k.org> + Copyright (C) 2000, Silicon Graphics, Inc. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307 USA. +*/ + +/* + afinternal.h + + This file defines the internal structures for the Audio File Library. +*/ + +#ifndef AFINTERNAL_H +#define AFINTERNAL_H + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <sys/types.h> +#include <stdio.h> +#include "audiofile.h" +#include "af_vfs.h" +#include "error.h" + +typedef int bool; +#define AF_TRUE (1) +#define AF_FALSE (0) + +typedef int status; +#define AF_SUCCEED (0) +#define AF_FAIL (-1) + +typedef union AFPVu +{ + long l; + double d; + void *v; +} AFPVu; + +typedef struct _SuppMiscInfo +{ + int type; /* AF_MISC_... */ + int count; /* 0 = unlimited */ +} _SuppMiscInfo; + +typedef struct _InstParamInfo +{ + int id; + int type; + char *name; + AFPVu defaultValue; +} _InstParamInfo; + +typedef struct _MarkerSetup +{ + int id; + char *name, *comment; +} _MarkerSetup; + +typedef struct _Marker +{ + short id; + unsigned long position; + char *name, *comment; +} _Marker; + +typedef struct _Loop +{ + int id; + int mode; /* AF_LOOP_MODE_... */ + int count; /* how many times the loop is played */ + int beginMarker, endMarker; + int trackid; +} _Loop; + +typedef struct _PCMInfo +{ + double slope, intercept, minClip, maxClip; +} _PCMInfo; + +typedef struct _AudioFormat +{ + double sampleRate; /* sampling rate in Hz */ + int sampleFormat; /* AF_SAMPFMT_... */ + int sampleWidth; /* sample width in bits */ + int byteOrder; /* AF_BYTEORDER_... */ + + _PCMInfo pcm; /* parameters of PCM data */ + + int channelCount; /* number of channels */ + + int compressionType; /* AF_COMPRESSION_... */ + void *compressionParams; /* NULL if no compression */ +} _AudioFormat; + +/* modules */ +struct _AFmoduleinst; +struct _AFchunk; + +typedef void (*_AFfnpmod) (struct _AFmoduleinst *i); +typedef void (*_AFfnpsimplemod) (struct _AFchunk *inc, + struct _AFchunk *outc, void *modspec); + +typedef struct _AFmodule +{ + char *name; + _AFfnpmod describe; + _AFfnpmod max_pull; + _AFfnpmod max_push; + _AFfnpmod run_pull; + _AFfnpmod reset1; + _AFfnpmod reset2; + _AFfnpmod run_push; + _AFfnpmod sync1; + _AFfnpmod sync2; + _AFfnpsimplemod run; + _AFfnpmod free; +} _AFmodule; + +typedef struct _AFchunk +{ + void *buf; /* chunk data */ + AFframecount nframes; /* # of frames in chunk */ + _AudioFormat f; /* format of data in chunk */ +} _AFchunk; + +typedef struct _AFmoduleinst +{ + _AFchunk *inc, *outc; + void *modspec; + union + { + struct { struct _AFmoduleinst *source; } pull; + struct { struct _AFmoduleinst *sink; } push; + } u; + _AFmodule *mod; + bool free_on_close; /* AF_TRUE=don't free module until close */ + bool valid; /* internal use only */ +#ifdef AF_DEBUG /* these are set in _AFsetupmodules */ + int margin; /* margin for printing of CHNK messages */ + bool dump; /* whether to dump chunks */ +#endif +} _AFmoduleinst; + +/* information private to module routines */ +typedef struct _AFmodulestate +{ + bool modulesdirty; + int nmodules; + + /* See comment at very end of arrangemodules(). */ + bool mustuseatomicnvframes; + + /* previous rates before user changed them */ + double old_f_rate, old_v_rate; + + _AFchunk *chunk; + _AFmoduleinst *module; + + /* array of pointers to buffers, one for each module */ + void **buffer; + + /* These modules have extended lifetimes. */ + + /* file read / write */ + _AFmoduleinst filemodinst; + + /* file module's rebuffer */ + _AFmoduleinst filemod_rebufferinst; + + /* rate conversion */ + _AFmoduleinst rateconvertinst; + + /* old rates */ + double rateconvert_inrate, rateconvert_outrate; + + /* rate conversion's rebuffer */ + _AFmoduleinst rateconvert_rebufferinst; +} _AFmodulestate; + +typedef struct _Track +{ + int id; /* usually AF_DEFAULT_TRACKID */ + + _AudioFormat f, v; /* file and virtual audio formats */ + + double *channelMatrix; + + int markerCount; + _Marker *markers; + + bool hasAESData; /* Is AES nonaudio data present? */ + unsigned char aesData[24]; /* AES nonaudio data */ + + AFframecount totalfframes; /* frameCount */ + AFframecount nextfframe; /* currentFrame */ + AFframecount frames2ignore; + AFfileoffset fpos_first_frame; /* dataStart */ + AFfileoffset fpos_next_frame; + AFfileoffset fpos_after_data; + AFframecount totalvframes; + AFframecount nextvframe; + AFfileoffset data_size; /* trackBytes */ + + _AFmodulestate ms; + + double taper, dynamic_range; + bool ratecvt_filter_params_set; + + bool filemodhappy; +} _Track; + +typedef struct _TrackSetup +{ + int id; + + _AudioFormat f; + + bool rateSet, sampleFormatSet, sampleWidthSet, byteOrderSet, + channelCountSet, compressionSet, aesDataSet, markersSet, + dataOffsetSet, frameCountSet; + + int markerCount; + _MarkerSetup *markers; + + AFfileoffset dataOffset; + AFframecount frameCount; +} _TrackSetup; + +typedef struct _LoopSetup +{ + int id; +} _LoopSetup; + +typedef struct _InstrumentSetup +{ + int id; + + int loopCount; + _LoopSetup *loops; + bool loopSet; +} _InstrumentSetup; + +typedef struct _Instrument +{ + int id; + + int loopCount; + _Loop *loops; + + AFPVu *values; +} _Instrument; + +typedef struct _Miscellaneous +{ + int id; + int type; + int size; + + void *buffer; + + AFfileoffset position; /* offset within the miscellaneous chunk */ +} _Miscellaneous; + +typedef struct _MiscellaneousSetup +{ + int id; + int type; + int size; +} _MiscellaneousSetup; + +typedef struct _AFfilesetup +{ + int valid; + + int fileFormat; + + bool trackSet, instrumentSet, miscellaneousSet; + + int trackCount; + _TrackSetup *tracks; + + int instrumentCount; + _InstrumentSetup *instruments; + + int miscellaneousCount; + _MiscellaneousSetup *miscellaneous; +} _AFfilesetup; + +typedef struct _AFfilehandle +{ + int valid; /* _AF_VALID_FILEHANDLE */ + int access; /* _AF_READ_ACCESS or _AF_WRITE_ACCESS */ + + bool seekok; + + AFvirtualfile *fh; + + char *fileName; + + int fileFormat; + + int trackCount; + _Track *tracks; + + int instrumentCount; + _Instrument *instruments; + + int miscellaneousCount; + _Miscellaneous *miscellaneous; + + void *formatSpecific; /* format-specific data */ +} _AFfilehandle; + +enum +{ + _AF_VALID_FILEHANDLE = 38212, + _AF_VALID_FILESETUP = 38213 +}; + +enum +{ + _AF_READ_ACCESS = 1, + _AF_WRITE_ACCESS = 2 +}; + +/* The following are tokens for compression parameters in PV lists. */ +enum +{ + _AF_SAMPLES_PER_BLOCK = 700, /* type: long */ + _AF_BLOCK_SIZE = 701, /* type: long */ + _AF_MS_ADPCM_NUM_COEFFICIENTS = 800, /* type: long */ + _AF_MS_ADPCM_COEFFICIENTS = 801 /* type: array of int16_t[2] */ +}; + +/* NeXT/Sun sampling rate */ +#define _AF_SRATE_CODEC (8012.8210513) + +#endif diff --git a/libaudiofile/aiff.c b/libaudiofile/aiff.c new file mode 100644 index 0000000..9ad4041 --- /dev/null +++ b/libaudiofile/aiff.c @@ -0,0 +1,795 @@ +/* + Audio File Library + Copyright (C) 1998-2000, 2003, Michael Pruett <michael@68k.org> + Copyright (C) 2000, Silicon Graphics, Inc. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307 USA. +*/ + +/* + aiff.c + + This file contains routines for parsing AIFF and AIFF-C sound + files. +*/ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <assert.h> +#include <sys/types.h> +#include <stdio.h> +#include <string.h> + +#ifdef HAVE_INTTYPES_H +#include <inttypes.h> +#endif + +#include "extended.h" +#include "audiofile.h" +#include "util.h" +#include "afinternal.h" +#include "byteorder.h" +#include "aiff.h" +#include "setup.h" +#include "track.h" +#include "marker.h" + +static status ParseFVER (AFfilehandle file, AFvirtualfile *fh, u_int32_t type, + size_t size); +static status ParseAESD (AFfilehandle file, AFvirtualfile *fh, u_int32_t type, + size_t size); +static status ParseMiscellaneous (AFfilehandle file, AFvirtualfile *fh, + u_int32_t type, size_t size); +static status ParseINST (AFfilehandle file, AFvirtualfile *fh, u_int32_t type, + size_t size); +static status ParseMARK (AFfilehandle file, AFvirtualfile *fh, u_int32_t type, + size_t size); +static status ParseCOMM (AFfilehandle file, AFvirtualfile *fh, u_int32_t type, + size_t size); +static status ParseSSND (AFfilehandle file, AFvirtualfile *fh, u_int32_t type, + size_t size); + +_InstParamInfo _af_aiff_inst_params[_AF_AIFF_NUM_INSTPARAMS] = +{ + { AF_INST_MIDI_BASENOTE, AU_PVTYPE_LONG, "MIDI base note", {60} }, + { AF_INST_NUMCENTS_DETUNE, AU_PVTYPE_LONG, "Detune in cents", {0} }, + { AF_INST_MIDI_LOVELOCITY, AU_PVTYPE_LONG, "Low velocity", {1} }, + { AF_INST_MIDI_HIVELOCITY, AU_PVTYPE_LONG, "High velocity", {127} }, + { AF_INST_MIDI_LONOTE, AU_PVTYPE_LONG, "Low note", {0} }, + { AF_INST_MIDI_HINOTE, AU_PVTYPE_LONG, "High note", {127} }, + { AF_INST_NUMDBS_GAIN, AU_PVTYPE_LONG, "Gain in dB", {0} }, + { AF_INST_SUSLOOPID, AU_PVTYPE_LONG, "Sustain loop id", {0} }, + { AF_INST_RELLOOPID, AU_PVTYPE_LONG, "Release loop id", {0} } +}; + +int _af_aiffc_compression_types[_AF_AIFF_NUM_COMPTYPES] = +{ + AF_COMPRESSION_G711_ULAW, + AF_COMPRESSION_G711_ALAW +}; + +_AFfilesetup _af_aiff_default_filesetup = +{ + _AF_VALID_FILESETUP, /* valid */ + AF_FILE_AIFF, /* fileFormat */ + AF_TRUE, /* trackSet */ + AF_TRUE, /* instrumentSet */ + AF_TRUE, /* miscellaneousSet */ + 1, /* trackCount */ + NULL, /* tracks */ + 1, /* instrumentCount */ + NULL, /* instruments */ + 0, /* miscellaneousCount */ + NULL /* miscellaneous */ +}; + +/* + FVER chunks are only present in AIFF-C files. +*/ +static status ParseFVER (AFfilehandle file, AFvirtualfile *fh, u_int32_t type, size_t size) +{ + u_int32_t timestamp; + + assert(!memcmp(&type, "FVER", 4)); + + af_fread(×tamp, sizeof (u_int32_t), 1, fh); + timestamp = BENDIAN_TO_HOST_INT32(timestamp); + /* timestamp holds the number of seconds since January 1, 1904. */ + + return AF_SUCCEED; +} + +/* + Parse AES recording data. +*/ +static status ParseAESD (AFfilehandle file, AFvirtualfile *fh, u_int32_t type, size_t size) +{ + _Track *track; + unsigned char aesChannelStatusData[24]; + + assert(!memcmp(&type, "AESD", 4)); + assert(size == 24); + + track = _af_filehandle_get_track(file, AF_DEFAULT_TRACK); + + track->hasAESData = AF_TRUE; + + /* + Try to read 24 bytes of AES nonaudio data from the file. + Fail if the file disappoints. + */ + if (af_fread(aesChannelStatusData, 1, 24, fh) != 24) + return AF_FAIL; + + memcpy(track->aesData, aesChannelStatusData, 24); + + return AF_SUCCEED; +} + +/* + Parse miscellaneous data chunks such as name, author, copyright, + and annotation chunks. +*/ +static status ParseMiscellaneous (AFfilehandle file, AFvirtualfile *fh, + u_int32_t type, size_t size) +{ + int misctype = AF_MISC_UNRECOGNIZED; + + assert(!memcmp(&type, "NAME", 4) || !memcmp(&type, "AUTH", 4) || + !memcmp(&type, "(c) ", 4) || !memcmp(&type, "ANNO", 4) || + !memcmp(&type, "APPL", 4) || !memcmp(&type, "MIDI", 4)); + + /* Skip zero-length miscellaneous chunks. */ + if (size == 0) + return AF_FAIL; + + file->miscellaneousCount++; + file->miscellaneous = _af_realloc(file->miscellaneous, + file->miscellaneousCount * sizeof (_Miscellaneous)); + + if (!memcmp(&type, "NAME", 4)) + misctype = AF_MISC_NAME; + else if (!memcmp(&type, "AUTH", 4)) + misctype = AF_MISC_AUTH; + else if (!memcmp(&type, "(c) ", 4)) + misctype = AF_MISC_COPY; + else if (!memcmp(&type, "ANNO", 4)) + misctype = AF_MISC_ANNO; + else if (!memcmp(&type, "APPL", 4)) + misctype = AF_MISC_APPL; + else if (!memcmp(&type, "MIDI", 4)) + misctype = AF_MISC_MIDI; + + file->miscellaneous[file->miscellaneousCount - 1].id = file->miscellaneousCount; + file->miscellaneous[file->miscellaneousCount - 1].type = misctype; + file->miscellaneous[file->miscellaneousCount - 1].size = size; + file->miscellaneous[file->miscellaneousCount - 1].position = 0; + file->miscellaneous[file->miscellaneousCount - 1].buffer = _af_malloc(size); + af_fread(file->miscellaneous[file->miscellaneousCount - 1].buffer, + size, 1, file->fh); + + return AF_SUCCEED; +} + +/* + Parse instrument chunks, which contain information about using + sound data as a sampled instrument. +*/ +static status ParseINST (AFfilehandle file, AFvirtualfile *fh, u_int32_t type, + size_t size) +{ + _Instrument *instrument; + u_int8_t baseNote; + int8_t detune; + u_int8_t lowNote, highNote, lowVelocity, highVelocity; + int16_t gain; + + u_int16_t sustainLoopPlayMode, sustainLoopBegin, sustainLoopEnd; + u_int16_t releaseLoopPlayMode, releaseLoopBegin, releaseLoopEnd; + + assert(!memcmp(&type, "INST", 4)); + + instrument = _af_calloc(1, sizeof (_Instrument)); + instrument->id = AF_DEFAULT_INST; + instrument->values = _af_calloc(_AF_AIFF_NUM_INSTPARAMS, sizeof (AFPVu)); + instrument->loopCount = 2; + instrument->loops = _af_calloc(2, sizeof (_Loop)); + + file->instrumentCount = 1; + file->instruments = instrument; + + af_fread(&baseNote, 1, 1, fh); + af_fread(&detune, 1, 1, fh); + af_fread(&lowNote, 1, 1, fh); + af_fread(&highNote, 1, 1, fh); + af_fread(&lowVelocity, 1, 1, fh); + af_fread(&highVelocity, 1, 1, fh); + af_fread(&gain, 2, 1, fh); + gain = BENDIAN_TO_HOST_INT16(gain); + +#ifdef DEBUG + printf("baseNote/detune/lowNote/highNote/lowVelocity/highVelocity/gain:" + " %d %d %d %d %d %d %d\n", + baseNote, detune, lowNote, highNote, lowVelocity, highVelocity, + gain); +#endif + + instrument->values[0].l = baseNote; + instrument->values[1].l = detune; + instrument->values[2].l = lowVelocity; + instrument->values[3].l = highVelocity; + instrument->values[4].l = lowNote; + instrument->values[5].l = highNote; + instrument->values[6].l = gain; + + instrument->values[7].l = 1; /* sustain loop id */ + instrument->values[8].l = 2; /* release loop id */ + + af_fread(&sustainLoopPlayMode, sizeof (u_int16_t), 1, fh); + sustainLoopPlayMode = BENDIAN_TO_HOST_INT16(sustainLoopPlayMode); + af_fread(&sustainLoopBegin, sizeof (u_int16_t), 1, fh); + sustainLoopBegin = BENDIAN_TO_HOST_INT16(sustainLoopBegin); + af_fread(&sustainLoopEnd, sizeof (u_int16_t), 1, fh); + sustainLoopEnd = BENDIAN_TO_HOST_INT16(sustainLoopEnd); + + af_fread(&releaseLoopPlayMode, sizeof (u_int16_t), 1, fh); + releaseLoopPlayMode = BENDIAN_TO_HOST_INT16(releaseLoopPlayMode); + af_fread(&releaseLoopBegin, sizeof (u_int16_t), 1, fh); + releaseLoopBegin = BENDIAN_TO_HOST_INT16(releaseLoopBegin); + af_fread(&releaseLoopEnd, sizeof (u_int16_t), 1, fh); + releaseLoopEnd = BENDIAN_TO_HOST_INT16(releaseLoopEnd); + +#ifdef DEBUG + printf("sustain loop: mode %d, begin %d, end %d\n", + sustainLoopPlayMode, sustainLoopBegin, sustainLoopEnd); + + printf("release loop: mode %d, begin %d, end %d\n", + releaseLoopPlayMode, releaseLoopBegin, releaseLoopEnd); +#endif + + instrument->loops[0].id = 1; + instrument->loops[0].mode = sustainLoopPlayMode; + instrument->loops[0].beginMarker = sustainLoopBegin; + instrument->loops[0].endMarker = sustainLoopEnd; + + instrument->loops[1].id = 2; + instrument->loops[1].mode = releaseLoopPlayMode; + instrument->loops[1].beginMarker = releaseLoopBegin; + instrument->loops[1].endMarker = releaseLoopEnd; + + return AF_SUCCEED; +} + +/* + Parse marker chunks, which contain the positions and names of loop markers. +*/ +static status ParseMARK (AFfilehandle file, AFvirtualfile *fh, u_int32_t type, + size_t size) +{ + _Track *track; + int i; + u_int16_t numMarkers; + + assert(!memcmp(&type, "MARK", 4)); + + track = _af_filehandle_get_track(file, AF_DEFAULT_TRACK); + + af_fread(&numMarkers, sizeof (u_int16_t), 1, fh); + numMarkers = BENDIAN_TO_HOST_INT16(numMarkers); + + track->markerCount = numMarkers; + if (numMarkers) + track->markers = _af_marker_new(numMarkers); + + for (i=0; i<numMarkers; i++) + { + u_int16_t markerID = 0; + u_int32_t markerPosition = 0; + u_int8_t sizeByte = 0; + char *markerName = NULL; + + af_fread(&markerID, sizeof (u_int16_t), 1, fh); + markerID = BENDIAN_TO_HOST_INT16(markerID); + af_fread(&markerPosition, sizeof (u_int32_t), 1, fh); + markerPosition = BENDIAN_TO_HOST_INT32(markerPosition); + af_fread(&sizeByte, sizeof (unsigned char), 1, fh); + markerName = _af_malloc(sizeByte + 1); + af_fread(markerName, sizeof (unsigned char), sizeByte, fh); + + markerName[sizeByte] = '\0'; + +#ifdef DEBUG + printf("marker id: %d, position: %d, name: %s\n", + markerID, markerPosition, markerName); + + printf("size byte: %d\n", sizeByte); +#endif + + /* + If sizeByte is even, then 1+sizeByte (the length + of the string) is odd. Skip an extra byte to + make it even. + */ + + if ((sizeByte % 2) == 0) + af_fseek(fh, 1, SEEK_CUR); + + track->markers[i].id = markerID; + track->markers[i].position = markerPosition; + track->markers[i].name = markerName; + track->markers[i].comment = _af_strdup(""); + } + + return AF_SUCCEED; +} + +/* + Parse common data chunks, which contain information regarding the + sampling rate, the number of sample frames, and the number of + sound channels. +*/ +static status ParseCOMM (AFfilehandle file, AFvirtualfile *fh, u_int32_t type, + size_t size) +{ + _Track *track; + u_int16_t numChannels; + u_int32_t numSampleFrames; + u_int16_t sampleSize; + unsigned char sampleRate[10]; + + assert(!memcmp(&type, "COMM", 4)); + + track = _af_filehandle_get_track(file, AF_DEFAULT_TRACK); + + af_fread(&numChannels, sizeof (u_int16_t), 1, fh); + track->f.channelCount = BENDIAN_TO_HOST_INT16(numChannels); + + af_fread(&numSampleFrames, sizeof (u_int32_t), 1, fh); + track->totalfframes = BENDIAN_TO_HOST_INT32(numSampleFrames); + + af_fread(&sampleSize, sizeof (u_int16_t), 1, fh); + track->f.sampleWidth = BENDIAN_TO_HOST_INT16(sampleSize); + + af_fread(sampleRate, 10, 1, fh); + track->f.sampleRate = _af_convert_from_ieee_extended(sampleRate); + + track->f.compressionType = AF_COMPRESSION_NONE; + track->f.sampleFormat = AF_SAMPFMT_TWOSCOMP; + track->f.byteOrder = AF_BYTEORDER_BIGENDIAN; + + if (file->fileFormat == AF_FILE_AIFFC) + { + u_int8_t compressionID[4]; + /* Pascal strings are at most 255 bytes long. */ + unsigned char compressionName[256]; + unsigned char compressionNameLength; + + af_fread(compressionID, 4, 1, fh); + + /* Read the Pascal-style string containing the name. */ + af_fread(&compressionNameLength, 1, 1, fh); + af_fread(compressionName, compressionNameLength, 1, fh); + compressionName[compressionNameLength] = '\0'; + + if (!memcmp(compressionID, "NONE", 4)) + track->f.compressionType = AF_COMPRESSION_NONE; + else if (!memcmp(compressionID, "ACE2", 4) || + !memcmp(compressionID, "ACE8", 4) || + !memcmp(compressionID, "MAC3", 4) || + !memcmp(compressionID, "MAC6", 4)) + { + _af_error(AF_BAD_NOT_IMPLEMENTED, "AIFF-C format does not support Apple's proprietary %s compression format", compressionName); + return AF_FAIL; + } + else if (!memcmp(compressionID, "ulaw", 4) || + !memcmp(compressionID, "ULAW", 4)) + { + track->f.compressionType = AF_COMPRESSION_G711_ULAW; + } + else if (!memcmp(compressionID, "alaw", 4) || + !memcmp(compressionID, "ALAW", 4)) + { + track->f.compressionType = AF_COMPRESSION_G711_ALAW; + } + else if (!memcmp(compressionID, "fl32", 4) || + !memcmp(compressionID, "FL32", 4)) + { + track->f.sampleFormat = AF_SAMPFMT_FLOAT; + track->f.sampleWidth = 32; + track->f.compressionType = AF_COMPRESSION_NONE; + } + else if (!memcmp(compressionID, "fl64", 4) || + !memcmp(compressionID, "FL64", 4)) + { + track->f.sampleFormat = AF_SAMPFMT_DOUBLE; + track->f.sampleWidth = 64; + track->f.compressionType = AF_COMPRESSION_NONE; + } + else if (!memcmp(compressionID, "sowt", 4)) + { + track->f.compressionType = AF_COMPRESSION_NONE; + track->f.byteOrder = AF_BYTEORDER_LITTLEENDIAN; + } + else + { + _af_error(AF_BAD_NOT_IMPLEMENTED, "AIFF-C compression type '%c%c%c%c' not currently supported", + compressionID[0], + compressionID[1], + compressionID[2], + compressionID[3]); + return AF_FAIL; + } + } + + _af_set_sample_format(&track->f, track->f.sampleFormat, track->f.sampleWidth); + + return AF_SUCCEED; +} + +/* + Parse the stored sound chunk, which usually contains little more + than the sound data. +*/ +static status ParseSSND (AFfilehandle file, AFvirtualfile *fh, u_int32_t type, + size_t size) +{ + _Track *track; + u_int32_t offset, blockSize; + + assert(!memcmp(&type, "SSND", 4)); + + track = _af_filehandle_get_track(file, AF_DEFAULT_TRACK); + + af_fread(&offset, sizeof (u_int32_t), 1, fh); + offset = BENDIAN_TO_HOST_INT32(offset); + af_fread(&blockSize, sizeof (u_int32_t), 1, fh); + blockSize = BENDIAN_TO_HOST_INT32(blockSize); + + /* + This seems like a reasonable way to calculate the number of + bytes in an SSND chunk. + */ + track->data_size = size - 8 - offset; + +#ifdef DEBUG + printf("offset: %d\n", offset); + printf("block size: %d\n", blockSize); +#endif + + track->fpos_first_frame = af_ftell(fh) + offset; + +#ifdef DEBUG + printf("data start: %d\n", track->fpos_first_frame); +#endif + + /* Sound data follows. */ + + return AF_SUCCEED; +} + +status _af_aiff_read_init (AFfilesetup setup, AFfilehandle file) +{ + u_int32_t type, size, formtype; + size_t index = 0; + bool hasCOMM, hasFVER, hasSSND, hasMARK, hasINST; + bool hasAESD, hasNAME, hasAUTH, hasCOPY; + _Track *track; + + hasCOMM = AF_FALSE; + hasFVER = AF_FALSE; + hasSSND = AF_FALSE; + hasMARK = AF_FALSE; + hasINST = AF_FALSE; + hasAESD = AF_FALSE; + hasNAME = AF_FALSE; + hasAUTH = AF_FALSE; + hasCOPY = AF_FALSE; + + assert(file != NULL); + assert(file->fh != NULL); + + af_fseek(file->fh, 0, SEEK_SET); + + af_fread(&type, 4, 1, file->fh); + af_fread(&size, 4, 1, file->fh); + size = BENDIAN_TO_HOST_INT32(size); + af_fread(&formtype, 4, 1, file->fh); + + if (memcmp(&type, "FORM", 4) != 0 || + (memcmp(&formtype, "AIFF", 4) && memcmp(&formtype, "AIFC", 4))) + return AF_FAIL; + +#ifdef DEBUG + printf("size: %d\n", size); +#endif + + file->instrumentCount = 0; + file->instruments = NULL; + file->miscellaneousCount = 0; + file->miscellaneous = NULL; + + /* AIFF files have only one track. */ + track = _af_track_new(); + file->trackCount = 1; + file->tracks = track; + + /* Include the offset of the form type. */ + index += 4; + + while (index < size) + { + u_int32_t chunkid = 0, chunksize = 0; + status result = AF_SUCCEED; + +#ifdef DEBUG + printf("index: %d\n", index); +#endif + af_fread(&chunkid, 4, 1, file->fh); + af_fread(&chunksize, 4, 1, file->fh); + chunksize = BENDIAN_TO_HOST_INT32(chunksize); + +#ifdef DEBUG + _af_printid(chunkid); + printf(" size: %d\n", chunksize); +#endif + + if (!memcmp("COMM", &chunkid, 4)) + { + hasCOMM = AF_TRUE; + result = ParseCOMM(file, file->fh, chunkid, chunksize); + } + else if (!memcmp("FVER", &chunkid, 4)) + { + hasFVER = AF_TRUE; + ParseFVER(file, file->fh, chunkid, chunksize); + } + else if (!memcmp("INST", &chunkid, 4)) + { + hasINST = AF_TRUE; + ParseINST(file, file->fh, chunkid, chunksize); + } + else if (!memcmp("MARK", &chunkid, 4)) + { + hasMARK = AF_TRUE; + ParseMARK(file, file->fh, chunkid, chunksize); + } + else if (!memcmp("AESD", &chunkid, 4)) + { + hasAESD = AF_TRUE; + ParseAESD(file, file->fh, chunkid, chunksize); + } + else if (!memcmp("NAME", &chunkid, 4) || + !memcmp("AUTH", &chunkid, 4) || + !memcmp("(c) ", &chunkid, 4) || + !memcmp("ANNO", &chunkid, 4) || + !memcmp("APPL", &chunkid, 4) || + !memcmp("MIDI", &chunkid, 4)) + { + ParseMiscellaneous(file, file->fh, chunkid, chunksize); + } + /* + The sound data chunk is required if there are more than + zero sample frames. + */ + else if (!memcmp("SSND", &chunkid, 4)) + { + if (hasSSND) + { + _af_error(AF_BAD_AIFF_SSND, "AIFF file has more than one SSND chunk"); + return AF_FAIL; + } + hasSSND = AF_TRUE; + result = ParseSSND(file, file->fh, chunkid, chunksize); + } + + if (result == AF_FAIL) + return AF_FAIL; + + index += chunksize + 8; + + /* all chunks must be aligned on an even number of bytes */ + if ((index % 2) != 0) + index++; + + af_fseek(file->fh, index + 8, SEEK_SET); + } + + if (!hasCOMM) + { + _af_error(AF_BAD_AIFF_COMM, "bad AIFF COMM chunk"); + } + + /* The file has been successfully parsed. */ + return AF_SUCCEED; +} + +bool _af_aiff_recognize (AFvirtualfile *fh) +{ + u_int8_t buffer[8]; + + af_fseek(fh, 0, SEEK_SET); + + if (af_fread(buffer, 1, 8, fh) != 8 || memcmp(buffer, "FORM", 4) != 0) + return AF_FALSE; + if (af_fread(buffer, 1, 4, fh) != 4 || memcmp(buffer, "AIFF", 4) != 0) + return AF_FALSE; + + return AF_TRUE; +} + +bool _af_aifc_recognize (AFvirtualfile *fh) +{ + u_int8_t buffer[8]; + + af_fseek(fh, 0, SEEK_SET); + + if (af_fread(buffer, 1, 8, fh) != 8 || memcmp(buffer, "FORM", 4) != 0) + return AF_FALSE; + if (af_fread(buffer, 1, 4, fh) != 4 || memcmp(buffer, "AIFC", 4) != 0) + return AF_FALSE; + + return AF_TRUE; +} + +AFfilesetup _af_aiff_complete_setup (AFfilesetup setup) +{ + _TrackSetup *track; + + bool isAIFF = setup->fileFormat == AF_FILE_AIFF; + + if (setup->trackSet && setup->trackCount != 1) + { + _af_error(AF_BAD_NUMTRACKS, "AIFF/AIFF-C file must have 1 track"); + return AF_NULL_FILESETUP; + } + + track = &setup->tracks[0]; + + if (track->sampleFormatSet) + { + if (track->f.sampleFormat == AF_SAMPFMT_UNSIGNED) + { + _af_error(AF_BAD_FILEFMT, "AIFF/AIFF-C format does not support unsigned data"); + return AF_NULL_FILESETUP; + } + else if (isAIFF && track->f.sampleFormat != AF_SAMPFMT_TWOSCOMP) + { + _af_error(AF_BAD_FILEFMT, "AIFF format supports only two's complement integer data"); + return AF_NULL_FILESETUP; + } + } + else + _af_set_sample_format(&track->f, AF_SAMPFMT_TWOSCOMP, + track->f.sampleWidth); + + /* Check sample width if writing two's complement. Otherwise ignore. */ + if (track->f.sampleFormat == AF_SAMPFMT_TWOSCOMP && + (track->f.sampleWidth < 1 || track->f.sampleWidth > 32)) + { + _af_error(AF_BAD_WIDTH, + "invalid sample width %d for AIFF/AIFF-C file " + "(must be 1-32)", track->f.sampleWidth); + return AF_NULL_FILESETUP; + } + + if (isAIFF && track->f.compressionType != AF_COMPRESSION_NONE) + { + _af_error(AF_BAD_FILESETUP, + "AIFF does not support compression; use AIFF-C"); + return AF_NULL_FILESETUP; + } + + /* XXXmpruett handle compression here */ + + if (track->byteOrderSet && + track->f.byteOrder != AF_BYTEORDER_BIGENDIAN && + track->f.sampleWidth > 8) + { + _af_error(AF_BAD_BYTEORDER, + "AIFF/AIFF-C format supports only big-endian data"); + } + track->f.byteOrder = AF_BYTEORDER_BIGENDIAN; + + if (setup->instrumentSet) + { + if (setup->instrumentCount != 0 && setup->instrumentCount != 1) + { + _af_error(AF_BAD_NUMINSTS, "AIFF/AIFF-C file must have 0 or 1 instrument chunk"); + return AF_NULL_FILESETUP; + } + if (setup->instruments != 0 && + setup->instruments[0].loopCount != 2) + { + _af_error(AF_BAD_NUMLOOPS, "AIFF/AIFF-C file with instrument must also have 2 loops"); + return AF_NULL_FILESETUP; + } + } + + if (setup->miscellaneousSet) + { + int i; + for (i=0; i<setup->miscellaneousCount; i++) + { + switch (setup->miscellaneous[i].type) + { + case AF_MISC_COPY: + case AF_MISC_AUTH: + case AF_MISC_NAME: + case AF_MISC_ANNO: + case AF_MISC_APPL: + case AF_MISC_MIDI: + break; + + default: + _af_error(AF_BAD_MISCTYPE, "invalid miscellaneous type %d for AIFF/AIFF-C file", setup->miscellaneous[i].type); + return AF_NULL_FILESETUP; + } + } + } + + return _af_filesetup_copy(setup, &_af_aiff_default_filesetup, AF_TRUE); +} + +bool _af_aiff_instparam_valid (AFfilehandle filehandle, AUpvlist list, int i) +{ + int param, type, lval; + + AUpvgetparam(list, i, ¶m); + AUpvgetvaltype(list, i, &type); + if (type != AU_PVTYPE_LONG) + return AF_FALSE; + + AUpvgetval(list, i, &lval); + + switch (param) + { + case AF_INST_MIDI_BASENOTE: + return ((lval >= 0) && (lval <= 127)); + + case AF_INST_NUMCENTS_DETUNE: + return ((lval >= -50) && (lval <= 50)); + + case AF_INST_MIDI_LOVELOCITY: + return ((lval >= 1) && (lval <= 127)); + + case AF_INST_MIDI_HIVELOCITY: + return ((lval >= 1) && (lval <= 127)); + + case AF_INST_MIDI_LONOTE: + return ((lval >= 0) && (lval <= 127)); + + case AF_INST_MIDI_HINOTE: + return ((lval >= 0) && (lval <= 127)); + + case AF_INST_NUMDBS_GAIN: + case AF_INST_SUSLOOPID: + case AF_INST_RELLOOPID: + return AF_TRUE; + + default: + return AF_FALSE; + break; + } + + return AF_TRUE; +} + +int _af_aifc_get_version (AFfilehandle file) +{ + return AIFC_VERSION_1; +} diff --git a/libaudiofile/aiff.h b/libaudiofile/aiff.h new file mode 100644 index 0000000..4f1ce18 --- /dev/null +++ b/libaudiofile/aiff.h @@ -0,0 +1,101 @@ +/* + Audio File Library + Copyright (C) 1998-2000, Michael Pruett <michael@68k.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307 USA. +*/ + +/* + aiff.h + + This file contains structures and constants related to the AIFF + and AIFF-C formats. +*/ + +#ifndef AIFF_H +#define AIFF_H + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#ifdef HAVE_INTTYPES_H +#include <inttypes.h> +#endif + +#define _AF_AIFF_NUM_INSTPARAMS 9 +#define _AF_AIFF_NUM_COMPTYPES 2 + +#define AIFC_VERSION_1 0xa2805140 + +struct _COMM +{ + short numChannels; + long numSampleFrames; + short sampleSize; + unsigned char sampleRate[10]; +}; + +struct _MARK +{ + short numMarkers; + struct _Marker *markers; +}; + +struct _INST +{ + u_int8_t baseNote; + int8_t detune; + u_int8_t lowNote, highNote; + u_int8_t lowVelocity, highVelocity; + int16_t gain; + + int16_t sustainLoopPlayMode; + int16_t sustainLoopBegin; + int16_t sustainLoopEnd; + + int16_t releaseLoopPlayMode; + int16_t releaseLoopBegin; + int16_t releaseLoopEnd; +}; + +bool _af_aiff_recognize (AFvirtualfile *fh); +bool _af_aifc_recognize (AFvirtualfile *fh); + +status _af_aiff_read_init (AFfilesetup, AFfilehandle); +status _af_aiff_write_init (AFfilesetup, AFfilehandle); +bool _af_aiff_instparam_valid (AFfilehandle, AUpvlist, int); + +AFfilesetup _af_aiff_complete_setup (AFfilesetup); + +status _af_aiff_update (AFfilehandle); + +int _af_aifc_get_version (AFfilehandle); + +#define _AF_AIFFC_NUM_COMPTYPES 2 + +typedef struct _AIFFInfo +{ + AFfileoffset miscellaneousPosition; + AFfileoffset FVER_offset; + AFfileoffset COMM_offset; + AFfileoffset MARK_offset; + AFfileoffset INST_offset; + AFfileoffset AESD_offset; + AFfileoffset SSND_offset; +} _AIFFInfo; + +#endif diff --git a/libaudiofile/aiffwrite.c b/libaudiofile/aiffwrite.c new file mode 100644 index 0000000..a8b5b9c --- /dev/null +++ b/libaudiofile/aiffwrite.c @@ -0,0 +1,581 @@ +/* + Audio File Library + Copyright (C) 1998-2000, Michael Pruett <michael@68k.org> + Copyright (C) 2000-2001, Silicon Graphics, Inc. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307 USA. +*/ + +/* + aiffwrite.c + + This file contains routines for writing AIFF and AIFF-C format + sound files. +*/ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <assert.h> +#include <sys/types.h> +#include <stdlib.h> +#include <string.h> + +#include "extended.h" +#include "afinternal.h" +#include "audiofile.h" +#include "aiff.h" +#include "byteorder.h" +#include "util.h" +#include "setup.h" + +status _af_aiff_update (AFfilehandle file); + +static status WriteCOMM (AFfilehandle file); +static status WriteSSND (AFfilehandle file); +static status WriteMARK (AFfilehandle file); +static status WriteINST (AFfilehandle file); +static status WriteFVER (AFfilehandle file); +static status WriteAESD (AFfilehandle file); +static status WriteMiscellaneous (AFfilehandle file); + +static _AIFFInfo *aiffinfo_new (void) +{ + _AIFFInfo *aiff = _af_malloc(sizeof (_AIFFInfo)); + + aiff->miscellaneousPosition = 0; + aiff->FVER_offset = 0; + aiff->COMM_offset = 0; + aiff->MARK_offset = 0; + aiff->INST_offset = 0; + aiff->AESD_offset = 0; + aiff->SSND_offset = 0; + + return aiff; +} + +status _af_aiff_write_init (AFfilesetup setup, AFfilehandle file) +{ + u_int32_t fileSize = HOST_TO_BENDIAN_INT32(0); + + assert(file); + assert(file->fileFormat == AF_FILE_AIFF || + file->fileFormat == AF_FILE_AIFFC); + + if (_af_filesetup_make_handle(setup, file) == AF_FAIL) + return AF_FAIL; + + file->formatSpecific = aiffinfo_new(); + + af_fwrite("FORM", 4, 1, file->fh); + af_fwrite(&fileSize, 4, 1, file->fh); + + if (file->fileFormat == AF_FILE_AIFF) + af_fwrite("AIFF", 4, 1, file->fh); + else if (file->fileFormat == AF_FILE_AIFFC) + af_fwrite("AIFC", 4, 1, file->fh); + + if (file->fileFormat == AF_FILE_AIFFC) + WriteFVER(file); + + WriteCOMM(file); + WriteMARK(file); + WriteINST(file); + WriteAESD(file); + WriteMiscellaneous(file); + WriteSSND(file); + + return AF_SUCCEED; +} + +status _af_aiff_update (AFfilehandle file) +{ + _Track *track; + u_int32_t length; + + assert(file); + + track = _af_filehandle_get_track(file, AF_DEFAULT_TRACK); + +#ifdef DEBUG + printf("_af_aiff_update called.\n"); +#endif + + /* Get the length of the file. */ + length = af_flength(file->fh); + length -= 8; + length = HOST_TO_BENDIAN_INT32(length); + + /* Set the length of the FORM chunk. */ + af_fseek(file->fh, 4, SEEK_SET); + af_fwrite(&length, 4, 1, file->fh); + + if (file->fileFormat == AF_FILE_AIFFC) + WriteFVER(file); + + WriteCOMM(file); + WriteMARK(file); + WriteINST(file); + WriteAESD(file); + WriteMiscellaneous(file); + WriteSSND(file); + + return AF_SUCCEED; +} + +static status WriteCOMM (const AFfilehandle file) +{ + _Track *track; + u_int32_t chunkSize; + _AIFFInfo *aiff; + bool isAIFFC; + + u_int16_t sb; + u_int32_t lb; + unsigned char eb[10]; + + u_int8_t compressionTag[4]; + /* Pascal strings can occupy only 255 bytes (+ a size byte). */ + char compressionName[256]; + + isAIFFC = file->fileFormat == AF_FILE_AIFFC; + + aiff = file->formatSpecific; + + /* + If COMM_offset hasn't been set yet, set it to the + current offset. + */ + if (aiff->COMM_offset == 0) + aiff->COMM_offset = af_ftell(file->fh); + else + af_fseek(file->fh, aiff->COMM_offset, SEEK_SET); + + track = _af_filehandle_get_track(file, AF_DEFAULT_TRACK); + + if (isAIFFC) + { + if (track->f.compressionType == AF_COMPRESSION_NONE) + { + if (track->f.sampleFormat == AF_SAMPFMT_TWOSCOMP) + { + memcpy(compressionTag, "NONE", 4); + strcpy(compressionName, "not compressed"); + } + else if (track->f.sampleFormat == AF_SAMPFMT_FLOAT) + { + memcpy(compressionTag, "fl32", 4); + strcpy(compressionName, "32-bit Floating Point"); + } + else if (track->f.sampleFormat == AF_SAMPFMT_DOUBLE) + { + memcpy(compressionTag, "fl64", 4); + strcpy(compressionName, "64-bit Floating Point"); + } + /* + We disallow unsigned sample data for + AIFF files in _af_aiff_complete_setup, + so the next condition should never be + satisfied. + */ + else if (track->f.sampleFormat == AF_SAMPFMT_UNSIGNED) + { + _af_error(AF_BAD_SAMPFMT, + "AIFF/AIFF-C format does not support unsigned data"); + assert(0); + return AF_FAIL; + } + } + else if (track->f.compressionType == AF_COMPRESSION_G711_ULAW) + { + memcpy(compressionTag, "ulaw", 4); + strcpy(compressionName, "CCITT G.711 u-law"); + } + else if (track->f.compressionType == AF_COMPRESSION_G711_ALAW) + { + memcpy(compressionTag, "alaw", 4); + strcpy(compressionName, "CCITT G.711 A-law"); + } + } + + af_fwrite("COMM", 4, 1, file->fh); + + /* + For AIFF-C files, the length of the COMM chunk is 22 + plus the length of the compression name plus the size + byte. If the length of the data is an odd number of + bytes, add a zero pad byte at the end, but don't + include the pad byte in the chunk's size. + */ + if (isAIFFC) + chunkSize = 22 + strlen(compressionName) + 1; + else + chunkSize = 18; + chunkSize = HOST_TO_BENDIAN_INT32(chunkSize); + af_fwrite(&chunkSize, 4, 1, file->fh); + + /* number of channels, 2 bytes */ + sb = HOST_TO_BENDIAN_INT16(track->f.channelCount); + af_fwrite(&sb, 2, 1, file->fh); + + /* number of sample frames, 4 bytes */ + lb = HOST_TO_BENDIAN_INT32(track->totalfframes); + af_fwrite(&lb, 4, 1, file->fh); + + /* sample size, 2 bytes */ + sb = HOST_TO_BENDIAN_INT16(track->f.sampleWidth); + af_fwrite(&sb, 2, 1, file->fh); + + /* sample rate, 10 bytes */ + _af_convert_to_ieee_extended(track->f.sampleRate, eb); + af_fwrite(eb, 10, 1, file->fh); + + if (file->fileFormat == AF_FILE_AIFFC) + { + u_int8_t sizeByte, zero = 0; + + af_fwrite(compressionTag, 4, 1, file->fh); + + sizeByte = strlen(compressionName); + + af_fwrite(&sizeByte, 1, 1, file->fh); + af_fwrite(compressionName, sizeByte, 1, file->fh); + + /* + If sizeByte is even, then 1+sizeByte + (the length of the string) is odd. Add an + extra byte to make the chunk's extent even + (even though the chunk's size may be odd). + */ + if ((sizeByte % 2) == 0) + af_fwrite(&zero, 1, 1, file->fh); + } + + return AF_SUCCEED; +} + +/* + The AESD chunk contains information pertinent to audio recording + devices. +*/ +static status WriteAESD (const AFfilehandle file) +{ + _Track *track; + u_int32_t size = 24; + _AIFFInfo *aiff; + + assert(file); + + aiff = file->formatSpecific; + + track = _af_filehandle_get_track(file, AF_DEFAULT_TRACK); + + if (track->hasAESData == AF_FALSE) + return AF_SUCCEED; + + if (aiff->AESD_offset == 0) + aiff->AESD_offset = af_ftell(file->fh); + else + af_fseek(file->fh, aiff->AESD_offset, SEEK_SET); + + if (af_fwrite("AESD", 4, 1, file->fh) < 1) + return AF_FAIL; + + size = HOST_TO_BENDIAN_INT32(size); + + if (af_fwrite(&size, 4, 1, file->fh) < 1) + return AF_FAIL; + + if (af_fwrite(track->aesData, 24, 1, file->fh) < 1) + return AF_FAIL; + + return AF_SUCCEED; +} + +static status WriteSSND (AFfilehandle file) +{ + _Track *track; + u_int32_t chunkSize, zero = 0; + _AIFFInfo *aiff; + + assert(file); + assert(file->fh); + + aiff = file->formatSpecific; + + track = _af_filehandle_get_track(file, AF_DEFAULT_TRACK); + + if (aiff->SSND_offset == 0) + aiff->SSND_offset = af_ftell(file->fh); + else + af_fseek(file->fh, aiff->SSND_offset, SEEK_SET); + + chunkSize = _af_format_frame_size(&track->f, AF_FALSE) * + track->totalfframes + 8; + + af_fwrite("SSND", 4, 1, file->fh); + chunkSize = HOST_TO_BENDIAN_INT32(chunkSize); + af_fwrite(&chunkSize, 4, 1, file->fh); + + /* data offset */ + af_fwrite(&zero, 4, 1, file->fh); + /* block size */ + af_fwrite(&zero, 4, 1, file->fh); + + if (track->fpos_first_frame == 0) + track->fpos_first_frame = af_ftell(file->fh); + + return AF_SUCCEED; +} + +static status WriteINST (AFfilehandle file) +{ + u_int32_t length; + struct _INST instrumentdata; + + length = 20; + length = HOST_TO_BENDIAN_INT32(length); + + instrumentdata.sustainLoopPlayMode = + HOST_TO_BENDIAN_INT16(afGetLoopMode(file, AF_DEFAULT_INST, 1)); + instrumentdata.sustainLoopBegin = + HOST_TO_BENDIAN_INT16(afGetLoopStart(file, AF_DEFAULT_INST, 1)); + instrumentdata.sustainLoopEnd = + HOST_TO_BENDIAN_INT16(afGetLoopEnd(file, AF_DEFAULT_INST, 1)); + + instrumentdata.releaseLoopPlayMode = + HOST_TO_BENDIAN_INT16(afGetLoopMode(file, AF_DEFAULT_INST, 2)); + instrumentdata.releaseLoopBegin = + HOST_TO_BENDIAN_INT16(afGetLoopStart(file, AF_DEFAULT_INST, 2)); + instrumentdata.releaseLoopEnd = + HOST_TO_BENDIAN_INT16(afGetLoopEnd(file, AF_DEFAULT_INST, 2)); + + af_fwrite("INST", 4, 1, file->fh); + af_fwrite(&length, 4, 1, file->fh); + + instrumentdata.baseNote = + afGetInstParamLong(file, AF_DEFAULT_INST, AF_INST_MIDI_BASENOTE); + af_fwrite(&instrumentdata.baseNote, 1, 1, file->fh); + instrumentdata.detune = + afGetInstParamLong(file, AF_DEFAULT_INST, AF_INST_NUMCENTS_DETUNE); + af_fwrite(&instrumentdata.detune, 1, 1, file->fh); + instrumentdata.lowNote = + afGetInstParamLong(file, AF_DEFAULT_INST, AF_INST_MIDI_LONOTE); + af_fwrite(&instrumentdata.lowNote, 1, 1, file->fh); + instrumentdata.highNote = + afGetInstParamLong(file, AF_DEFAULT_INST, AF_INST_MIDI_HINOTE); + af_fwrite(&instrumentdata.highNote, 1, 1, file->fh); + instrumentdata.lowVelocity = + afGetInstParamLong(file, AF_DEFAULT_INST, AF_INST_MIDI_LOVELOCITY); + af_fwrite(&instrumentdata.lowVelocity, 1, 1, file->fh); + instrumentdata.highVelocity = + afGetInstParamLong(file, AF_DEFAULT_INST, AF_INST_MIDI_HIVELOCITY); + af_fwrite(&instrumentdata.highVelocity, 1, 1, file->fh); + + instrumentdata.gain = + afGetInstParamLong(file, AF_DEFAULT_INST, AF_INST_NUMDBS_GAIN); + instrumentdata.gain = HOST_TO_BENDIAN_INT16(instrumentdata.gain); + af_fwrite(&instrumentdata.gain, 2, 1, file->fh); + + af_fwrite(&instrumentdata.sustainLoopPlayMode, 2, 1, file->fh); + af_fwrite(&instrumentdata.sustainLoopBegin, 2, 1, file->fh); + af_fwrite(&instrumentdata.sustainLoopEnd, 2, 1, file->fh); + + af_fwrite(&instrumentdata.releaseLoopPlayMode, 2, 1, file->fh); + af_fwrite(&instrumentdata.releaseLoopBegin, 2, 1, file->fh); + af_fwrite(&instrumentdata.releaseLoopEnd, 2, 1, file->fh); + + return AF_SUCCEED; +} + +static status WriteMARK (AFfilehandle file) +{ + AFfileoffset chunkStartPosition, chunkEndPosition; + u_int32_t length = 0; + u_int16_t numMarkers, sb; + int i, *markids; + _AIFFInfo *aiff; + + assert(file); + + numMarkers = afGetMarkIDs(file, AF_DEFAULT_TRACK, NULL); + if (numMarkers == 0) + return AF_SUCCEED; + + aiff = file->formatSpecific; + + if (aiff->MARK_offset == 0) + aiff->MARK_offset = af_ftell(file->fh); + else + af_fseek(file->fh, aiff->MARK_offset, SEEK_SET); + + af_fwrite("MARK", 4, 1, file->fh); + af_fwrite(&length, 4, 1, file->fh); + + chunkStartPosition = af_ftell(file->fh); + + markids = _af_calloc(numMarkers, sizeof (int)); + assert(markids); + afGetMarkIDs(file, AF_DEFAULT_TRACK, markids); + + sb = HOST_TO_BENDIAN_INT16(numMarkers); + af_fwrite(&sb, 2, 1, file->fh); + + for (i=0; i<numMarkers; i++) + { + u_int8_t namelength, zero = 0; + u_int16_t id; + u_int32_t position; + char *name; + + id = markids[i]; + position = afGetMarkPosition(file, AF_DEFAULT_TRACK, markids[i]); + + id = HOST_TO_BENDIAN_INT16(id); + position = HOST_TO_BENDIAN_INT32(position); + + af_fwrite(&id, 2, 1, file->fh); + af_fwrite(&position, 4, 1, file->fh); + + name = afGetMarkName(file, AF_DEFAULT_TRACK, markids[i]); + assert(name); + namelength = strlen(name); + + /* Write the name as a Pascal-style string. */ + af_fwrite(&namelength, 1, 1, file->fh); + af_fwrite(name, 1, namelength, file->fh); + + /* + We need a pad byte if the length of the + Pascal-style string (including the size byte) + is odd, i.e. if namelength + 1 % 2 == 1. + */ + if ((namelength % 2) == 0) + af_fwrite(&zero, 1, 1, file->fh); + } + + free(markids); + + chunkEndPosition = af_ftell(file->fh); + length = chunkEndPosition - chunkStartPosition; + +#ifdef DEBUG + printf(" end: %d\n", chunkEndPosition); + printf(" length: %d\n", length); +#endif + + af_fseek(file->fh, chunkStartPosition - 4, SEEK_SET); + + length = HOST_TO_BENDIAN_INT32(length); + af_fwrite(&length, 4, 1, file->fh); + af_fseek(file->fh, chunkEndPosition, SEEK_SET); + + return AF_SUCCEED; +} + +/* + The FVER chunk, if present, is always the first chunk in the file. +*/ +static status WriteFVER (AFfilehandle file) +{ + u_int32_t chunkSize, timeStamp; + _AIFFInfo *aiff; + + assert(file->fileFormat == AF_FILE_AIFFC); + + aiff = file->formatSpecific; + + if (aiff->FVER_offset == 0) + aiff->FVER_offset = af_ftell(file->fh); + else + af_fseek(file->fh, aiff->FVER_offset, SEEK_SET); + + af_fwrite("FVER", 4, 1, file->fh); + + chunkSize = 4; + chunkSize = HOST_TO_BENDIAN_INT32(chunkSize); + af_fwrite(&chunkSize, 4, 1, file->fh); + + timeStamp = AIFC_VERSION_1; + timeStamp = HOST_TO_BENDIAN_INT32(timeStamp); + af_fwrite(&timeStamp, 4, 1, file->fh); + + return AF_SUCCEED; +} + +/* + WriteMiscellaneous writes all the miscellaneous data chunks in a + file handle structure to an AIFF or AIFF-C file. +*/ +static status WriteMiscellaneous (AFfilehandle file) +{ + _AIFFInfo *aiff; + int i; + + aiff = (_AIFFInfo *) file->formatSpecific; + + if (aiff->miscellaneousPosition == 0) + aiff->miscellaneousPosition = af_ftell(file->fh); + else + af_fseek(file->fh, aiff->miscellaneousPosition, SEEK_SET); + + for (i=0; i<file->miscellaneousCount; i++) + { + _Miscellaneous *misc = &file->miscellaneous[i]; + u_int32_t chunkType, chunkSize; + u_int8_t padByte = 0; + +#ifdef DEBUG + printf("WriteMiscellaneous: %d, type %d\n", i, misc->type); +#endif + + switch (misc->type) + { + case AF_MISC_NAME: + memcpy(&chunkType, "NAME", 4); break; + case AF_MISC_AUTH: + memcpy(&chunkType, "AUTH", 4); break; + case AF_MISC_COPY: + memcpy(&chunkType, "(c) ", 4); break; + case AF_MISC_ANNO: + memcpy(&chunkType, "ANNO", 4); break; + case AF_MISC_MIDI: + memcpy(&chunkType, "MIDI", 4); break; + case AF_MISC_APPL: + memcpy(&chunkType, "APPL", 4); break; + } + + chunkSize = HOST_TO_BENDIAN_INT32(misc->size); + + af_fwrite(&chunkType, 4, 1, file->fh); + af_fwrite(&chunkSize, 4, 1, file->fh); + /* + Write the miscellaneous buffer and then a pad byte + if necessary. If the buffer is null, skip the space + for now. + */ + if (misc->buffer != NULL) + af_fwrite(misc->buffer, misc->size, 1, file->fh); + else + af_fseek(file->fh, misc->size, SEEK_CUR); + + if (misc->size % 2 != 0) + af_fwrite(&padByte, 1, 1, file->fh); + } + + return AF_SUCCEED; +} diff --git a/libaudiofile/audiofile.exports b/libaudiofile/audiofile.exports new file mode 100644 index 0000000..3050bd6 --- /dev/null +++ b/libaudiofile/audiofile.exports @@ -0,0 +1,104 @@ +AUpvfree +AUpvgetmaxitems +AUpvgetparam +AUpvgetval +AUpvgetvaltype +AUpvnew +AUpvsetparam +AUpvsetval +AUpvsetvaltype +afCloseFile +afFreeFileSetup +afGetAESChannelData +afGetByteOrder +afGetChannels +afGetCompression +afGetDataOffset +afGetFileFormat +afGetFrameCount +afGetFrameSize +afGetInstIDs +afGetInstParamLong +afGetInstParams +afGetLoopCount +afGetLoopEnd +afGetLoopEndFrame +afGetLoopIDs +afGetLoopMode +afGetLoopStart +afGetLoopStartFrame +afGetLoopTrack +afGetMarkComment +afGetMarkIDs +afGetMarkName +afGetMarkPosition +afGetMiscIDs +afGetMiscSize +afGetMiscType +afGetPCMMapping +afGetRate +afGetSampleFormat +afGetTrackBytes +afGetTrackIDs +afGetVirtualByteOrder +afGetVirtualChannels +afGetVirtualFrameSize +afGetVirtualPCMMapping +afGetVirtualSampleFormat +afIdentifyFD +afIdentifyNamedFD +afInitAESChannelData +afInitAESChannelDataTo +afInitByteOrder +afInitChannels +afInitCompression +afInitDataOffset +afInitFileFormat +afInitFrameCount +afInitInstIDs +afInitLoopIDs +afInitMarkComment +afInitMarkIDs +afInitMarkName +afInitMiscIDs +afInitMiscSize +afInitMiscType +afInitPCMMapping +afInitRate +afInitSampleFormat +afInitTrackIDs +afNewFileSetup +afOpenFD +afOpenFile +afOpenNamedFD +afOpenVirtualFile +afQuery +afQueryDouble +afQueryLong +afQueryPointer +afReadFrames +afReadMisc +afSeekFrame +afSeekMisc +afSetAESChannelData +afSetChannelMatrix +afSetErrorHandler +afSetInstParamLong +afSetInstParams +afSetLoopCount +afSetLoopEnd +afSetLoopEndFrame +afSetLoopMode +afSetLoopStart +afSetLoopStartFrame +afSetLoopTrack +afSetMarkPosition +afSetTrackPCMMapping +afSetVirtualByteOrder +afSetVirtualChannels +afSetVirtualPCMMapping +afSetVirtualSampleFormat +afSyncFile +afTellFrame +afWriteFrames +afWriteMisc diff --git a/libaudiofile/audiofile.h b/libaudiofile/audiofile.h new file mode 100644 index 0000000..a39c0ba --- /dev/null +++ b/libaudiofile/audiofile.h @@ -0,0 +1,601 @@ +/* + Audio File Library + Copyright (C) 1998-2000, Michael Pruett <michael@68k.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307 USA. +*/ + +/* + audiofile.h + + This file contains the public interfaces to the Audio File Library. +*/ + +#ifndef AUDIOFILE_H +#define AUDIOFILE_H + +#include <sys/types.h> +#include <aupvlist.h> + +#define LIBAUDIOFILE_MAJOR_VERSION 0 +#define LIBAUDIOFILE_MINOR_VERSION 2 +#define LIBAUDIOFILE_MICRO_VERSION 4 + +#ifdef __cplusplus +extern "C" +{ +#endif /* __cplusplus */ + +typedef struct _AFvirtualfile AFvirtualfile; + +typedef struct _AFfilesetup *AFfilesetup; +typedef struct _AFfilehandle *AFfilehandle; +typedef void (*AFerrfunc)(long, const char *); + +typedef off_t AFframecount; +typedef off_t AFfileoffset; + +#define AF_NULL_FILESETUP ((struct _AFfilesetup *) 0) +#define AF_NULL_FILEHANDLE ((struct _AFfilehandle *) 0) + +#define AF_ERR_BASE 3000 + +enum +{ + AF_DEFAULT_TRACK = 1001 +}; + +enum +{ + AF_DEFAULT_INST = 2001 +}; + +enum +{ + AF_NUM_UNLIMITED = 99999 +}; + +enum +{ + AF_BYTEORDER_BIGENDIAN = 501, + AF_BYTEORDER_LITTLEENDIAN = 502 +}; + +enum +{ + AF_FILE_UNKNOWN = -1, + AF_FILE_RAWDATA = 0, + AF_FILE_AIFFC = 1, + AF_FILE_AIFF = 2, + AF_FILE_NEXTSND = 3, + AF_FILE_WAVE = 4, + AF_FILE_BICSF = 5, + AF_FILE_IRCAM = AF_FILE_BICSF, + AF_FILE_MPEG1BITSTREAM = 6, /* not implemented */ + AF_FILE_SOUNDDESIGNER1 = 7, /* not implemented */ + AF_FILE_SOUNDDESIGNER2 = 8, /* not implemented */ + AF_FILE_AVR = 9, + AF_FILE_IFF_8SVX = 10, + AF_FILE_SAMPLEVISION = 11, /* not implemented */ + AF_FILE_VOC = 12, /* not implemented */ + AF_FILE_NIST_SPHERE = 13, + AF_FILE_SOUNDFONT2 = 14 /* not implemented */ +}; + +enum +{ + AF_LOOP_MODE_NOLOOP = 0, + AF_LOOP_MODE_FORW = 1, + AF_LOOP_MODE_FORWBAKW = 2 +}; + +enum +{ + AF_SAMPFMT_TWOSCOMP = 401, /* linear two's complement */ + AF_SAMPFMT_UNSIGNED = 402, /* unsigned integer */ + AF_SAMPFMT_FLOAT = 403, /* 32-bit IEEE floating-point */ + AF_SAMPFMT_DOUBLE = 404 /* 64-bit IEEE double-precision floating-point */ +}; + +enum +{ + AF_INST_LOOP_OFF = 0, /* no looping */ + AF_INST_LOOP_CONTINUOUS = 1, /* loop continuously through decay */ + AF_INST_LOOP_SUSTAIN = 3 /* loop during sustain, then continue */ +}; + +enum +{ + AF_INST_MIDI_BASENOTE = 301, + AF_INST_NUMCENTS_DETUNE = 302, + AF_INST_MIDI_LONOTE = 303, + AF_INST_MIDI_HINOTE = 304, + AF_INST_MIDI_LOVELOCITY = 305, + AF_INST_MIDI_HIVELOCITY = 306, + AF_INST_NUMDBS_GAIN = 307, + AF_INST_SUSLOOPID = 308, /* loop id for AIFF sustain loop */ + AF_INST_RELLOOPID = 309, /* loop id for AIFF release loop */ + AF_INST_SAMP_STARTFRAME = 310, /* start sample for this inst */ + AF_INST_SAMP_ENDFRAME = 311, /* end sample for this inst */ + AF_INST_SAMP_MODE = 312, /* looping mode for this inst */ + AF_INST_TRACKID = 313, + AF_INST_NAME = 314, /* name of this inst */ + AF_INST_SAMP_RATE = 315, /* sample rate of this inst's sample */ + AF_INST_PRESETID = 316, /* ID of preset containing this inst */ + AF_INST_PRESET_NAME = 317 /* name of preset containing this inst */ +}; + +enum +{ + AF_MISC_UNRECOGNIZED = 0, /* unrecognized data chunk */ + AF_MISC_COPY = 201, /* copyright string */ + AF_MISC_AUTH = 202, /* author string */ + AF_MISC_NAME = 203, /* name string */ + AF_MISC_ANNO = 204, /* annotation string */ + AF_MISC_APPL = 205, /* application-specific data */ + AF_MISC_MIDI = 206, /* MIDI exclusive data */ + AF_MISC_PCMMAP = 207, /* PCM mapping information (future use) */ + AF_MISC_NeXT = 208, /* misc binary data appended to NeXT header */ + AF_MISC_IRCAM_PEAKAMP = 209, /* peak amplitude information */ + AF_MISC_IRCAM_COMMENT = 210, /* BICSF text comment */ + AF_MISC_COMMENT = 210, /* general text comment */ + + AF_MISC_ICMT = AF_MISC_COMMENT, /* comments chunk (WAVE format) */ + AF_MISC_ICRD = 211, /* creation date (WAVE format) */ + AF_MISC_ISFT = 212 /* software name (WAVE format) */ +}; + +enum +{ + /* supported compression schemes */ + AF_COMPRESSION_UNKNOWN = -1, + AF_COMPRESSION_NONE = 0, + AF_COMPRESSION_G722 = 501, + AF_COMPRESSION_G711_ULAW = 502, + AF_COMPRESSION_G711_ALAW = 503, + + /* Apple proprietary AIFF-C compression schemes (not supported) */ + AF_COMPRESSION_APPLE_ACE2 = 504, + AF_COMPRESSION_APPLE_ACE8 = 505, + AF_COMPRESSION_APPLE_MAC3 = 506, + AF_COMPRESSION_APPLE_MAC6 = 507, + + AF_COMPRESSION_G726 = 517, + AF_COMPRESSION_G728 = 518, + AF_COMPRESSION_DVI_AUDIO = 519, + AF_COMPRESSION_IMA = AF_COMPRESSION_DVI_AUDIO, + AF_COMPRESSION_GSM = 520, + AF_COMPRESSION_FS1016 = 521, + AF_COMPRESSION_DV = 522, + AF_COMPRESSION_MS_ADPCM = 523 +}; + +/* tokens for afQuery() -- see the man page for instructions */ +/* level 1 selectors */ +enum +{ + AF_QUERYTYPE_INSTPARAM = 500, + AF_QUERYTYPE_FILEFMT = 501, + AF_QUERYTYPE_COMPRESSION = 502, + AF_QUERYTYPE_COMPRESSIONPARAM = 503, + AF_QUERYTYPE_MISC = 504, + AF_QUERYTYPE_INST = 505, + AF_QUERYTYPE_MARK = 506, + AF_QUERYTYPE_LOOP = 507 +}; + +/* level 2 selectors */ +enum +{ + AF_QUERY_NAME = 600, /* get name (1-3 words) */ + AF_QUERY_DESC = 601, /* get description */ + AF_QUERY_LABEL = 602, /* get 4- or 5-char label */ + AF_QUERY_TYPE = 603, /* get type token */ + AF_QUERY_DEFAULT = 604, /* dflt. value for param */ + AF_QUERY_ID_COUNT = 605, /* get number of ids avail. */ + AF_QUERY_IDS = 606, /* get array of id tokens */ + AF_QUERY_IMPLEMENTED = 613, /* boolean */ + AF_QUERY_TYPE_COUNT = 607, /* get number of types av. */ + AF_QUERY_TYPES = 608, /* get array of types */ + AF_QUERY_NATIVE_SAMPFMT = 609, /* for compression */ + AF_QUERY_NATIVE_SAMPWIDTH = 610, + AF_QUERY_SQUISHFAC = 611, /* 1.0 means variable */ + AF_QUERY_MAX_NUMBER = 612, /* max allowed in file */ + AF_QUERY_SUPPORTED = 613 /* insts, loops, etc., supported? */ +}; + +/* level 2 selectors which have sub-selectors */ +enum +{ + AF_QUERY_TRACKS = 620, + AF_QUERY_CHANNELS = 621, + AF_QUERY_SAMPLE_SIZES = 622, + AF_QUERY_SAMPLE_FORMATS = 623, + AF_QUERY_COMPRESSION_TYPES = 624 +}; + +/* level 3 sub-selectors */ +enum +{ + AF_QUERY_VALUE_COUNT = 650, /* number of values of the above */ + AF_QUERY_VALUES = 651 /* array of those values */ +}; + + +/* + Old Audio File Library error codes. These are still returned by the + AFerrorhandler calls, but are not used by the new digital media library + error reporting routines. See the bottom of this file for the new error + tokens. +*/ + +enum +{ + AF_BAD_NOT_IMPLEMENTED = 0, /* not implemented yet */ + AF_BAD_FILEHANDLE = 1, /* tried to use invalid filehandle */ + AF_BAD_OPEN = 3, /* unix open failed */ + AF_BAD_CLOSE = 4, /* unix close failed */ + AF_BAD_READ = 5, /* unix read failed */ + AF_BAD_WRITE = 6, /* unix write failed */ + AF_BAD_LSEEK = 7, /* unix lseek failed */ + AF_BAD_NO_FILEHANDLE = 8, /* failed to allocate a filehandle struct */ + AF_BAD_ACCMODE = 10, /* unrecognized audio file access mode */ + AF_BAD_NOWRITEACC = 11, /* file not open for writing */ + AF_BAD_NOREADACC = 12, /* file not open for reading */ + AF_BAD_FILEFMT = 13, /* unrecognized audio file format */ + AF_BAD_RATE = 14, /* invalid sample rate */ + AF_BAD_CHANNELS = 15, /* invalid number of channels*/ + AF_BAD_SAMPCNT = 16, /* invalid sample count */ + AF_BAD_WIDTH = 17, /* invalid sample width */ + AF_BAD_SEEKMODE = 18, /* invalid seek mode */ + AF_BAD_NO_LOOPDATA = 19, /* failed to allocate loop struct */ + AF_BAD_MALLOC = 20, /* malloc failed somewhere */ + AF_BAD_LOOPID = 21, + AF_BAD_SAMPFMT = 22, /* bad sample format */ + AF_BAD_FILESETUP = 23, /* bad file setup structure*/ + AF_BAD_TRACKID = 24, /* no track corresponding to id */ + AF_BAD_NUMTRACKS = 25, /* wrong number of tracks for file format */ + AF_BAD_NO_FILESETUP = 26, /* failed to allocate a filesetup struct*/ + AF_BAD_LOOPMODE = 27, /* unrecognized loop mode value */ + AF_BAD_INSTID = 28, /* invalid instrument id */ + AF_BAD_NUMLOOPS = 29, /* bad number of loops */ + AF_BAD_NUMMARKS = 30, /* bad number of markers */ + AF_BAD_MARKID = 31, /* bad marker id */ + AF_BAD_MARKPOS = 32, /* invalid marker position value */ + AF_BAD_NUMINSTS = 33, /* invalid number of instruments */ + AF_BAD_NOAESDATA = 34, + AF_BAD_MISCID = 35, + AF_BAD_NUMMISC = 36, + AF_BAD_MISCSIZE = 37, + AF_BAD_MISCTYPE = 38, + AF_BAD_MISCSEEK = 39, + AF_BAD_STRLEN = 40, /* invalid string length */ + AF_BAD_RATECONV = 45, + AF_BAD_SYNCFILE = 46, + AF_BAD_CODEC_CONFIG = 47, /* improperly configured codec */ + AF_BAD_CODEC_STATE = 48, /* invalid codec state: can't recover */ + AF_BAD_CODEC_LICENSE = 49, /* no license available for codec */ + AF_BAD_CODEC_TYPE = 50, /* unsupported codec type */ + AF_BAD_COMPRESSION = AF_BAD_CODEC_CONFIG, /* for back compat */ + AF_BAD_COMPTYPE = AF_BAD_CODEC_TYPE, /* for back compat */ + + AF_BAD_INSTPTYPE = 51, /* invalid instrument parameter type */ + AF_BAD_INSTPID = 52, /* invalid instrument parameter id */ + AF_BAD_BYTEORDER = 53, + AF_BAD_FILEFMT_PARAM = 54, /* unrecognized file format parameter */ + AF_BAD_COMP_PARAM = 55, /* unrecognized compression parameter */ + AF_BAD_DATAOFFSET = 56, /* bad data offset */ + AF_BAD_FRAMECNT = 57, /* bad frame count */ + AF_BAD_QUERYTYPE = 58, /* bad query type */ + AF_BAD_QUERY = 59, /* bad argument to afQuery() */ + AF_WARNING_CODEC_RATE = 60, /* using 8k instead of codec rate 8012 */ + AF_WARNING_RATECVT = 61, /* warning about rate conversion used */ + + AF_BAD_HEADER = 62, /* failed to parse header */ + AF_BAD_FRAME = 63, /* bad frame number */ + AF_BAD_LOOPCOUNT = 64, /* bad loop count */ + AF_BAD_DMEDIA_CALL = 65, /* error in dmedia subsystem call */ + + /* AIFF/AIFF-C specific errors when parsing file header */ + AF_BAD_AIFF_HEADER = 108, /* failed to parse chunk header */ + AF_BAD_AIFF_FORM = 109, /* failed to parse FORM chunk */ + AF_BAD_AIFF_SSND = 110, /* failed to parse SSND chunk */ + AF_BAD_AIFF_CHUNKID = 111, /* unrecognized AIFF/AIFF-C chunk id */ + AF_BAD_AIFF_COMM = 112, /* failed to parse COMM chunk */ + AF_BAD_AIFF_INST = 113, /* failed to parse INST chunk */ + AF_BAD_AIFF_MARK = 114, /* failed to parse MARK chunk */ + AF_BAD_AIFF_SKIP = 115, /* failed to skip unsupported chunk */ + AF_BAD_AIFF_LOOPMODE = 116 /* unrecognized loop mode (forw, etc)*/ +}; + +/* new error codes which may be retrieved via dmGetError() */ +/* The old error tokens continue to be retrievable via the AFerrorhandler */ +/* AF_ERR_BASE is #defined in dmedia/dmedia.h */ + +enum +{ + AF_ERR_NOT_IMPLEMENTED = 0+AF_ERR_BASE, /* not implemented yet */ + AF_ERR_BAD_FILEHANDLE = 1+AF_ERR_BASE, /* invalid filehandle */ + AF_ERR_BAD_READ = 5+AF_ERR_BASE, /* unix read failed */ + AF_ERR_BAD_WRITE = 6+AF_ERR_BASE, /* unix write failed */ + AF_ERR_BAD_LSEEK = 7+AF_ERR_BASE, /* unix lseek failed */ + AF_ERR_BAD_ACCMODE = 10+AF_ERR_BASE, /* unrecognized audio file access mode */ + AF_ERR_NO_WRITEACC = 11+AF_ERR_BASE, /* file not open for writing */ + AF_ERR_NO_READACC = 12+AF_ERR_BASE, /* file not open for reading */ + AF_ERR_BAD_FILEFMT = 13+AF_ERR_BASE, /* unrecognized audio file format */ + AF_ERR_BAD_RATE = 14+AF_ERR_BASE, /* invalid sample rate */ + AF_ERR_BAD_CHANNELS = 15+AF_ERR_BASE, /* invalid # channels*/ + AF_ERR_BAD_SAMPCNT = 16+AF_ERR_BASE, /* invalid sample count */ + AF_ERR_BAD_WIDTH = 17+AF_ERR_BASE, /* invalid sample width */ + AF_ERR_BAD_SEEKMODE = 18+AF_ERR_BASE, /* invalid seek mode */ + AF_ERR_BAD_LOOPID = 21+AF_ERR_BASE, /* invalid loop id */ + AF_ERR_BAD_SAMPFMT = 22+AF_ERR_BASE, /* bad sample format */ + AF_ERR_BAD_FILESETUP = 23+AF_ERR_BASE, /* bad file setup structure*/ + AF_ERR_BAD_TRACKID = 24+AF_ERR_BASE, /* no track corresponding to id */ + AF_ERR_BAD_NUMTRACKS = 25+AF_ERR_BASE, /* wrong number of tracks for file format */ + AF_ERR_BAD_LOOPMODE = 27+AF_ERR_BASE, /* unrecognized loop mode symbol */ + AF_ERR_BAD_INSTID = 28+AF_ERR_BASE, /* invalid instrument id */ + AF_ERR_BAD_NUMLOOPS = 29+AF_ERR_BASE, /* bad number of loops */ + AF_ERR_BAD_NUMMARKS = 30+AF_ERR_BASE, /* bad number of markers */ + AF_ERR_BAD_MARKID = 31+AF_ERR_BASE, /* bad marker id */ + AF_ERR_BAD_MARKPOS = 32+AF_ERR_BASE, /* invalid marker position value */ + AF_ERR_BAD_NUMINSTS = 33+AF_ERR_BASE, /* invalid number of instruments */ + AF_ERR_BAD_NOAESDATA = 34+AF_ERR_BASE, + AF_ERR_BAD_MISCID = 35+AF_ERR_BASE, + AF_ERR_BAD_NUMMISC = 36+AF_ERR_BASE, + AF_ERR_BAD_MISCSIZE = 37+AF_ERR_BASE, + AF_ERR_BAD_MISCTYPE = 38+AF_ERR_BASE, + AF_ERR_BAD_MISCSEEK = 39+AF_ERR_BASE, + AF_ERR_BAD_STRLEN = 40+AF_ERR_BASE, /* invalid string length */ + AF_ERR_BAD_RATECONV = 45+AF_ERR_BASE, + AF_ERR_BAD_SYNCFILE = 46+AF_ERR_BASE, + AF_ERR_BAD_CODEC_CONFIG = 47+AF_ERR_BASE, /* improperly configured codec */ + AF_ERR_BAD_CODEC_TYPE = 50+AF_ERR_BASE, /* unsupported codec type */ + AF_ERR_BAD_INSTPTYPE = 51+AF_ERR_BASE, /* invalid instrument parameter type */ + AF_ERR_BAD_INSTPID = 52+AF_ERR_BASE, /* invalid instrument parameter id */ + + AF_ERR_BAD_BYTEORDER = 53+AF_ERR_BASE, + AF_ERR_BAD_FILEFMT_PARAM = 54+AF_ERR_BASE, /* unrecognized file format parameter */ + AF_ERR_BAD_COMP_PARAM = 55+AF_ERR_BASE, /* unrecognized compression parameter */ + AF_ERR_BAD_DATAOFFSET = 56+AF_ERR_BASE, /* bad data offset */ + AF_ERR_BAD_FRAMECNT = 57+AF_ERR_BASE, /* bad frame count */ + + AF_ERR_BAD_QUERYTYPE = 58+AF_ERR_BASE, /* bad query type */ + AF_ERR_BAD_QUERY = 59+AF_ERR_BASE, /* bad argument to afQuery() */ + AF_ERR_BAD_HEADER = 62+AF_ERR_BASE, /* failed to parse header */ + AF_ERR_BAD_FRAME = 63+AF_ERR_BASE, /* bad frame number */ + AF_ERR_BAD_LOOPCOUNT = 64+AF_ERR_BASE, /* bad loop count */ + + /* AIFF/AIFF-C specific errors when parsing file header */ + + AF_ERR_BAD_AIFF_HEADER = 66+AF_ERR_BASE, /* failed to parse chunk header */ + AF_ERR_BAD_AIFF_FORM = 67+AF_ERR_BASE, /* failed to parse FORM chunk */ + AF_ERR_BAD_AIFF_SSND = 68+AF_ERR_BASE, /* failed to parse SSND chunk */ + AF_ERR_BAD_AIFF_CHUNKID = 69+AF_ERR_BASE, /* unrecognized AIFF/AIFF-C chunk id */ + AF_ERR_BAD_AIFF_COMM = 70+AF_ERR_BASE, /* failed to parse COMM chunk */ + AF_ERR_BAD_AIFF_INST = 71+AF_ERR_BASE, /* failed to parse INST chunk */ + AF_ERR_BAD_AIFF_MARK = 72+AF_ERR_BASE, /* failed to parse MARK chunk */ + AF_ERR_BAD_AIFF_SKIP = 73+AF_ERR_BASE, /* failed to skip unsupported chunk */ + AF_ERR_BAD_AIFF_LOOPMODE = 74+AF_ERR_BASE /* unrecognized loop mode (forw, etc) */ +}; + + +/* global routines */ +AFerrfunc afSetErrorHandler (AFerrfunc efunc); + +/* query routines */ +AUpvlist afQuery (int querytype, int arg1, int arg2, int arg3, int arg4); +long afQueryLong (int querytype, int arg1, int arg2, int arg3, int arg4); +double afQueryDouble (int querytype, int arg1, int arg2, int arg3, int arg4); +void *afQueryPointer (int querytype, int arg1, int arg2, int arg3, int arg4); + +/* basic operations on file handles and file setups */ +AFfilesetup afNewFileSetup (void); +void afFreeFileSetup (AFfilesetup); +int afIdentifyFD (int); +int afIdentifyNamedFD (int, const char *filename, int *implemented); + +AFfilehandle afOpenFile (const char *filename, const char *mode, + AFfilesetup setup); +AFfilehandle afOpenVirtualFile (AFvirtualfile *vfile, const char *mode, + AFfilesetup setup); +AFfilehandle afOpenFD (int fd, const char *mode, AFfilesetup setup); +AFfilehandle afOpenNamedFD (int fd, const char *mode, AFfilesetup setup, + const char *filename); + +void afSaveFilePosition (AFfilehandle file); +void afRestoreFilePosition (AFfilehandle file); +int afSyncFile (AFfilehandle file); +int afCloseFile (AFfilehandle file); + +void afInitFileFormat (AFfilesetup, int format); +int afGetFileFormat (AFfilehandle, int *version); + +/* track */ +void afInitTrackIDs (AFfilesetup, int *trackids, int trackCount); +int afGetTrackIDs (AFfilehandle, int *trackids); + +/* track data: reading, writng, seeking, sizing frames */ +int afReadFrames (AFfilehandle, int track, void *buffer, int frameCount); +int afWriteFrames (AFfilehandle, int track, const void *buffer, int frameCount); +AFframecount afSeekFrame (AFfilehandle, int track, AFframecount frameoffset); +AFframecount afTellFrame (AFfilehandle, int track); +AFfileoffset afGetTrackBytes (AFfilehandle, int track); +float afGetFrameSize (AFfilehandle, int track, int expand3to4); +float afGetVirtualFrameSize (AFfilehandle, int track, int expand3to4); + +/* track data: AES data */ +/* afInitAESChannelData is obsolete -- use afInitAESChannelDataTo() */ +void afInitAESChannelData (AFfilesetup, int track); /* obsolete */ +void afInitAESChannelDataTo (AFfilesetup, int track, int willBeData); +int afGetAESChannelData (AFfilehandle, int track, unsigned char buf[24]); +void afSetAESChannelData (AFfilehandle, int track, unsigned char buf[24]); + +#if 0 +/* track setup format initialized via DMparams */ +/* track format retrieved via DMparams */ +DMstatus afInitFormatParams (AFfilesetup, int track, DMparams *params); +/* virtual format set via DMparams */ +DMstatus afGetFormatParams (AFfilehandle, int track, DMparams *params); +/* virtual format retrieved via DMparams */ +DMstatus afSetVirtualFormatParams (AFfilehandle, int track, DMparams *params); +DMstatus afGetVirtualFormatParams (AFfilehandle, int track, DMparams *params); +/* conversion/compression params set via DMparams */ +DMstatus afSetConversionParams (AFfilehandle, int track, DMparams *params); +/* conversion/compression params retrieved via DMparams */ +DMstatus afGetConversionParams (AFfilehandle, int track, DMparams *params); +#endif + +/* track data: byte order */ +void afInitByteOrder (AFfilesetup, int track, int byteOrder); +int afGetByteOrder (AFfilehandle, int track); +int afSetVirtualByteOrder (AFfilehandle, int track, int byteOrder); +int afGetVirtualByteOrder (AFfilehandle, int track); + +/* track data: number of channels */ +void afInitChannels (AFfilesetup, int track, int nchannels); +int afGetChannels (AFfilehandle, int track); +int afSetVirtualChannels (AFfilehandle, int track, int channelCount); +int afGetVirtualChannels (AFfilehandle, int track); +void afSetChannelMatrix (AFfilehandle, int track, double *matrix); + +/* track data: sample format and sample width */ +void afInitSampleFormat (AFfilesetup, int track, int sampleFormat, + int sampleWidth); +void afGetSampleFormat (AFfilehandle file, int track, int *sampfmt, + int *sampwidth); +void afGetVirtualSampleFormat (AFfilehandle file, int track, int *sampfmt, + int *sampwidth); +int afSetVirtualSampleFormat (AFfilehandle, int track, + int sampleFormat, int sampleWidth); +void afGetVirtualSampleFormat (AFfilehandle, int track, + int *sampleFormat, int *sampleWidth); + +/* track data: sampling rate */ +void afInitRate (AFfilesetup, int track, double rate); +double afGetRate (AFfilehandle, int track); + +#if 0 +int afSetVirtualRate (AFfilehandle, int track, double rate); +double afGetVirtualRate (AFfilehandle, int track); +#endif + +/* track data: compression */ +void afInitCompression (AFfilesetup, int track, int compression); +#if 0 +void afInitCompressionParams (AFfilesetup, int track, int compression + AUpvlist params, int parameterCount); +#endif + +int afGetCompression (AFfilehandle, int track); +#if 0 +void afGetCompressionParams (AFfilehandle, int track, int *compression, + AUpvlist params, int parameterCount); + +int afSetVirtualCompression (AFfilesetup, int track, int compression); +void afSetVirtualCompressionParams (AFfilehandle, int track, int compression, + AUpvlist params, int parameterCount); + +int afGetVirtualCompression (AFfilesetup, int track, int compression); +void afGetVirtualCompressionParams (AFfilehandle, int track, int *compression, + AUpvlist params, int parameterCount); +#endif + +/* track data: pcm mapping */ +void afInitPCMMapping (AFfilesetup filesetup, int track, + double slope, double intercept, double minClip, double maxClip); +void afGetPCMMapping (AFfilehandle file, int track, + double *slope, double *intercept, double *minClip, double *maxClip); +/* NOTE: afSetTrackPCMMapping() is special--it does not set the virtual */ +/* format; it changes what the AF thinks the track format is! Be careful. */ +int afSetTrackPCMMapping (AFfilehandle file, int track, + double slope, double intercept, double minClip, double maxClip); +/* NOTE: afSetVirtualPCMMapping() is different from afSetTrackPCMMapping(): */ +/* see comment for afSetTrackPCMMapping(). */ +int afSetVirtualPCMMapping (AFfilehandle file, int track, + double slope, double intercept, double minClip, double maxClip); +void afGetVirtualPCMMapping (AFfilehandle file, int track, + double *slope, double *intercept, double *minClip, double *maxClip); + +/* track data: data offset within the file */ +/* initialize for raw reading only */ +void afInitDataOffset(AFfilesetup, int track, AFfileoffset offset); +AFfileoffset afGetDataOffset (AFfilehandle, int track); + +/* track data: count of frames in file */ +void afInitFrameCount (AFfilesetup, int track, AFframecount frameCount); +AFframecount afGetFrameCount (AFfilehandle file, int track); + +/* loop operations */ +void afInitLoopIDs (AFfilesetup, int instid, int ids[], int nids); +int afGetLoopIDs (AFfilehandle, int instid, int loopids[]); +void afSetLoopMode (AFfilehandle, int instid, int loop, int mode); +int afGetLoopMode (AFfilehandle, int instid, int loopid); +int afSetLoopCount (AFfilehandle, int instid, int loop, int count); +int afGetLoopCount (AFfilehandle, int instid, int loopid); +void afSetLoopStart (AFfilehandle, int instid, int loopid, int markerid); +int afGetLoopStart (AFfilehandle, int instid, int loopid); +void afSetLoopEnd (AFfilehandle, int instid, int loopid, int markerid); +int afGetLoopEnd (AFfilehandle, int instid, int loopid); + +int afSetLoopStartFrame (AFfilehandle, int instid, int loop, + AFframecount startFrame); +AFframecount afGetLoopStartFrame (AFfilehandle, int instid, int loop); +int afSetLoopEndFrame (AFfilehandle, int instid, int loop, + AFframecount startFrame); +AFframecount afGetLoopEndFrame (AFfilehandle, int instid, int loop); + +void afSetLoopTrack (AFfilehandle, int instid, int loopid, int trackid); +int afGetLoopTrack (AFfilehandle, int instid, int loopid); + +/* marker operations */ +void afInitMarkIDs (AFfilesetup, int trackid, int *ids, int nids); +int afGetMarkIDs (AFfilehandle file, int trackid, int markids[]); +void afSetMarkPosition (AFfilehandle file, int trackid, int markid, + AFframecount markpos); +AFframecount afGetMarkPosition (AFfilehandle file, int trackid, int markid); +void afInitMarkName (AFfilesetup, int trackid, int marker, const char *name); +void afInitMarkComment (AFfilesetup, int trackid, int marker, + const char *comment); +char *afGetMarkName (AFfilehandle file, int trackid, int markid); +char *afGetMarkComment (AFfilehandle file, int trackid, int markid); + +/* instrument operations */ +void afInitInstIDs (AFfilesetup, int *ids, int nids); +int afGetInstIDs (AFfilehandle file, int *instids); +void afGetInstParams (AFfilehandle file, int instid, AUpvlist pvlist, + int nparams); +void afSetInstParams (AFfilehandle file, int instid, AUpvlist pvlist, + int nparams); +long afGetInstParamLong (AFfilehandle file, int instid, int param); +void afSetInstParamLong (AFfilehandle file, int instid, int param, long value); + +/* miscellaneous data operations */ +void afInitMiscIDs (AFfilesetup, int *ids, int nids); +int afGetMiscIDs (AFfilehandle, int *ids); +void afInitMiscType (AFfilesetup, int miscellaneousid, int type); +int afGetMiscType (AFfilehandle, int miscellaneousid); +void afInitMiscSize (AFfilesetup, int miscellaneousid, int size); +int afGetMiscSize (AFfilehandle, int miscellaneousid); +int afWriteMisc (AFfilehandle, int miscellaneousid, void *buf, int bytes); +int afReadMisc (AFfilehandle, int miscellaneousid, void *buf, int bytes); +int afSeekMisc (AFfilehandle, int miscellaneousid, int offset); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* AUDIOFILE_H */ diff --git a/libaudiofile/aupv.c b/libaudiofile/aupv.c new file mode 100644 index 0000000..9ac5be1 --- /dev/null +++ b/libaudiofile/aupv.c @@ -0,0 +1,251 @@ +/* + Audio File Library + Copyright (C) 1998-2000, Michael Pruett <michael@68k.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307 USA. +*/ + +/* + aupv.c + + This file contains an implementation of SGI's Audio Library parameter + value list functions. +*/ + +#include <sys/types.h> +#include <stdlib.h> +#include <string.h> +#include <assert.h> + +#include "aupvinternal.h" +#include "aupvlist.h" + +AUpvlist AUpvnew (int maxitems) +{ + AUpvlist aupvlist; + int i; + + if (maxitems <= 0) + return AU_NULL_PVLIST; + + aupvlist = (AUpvlist) malloc(sizeof (struct _AUpvlist)); + assert(aupvlist); + if (aupvlist == NULL) + return AU_NULL_PVLIST; + + aupvlist->items = calloc(maxitems, sizeof (struct _AUpvitem)); + + assert(aupvlist->items); + if (aupvlist->items == NULL) + { + free(aupvlist); + return AU_NULL_PVLIST; + } + + /* Initialize the items in the list. */ + for (i=0; i<maxitems; i++) + { + aupvlist->items[i].valid = _AU_VALID_PVITEM; + aupvlist->items[i].type = AU_PVTYPE_LONG; + aupvlist->items[i].parameter = 0; + memset(&aupvlist->items[i].value, 0, sizeof (aupvlist->items[i].value)); + } + + aupvlist->valid = _AU_VALID_PVLIST; + aupvlist->count = maxitems; + + return aupvlist; +} + +int AUpvgetmaxitems (AUpvlist list) +{ + assert(list); + + if (list == AU_NULL_PVLIST) + return AU_BAD_PVLIST; + if (list->valid != _AU_VALID_PVLIST) + return AU_BAD_PVLIST; + + return list->count; +} + +int AUpvfree (AUpvlist list) +{ + assert(list); + assert(list->items); + + if (list == AU_NULL_PVLIST) + return AU_BAD_PVLIST; + if (list->valid != _AU_VALID_PVLIST) + return AU_BAD_PVLIST; + + if ((list->items != _AU_NULL_PVITEM) && + (list->items[0].valid == _AU_VALID_PVITEM)) + { + free(list->items); + } + + free(list); + + return _AU_SUCCESS; +} + +int AUpvsetparam (AUpvlist list, int item, int param) +{ + assert(list); + assert(list->items); + assert(item >= 0); + assert(item < list->count); + + if (list == AU_NULL_PVLIST) + return AU_BAD_PVLIST; + if (list->valid != _AU_VALID_PVLIST) + return AU_BAD_PVLIST; + if ((item < 0) || (item > list->count - 1)) + return AU_BAD_PVITEM; + if (list->items[item].valid != _AU_VALID_PVITEM) + return AU_BAD_PVLIST; + + list->items[item].parameter = param; + return _AU_SUCCESS; +} + +int AUpvsetvaltype (AUpvlist list, int item, int type) +{ + assert(list); + assert(list->items); + assert(item >= 0); + assert(item < list->count); + + if (list == AU_NULL_PVLIST) + return AU_BAD_PVLIST; + if (list->valid != _AU_VALID_PVLIST) + return AU_BAD_PVLIST; + if ((item < 0) || (item > list->count - 1)) + return AU_BAD_PVITEM; + if (list->items[item].valid != _AU_VALID_PVITEM) + return AU_BAD_PVLIST; + + list->items[item].type = type; + return _AU_SUCCESS; +} + +int AUpvsetval (AUpvlist list, int item, void *val) +{ + assert(list); + assert(list->items); + assert(item >= 0); + assert(item < list->count); + + if (list == AU_NULL_PVLIST) + return AU_BAD_PVLIST; + if (list->valid != _AU_VALID_PVLIST) + return AU_BAD_PVLIST; + if ((item < 0) || (item > list->count - 1)) + return AU_BAD_PVITEM; + if (list->items[item].valid != _AU_VALID_PVITEM) + return AU_BAD_PVLIST; + + switch (list->items[item].type) + { + case AU_PVTYPE_LONG: + list->items[item].value.l = *((long *) val); + break; + case AU_PVTYPE_DOUBLE: + list->items[item].value.d = *((double *) val); + break; + case AU_PVTYPE_PTR: + list->items[item].value.v = *((void **) val); + break; + default: + assert(0); + return AU_BAD_PVLIST; + } + + return _AU_SUCCESS; +} + +int AUpvgetparam (AUpvlist list, int item, int *param) +{ + assert(list); + assert(list->items); + assert(item >= 0); + assert(item < list->count); + + if (list == AU_NULL_PVLIST) + return AU_BAD_PVLIST; + if (list->valid != _AU_VALID_PVLIST) + return AU_BAD_PVLIST; + if ((item < 0) || (item > list->count - 1)) + return AU_BAD_PVITEM; + if (list->items[item].valid != _AU_VALID_PVITEM) + return AU_BAD_PVLIST; + + *param = list->items[item].parameter; + return _AU_SUCCESS; +} + +int AUpvgetvaltype (AUpvlist list, int item, int *type) +{ + assert(list); + assert(list->items); + assert(item >= 0); + assert(item < list->count); + + if (list == AU_NULL_PVLIST) + return AU_BAD_PVLIST; + if (list->valid != _AU_VALID_PVLIST) + return AU_BAD_PVLIST; + if ((item < 0) || (item > list->count - 1)) + return AU_BAD_PVITEM; + if (list->items[item].valid != _AU_VALID_PVITEM) + return AU_BAD_PVLIST; + + *type = list->items[item].type; + return _AU_SUCCESS; +} + +int AUpvgetval (AUpvlist list, int item, void *val) +{ + assert(list); + assert(list->items); + assert(item >= 0); + assert(item < list->count); + + if (list == AU_NULL_PVLIST) + return AU_BAD_PVLIST; + if (list->valid != _AU_VALID_PVLIST) + return AU_BAD_PVLIST; + if ((item < 0) || (item > list->count - 1)) + return AU_BAD_PVITEM; + if (list->items[item].valid != _AU_VALID_PVITEM) + return AU_BAD_PVLIST; + + switch (list->items[item].type) + { + case AU_PVTYPE_LONG: + *((long *) val) = list->items[item].value.l; + break; + case AU_PVTYPE_DOUBLE: + *((double *) val) = list->items[item].value.d; + break; + case AU_PVTYPE_PTR: + *((void **) val) = list->items[item].value.v; + break; + } + + return _AU_SUCCESS; +} diff --git a/libaudiofile/aupvinternal.h b/libaudiofile/aupvinternal.h new file mode 100644 index 0000000..1b8c8af --- /dev/null +++ b/libaudiofile/aupvinternal.h @@ -0,0 +1,75 @@ +/* + Audio File Library + Copyright (C) 1998-2000, Michael Pruett <michael@68k.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307 USA. +*/ + +/* + aupvinternal.h + + This file contains the private data structures for the parameter + value list data types. +*/ + +#ifndef AUPVINTERNAL_H +#define AUPVINTERNAL_H + +struct _AUpvitem +{ + int valid; + int type; + int parameter; + + union + { + long l; + double d; + void *v; + } + value; +}; + +struct _AUpvlist +{ + int valid; + size_t count; + struct _AUpvitem *items; +}; + +enum +{ + _AU_VALID_PVLIST = 30932, + _AU_VALID_PVITEM = 30933 +}; + +enum +{ + AU_BAD_PVLIST = -5, + AU_BAD_PVITEM = -6, + AU_BAD_PVTYPE = -7, + AU_BAD_ALLOC = -8 +}; + +enum +{ + _AU_FAIL = -1, + _AU_SUCCESS = 0 +}; + +#define _AU_NULL_PVITEM ((struct _AUpvitem *) NULL) + +#endif diff --git a/libaudiofile/aupvlist.h b/libaudiofile/aupvlist.h new file mode 100644 index 0000000..7286f41 --- /dev/null +++ b/libaudiofile/aupvlist.h @@ -0,0 +1,61 @@ +/* + Audio File Library + Copyright (C) 1998-2000, Michael Pruett <michael@68k.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307 USA. +*/ + +/* + aupvlist.h + + This file contains the interface to the parameter value list data + structures and routines. +*/ + +#ifndef AUPVLIST_H +#define AUPVLIST_H + +#ifdef __cplusplus +extern "C" +{ +#endif /* __cplusplus */ + +enum +{ + AU_PVTYPE_LONG = 1, + AU_PVTYPE_DOUBLE = 2, + AU_PVTYPE_PTR = 3 +}; + +typedef struct _AUpvlist *AUpvlist; + +#define AU_NULL_PVLIST ((struct _AUpvlist *) 0) + +AUpvlist AUpvnew (int maxItems); +int AUpvgetmaxitems (AUpvlist); +int AUpvfree (AUpvlist); +int AUpvsetparam (AUpvlist, int item, int param); +int AUpvsetvaltype (AUpvlist, int item, int type); +int AUpvsetval (AUpvlist, int item, void *val); +int AUpvgetparam (AUpvlist, int item, int *param); +int AUpvgetvaltype (AUpvlist, int item, int *type); +int AUpvgetval (AUpvlist, int item, void *val); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* AUPVLIST_H */ diff --git a/libaudiofile/avr.c b/libaudiofile/avr.c new file mode 100644 index 0000000..b7e03dc --- /dev/null +++ b/libaudiofile/avr.c @@ -0,0 +1,258 @@ +/* + Audio File Library + Copyright (C) 2004, Michael Pruett <michael@68k.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307 USA. +*/ + +/* + avr.c + + This file contains routines for parsing AVR (Audio Visual + Research) sound files. +*/ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <assert.h> +#include <sys/types.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#ifdef HAVE_INTTYPES_H +#include <inttypes.h> +#endif + +#include "audiofile.h" +#include "afinternal.h" +#include "track.h" +#include "util.h" +#include "setup.h" +#include "byteorder.h" + +#include "avr.h" + +_AFfilesetup _af_avr_default_filesetup = +{ + _AF_VALID_FILESETUP, /* valid */ + AF_FILE_AVR, /* fileFormat */ + AF_TRUE, /* trackSet */ + AF_TRUE, /* instrumentSet */ + AF_TRUE, /* miscellaneousSet */ + 1, /* trackCount */ + NULL, /* tracks */ + 0, /* instrumentCount */ + NULL, /* instruments */ + 0, /* miscellaneousCount */ + NULL /* miscellaneous */ +}; + +bool _af_avr_recognize (AFvirtualfile *fh) +{ + u_int32_t magic; + + af_fseek(fh, 0, SEEK_SET); + + if (af_fread(&magic, 4, 1, fh) != 1 || memcmp(&magic, "2BIT", 4) != 0) + return AF_FALSE; + + return AF_TRUE; +} + +status _af_avr_read_init (AFfilesetup setup, AFfilehandle file) +{ + u_int32_t magic; + char name[8]; + u_int16_t mono, resolution, sign, loop, midi; + u_int32_t rate, size, loopStart, loopEnd; + char reserved[26]; + char user[64]; + + _Track *track; + + assert(file != NULL); + assert(file->fh != NULL); + + af_fseek(file->fh, 0, SEEK_SET); + + if (af_fread(&magic, 4, 1, file->fh) != 1) + { + _af_error(AF_BAD_READ, "could not read AVR file header"); + return AF_FAIL; + } + + if (memcmp(&magic, "2BIT", 4) != 0) + { + _af_error(AF_BAD_FILEFMT, "file is not AVR format"); + return AF_FAIL; + } + + /* Read name. */ + af_fread(name, 8, 1, file->fh); + + af_read_uint16_be(&mono, file->fh); + af_read_uint16_be(&resolution, file->fh); + af_read_uint16_be(&sign, file->fh); + af_read_uint16_be(&loop, file->fh); + af_read_uint16_be(&midi, file->fh); + + af_read_uint32_be(&rate, file->fh); + af_read_uint32_be(&size, file->fh); + af_read_uint32_be(&loopStart, file->fh); + af_read_uint32_be(&loopEnd, file->fh); + + af_fread(reserved, 26, 1, file->fh); + af_fread(user, 64, 1, file->fh); + + if ((track = _af_track_new()) == NULL) + return AF_FAIL; + + file->tracks = track; + file->trackCount = 1; + + file->instruments = NULL; + file->instrumentCount = 0; + + file->miscellaneous = NULL; + file->miscellaneousCount = 0; + + file->formatSpecific = NULL; + + /* Use only low-order three bytes of sample rate. */ + track->f.sampleRate = rate & 0xffffff; + + if (sign == 0) + track->f.sampleFormat = AF_SAMPFMT_UNSIGNED; + else if (sign == 0xffff) + track->f.sampleFormat = AF_SAMPFMT_TWOSCOMP; + else + { + _af_error(AF_BAD_SAMPFMT, "bad sample format in AVR file"); + return AF_FAIL; + } + + if (resolution != 8 && resolution != 16) + { + _af_error(AF_BAD_WIDTH, "bad sample width %d in AVR file", + resolution); + return AF_FAIL; + } + track->f.sampleWidth = resolution; + + track->f.byteOrder = AF_BYTEORDER_BIGENDIAN; + + if (mono == 0) + track->f.channelCount = 1; + else if (mono == 0xffff) + track->f.channelCount = 2; + else + { + _af_error(AF_BAD_CHANNELS, + "invalid number of channels in AVR file"); + return AF_FAIL; + } + + track->f.compressionType = AF_COMPRESSION_NONE; + + _af_set_sample_format(&track->f, track->f.sampleFormat, track->f.sampleWidth); + + track->fpos_first_frame = af_ftell(file->fh); + track->totalfframes = size; + track->data_size = track->totalfframes * + _af_format_frame_size(&track->f, AF_FALSE); + track->nextfframe = 0; + track->fpos_next_frame = track->fpos_first_frame; + + /* The file has been parsed successfully. */ + return AF_SUCCEED; +} + +AFfilesetup _af_avr_complete_setup (AFfilesetup setup) +{ + _TrackSetup *track; + + if (setup->trackSet && setup->trackCount != 1) + { + _af_error(AF_BAD_NUMTRACKS, "AVR files must have exactly 1 track"); + return AF_NULL_FILESETUP; + } + + track = _af_filesetup_get_tracksetup(setup, AF_DEFAULT_TRACK); + + /* AVR allows only unsigned and two's complement integer data. */ + if (track->f.sampleFormat != AF_SAMPFMT_UNSIGNED && + track->f.sampleFormat != AF_SAMPFMT_TWOSCOMP) + { + _af_error(AF_BAD_FILEFMT, "AVR format does supports only unsigned and two's complement integer data"); + return AF_NULL_FILESETUP; + } + + /* For now we support only 8- and 16-bit samples. */ + if (track->f.sampleWidth != 8 && track->f.sampleWidth != 16) + { + _af_error(AF_BAD_WIDTH, "invalid sample width %d for AVR file (only 8- and 16-bit sample widths are allowed)"); + return AF_NULL_FILESETUP; + } + + /* AVR does not support compression. */ + if (track->f.compressionType != AF_COMPRESSION_NONE) + { + _af_error(AF_BAD_NOT_IMPLEMENTED, "compression not supported for AVR files"); + return AF_NULL_FILESETUP; + } + + /* AVR audio data is big-endian. */ + if (track->f.byteOrder != AF_BYTEORDER_BIGENDIAN) + { + if (track->byteOrderSet) + { + _af_error(AF_BAD_BYTEORDER, + "AVR format supports only big-endian data"); + return AF_NULL_FILESETUP; + } + else + track->f.byteOrder = AF_BYTEORDER_BIGENDIAN; + } + + if (track->aesDataSet) + { + _af_error(AF_BAD_FILESETUP, "AVR files do not support AES data"); + return AF_NULL_FILESETUP; + } + + if (track->markersSet && track->markerCount != 0) + { + _af_error(AF_BAD_FILESETUP, "AVR format does not support markers"); + return AF_NULL_FILESETUP; + } + + if (setup->instrumentSet && setup->instrumentCount != 0) + { + _af_error(AF_BAD_FILESETUP, "AVR format does not support instruments"); + return AF_NULL_FILESETUP; + } + + if (setup->miscellaneousSet && setup->miscellaneousCount != 0) + { + _af_error(AF_BAD_FILESETUP, "AVR format does not support miscellaneous data"); + return AF_NULL_FILESETUP; + } + + return _af_filesetup_copy(setup, &_af_avr_default_filesetup, AF_FALSE); +} diff --git a/libaudiofile/avr.h b/libaudiofile/avr.h new file mode 100644 index 0000000..1e4296a --- /dev/null +++ b/libaudiofile/avr.h @@ -0,0 +1,39 @@ +/* + Audio File Library + Copyright (C) 2004, Michael Pruett <michael@68k.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307 USA. +*/ + +/* + avr.h + + This file contains headers and constants related to the AVR + (Audio Visual Research) sound file format. +*/ + +#include "afinternal.h" + +#ifndef AVR_H +#define AVR_H + +bool _af_avr_recognize (AFvirtualfile *fh); +status _af_avr_read_init (AFfilesetup, AFfilehandle); +status _af_avr_write_init (AFfilesetup, AFfilehandle); +status _af_avr_update (AFfilehandle); +AFfilesetup _af_avr_complete_setup (AFfilesetup); + +#endif diff --git a/libaudiofile/avrwrite.c b/libaudiofile/avrwrite.c new file mode 100644 index 0000000..8d8a896 --- /dev/null +++ b/libaudiofile/avrwrite.c @@ -0,0 +1,153 @@ +/* + Audio File Library + Copyright (C) 2004, Michael Pruett <michael@68k.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307 USA. +*/ + +/* + avrwrite.c + + This file contains routines for writing AVR (Audio Visual + Research) sound files. +*/ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <assert.h> + +#ifdef HAVE_INTTYPES_H +#include <inttypes.h> +#endif + +#include "audiofile.h" +#include "afinternal.h" +#include "byteorder.h" +#include "util.h" +#include "setup.h" + +#include "avr.h" + +status _af_avr_update (AFfilehandle file) +{ + _Track *track; + u_int32_t size, loopStart, loopEnd; + + track = _af_filehandle_get_track(file, AF_DEFAULT_TRACK); + + /* Seek to the position of the size field. */ + af_fseek(file->fh, 26, SEEK_SET); + + size = track->totalfframes; + + /* For the case of no loops, loopStart = 0 and loopEnd = size. */ + loopStart = 0; + loopEnd = size; + + af_write_uint32_be(&size, file->fh); + af_write_uint32_be(&loopStart, file->fh); + af_write_uint32_be(&loopEnd, file->fh); + + return AF_SUCCEED; +} + +static char *af_basename (char *filename) +{ + char *base; + base = strrchr(filename, '/'); + if (base == NULL) + return filename; + else + return base + 1; +} + +status _af_avr_write_init (AFfilesetup setup, AFfilehandle filehandle) +{ + _Track *track; + char name[8]; + u_int16_t mono, resolution, sign, loop, midi; + u_int32_t rate, size, loopStart, loopEnd; + char reserved[26]; + char user[64]; + + if (_af_filesetup_make_handle(setup, filehandle) == AF_FAIL) + return AF_FAIL; + + filehandle->formatSpecific = NULL; + + track = _af_filehandle_get_track(filehandle, AF_DEFAULT_TRACK); + + if (af_fseek(filehandle->fh, 0, SEEK_SET) != 0) + { + _af_error(AF_BAD_LSEEK, "bad seek"); + return AF_FAIL; + } + + af_fwrite("2BIT", 4, 1, filehandle->fh); + memset(name, 0, 8); + if (filehandle->fileName != NULL) + strncpy(name, af_basename(filehandle->fileName), 8); + af_fwrite(name, 8, 1, filehandle->fh); + + if (track->f.channelCount == 1) + mono = 0x0; + else + mono = 0xffff; + af_write_uint16_be(&mono, filehandle->fh); + + resolution = track->f.sampleWidth; + af_write_uint16_be(&resolution, filehandle->fh); + + if (track->f.sampleFormat == AF_SAMPFMT_UNSIGNED) + sign = 0x0; + else + sign = 0xffff; + af_write_uint16_be(&sign, filehandle->fh); + + /* We do not currently support loops. */ + loop = 0; + af_write_uint16_be(&loop, filehandle->fh); + midi = 0xffff; + af_write_uint16_be(&midi, filehandle->fh); + + rate = track->f.sampleRate; + /* Set the high-order byte of rate to 0xff. */ + rate |= 0xff000000; + size = track->totalfframes; + loopStart = 0; + loopEnd = size; + + af_write_uint32_be(&rate, filehandle->fh); + af_write_uint32_be(&size, filehandle->fh); + af_write_uint32_be(&loopStart, filehandle->fh); + af_write_uint32_be(&loopEnd, filehandle->fh); + + memset(reserved, 0, 26); + af_fwrite(reserved, 26, 1, filehandle->fh); + + memset(user, 0, 64); + af_fwrite(user, 64, 1, filehandle->fh); + + if (track->fpos_first_frame == 0) + track->fpos_first_frame = af_ftell(filehandle->fh); + + return AF_SUCCEED; +} diff --git a/libaudiofile/byteorder.c b/libaudiofile/byteorder.c new file mode 100644 index 0000000..182926a --- /dev/null +++ b/libaudiofile/byteorder.c @@ -0,0 +1,85 @@ +/* + Audio File Library + Copyright (C) 1998, Michael Pruett <michael@68k.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307 USA. +*/ + +/* + byteorder.c + + This file defines functions which swap bytes. +*/ + +#include <sys/types.h> +#include "byteorder.h" +#include "util.h" + +u_int16_t _af_byteswap_int16 (u_int16_t x) +{ + return ((x << 8) | (x >> 8)); +} + +u_int32_t _af_byteswap_int32 (u_int32_t x) +{ + u_int8_t b1, b2, b3, b4; + + b1 = x>>24; + b2 = (x>>16) & 0xff; + b3 = (x>>8) & 0xff; + b4 = x & 0xff; + + return b1 | (b2 << 8) | (b3 << 16) | (b4 << 24); +} + +float _af_byteswap_float32 (float x) +{ + float f = x; + u_int32_t *l = (u_int32_t *) &f; + + *l = _af_byteswap_int32(*l); + + return f; +} + +/* +uint64_t _af_byteswap_int64 (uint64_t x) +{ + u_int8_t b1, b2, b3, b4, b5, b6, b7, b8; + + b1 = (x>>56) & 0xff; + b2 = (x>>48) & 0xff; + b3 = (x>>40) & 0xff; + b4 = (x>>32) & 0xff; + b5 = (x>>24) & 0xff; + b6 = (x>>16) & 0xff; + b7 = (x>>8) & 0xff; + b8 = x & 0xff; + + return b1 | (b2 << 8) | (b3 << 16) | (b4 << 24) | + (b5 << 32) | (b6 << 40) | (b7 << 48) | (b8 << 56); +} + +main () +{ + long ldata = '1234'; + unsigned long long data = 0x1122334455667788; + printf("%llx\n", data); + printf("%llx\n", _af_byteswap_int64(data)); + printf("%x\n", ldata); + printf("%x\n", _af_byteswap_int32(ldata)); +} +*/ diff --git a/libaudiofile/byteorder.h b/libaudiofile/byteorder.h new file mode 100644 index 0000000..045b0a0 --- /dev/null +++ b/libaudiofile/byteorder.h @@ -0,0 +1,106 @@ +/* + Audio File Library + Copyright (C) 1998-1999, Michael Pruett <michael@68k.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307 USA. +*/ + +/* + byteorder.h + + This file declares functions useful for dealing with byte + swapping. +*/ + +#ifndef BYTEORDER_H +#define BYTEORDER_H + +#include <config.h> + +#if WORDS_BIGENDIAN + #define __BIGENDIAN__ + #define _AF_BYTEORDER_NATIVE (AF_BYTEORDER_BIGENDIAN) +#else + #define __LITTLEENDIAN__ + #define _AF_BYTEORDER_NATIVE (AF_BYTEORDER_LITTLEENDIAN) +#endif + +#ifndef uint16 +typedef u_int16_t uint16; +#endif + +#ifndef uint32 +typedef u_int32_t uint32; +#endif + +#ifdef __LITTLEENDIAN__ + +#define HOST_TO_LENDIAN_INT16(x) ((uint16) (x)) +#define HOST_TO_LENDIAN_INT32(x) ((uint32) (x)) +#define HOST_TO_LENDIAN_FLOAT32(x) ((float) (x)) +#define HOST_TO_LENDIAN_DOUBLE64(x) ((double) (x)) + +#define LENDIAN_TO_HOST_INT16(x) ((uint16) (x)) +#define LENDIAN_TO_HOST_INT32(x) ((uint32) (x)) +#define LENDIAN_TO_HOST_FLOAT32(x) ((float) (x)) +#define LENDIAN_TO_HOST_DOUBLE64(x) ((double) (x)) + +#else + +#define HOST_TO_LENDIAN_INT16(x) _af_byteswap_int16(x) +#define HOST_TO_LENDIAN_INT32(x) _af_byteswap_int32(x) +#define HOST_TO_LENDIAN_FLOAT32(x) _af_byteswap_float32(x) +#define HOST_TO_LENDIAN_DOUBLE64(x) _af_byteswap_double64(x) + +#define LENDIAN_TO_HOST_INT16(x) _af_byteswap_int16(x) +#define LENDIAN_TO_HOST_INT32(x) _af_byteswap_int32(x) +#define LENDIAN_TO_HOST_FLOAT32(x) _af_byteswap_float32(x) +#define LENDIAN_TO_HOST_DOUBLE64(x) _af_byteswap_double64(x) + +#endif + +#ifdef __BIGENDIAN__ + +#define HOST_TO_BENDIAN_INT16(x) ((uint16) (x)) +#define HOST_TO_BENDIAN_INT32(x) ((uint32) (x)) +#define HOST_TO_BENDIAN_FLOAT32(x) ((float) (x)) +#define HOST_TO_BENDIAN_DOUBLE64(x) ((double) (x)) + +#define BENDIAN_TO_HOST_INT16(x) ((uint16) (x)) +#define BENDIAN_TO_HOST_INT32(x) ((uint32) (x)) +#define BENDIAN_TO_HOST_FLOAT32(x) ((float) (x)) +#define BENDIAN_TO_HOST_DOUBLE64(x) ((double) (x)) + +#else + +#define HOST_TO_BENDIAN_INT16(x) _af_byteswap_int16(x) +#define HOST_TO_BENDIAN_INT32(x) _af_byteswap_int32(x) +#define HOST_TO_BENDIAN_FLOAT32(x) _af_byteswap_float32(x) +#define HOST_TO_BENDIAN_DOUBLE64(x) _af_byteswap_double64(x) + +#define BENDIAN_TO_HOST_INT16(x) _af_byteswap_int16(x) +#define BENDIAN_TO_HOST_INT32(x) _af_byteswap_int32(x) +#define BENDIAN_TO_HOST_FLOAT32(x) _af_byteswap_float32(x) +#define BENDIAN_TO_HOST_DOUBLE64(x) _af_byteswap_double64(x) + +#endif + +u_int16_t _af_byteswap_int16 (u_int16_t x); +u_int32_t _af_byteswap_int32 (u_int32_t x); +float _af_byteswap_float32 (float x); +double _af_byteswap_double64 (double x); + +#endif diff --git a/libaudiofile/compression.c b/libaudiofile/compression.c new file mode 100644 index 0000000..762751e --- /dev/null +++ b/libaudiofile/compression.c @@ -0,0 +1,111 @@ +/* + Audio File Library + Copyright (C) 1999-2000, Michael Pruett <michael@68k.org> + Copyright (C) 2000, Silicon Graphics, Inc. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307 USA. +*/ + +/* + compression.c + + This file contains routines for configuring compressed audio. +*/ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <assert.h> + +#include "audiofile.h" +#include "afinternal.h" +#include "aupvlist.h" +#include "units.h" +#include "util.h" + +extern _CompressionUnit _af_compression[]; + +int _af_compression_index_from_id (int compressionid) +{ + int i; + + for (i=0; i<_AF_NUM_COMPRESSION; i++) + { + if (_af_compression[i].compressionID == compressionid) + return i; + } + + _af_error(AF_BAD_COMPTYPE, "compression type %d not available", compressionid); + + return -1; +} + +static _CompressionUnit *findCompression (int compressionid) +{ + int compressionno; + + compressionno = _af_compression_index_from_id(compressionid); + if (compressionno != -1) + return &_af_compression[compressionno]; + + return NULL; +} + +int afGetCompression (AFfilehandle file, int trackid) +{ + _Track *track; + + if (!_af_filehandle_ok(file)) + return -1; + + if ((track = _af_filehandle_get_track(file, trackid)) == NULL) + return -1; + + return track->f.compressionType; +} + +void afInitCompression (AFfilesetup setup, int trackid, int compression) +{ + _TrackSetup *track; + + if (!_af_filesetup_ok(setup)) + return; + + if ((track = _af_filesetup_get_tracksetup(setup, trackid)) == NULL) + return; + + if (findCompression(compression) == NULL) + return; + + track->f.compressionType = compression; +} + +#if 0 +int afGetCompressionParams (AFfilehandle file, int trackid, + int *compression, AUpvlist pvlist, int numitems) +{ + assert(file); + assert(trackid == AF_DEFAULT_TRACK); +} + +void afInitCompressionParams (AFfilesetup setup, int trackid, + int compression, AUpvlist pvlist, int numitems) +{ + assert(setup); + assert(trackid == AF_DEFAULT_TRACK); +} +#endif diff --git a/libaudiofile/compression.h b/libaudiofile/compression.h new file mode 100644 index 0000000..ee190d7 --- /dev/null +++ b/libaudiofile/compression.h @@ -0,0 +1,32 @@ +/* + Audio File Library + Copyright (C) 1999, Michael Pruett <michael@68k.org> + Copyright (C) 2000, Silicon Graphics, Inc. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307 USA. +*/ + +/* + compression.h +*/ + +#ifndef COMPRESSION_H +#define COMPRESSION_H + +/* Provide an index into _af_compression given a compression id. */ +int _af_compression_index_from_id (int compressionid); + +#endif diff --git a/libaudiofile/data.c b/libaudiofile/data.c new file mode 100644 index 0000000..f02daed --- /dev/null +++ b/libaudiofile/data.c @@ -0,0 +1,242 @@ +/* + Audio File Library + Copyright (C) 1998-2000, Michael Pruett <michael@68k.org> + Copyright (C) 2000, Silicon Graphics, Inc. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307 USA. +*/ + +/* + data.c +*/ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <assert.h> +#include <stdlib.h> +#include <string.h> + +#include "audiofile.h" +#include "afinternal.h" +#include "util.h" +#include "modules.h" + +int afWriteFrames (AFfilehandle file, int trackid, const void *samples, + int nvframes2write) +{ + _AFmoduleinst *firstmod; + _AFchunk *userc; + _Track *track; + int bytes_per_vframe; + AFframecount vframe; + + if (!_af_filehandle_ok(file)) + return -1; + + if (!_af_filehandle_can_write(file)) + return -1; + + if ((track = _af_filehandle_get_track(file, trackid)) == NULL) + return -1; + + if (track->ms.modulesdirty) + { + if (_AFsetupmodules(file, track) != AF_SUCCEED) + return -1; + } + + /*if (file->seekok) {*/ + + if (af_fseek(file->fh, track->fpos_next_frame, SEEK_SET) < 0) + { + _af_error(AF_BAD_LSEEK, "unable to position write pointer at next frame"); + return -1; + } + + /* } */ + + bytes_per_vframe = _af_format_frame_size(&track->v, AF_TRUE); + + firstmod = &track->ms.module[0]; + userc = &track->ms.chunk[0]; + + track->filemodhappy = AF_TRUE; + + vframe = 0; +#ifdef UNLIMITED_CHUNK_NVFRAMES + /* + OPTIMIZATION: see the comment at the very end of + arrangemodules() in modules.c for an explanation of this: + */ + if (!trk->ms.mustuseatomicnvframes) + { + userc->buf = (char *)buf; + userc->nframes = nvframes2write; + + (*firstmod->mod->run_push)(firstmod); + + /* Count this chunk if there was no i/o error. */ + if (trk->filemodhappy) + vframe += userc->nframes; + } + else +#else + /* Optimization must be off. */ + assert(track->ms.mustuseatomicnvframes); +#endif + { + while (vframe < nvframes2write) + { + userc->buf = (char *) samples + bytes_per_vframe * vframe; + if (vframe <= nvframes2write - _AF_ATOMIC_NVFRAMES) + userc->nframes = _AF_ATOMIC_NVFRAMES; + else + userc->nframes = nvframes2write - vframe; + + (*firstmod->mod->run_push)(firstmod); + + if (track->filemodhappy == AF_FALSE) + break; + + vframe += userc->nframes; + } + } + + track->nextvframe += vframe; + track->totalvframes += vframe; + + return vframe; +} + +int afReadFrames (AFfilehandle file, int trackid, void *samples, + int nvframeswanted) +{ + _Track *track; + _AFmoduleinst *firstmod; + _AFchunk *userc; + AFframecount nvframesleft, nvframes2read; + int bytes_per_vframe; + AFframecount vframe; + + if (!_af_filehandle_ok(file)) + return -1; + + if (!_af_filehandle_can_read(file)) + return -1; + + if ((track = _af_filehandle_get_track(file, trackid)) == NULL) + return -1; + + if (track->ms.modulesdirty) + { + if (_AFsetupmodules(file, track) != AF_SUCCEED) + return -1; + } + + /*if (file->seekok) {*/ + + if (af_fseek(file->fh, track->fpos_next_frame, SEEK_SET) < 0) + { + _af_error(AF_BAD_LSEEK, "unable to position read pointer at next frame"); + return -1; + } + + /* } */ + + if (track->totalvframes == -1) + nvframes2read = nvframeswanted; + else + { + nvframesleft = track->totalvframes - track->nextvframe; + nvframes2read = (nvframeswanted > nvframesleft) ? + nvframesleft : nvframeswanted; + } + bytes_per_vframe = _af_format_frame_size(&track->v, AF_TRUE); + + firstmod = &track->ms.module[track->ms.nmodules-1]; + userc = &track->ms.chunk[track->ms.nmodules]; + + track->filemodhappy = AF_TRUE; + + vframe = 0; + + if (!track->ms.mustuseatomicnvframes) + { + assert(track->frames2ignore == 0); + userc->buf = samples; + userc->nframes = nvframes2read; + + (*firstmod->mod->run_pull)(firstmod); + if (track->filemodhappy) + vframe += userc->nframes; + } + else + { + bool eof = AF_FALSE; + + if (track->frames2ignore != 0) + { + userc->nframes = track->frames2ignore; + userc->buf = _af_malloc(track->frames2ignore * bytes_per_vframe); + if (userc->buf == AF_NULL) + return 0; + + (*firstmod->mod->run_pull)(firstmod); + + /* Have we hit EOF? */ + if (userc->nframes < track->frames2ignore) + eof = AF_TRUE; + + track->frames2ignore = 0; + + free(userc->buf); + userc->buf = NULL; + } + + /* + Now start reading useful frames, until EOF or + premature EOF. + */ + + while (track->filemodhappy && !eof && vframe < nvframes2read) + { + AFframecount nvframes2pull; + userc->buf = (char *) samples + bytes_per_vframe * vframe; + + if (vframe <= nvframes2read - _AF_ATOMIC_NVFRAMES) + nvframes2pull = _AF_ATOMIC_NVFRAMES; + else + nvframes2pull = nvframes2read - vframe; + + userc->nframes = nvframes2pull; + + (*firstmod->mod->run_pull)(firstmod); + + if (track->filemodhappy) + { + vframe += userc->nframes; + if (userc->nframes < nvframes2pull) + eof = AF_TRUE; + } + } + } + + track->nextvframe += vframe; + + return vframe; +} diff --git a/libaudiofile/debug.c b/libaudiofile/debug.c new file mode 100644 index 0000000..c075357 --- /dev/null +++ b/libaudiofile/debug.c @@ -0,0 +1,417 @@ +/* + Audio File Library + Copyright (C) 1998-2000, Michael Pruett <michael@68k.org> + Copyright (C) 2000, Silicon Graphics, Inc. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307 USA. +*/ + +/* + debug.c + + This file contains debugging routines for the Audio File + Library. +*/ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <stdio.h> +#include <stdlib.h> +#include <math.h> +#include <string.h> +#include <assert.h> + +#include "audiofile.h" +#include "aupvlist.h" + +#include "afinternal.h" +#include "util.h" +#include "units.h" +#include "compression.h" +#include "modules.h" +#include "byteorder.h" +#include "aupvinternal.h" +#include "print.h" +#include "debug.h" + +extern _CompressionUnit _af_compression[]; + +void _af_printid (u_int32_t id) +{ + printf("%c%c%c%c", + (id >> 24) & 0xff, + (id >> 16) & 0xff, + (id >> 8) & 0xff, + id & 0xff); +} + +void _af_print_pvlist (AUpvlist list) +{ + int i; + + assert(list); + + printf("list.valid: %d\n", list->valid); + printf("list.count: %d\n", list->count); + + for (i=0; i<list->count; i++) + { + printf("item %d valid %d, should be %d\n", + i, list->items[i].valid, _AU_VALID_PVITEM); + + switch (list->items[i].type) + { + case AU_PVTYPE_LONG: + printf("item #%d, parameter %d, long: %ld\n", + i, list->items[i].parameter, + list->items[i].value.l); + break; + case AU_PVTYPE_DOUBLE: + printf("item #%d, parameter %d, double: %f\n", + i, list->items[i].parameter, + list->items[i].value.d); + break; + case AU_PVTYPE_PTR: + printf("item #%d, parameter %d, pointer: %p\n", + i, list->items[i].parameter, + list->items[i].value.v); + break; + + default: + printf("item #%d, invalid type %d\n", i, + list->items[i].type); + assert(0); + break; + } + } +} + +void _af_print_audioformat (_AudioFormat *fmt) +{ + /* sampleRate, channelCount */ + printf("{ %7.2f Hz %d ch ", fmt->sampleRate, fmt->channelCount); + + /* sampleFormat, sampleWidth */ + switch (fmt->sampleFormat) + { + case AF_SAMPFMT_TWOSCOMP: + printf("%db 2 ", fmt->sampleWidth); + break; + case AF_SAMPFMT_UNSIGNED: + printf("%db u ", fmt->sampleWidth); + break; + case AF_SAMPFMT_FLOAT: + printf("flt "); + break; + case AF_SAMPFMT_DOUBLE: + printf("dbl "); + break; + default: + printf("%dsampfmt? ", fmt->sampleFormat); + } + + /* pcm */ + printf("(%.30g+-%.30g [%.30g,%.30g]) ", + fmt->pcm.intercept, fmt->pcm.slope, + fmt->pcm.minClip, fmt->pcm.maxClip); + + /* byteOrder */ + switch (fmt->byteOrder) + { + case AF_BYTEORDER_BIGENDIAN: + printf("big "); + break; + case AF_BYTEORDER_LITTLEENDIAN: + printf("little "); + break; + default: + printf("%dbyteorder? ", fmt->byteOrder); + break; + } + + /* compression */ + { + int idx = _af_compression_index_from_id(fmt->compressionType); + if (idx < 0) + { + printf("%dcompression?", fmt->compressionType); + } + else if (fmt->compressionType == AF_COMPRESSION_NONE) + printf("pcm"); + else + printf("%s", _af_compression[idx].label); + } + + printf(" }"); +} + +void _af_print_tracks (AFfilehandle filehandle) +{ + int i; + for (i=0; i<filehandle->trackCount; i++) + { + _Track *track = &filehandle->tracks[i]; + printf("track %d\n", i); + printf(" id %d\n", track->id); + printf(" sample format\n"); + _af_print_audioformat(&track->f); + printf(" virtual format\n"); + _af_print_audioformat(&track->v); + printf(" total file frames: %" AF_FRAMECOUNT_PRINT_FMT "\n", + track->totalfframes); + printf(" total virtual frames: %" AF_FRAMECOUNT_PRINT_FMT "\n", + track->totalvframes); + printf(" next file frame: %" AF_FRAMECOUNT_PRINT_FMT "\n", + track->nextfframe); + printf(" next virtual frame: %" AF_FRAMECOUNT_PRINT_FMT "\n", + track->nextvframe); + printf(" frames to ignore: %" AF_FRAMECOUNT_PRINT_FMT "\n", + track->frames2ignore); + + printf(" data_size: %" AF_FILEOFFSET_PRINT_FMT "\n", + track->data_size); + printf(" fpos_first_frame: %" AF_FILEOFFSET_PRINT_FMT "\n", + track->fpos_first_frame); + printf(" fpos_next_frame: %" AF_FILEOFFSET_PRINT_FMT "\n", + track->fpos_next_frame); + printf(" fpos_after_data: %" AF_FILEOFFSET_PRINT_FMT "\n", + track->fpos_after_data); + + printf(" channel matrix:"); + _af_print_channel_matrix(track->channelMatrix, + track->f.channelCount, track->v.channelCount); + printf("\n"); + + printf(" marker count: %d\n", track->markerCount); + } +} + +void _af_print_filehandle (AFfilehandle filehandle) +{ + printf("file handle: 0x%p\n", filehandle); + + if (filehandle->valid == _AF_VALID_FILEHANDLE) + printf("valid\n"); + else + printf("invalid!\n"); + + printf(" access: "); + if (filehandle->access == _AF_READ_ACCESS) + putchar('r'); + else + putchar('w'); + + printf(" fileFormat: %d\n", filehandle->fileFormat); + + printf(" instrument count: %d\n", filehandle->instrumentCount); + printf(" instruments: 0x%p\n", filehandle->instruments); + + printf(" miscellaneous count: %d\n", filehandle->miscellaneousCount); + printf(" miscellaneous: 0x%p\n", filehandle->miscellaneous); + + printf(" trackCount: %d\n", filehandle->trackCount); + printf(" tracks: 0x%p\n", filehandle->tracks); + _af_print_tracks(filehandle); +} + +void _af_print_channel_matrix (double *matrix, int fchans, int vchans) +{ + int v, f; + + if (!matrix) + { + printf("NULL"); + return; + } + + printf("{"); + for (v=0; v < vchans; v++) + { + if (v) printf(" "); + printf("{"); + for (f=0; f < fchans; f++) + { + if (f) printf(" "); + printf("%5.2f", *(matrix + v*fchans + f)); + } + printf("}"); + } + printf("}"); +} + +void _af_print_frame (AFframecount frameno, double *frame, int nchannels, + char *formatstring, int numberwidth, + double slope, double intercept, double minclip, double maxclip) +{ + char linebuf[81]; + int wavewidth = wavewidth = 78 - numberwidth*nchannels - 6; + int c; + + memset(linebuf, ' ', 80); + linebuf[0] = '|'; + linebuf[wavewidth-1] = '|'; + linebuf[wavewidth] = 0; + + printf("%05" AF_FRAMECOUNT_PRINT_FMT " ", frameno); + + for (c=0; c < nchannels; c++) + { + double pcm = frame[c]; + printf(formatstring, pcm); + } + for (c=0; c < nchannels; c++) + { + double pcm = frame[c], volts; + if (maxclip > minclip) + { + if (pcm < minclip) pcm = minclip; + if (pcm > maxclip) pcm = maxclip; + } + volts = (pcm - intercept) / slope; + linebuf[(int)((volts/2 + 0.5)*(wavewidth-3)) + 1] = '0' + c; + } + printf("%s\n", linebuf); +} + +void _af_print_chunk (_AFchunk *chnk) +{ + _AudioFormat fmt = chnk->f; + AFframecount nframes = chnk->nframes; + AFframecount nsamps = nframes * fmt.channelCount; + AFframecount fr; + + double *outbuf; + char formatstring[20]; + int digits, numberwidth; + + switch (fmt.compressionType) + { + case AF_COMPRESSION_NONE: + break; + + case AF_COMPRESSION_G711_ULAW: + printf("WARNING dumping ulaw data as if it were 8-bit unsigned\n"); + fmt.compressionType = AF_COMPRESSION_NONE; + fmt.sampleWidth = 8; + fmt.sampleFormat = AF_SAMPFMT_UNSIGNED; + break; + + default: + printf("LAME-O chunk dumper cannot deal with '%s' compression\n", + _af_compression[_af_compression_index_from_id(fmt.compressionType)].name); + return; + } + + if (fmt.sampleWidth > 8 && fmt.byteOrder != _AF_BYTEORDER_NATIVE) + { + printf("LAME-O chunk dumper cannot deal with non-native byte order\n"); + return; + } + +#define transfer(type) \ + { \ + int s; \ + for(s=0; s < nsamps; s++) \ + outbuf[s] = (double)(((type *)chnk->buf)[s]); \ + } + + /* Make the buffer large enough to hold doubles. */ + outbuf = malloc(sizeof(double) * nsamps); + + switch (fmt.sampleFormat) + { + case AF_SAMPFMT_DOUBLE: + case AF_SAMPFMT_FLOAT: + { + if (fmt.sampleFormat == AF_SAMPFMT_DOUBLE) + { + transfer(double); + } + else + { + transfer(float); + } + + digits = (int) log10(fmt.pcm.intercept + fabs(fmt.pcm.slope)) + 1; + /* Account for the sign character. */ + digits += 1; + + if (digits > 4) + { + sprintf(formatstring, "%%%d.0f ", digits); + numberwidth = digits + 1; + } + else + { + sprintf(formatstring, "%%%d.2f ", digits+3); + numberwidth = digits + 3 + 1; + } + } + break; + + case AF_SAMPFMT_TWOSCOMP: + case AF_SAMPFMT_UNSIGNED: + { + bool issigned = (fmt.sampleFormat==AF_SAMPFMT_TWOSCOMP); + + /* # of bytes taken by the value */ + int realbytes = _af_format_sample_size_uncompressed(&fmt, AF_TRUE); + + switch (realbytes) + { + case 1: + if (issigned) { transfer(schar1); } + else { transfer(uchar1); } + break; + case 2: + if (issigned) { transfer(schar2); } + else { transfer(uchar2); } + break; + case 4: + if (issigned) { transfer(schar4); } + else { transfer(uchar4); } + break; + default: + printf("LAME-O chunk dumper cannot deal with %d bits\n", + realbytes*8); + free(outbuf); + return; + } + + digits = (int) log10(fmt.pcm.intercept + fabs(fmt.pcm.slope)) + 1; + if (issigned) + digits++; + + sprintf(formatstring, "%%%d.0f ", digits); + numberwidth = digits + 1; + } + break; + + default: + assert(0); + return; + } + + for (fr=0; fr < nframes; fr++) + _af_print_frame(fr, &outbuf[fr*fmt.channelCount], + fmt.channelCount, formatstring, numberwidth, + fmt.pcm.slope, fmt.pcm.intercept, + fmt.pcm.minClip, fmt.pcm.maxClip); + + free(outbuf); +} diff --git a/libaudiofile/debug.h b/libaudiofile/debug.h new file mode 100644 index 0000000..bdcfbf5 --- /dev/null +++ b/libaudiofile/debug.h @@ -0,0 +1,47 @@ +/* + Audio File Library + Copyright (C) 1998-2000, Michael Pruett <michael@68k.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307 USA. +*/ + +/* + debug.h + + This header file declares debugging functions for the Audio + File Library. +*/ + +#ifndef DEBUG_H +#define DEBUG_H + +#include <sys/types.h> +#include "audiofile.h" +#include "afinternal.h" + +void _af_printid (u_int32_t id); +void _af_print_filehandle (AFfilehandle filehandle); +void _af_print_tracks (AFfilehandle filehandle); +void _af_print_channel_matrix (double *matrix, int fchans, int vchans); +void _af_print_pvlist (AUpvlist list); + +void _af_print_audioformat (_AudioFormat *format); +void _af_print_chunk (_AFchunk *chunk); +void _af_print_frame (AFframecount frameno, double *frame, int nchannels, + char *formatstring, int numberwidth, + double slope, double intercept, double minclip, double maxclip); + +#endif diff --git a/libaudiofile/error.c b/libaudiofile/error.c new file mode 100644 index 0000000..108e939 --- /dev/null +++ b/libaudiofile/error.c @@ -0,0 +1,71 @@ +/* + Audio File Library + Copyright (C) 1998, Michael Pruett <michael@68k.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307 USA. +*/ + +/* + error.c + + This file contains the routines used in the Audio File Library's + error handling. +*/ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <stdio.h> +#include <stdarg.h> +#include <assert.h> +#include "audiofile.h" + +static void defaultErrorFunction (long error, const char *str); + +static AFerrfunc errorFunction = defaultErrorFunction; + +AFerrfunc afSetErrorHandler (AFerrfunc efunc) +{ + AFerrfunc old; + + old = errorFunction; + errorFunction = efunc; + + return old; +} + +static void defaultErrorFunction (long error, const char *str) +{ + fprintf(stderr, "Audio File Library: "); + fprintf(stderr, "%s", str); + fprintf(stderr, " [error %ld]\n", error); +} + +void _af_error (int errorCode, const char *fmt, ...) +{ + char buf[1024]; + va_list ap; + + va_start(ap, fmt); + + vsnprintf(buf, 1024, fmt, ap); + + va_end(ap); + + if (errorFunction != NULL) + errorFunction(errorCode, buf); +} diff --git a/libaudiofile/error.h b/libaudiofile/error.h new file mode 100644 index 0000000..e3d54f6 --- /dev/null +++ b/libaudiofile/error.h @@ -0,0 +1,26 @@ +/* + Audio File Library + Copyright (C) 1998, Michael Pruett <michael@68k.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307 USA. +*/ + +#ifndef ERROR_H +#define ERROR_H + +void _af_error (int errorCode, const char *fmt, ...); + +#endif diff --git a/libaudiofile/extended.c b/libaudiofile/extended.c new file mode 100644 index 0000000..b3050b7 --- /dev/null +++ b/libaudiofile/extended.c @@ -0,0 +1,175 @@ +#include <math.h> +#include "extended.h" + +/* + * C O N V E R T T O I E E E E X T E N D E D + */ + +/* Copyright (C) 1988-1991 Apple Computer, Inc. + * All rights reserved. + * + * Machine-independent I/O routines for IEEE floating-point numbers. + * + * NaN's and infinities are converted to HUGE_VAL or HUGE, which + * happens to be infinity on IEEE machines. Unfortunately, it is + * impossible to preserve NaN's in a machine-independent way. + * Infinities are, however, preserved on IEEE machines. + * + * These routines have been tested on the following machines: + * Apple Macintosh, MPW 3.1 C compiler + * Apple Macintosh, THINK C compiler + * Silicon Graphics IRIS, MIPS compiler + * Cray X/MP and Y/MP + * Digital Equipment VAX + * + * + * Implemented by Malcolm Slaney and Ken Turkowski. + * + * Malcolm Slaney contributions during 1988-1990 include big- and little- + * endian file I/O, conversion to and from Motorola's extended 80-bit + * floating-point format, and conversions to and from IEEE single- + * precision floating-point format. + * + * In 1991, Ken Turkowski implemented the conversions to and from + * IEEE double-precision format, added more precision to the extended + * conversions, and accommodated conversions involving +/- infinity, + * NaN's, and denormalized numbers. + */ + +#ifndef HUGE_VAL +#define HUGE_VAL HUGE +#endif /*HUGE_VAL*/ + +#define FloatToUnsigned(f) ((unsigned long) (((long) (f - 2147483648.0)) + 2147483647L) + 1) + +void _af_convert_to_ieee_extended (double num, unsigned char *bytes) +{ + int sign; + int expon; + double fMant, fsMant; + unsigned long hiMant, loMant; + + if (num < 0) { + sign = 0x8000; + num *= -1; + } else { + sign = 0; + } + + if (num == 0) { + expon = 0; hiMant = 0; loMant = 0; + } + else { + fMant = frexp(num, &expon); + if ((expon > 16384) || !(fMant < 1)) { /* Infinity or NaN */ + expon = sign|0x7FFF; hiMant = 0; loMant = 0; /* infinity */ + } + else { /* Finite */ + expon += 16382; + if (expon < 0) { /* denormalized */ + fMant = ldexp(fMant, expon); + expon = 0; + } + expon |= sign; + fMant = ldexp(fMant, 32); + fsMant = floor(fMant); + hiMant = FloatToUnsigned(fsMant); + fMant = ldexp(fMant - fsMant, 32); + fsMant = floor(fMant); + loMant = FloatToUnsigned(fsMant); + } + } + + bytes[0] = expon >> 8; + bytes[1] = expon; + bytes[2] = hiMant >> 24; + bytes[3] = hiMant >> 16; + bytes[4] = hiMant >> 8; + bytes[5] = hiMant; + bytes[6] = loMant >> 24; + bytes[7] = loMant >> 16; + bytes[8] = loMant >> 8; + bytes[9] = loMant; +} + +/* + * C O N V E R T F R O M I E E E E X T E N D E D + */ + +/* + * Copyright (C) 1988-1991 Apple Computer, Inc. + * All rights reserved. + * + * Machine-independent I/O routines for IEEE floating-point numbers. + * + * NaN's and infinities are converted to HUGE_VAL or HUGE, which + * happens to be infinity on IEEE machines. Unfortunately, it is + * impossible to preserve NaN's in a machine-independent way. + * Infinities are, however, preserved on IEEE machines. + * + * These routines have been tested on the following machines: + * Apple Macintosh, MPW 3.1 C compiler + * Apple Macintosh, THINK C compiler + * Silicon Graphics IRIS, MIPS compiler + * Cray X/MP and Y/MP + * Digital Equipment VAX + * + * + * Implemented by Malcolm Slaney and Ken Turkowski. + * + * Malcolm Slaney contributions during 1988-1990 include big- and little- + * endian file I/O, conversion to and from Motorola's extended 80-bit + * floating-point format, and conversions to and from IEEE single- + * precision floating-point format. + * + * In 1991, Ken Turkowski implemented the conversions to and from + * IEEE double-precision format, added more precision to the extended + * conversions, and accommodated conversions involving +/- infinity, + * NaN's, and denormalized numbers. + */ + +#ifndef HUGE_VAL +# define HUGE_VAL HUGE +#endif /*HUGE_VAL*/ + +# define UnsignedToFloat(u) (((double) ((long) (u - 2147483647L - 1))) + 2147483648.0) + +/**************************************************************** + * Extended precision IEEE floating-point conversion routine. + ****************************************************************/ + +double _af_convert_from_ieee_extended (const unsigned char *bytes) +{ + double f; + int expon; + unsigned long hiMant, loMant; + + expon = ((bytes[0] & 0x7F) << 8) | (bytes[1] & 0xFF); + hiMant = ((unsigned long)(bytes[2] & 0xFF) << 24) + | ((unsigned long) (bytes[3] & 0xFF) << 16) + | ((unsigned long) (bytes[4] & 0xFF) << 8) + | ((unsigned long) (bytes[5] & 0xFF)); + loMant = ((unsigned long) (bytes[6] & 0xFF) << 24) + | ((unsigned long) (bytes[7] & 0xFF) << 16) + | ((unsigned long) (bytes[8] & 0xFF) << 8) + | ((unsigned long) (bytes[9] & 0xFF)); + + if (expon == 0 && hiMant == 0 && loMant == 0) { + f = 0; + } + else { + if (expon == 0x7FFF) { /* Infinity or NaN */ + f = HUGE_VAL; + } + else { + expon -= 16383; + f = ldexp(UnsignedToFloat(hiMant), expon-=31); + f += ldexp(UnsignedToFloat(loMant), expon-=32); + } + } + + if (bytes[0] & 0x80) + return -f; + else + return f; +} diff --git a/libaudiofile/extended.h b/libaudiofile/extended.h new file mode 100644 index 0000000..c07f853 --- /dev/null +++ b/libaudiofile/extended.h @@ -0,0 +1,34 @@ +/* + Audio File Library + Copyright (C) 1998, Michael Pruett <michael@68k.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307 USA. +*/ + +/* + extended.h + + This file defines interfaces to Apple's extended floating-point + conversion routines. +*/ + +#ifndef EXTENDED_H +#define EXTENDED_H + +void _af_convert_to_ieee_extended (double num, unsigned char *bytes); +double _af_convert_from_ieee_extended (const unsigned char *bytes); + +#endif diff --git a/libaudiofile/format.c b/libaudiofile/format.c new file mode 100644 index 0000000..6ee0361 --- /dev/null +++ b/libaudiofile/format.c @@ -0,0 +1,396 @@ +/* + Audio File Library + Copyright (C) 1998-2000, Michael Pruett <michael@68k.org> + Copyright (C) 2000, Silicon Graphics, Inc. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307 USA. +*/ + +/* + audiofile.c + + This file implements many of the main interface routines of the + Audio File Library. +*/ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <sys/types.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <assert.h> + +#include "audiofile.h" +#include "util.h" +#include "afinternal.h" +#include "afinternal.h" +#include "units.h" +#include "modules.h" + +extern _Unit _af_units[]; + +AFfileoffset afGetDataOffset (AFfilehandle file, int trackid) +{ + _Track *track; + + if (!_af_filehandle_ok(file)) + return -1; + + if ((track = _af_filehandle_get_track(file, trackid)) == NULL) + return -1; + + return track->fpos_first_frame; +} + +AFfileoffset afGetTrackBytes (AFfilehandle file, int trackid) +{ + _Track *track; + + if (!_af_filehandle_ok(file)) + return -1; + + track = _af_filehandle_get_track(file, trackid); + + return track->data_size; +} + +/* + afGetFrameSize returns the size (in bytes) of a sample frame from + the specified track of an audio file. + + stretch3to4 == AF_TRUE: size which user sees + stretch3to4 == AF_FALSE: size used in file +*/ +float afGetFrameSize (AFfilehandle file, int trackid, int stretch3to4) +{ + _Track *track; + + if (!_af_filehandle_ok(file)) + return -1; + + if ((track = _af_filehandle_get_track(file, trackid)) == NULL) + return -1; + + return _af_format_frame_size(&track->f, stretch3to4); +} + +float afGetVirtualFrameSize (AFfilehandle file, int trackid, int stretch3to4) +{ + _Track *track; + + if (!_af_filehandle_ok(file)) + return -1; + + if ((track = _af_filehandle_get_track(file, trackid)) == NULL) + return -1; + + return _af_format_frame_size(&track->v, stretch3to4); +} + +AFframecount afSeekFrame (AFfilehandle file, int trackid, AFframecount frame) +{ + _Track *track; + + if (!_af_filehandle_ok(file)) + return -1; + + if (!_af_filehandle_can_read(file)) + return -1; + + if ((track = _af_filehandle_get_track(file, trackid)) == NULL) + return -1; + + if (track->ms.modulesdirty) + if (_AFsetupmodules(file, track) != AF_SUCCEED) + return -1; + + if (frame < 0) + return track->nextvframe; + + /* Optimize the case of seeking to the current position. */ + if (frame == track->nextvframe) + return track->nextvframe; + + /* Limit request to the number of frames in the file. */ + if (track->totalvframes != -1) + if (frame > track->totalvframes) + frame = track->totalvframes - 1; + + /* + Now that the modules are not dirty and frame + represents a valid virtual frame, we call + _AFsetupmodules again after setting track->nextvframe. + + _AFsetupmodules will look at track->nextvframe and + compute track->nextfframe in clever and mysterious + ways. + */ + track->nextvframe = frame; + + if (_AFsetupmodules(file, track) != AF_SUCCEED) + return -1; + + return track->nextvframe; +} + +AFfileoffset afTellFrame (AFfilehandle file, int trackid) +{ + return afSeekFrame(file, trackid, -1); +} + +int afSetVirtualByteOrder (AFfilehandle handle, int track, int byteorder) +{ + _Track *currentTrack; + + if (!_af_filehandle_ok(handle)) + return -1; + + if (NULL == (currentTrack = _af_filehandle_get_track(handle, track))) + return AF_FAIL; + + if (byteorder != AF_BYTEORDER_BIGENDIAN && + byteorder != AF_BYTEORDER_LITTLEENDIAN) + { + _af_error(AF_BAD_BYTEORDER, "invalid byte order %d", byteorder); + return AF_FAIL; + } + + currentTrack->v.byteOrder = byteorder; + currentTrack->ms.modulesdirty = AF_TRUE; + + return AF_SUCCEED; +} + +int afGetByteOrder (AFfilehandle handle, int track) +{ + _Track *currentTrack; + + if (!_af_filehandle_ok(handle)) + return -1; + + if ((currentTrack = _af_filehandle_get_track(handle, track)) == NULL) + return -1; + + return (currentTrack->f.byteOrder); +} + +int afGetVirtualByteOrder (AFfilehandle handle, int track) +{ + _Track *currentTrack; + + if (!_af_filehandle_ok(handle)) + return -1; + + if ((currentTrack = _af_filehandle_get_track(handle, track)) == NULL) + return -1; + + return (currentTrack->v.byteOrder); +} + +AFframecount afGetFrameCount (AFfilehandle file, int trackid) +{ + _Track *track; + + if (!_af_filehandle_ok(file)) + return -1; + + if ((track = _af_filehandle_get_track(file, trackid)) == NULL) + return -1; + + if (track->ms.modulesdirty) + { + if (_AFsetupmodules(file, track) != AF_SUCCEED) + return -1; + } + + return track->totalvframes; +} + +double afGetRate (AFfilehandle file, int trackid) +{ + _Track *track; + + if ((track = _af_filehandle_get_track(file, trackid)) == NULL) + return -1; + + return track->f.sampleRate; +} + +int afGetChannels (AFfilehandle file, int trackid) +{ + _Track *track; + + if ((track = _af_filehandle_get_track(file, trackid)) == NULL) + return -1; + + return track->f.channelCount; +} + +void afGetSampleFormat (AFfilehandle file, int trackid, int *sampleFormat, int *sampleWidth) +{ + _Track *track; + + if ((track = _af_filehandle_get_track(file, trackid)) == NULL) + return; + + if (sampleFormat != NULL) + *sampleFormat = track->f.sampleFormat; + + if (sampleFormat != NULL) + *sampleWidth = track->f.sampleWidth; +} + +void afGetVirtualSampleFormat (AFfilehandle file, int trackid, int *sampleFormat, int *sampleWidth) +{ + _Track *track; + + if ((track = _af_filehandle_get_track(file, trackid)) == NULL) + return; + + if (sampleFormat != NULL) + *sampleFormat = track->v.sampleFormat; + + if (sampleFormat != NULL) + *sampleWidth = track->v.sampleWidth; +} + +int afSetVirtualSampleFormat (AFfilehandle file, int trackid, + int sampleFormat, int sampleWidth) +{ + _Track *track; + + if (!_af_filehandle_ok(file)) + return -1; + + if ((track = _af_filehandle_get_track(file, trackid)) == NULL) + return -1; + + if (_af_set_sample_format(&track->v, sampleFormat, sampleWidth) == AF_FAIL) + return -1; + + track->ms.modulesdirty = AF_TRUE; + + return 0; +} + +/* XXXmpruett fix the version */ +int afGetFileFormat (AFfilehandle file, int *version) +{ + if (!_af_filehandle_ok(file)) + return -1; + + if (version != NULL) + { + if (_af_units[file->fileFormat].getversion) + *version = _af_units[file->fileFormat].getversion(file); + else + *version = 0; + } + + return file->fileFormat; +} + +int afSetVirtualChannels (AFfilehandle file, int trackid, int channelCount) +{ + _Track *track; + + if (!_af_filehandle_ok(file)) + return -1; + + if ((track = _af_filehandle_get_track(file, trackid)) == NULL) + return -1; + + track->v.channelCount = channelCount; + track->ms.modulesdirty = AF_TRUE; + + if (track->channelMatrix) + free(track->channelMatrix); + track->channelMatrix = NULL; + + return 0; +} + +double afGetVirtualRate (AFfilehandle file, int trackid) +{ + _Track *track; + + if ((track = _af_filehandle_get_track(file, trackid)) == NULL) + return -1; + + return track->v.sampleRate; +} + +int afSetVirtualRate (AFfilehandle file, int trackid, double rate) +{ + _Track *track; + + if (!_af_filehandle_ok(file)) + return -1; + + if ((track = _af_filehandle_get_track(file, trackid)) == NULL) + return -1; + + if (rate < 0) + { + _af_error(AF_BAD_RATE, "invalid sampling rate %.30g", rate); + return -1; + } + + track->v.sampleRate = rate; + track->ms.modulesdirty = AF_TRUE; + + return 0; +} + +void afSetChannelMatrix (AFfilehandle file, int trackid, double* matrix) +{ + _Track *track; + + if (!_af_filehandle_ok(file)) + return; + + if ((track = _af_filehandle_get_track(file, trackid)) == NULL) + return; + + if (track->channelMatrix != NULL) + free(track->channelMatrix); + track->channelMatrix = NULL; + + if (matrix != NULL) + { + int i, size; + + size = track->v.channelCount * track->f.channelCount; + + track->channelMatrix = (double *) malloc(size * sizeof (double)); + + for (i = 0; i < size; i++) + track->channelMatrix[i] = matrix[i]; + } +} + +int afGetVirtualChannels (AFfilehandle file, int trackid) +{ + _Track *track; + + if ((track = _af_filehandle_get_track(file, trackid)) == NULL) + return -1; + + return track->v.channelCount; +} diff --git a/libaudiofile/g711.c b/libaudiofile/g711.c new file mode 100644 index 0000000..57099fe --- /dev/null +++ b/libaudiofile/g711.c @@ -0,0 +1,288 @@ +/* + * This source code is a product of Sun Microsystems, Inc. and is provided + * for unrestricted use. Users may copy or modify this source code without + * charge. + * + * SUN SOURCE CODE IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING + * THE WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR + * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. + * + * Sun source code is provided with no support and without any obligation on + * the part of Sun Microsystems, Inc. to assist in its use, correction, + * modification or enhancement. + * + * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE + * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY THIS SOFTWARE + * OR ANY PART THEREOF. + * + * In no event will Sun Microsystems, Inc. be liable for any lost revenue + * or profits or other special, indirect and consequential damages, even if + * Sun has been advised of the possibility of such damages. + * + * Sun Microsystems, Inc. + * 2550 Garcia Avenue + * Mountain View, California 94043 + */ + +#define SUPERCEDED + +/* + * g711.c + * + * u-law, A-law and linear PCM conversions. + */ +#define SIGN_BIT (0x80) /* Sign bit for a A-law byte. */ +#define QUANT_MASK (0xf) /* Quantization field mask. */ +#define NSEGS (8) /* Number of A-law segments. */ +#define SEG_SHIFT (4) /* Left shift for segment number. */ +#define SEG_MASK (0x70) /* Segment field mask. */ + +/* copy from CCITT G.711 specifications */ +static unsigned char _u2a[128] = { /* u- to A-law conversions */ + 1, 1, 2, 2, 3, 3, 4, 4, + 5, 5, 6, 6, 7, 7, 8, 8, + 9, 10, 11, 12, 13, 14, 15, 16, + 17, 18, 19, 20, 21, 22, 23, 24, + 25, 27, 29, 31, 33, 34, 35, 36, + 37, 38, 39, 40, 41, 42, 43, 44, + 46, 48, 49, 50, 51, 52, 53, 54, + 55, 56, 57, 58, 59, 60, 61, 62, + 64, 65, 66, 67, 68, 69, 70, 71, + 72, 73, 74, 75, 76, 77, 78, 79, + 81, 82, 83, 84, 85, 86, 87, 88, + 89, 90, 91, 92, 93, 94, 95, 96, + 97, 98, 99, 100, 101, 102, 103, 104, + 105, 106, 107, 108, 109, 110, 111, 112, + 113, 114, 115, 116, 117, 118, 119, 120, + 121, 122, 123, 124, 125, 126, 127, 128}; + +static unsigned char _a2u[128] = { /* A- to u-law conversions */ + 1, 3, 5, 7, 9, 11, 13, 15, + 16, 17, 18, 19, 20, 21, 22, 23, + 24, 25, 26, 27, 28, 29, 30, 31, + 32, 32, 33, 33, 34, 34, 35, 35, + 36, 37, 38, 39, 40, 41, 42, 43, + 44, 45, 46, 47, 48, 48, 49, 49, + 50, 51, 52, 53, 54, 55, 56, 57, + 58, 59, 60, 61, 62, 63, 64, 64, + 65, 66, 67, 68, 69, 70, 71, 72, + 73, 74, 75, 76, 77, 78, 79, 79, + 80, 81, 82, 83, 84, 85, 86, 87, + 88, 89, 90, 91, 92, 93, 94, 95, + 96, 97, 98, 99, 100, 101, 102, 103, + 104, 105, 106, 107, 108, 109, 110, 111, + 112, 113, 114, 115, 116, 117, 118, 119, + 120, 121, 122, 123, 124, 125, 126, 127}; + +/* see libst.h */ +#ifdef SUPERCEDED + +static short seg_end[8] = {0xFF, 0x1FF, 0x3FF, 0x7FF, + 0xFFF, 0x1FFF, 0x3FFF, 0x7FFF}; + +static int +search(val, table, size) + int val; + short *table; + int size; +{ + int i; + + for (i = 0; i < size; i++) { + if (val <= *table++) + return (i); + } + return (size); +} + +/* + * linear2alaw() - Convert a 16-bit linear PCM value to 8-bit A-law + * + * linear2alaw() accepts an 16-bit integer and encodes it as A-law data. + * + * Linear Input Code Compressed Code + * ------------------------ --------------- + * 0000000wxyza 000wxyz + * 0000001wxyza 001wxyz + * 000001wxyzab 010wxyz + * 00001wxyzabc 011wxyz + * 0001wxyzabcd 100wxyz + * 001wxyzabcde 101wxyz + * 01wxyzabcdef 110wxyz + * 1wxyzabcdefg 111wxyz + * + * For further information see John C. Bellamy's Digital Telephony, 1982, + * John Wiley & Sons, pps 98-111 and 472-476. + */ +unsigned char +_af_linear2alaw(pcm_val) + int pcm_val; /* 2's complement (16-bit range) */ +{ + int mask; + int seg; + unsigned char aval; + + if (pcm_val >= 0) { + mask = 0xD5; /* sign (7th) bit = 1 */ + } else { + mask = 0x55; /* sign bit = 0 */ + pcm_val = -pcm_val - 8; + } + + /* Convert the scaled magnitude to segment number. */ + seg = search(pcm_val, seg_end, 8); + + /* Combine the sign, segment, and quantization bits. */ + + if (seg >= 8) /* out of range, return maximum value. */ + return (0x7F ^ mask); + else { + aval = seg << SEG_SHIFT; + if (seg < 2) + aval |= (pcm_val >> 4) & QUANT_MASK; + else + aval |= (pcm_val >> (seg + 3)) & QUANT_MASK; + return (aval ^ mask); + } +} + +/* + * alaw2linear() - Convert an A-law value to 16-bit linear PCM + * + */ +int +_af_alaw2linear(a_val) + unsigned char a_val; +{ + int t; + int seg; + + a_val ^= 0x55; + + t = (a_val & QUANT_MASK) << 4; + seg = ((unsigned)a_val & SEG_MASK) >> SEG_SHIFT; + switch (seg) { + case 0: + t += 8; + break; + case 1: + t += 0x108; + break; + default: + t += 0x108; + t <<= seg - 1; + } + return ((a_val & SIGN_BIT) ? t : -t); +} + +#define BIAS (0x84) /* Bias for linear code. */ + +/* + * linear2ulaw() - Convert a linear PCM value to u-law + * + * In order to simplify the encoding process, the original linear magnitude + * is biased by adding 33 which shifts the encoding range from (0 - 8158) to + * (33 - 8191). The result can be seen in the following encoding table: + * + * Biased Linear Input Code Compressed Code + * ------------------------ --------------- + * 00000001wxyza 000wxyz + * 0000001wxyzab 001wxyz + * 000001wxyzabc 010wxyz + * 00001wxyzabcd 011wxyz + * 0001wxyzabcde 100wxyz + * 001wxyzabcdef 101wxyz + * 01wxyzabcdefg 110wxyz + * 1wxyzabcdefgh 111wxyz + * + * Each biased linear code has a leading 1 which identifies the segment + * number. The value of the segment number is equal to 7 minus the number + * of leading 0's. The quantization interval is directly available as the + * four bits wxyz. * The trailing bits (a - h) are ignored. + * + * Ordinarily the complement of the resulting code word is used for + * transmission, and so the code word is complemented before it is returned. + * + * For further information see John C. Bellamy's Digital Telephony, 1982, + * John Wiley & Sons, pps 98-111 and 472-476. + */ + +/* 2's complement (16-bit range) */ +unsigned char _af_linear2ulaw (int pcm_val) +{ + int mask; + int seg; + unsigned char uval; + + /* Get the sign and the magnitude of the value. */ + if (pcm_val < 0) { + pcm_val = BIAS - pcm_val; + mask = 0x7F; + } else { + pcm_val += BIAS; + mask = 0xFF; + } + + /* Convert the scaled magnitude to segment number. */ + seg = search(pcm_val, seg_end, 8); + + /* + * Combine the sign, segment, quantization bits; + * and complement the code word. + */ + if (seg >= 8) /* out of range, return maximum value. */ + return (0x7F ^ mask); + else { + uval = (seg << 4) | ((pcm_val >> (seg + 3)) & 0xF); + return (uval ^ mask); + } + +} + +/* + * ulaw2linear() - Convert a u-law value to 16-bit linear PCM + * + * First, a biased linear code is derived from the code word. An unbiased + * output can then be obtained by subtracting 33 from the biased code. + * + * Note that this function expects to be passed the complement of the + * original code word. This is in keeping with ISDN conventions. + */ +int _af_ulaw2linear (unsigned char u_val) +{ + int t; + + /* Complement to obtain normal u-law value. */ + u_val = ~u_val; + + /* + * Extract and bias the quantization bits. Then + * shift up by the segment number and subtract out the bias. + */ + t = ((u_val & QUANT_MASK) << 3) + BIAS; + t <<= ((unsigned)u_val & SEG_MASK) >> SEG_SHIFT; + + return ((u_val & SIGN_BIT) ? (BIAS - t) : (t - BIAS)); +} + +#endif + +/* A-law to u-law conversion */ +static unsigned char +alaw2ulaw(aval) + unsigned char aval; +{ + aval &= 0xff; + return ((aval & 0x80) ? (0xFF ^ _a2u[aval ^ 0xD5]) : + (0x7F ^ _a2u[aval ^ 0x55])); +} + +/* u-law to A-law conversion */ +static unsigned char +ulaw2alaw(uval) + unsigned char uval; +{ + uval &= 0xff; + return ((uval & 0x80) ? (0xD5 ^ (_u2a[0xFF ^ uval] - 1)) : + (0x55 ^ (_u2a[0x7F ^ uval] - 1))); +} diff --git a/libaudiofile/g711.h b/libaudiofile/g711.h new file mode 100644 index 0000000..0423ffe --- /dev/null +++ b/libaudiofile/g711.h @@ -0,0 +1,111 @@ +/* + * This source code is a product of Sun Microsystems, Inc. and is provided + * for unrestricted use. Users may copy or modify this source code without + * charge. + * + * SUN SOURCE CODE IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING + * THE WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR + * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. + * + * Sun source code is provided with no support and without any obligation on + * the part of Sun Microsystems, Inc. to assist in its use, correction, + * modification or enhancement. + * + * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE + * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY THIS SOFTWARE + * OR ANY PART THEREOF. + * + * In no event will Sun Microsystems, Inc. be liable for any lost revenue + * or profits or other special, indirect and consequential damages, even if + * Sun has been advised of the possibility of such damages. + * + * Sun Microsystems, Inc. + * 2550 Garcia Avenue + * Mountain View, California 94043 + */ + +/* + * g711.h + * + * u-law, A-law and linear PCM conversions. + */ + +#ifndef G711_H +#define G711_H + +/* + * linear2alaw() - Convert a 16-bit linear PCM value to 8-bit A-law + * + * linear2alaw() accepts an 16-bit integer and encodes it as A-law data. + * + * Linear Input Code Compressed Code + * ------------------------ --------------- + * 0000000wxyza 000wxyz + * 0000001wxyza 001wxyz + * 000001wxyzab 010wxyz + * 00001wxyzabc 011wxyz + * 0001wxyzabcd 100wxyz + * 001wxyzabcde 101wxyz + * 01wxyzabcdef 110wxyz + * 1wxyzabcdefg 111wxyz + * + * For further information see John C. Bellamy's Digital Telephony, 1982, + * John Wiley & Sons, pps 98-111 and 472-476. + */ + +/* pcm_val is 2's complement (16-bit range) */ +unsigned char _af_linear2alaw (int pcm_val); + +/* + * alaw2linear() - Convert an A-law value to 16-bit linear PCM + * + */ + +int _af_alaw2linear (unsigned char a_val); + +/* + * linear2ulaw() - Convert a linear PCM value to u-law + * + * In order to simplify the encoding process, the original linear magnitude + * is biased by adding 33 which shifts the encoding range from (0 - 8158) to + * (33 - 8191). The result can be seen in the following encoding table: + * + * Biased Linear Input Code Compressed Code + * ------------------------ --------------- + * 00000001wxyza 000wxyz + * 0000001wxyzab 001wxyz + * 000001wxyzabc 010wxyz + * 00001wxyzabcd 011wxyz + * 0001wxyzabcde 100wxyz + * 001wxyzabcdef 101wxyz + * 01wxyzabcdefg 110wxyz + * 1wxyzabcdefgh 111wxyz + * + * Each biased linear code has a leading 1 which identifies the segment + * number. The value of the segment number is equal to 7 minus the number + * of leading 0's. The quantization interval is directly available as the + * four bits wxyz. * The trailing bits (a - h) are ignored. + * + * Ordinarily the complement of the resulting code word is used for + * transmission, and so the code word is complemented before it is returned. + * + * For further information see John C. Bellamy's Digital Telephony, 1982, + * John Wiley & Sons, pps 98-111 and 472-476. + */ + +/* pcm_val is 2's complement (16-bit range) */ +unsigned char _af_linear2ulaw (int pcm_val); + +/* + * ulaw2linear() - Convert a u-law value to 16-bit linear PCM + * + * First, a biased linear code is derived from the code word. An unbiased + * output can then be obtained by subtracting 33 from the biased code. + * + * Note that this function expects to be passed the complement of the + * original code word. This is in keeping with ISDN conventions. + */ + +int _af_ulaw2linear (unsigned char u_val); + +#endif /* G711_H */ diff --git a/libaudiofile/iff.c b/libaudiofile/iff.c new file mode 100644 index 0000000..b452339 --- /dev/null +++ b/libaudiofile/iff.c @@ -0,0 +1,332 @@ +/* + Audio File Library + Copyright (C) 2004, Michael Pruett <michael@68k.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307 USA. +*/ + +/* + iff.c + + This file contains routines for parsing IFF/8SVX sound + files. +*/ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <assert.h> +#include <sys/types.h> +#include <stdio.h> +#include <string.h> + +#ifdef HAVE_INTTYPES_H +#include <inttypes.h> +#endif + +#include "audiofile.h" +#include "afinternal.h" +#include "byteorder.h" +#include "util.h" +#include "setup.h" +#include "track.h" +#include "marker.h" + +#include "iff.h" + +static status ParseMiscellaneous (AFfilehandle file, AFvirtualfile *fh, + u_int32_t type, size_t size); +static status ParseVHDR (AFfilehandle file, AFvirtualfile *fh, u_int32_t type, + size_t size); +static status ParseBODY (AFfilehandle file, AFvirtualfile *fh, u_int32_t type, + size_t size); + +_AFfilesetup _af_iff_default_filesetup = +{ + _AF_VALID_FILESETUP, /* valid */ + AF_FILE_IFF_8SVX, /* fileFormat */ + AF_TRUE, /* trackSet */ + AF_TRUE, /* instrumentSet */ + AF_TRUE, /* miscellaneousSet */ + 1, /* trackCount */ + NULL, /* tracks */ + 0, /* instrumentCount */ + NULL, /* instruments */ + 0, /* miscellaneousCount */ + NULL /* miscellaneous */ +}; + +bool _af_iff_recognize (AFvirtualfile *fh) +{ + u_int8_t buffer[8]; + + af_fseek(fh, 0, SEEK_SET); + + if (af_fread(buffer, 1, 8, fh) != 8 || memcmp(buffer, "FORM", 4) != 0) + return AF_FALSE; + if (af_fread(buffer, 1, 4, fh) != 4 || memcmp(buffer, "8SVX", 4) != 0) + return AF_FALSE; + + return AF_TRUE; +} + +/* + Parse miscellaneous data chunks such as name, author, copyright, + and annotation chunks. +*/ +static status ParseMiscellaneous (AFfilehandle file, AFvirtualfile *fh, + u_int32_t type, size_t size) +{ + int misctype = AF_MISC_UNRECOGNIZED; + + assert(!memcmp(&type, "NAME", 4) || !memcmp(&type, "AUTH", 4) || + !memcmp(&type, "(c) ", 4) || !memcmp(&type, "ANNO", 4)); + + /* Skip zero-length miscellaneous chunks. */ + if (size == 0) + return AF_FAIL; + + file->miscellaneousCount++; + file->miscellaneous = _af_realloc(file->miscellaneous, + file->miscellaneousCount * sizeof (_Miscellaneous)); + + if (!memcmp(&type, "NAME", 4)) + misctype = AF_MISC_NAME; + else if (!memcmp(&type, "AUTH", 4)) + misctype = AF_MISC_AUTH; + else if (!memcmp(&type, "(c) ", 4)) + misctype = AF_MISC_COPY; + else if (!memcmp(&type, "ANNO", 4)) + misctype = AF_MISC_ANNO; + + file->miscellaneous[file->miscellaneousCount - 1].id = file->miscellaneousCount; + file->miscellaneous[file->miscellaneousCount - 1].type = misctype; + file->miscellaneous[file->miscellaneousCount - 1].size = size; + file->miscellaneous[file->miscellaneousCount - 1].position = 0; + file->miscellaneous[file->miscellaneousCount - 1].buffer = _af_malloc(size); + af_fread(file->miscellaneous[file->miscellaneousCount - 1].buffer, + size, 1, file->fh); + + return AF_SUCCEED; +} + +/* + Parse voice header chunk. +*/ +static status ParseVHDR (AFfilehandle file, AFvirtualfile *fh, u_int32_t type, + size_t size) +{ + _Track *track; + u_int32_t oneShotSamples, repeatSamples, samplesPerRepeat; + u_int16_t sampleRate; + u_int8_t octaves, compression; + u_int32_t volume; + + assert(!memcmp(&type, "VHDR", 4)); + + track = _af_filehandle_get_track(file, AF_DEFAULT_TRACK); + + af_read_uint32_be(&oneShotSamples, fh); + af_read_uint32_be(&repeatSamples, fh); + af_read_uint32_be(&samplesPerRepeat, fh); + af_read_uint16_be(&sampleRate, fh); + af_fread(&octaves, 1, 1, fh); + af_fread(&compression, 1, 1, fh); + af_read_uint32_be(&volume, fh); + + track->f.sampleWidth = 8; + track->f.sampleRate = sampleRate; + track->f.sampleFormat = AF_SAMPFMT_TWOSCOMP; + track->f.compressionType = AF_COMPRESSION_NONE; + track->f.byteOrder = AF_BYTEORDER_BIGENDIAN; + track->f.channelCount = 1; + + _af_set_sample_format(&track->f, track->f.sampleFormat, track->f.sampleWidth); + + return AF_SUCCEED; +} + +static status ParseBODY (AFfilehandle file, AFvirtualfile *fh, u_int32_t type, + size_t size) +{ + _Track *track; + + track = _af_filehandle_get_track(file, AF_DEFAULT_TRACK); + + /* + IFF/8SVX files have only one audio channel with one + byte per sample, so the number of frames is equal to + the number of bytes. + */ + track->totalfframes = size; + track->data_size = size; + + /* Sound data follows. */ + track->fpos_first_frame = af_ftell(fh); + + return AF_SUCCEED; +} + +status _af_iff_read_init (AFfilesetup setup, AFfilehandle file) +{ + u_int32_t type, size, formtype; + size_t index; + _Track *track; + + assert(file != NULL); + assert(file->fh != NULL); + + af_fseek(file->fh, 0, SEEK_SET); + + af_fread(&type, 4, 1, file->fh); + af_read_uint32_be(&size, file->fh); + af_fread(&formtype, 4, 1, file->fh); + + if (memcmp(&type, "FORM", 4) != 0 || memcmp(&formtype, "8SVX", 4) != 0) + return AF_FAIL; + + file->instrumentCount = 0; + file->instruments = NULL; + file->miscellaneousCount = 0; + file->miscellaneous = NULL; + + /* IFF/8SVX files have only one track. */ + track = _af_track_new(); + file->trackCount = 1; + file->tracks = track; + + /* Set the index to include the form type ('8SVX' in this case). */ + index = 4; + + while (index < size) + { + u_int32_t chunkid = 0, chunksize = 0; + status result = AF_SUCCEED; + + af_fread(&chunkid, 4, 1, file->fh); + af_read_uint32_be(&chunksize, file->fh); + + if (!memcmp("VHDR", &chunkid, 4)) + { + result = ParseVHDR(file, file->fh, chunkid, chunksize); + } + else if (!memcmp("BODY", &chunkid, 4)) + { + result = ParseBODY(file, file->fh, chunkid, chunksize); + } + else if (!memcmp("NAME", &chunkid, 4) || + !memcmp("AUTH", &chunkid, 4) || + !memcmp("(c) ", &chunkid, 4) || + !memcmp("ANNO", &chunkid, 4)) + { + ParseMiscellaneous(file, file->fh, chunkid, chunksize); + } + + if (result == AF_FAIL) + return AF_FAIL; + + /* + Increment the index by the size of the chunk + plus the size of the chunk header. + */ + index += chunksize + 8; + + /* All chunks must be aligned on an even number of bytes. */ + if ((index % 2) != 0) + index++; + + /* Set the seek position to the beginning of the next chunk. */ + af_fseek(file->fh, index + 8, SEEK_SET); + } + + /* The file has been successfully parsed. */ + return AF_SUCCEED; +} + +AFfilesetup _af_iff_complete_setup (AFfilesetup setup) +{ + _TrackSetup *track; + + if (setup->trackSet && setup->trackCount != 1) + { + _af_error(AF_BAD_NUMTRACKS, "IFF/8SVX file must have 1 track"); + return AF_NULL_FILESETUP; + } + + track = &setup->tracks[0]; + + if (track->sampleFormatSet && + track->f.sampleFormat != AF_SAMPFMT_TWOSCOMP) + { + _af_error(AF_BAD_SAMPFMT, + "IFF/8SVX format supports only two's complement integer data"); + return AF_NULL_FILESETUP; + } + + if (track->sampleFormatSet && track->f.sampleWidth != 8) + { + _af_error(AF_BAD_WIDTH, + "IFF/8SVX file allows only 8 bits per sample " + "(%d bits requested)", track->f.sampleWidth); + return AF_NULL_FILESETUP; + } + + if (track->channelCountSet && track->f.channelCount != 1) + { + _af_error(AF_BAD_CHANNELS, + "invalid channel count (%d) for IFF/8SVX format " + "(only 1 channel supported)", + track->f.channelCount); + return AF_NULL_FILESETUP; + } + + if (track->f.compressionType != AF_COMPRESSION_NONE) + { + _af_error(AF_BAD_COMPRESSION, + "IFF/8SVX does not support compression"); + return AF_NULL_FILESETUP; + } + + /* Ignore requested byte order since samples are only one byte. */ + track->f.byteOrder = AF_BYTEORDER_BIGENDIAN; + /* Either one channel was requested or no request was made. */ + track->f.channelCount = 1; + _af_set_sample_format(&track->f, AF_SAMPFMT_TWOSCOMP, 8); + + if (track->markersSet && track->markerCount != 0) + { + _af_error(AF_BAD_NUMMARKS, + "IFF/8SVX format does not support markers"); + return AF_NULL_FILESETUP; + } + + if (setup->instrumentSet && setup->instrumentCount != 0) + { + _af_error(AF_BAD_NUMINSTS, + "IFF/8SVX format does not support instruments"); + return AF_NULL_FILESETUP; + } + + if (setup->miscellaneousSet && setup->miscellaneousCount != 0) + { + _af_error(AF_BAD_NOT_IMPLEMENTED, "IFF/8SVX format does not " + "currently support miscellaneous chunks"); + return AF_NULL_FILESETUP; + } + + return _af_filesetup_copy(setup, &_af_iff_default_filesetup, AF_TRUE); +} diff --git a/libaudiofile/iff.h b/libaudiofile/iff.h new file mode 100644 index 0000000..f072de9 --- /dev/null +++ b/libaudiofile/iff.h @@ -0,0 +1,50 @@ +/* + Audio File Library + Copyright (C) 2004, Michael Pruett <michael@68k.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307 USA. +*/ + +/* + iff.h + + This file declares constants and functions related to the + IFF/8SVX file format. +*/ + +#ifndef IFF_H +#define IFF_H + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include "audiofile.h" + +bool _af_iff_recognize (AFvirtualfile *fh); +status _af_iff_read_init (AFfilesetup, AFfilehandle); +AFfilesetup _af_iff_complete_setup (AFfilesetup); +status _af_iff_write_init (AFfilesetup, AFfilehandle); +status _af_iff_update (AFfilehandle); + +typedef struct +{ + AFfileoffset miscellaneousPosition; + AFfileoffset VHDR_offset; + AFfileoffset BODY_offset; +} _IFFinfo; + +#endif diff --git a/libaudiofile/iffwrite.c b/libaudiofile/iffwrite.c new file mode 100644 index 0000000..7fb2e49 --- /dev/null +++ b/libaudiofile/iffwrite.c @@ -0,0 +1,247 @@ +/* + Audio File Library + Copyright (C) 2004, Michael Pruett <michael@68k.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307 USA. +*/ + +/* + iffwrite.c + + This file contains routines for writing IFF/8SVX format sound + files. +*/ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <assert.h> +#include <sys/types.h> +#include <stdlib.h> +#include <string.h> + +#include "afinternal.h" +#include "audiofile.h" +#include "byteorder.h" +#include "util.h" +#include "setup.h" + +#include "iff.h" + +status _af_iff_update (AFfilehandle file); + +static status WriteVHDR (AFfilehandle file); +static status WriteMiscellaneous (AFfilehandle file); +static status WriteBODY (AFfilehandle file); + +static _IFFinfo *iff_info_new (void) +{ + _IFFinfo *iff = _af_calloc(1, sizeof (_IFFinfo)); + + iff->miscellaneousPosition = 0; + iff->VHDR_offset = 0; + iff->BODY_offset = 0; + + return iff; +} + +status _af_iff_write_init (AFfilesetup setup, AFfilehandle file) +{ + u_int32_t fileSize = 0; + + if (_af_filesetup_make_handle(setup, file) == AF_FAIL) + return AF_FAIL; + + file->formatSpecific = iff_info_new(); + + af_fwrite("FORM", 4, 1, file->fh); + af_write_uint32_be(&fileSize, file->fh); + + af_fwrite("8SVX", 4, 1, file->fh); + + WriteVHDR(file); + WriteMiscellaneous(file); + WriteBODY(file); + + return AF_SUCCEED; +} + +status _af_iff_update (AFfilehandle file) +{ + u_int32_t length; + + WriteVHDR(file); + WriteMiscellaneous(file); + WriteBODY(file); + + /* Get the length of the file. */ + length = af_flength(file->fh); + length -= 8; + + /* Set the length of the FORM chunk. */ + af_fseek(file->fh, 4, SEEK_SET); + af_write_uint32_be(&length, file->fh); + + return AF_SUCCEED; +} + +static status WriteVHDR (const AFfilehandle file) +{ + _Track *track; + _IFFinfo *iff; + u_int32_t chunkSize; + u_int32_t oneShotSamples, repeatSamples, samplesPerRepeat; + u_int16_t sampleRate; + u_int8_t octaves, compression; + u_int32_t volume; + + iff = (_IFFinfo *) file->formatSpecific; + + /* + If VHDR_offset hasn't been set yet, set it to the + current offset. + */ + if (iff->VHDR_offset == 0) + iff->VHDR_offset = af_ftell(file->fh); + else + af_fseek(file->fh, iff->VHDR_offset, SEEK_SET); + + track = _af_filehandle_get_track(file, AF_DEFAULT_TRACK); + + af_fwrite("VHDR", 4, 1, file->fh); + + chunkSize = 20; + af_write_uint32_be(&chunkSize, file->fh); + + /* + IFF/8SVX files have only one audio channel, so the + number of samples is equal to the number of frames. + */ + oneShotSamples = track->totalfframes; + af_write_uint32_be(&oneShotSamples, file->fh); + repeatSamples = 0; + af_write_uint32_be(&repeatSamples, file->fh); + samplesPerRepeat = 0; + af_write_uint32_be(&samplesPerRepeat, file->fh); + + sampleRate = track->f.sampleRate; + af_write_uint16_be(&sampleRate, file->fh); + + octaves = 0; + compression = 0; + af_fwrite(&octaves, 1, 1, file->fh); + af_fwrite(&compression, 1, 1, file->fh); + + /* Volume is in fixed-point notation; 65536 means gain of 1.0. */ + volume = 65536; + af_write_uint32_be(&volume, file->fh); + + return AF_SUCCEED; +} + +static status WriteBODY (AFfilehandle file) +{ + _Track *track; + u_int32_t chunkSize; + _IFFinfo *iff = (_IFFinfo *) file->formatSpecific; + + track = _af_filehandle_get_track(file, AF_DEFAULT_TRACK); + + if (iff->BODY_offset == 0) + iff->BODY_offset = af_ftell(file->fh); + else + af_fseek(file->fh, iff->BODY_offset, SEEK_SET); + + af_fwrite("BODY", 4, 1, file->fh); + + /* + IFF/8SVX supports only one channel, so the number of + frames is equal to the number of samples, and each + sample is one byte. + */ + chunkSize = track->totalfframes; + af_write_uint32_be(&chunkSize, file->fh); + + if (track->fpos_first_frame == 0) + track->fpos_first_frame = af_ftell(file->fh); + + /* Add a pad byte to the end of the chunk if the chunk size is odd. */ + if ((chunkSize % 2) == 1) + { + u_int8_t zero = 0; + af_fseek(file->fh, iff->BODY_offset + 8 + chunkSize, SEEK_SET); + af_fwrite(&zero, 1, 1, file->fh); + } + + return AF_SUCCEED; +} + +/* + WriteMiscellaneous writes all the miscellaneous data chunks in a + file handle structure to an IFF/8SVX file. +*/ +static status WriteMiscellaneous (AFfilehandle file) +{ + _IFFinfo *iff; + int i; + + iff = (_IFFinfo *) file->formatSpecific; + + if (iff->miscellaneousPosition == 0) + iff->miscellaneousPosition = af_ftell(file->fh); + else + af_fseek(file->fh, iff->miscellaneousPosition, SEEK_SET); + + for (i=0; i<file->miscellaneousCount; i++) + { + _Miscellaneous *misc = &file->miscellaneous[i]; + u_int32_t chunkType, chunkSize; + u_int8_t padByte = 0; + + switch (misc->type) + { + case AF_MISC_NAME: + memcpy(&chunkType, "NAME", 4); break; + case AF_MISC_AUTH: + memcpy(&chunkType, "AUTH", 4); break; + case AF_MISC_COPY: + memcpy(&chunkType, "(c) ", 4); break; + case AF_MISC_ANNO: + memcpy(&chunkType, "ANNO", 4); break; + } + + af_fwrite(&chunkType, 4, 1, file->fh); + + chunkSize = misc->size; + af_write_uint32_be(&chunkSize, file->fh); + + /* + Write the miscellaneous buffer and then a pad byte + if necessary. If the buffer is null, skip the space + for now. + */ + if (misc->buffer != NULL) + af_fwrite(misc->buffer, misc->size, 1, file->fh); + else + af_fseek(file->fh, misc->size, SEEK_CUR); + + if (misc->size % 2 != 0) + af_fwrite(&padByte, 1, 1, file->fh); + } + + return AF_SUCCEED; +} diff --git a/libaudiofile/instrument.c b/libaudiofile/instrument.c new file mode 100644 index 0000000..a179c72 --- /dev/null +++ b/libaudiofile/instrument.c @@ -0,0 +1,305 @@ +/* + Audio File Library + Copyright (C) 1998-2000, Michael Pruett <michael@68k.org> + Copyright (C) 2000, Silicon Graphics, Inc. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307 USA. +*/ + +/* + instrument.c + + Info about instrument parameters: + + Each unit has an array of _InstParamInfo structures, one for + each instrument parameter. Each of these structures describes + the inst parameters. + + id: a 4-byte id as in AIFF file + type: data type AU_PVLIST_* + name: text name + defaultValue: default value, to which it is set when a file with + instruments is first opened for writing. + + Each inst has only an array of values (_AFPVu's). Each value in the + instrument's array is the value of the corresponding index into the + unit's instparaminfo array. + + So for a unit u and an instrument i, u.instparam[N] describes + the parameter whose value is given in i.value[N]. +*/ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <audiofile.h> +#include "afinternal.h" +#include "instrument.h" +#include "units.h" +#include "setup.h" +#include "util.h" + +#include <stdio.h> + +extern _Unit _af_units[]; + +/* + Initialize instrument id list for audio file. +*/ +void afInitInstIDs (AFfilesetup setup, int *instids, int ninsts) +{ + int i; + + if (!_af_filesetup_ok(setup)) + return; + + if (!_af_unique_ids(instids, ninsts, "instrument", AF_BAD_INSTID)) + return; + + _af_setup_free_instruments(setup); + + setup->instrumentCount = ninsts; + setup->instrumentSet = AF_TRUE; + + setup->instruments = _af_instsetup_new(setup->instrumentCount); + + for (i=0; i < setup->instrumentCount; i++) + setup->instruments[i].id = instids[i]; +} + +int afGetInstIDs (AFfilehandle file, int *instids) +{ + int i; + + if (!_af_filehandle_ok(file)) + return -1; + + if (instids) + for (i=0; i < file->instrumentCount; i++) + instids[i] = file->instruments[i].id; + + return file->instrumentCount; +} + +/* + This routine checks and sets instrument parameters. + npv is number of valid AUpvlist pairs. +*/ +void _af_instparam_set (AFfilehandle file, int instid, AUpvlist pvlist, int npv) +{ + int i, instno, j; + + if (!_af_filehandle_ok(file)) + return; + + if (!_af_filehandle_can_write(file)) + return; + + if ((instno = _af_handle_instrument_index_from_id(file, instid)) == -1) + return; + + if (AUpvgetmaxitems(pvlist) < npv) + npv = AUpvgetmaxitems(pvlist); + + for (i=0; i < npv; i++) + { + int param; + int type; + + AUpvgetparam(pvlist, i, ¶m); + + if ((j = _af_instparam_index_from_id(file->fileFormat, param)) == -1) + /* no parameter with that id; ignore */ + continue; + + if (_af_units[file->fileFormat].write.instparamvalid && + !_af_units[file->fileFormat].write.instparamvalid(file, pvlist, i)) + /* bad parameter value; ignore */ + continue; + + type = _af_units[file->fileFormat].instrumentParameters[j].type; + + switch (type) + { + case AU_PVTYPE_LONG: + AUpvgetval(pvlist, i, &file->instruments[instno].values[j].l); + break; + case AU_PVTYPE_DOUBLE: + AUpvgetval(pvlist, i, &file->instruments[instno].values[j].d); + break; + case AU_PVTYPE_PTR: + AUpvgetval(pvlist, i, &file->instruments[instno].values[j].v); + break; + default: + return; + } + } +} + +void afSetInstParams (AFfilehandle file, int instid, AUpvlist pvlist, int npv) +{ + _af_instparam_set(file, instid, pvlist, npv); +} + +void afSetInstParamLong (AFfilehandle file, int instid, int param, long value) +{ + AUpvlist pvlist = AUpvnew(1); + + AUpvsetparam(pvlist, 0, param); + AUpvsetvaltype(pvlist, 0, AU_PVTYPE_LONG); + AUpvsetval(pvlist, 0, &value); + + _af_instparam_set(file, instid, pvlist, 1); + + AUpvfree(pvlist); +} + +/* + This routine gets instrument parameters. + npv is number of valid AUpvlist pairs +*/ +void _af_instparam_get (AFfilehandle file, int instid, AUpvlist pvlist, int npv, + bool forceLong) +{ + int i, instno, j; + + if (!_af_filehandle_ok(file)) + return; + + if ((instno = _af_handle_instrument_index_from_id(file, instid)) == -1) + return; + + if (AUpvgetmaxitems(pvlist) < npv) + npv = AUpvgetmaxitems(pvlist); + + for (i=0; i < npv; i++) + { + int param; + int type; + AUpvgetparam(pvlist, i, ¶m); + + if ((j = _af_instparam_index_from_id(file->fileFormat, param)) == -1) + /* no parameter with that id; ignore */ + continue; + + type = _af_units[file->fileFormat].instrumentParameters[j].type; + + /* + forceLong is true when this routine called by + afGetInstParamLong(). + */ + if (forceLong && type != AU_PVTYPE_LONG) + { + _af_error(AF_BAD_INSTPTYPE, "type of instrument parameter %d is not AU_PVTYPE_LONG", param); + continue; + } + + AUpvsetvaltype(pvlist, i, type); + + switch (type) + { + case AU_PVTYPE_LONG: + AUpvsetval(pvlist, i, &file->instruments[instno].values[j].l); + break; + case AU_PVTYPE_DOUBLE: + AUpvsetval(pvlist, i, &file->instruments[instno].values[j].d); + break; + case AU_PVTYPE_PTR: + AUpvsetval(pvlist, i, &file->instruments[instno].values[j].v); + break; + + default: + _af_error(AF_BAD_INSTPTYPE, "invalid instrument parameter type %d", type); + return; + } + } +} + +/* + afGetInstParams -- get a parameter-value array containing + instrument parameters for the specified instrument chunk +*/ +void afGetInstParams (AFfilehandle file, int inst, AUpvlist pvlist, int npv) +{ + _af_instparam_get(file, inst, pvlist, npv, AF_FALSE); +} + +long afGetInstParamLong (AFfilehandle file, int inst, int param) +{ + long val; + AUpvlist pvlist = AUpvnew(1); + + AUpvsetparam(pvlist, 0, param); + AUpvsetvaltype(pvlist, 0, AU_PVTYPE_LONG); + + _af_instparam_get(file, inst, pvlist, 1, AF_TRUE); + + AUpvgetval(pvlist, 0, &val); + AUpvfree(pvlist); + + return(val); +} + +/* + Search _af_units[fileFormat].instrumentParameters for the instrument + parameter with the specified id. + + Report an error and return -1 if no such instrument parameter + exists. +*/ + +int _af_instparam_index_from_id (int filefmt, int id) +{ + int i; + + for (i = 0; i < _af_units[filefmt].instrumentParameterCount; i++) + if (_af_units[filefmt].instrumentParameters[i].id == id) + break; + + if (i == _af_units[filefmt].instrumentParameterCount) + { + _af_error(AF_BAD_INSTPID, "invalid instrument parameter id %d", + id); + return -1; + } + + return i; +} + +int _af_handle_instrument_index_from_id (AFfilehandle file, int id) +{ + int i; + + for (i = 0; i < file->instrumentCount; i++) + if (file->instruments[i].id == id) + return i; + + _af_error(AF_BAD_INSTID, "invalid instrument id %d", id); + return -1; +} + +int _af_setup_instrument_index_from_id (AFfilesetup setup, int id) +{ + int i; + + for (i = 0; i < setup->instrumentCount; i++) + if (setup->instruments[i].id == id) + return i; + + _af_error(AF_BAD_INSTID, "invalid instrument id %d", id); + return -1; +} diff --git a/libaudiofile/instrument.h b/libaudiofile/instrument.h new file mode 100644 index 0000000..dcad95b --- /dev/null +++ b/libaudiofile/instrument.h @@ -0,0 +1,43 @@ +/* + Audio File Library + Copyright (C) 2000, Michael Pruett <michael@68k.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307 USA. +*/ + +/* + instrument.h + + This file declares routines for dealing with instruments. +*/ + +#ifndef INSTRUMENT_H +#define INSTRUMENT_H + +#include <audiofile.h> +#include <aupvlist.h> + +void _af_instparam_get (AFfilehandle file, int instid, AUpvlist pvlist, + int npv, bool forceLong); + +void _af_instparam_set (AFfilehandle file, int instid, AUpvlist pvlist, + int npv); + +int _af_instparam_index_from_id (int fileFormat, int id); +int _af_handle_instrument_index_from_id (AFfilehandle file, int id); +int _af_setup_instrument_index_from_id (AFfilesetup setup, int id); + +#endif /* INSTRUMENT_H */ diff --git a/libaudiofile/ircam.c b/libaudiofile/ircam.c new file mode 100644 index 0000000..2b5bacd --- /dev/null +++ b/libaudiofile/ircam.c @@ -0,0 +1,317 @@ +/* + Audio File Library + Copyright (C) 2001, Silicon Graphics, Inc. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307 USA. +*/ + +/* + ircam.c + + This file contains routines for parsing Berkeley/IRCAM/CARL + format files. +*/ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <assert.h> +#include <sys/types.h> +#include <stdio.h> +#include <string.h> + +#ifdef HAVE_INTTYPES_H +#include <inttypes.h> +#endif + +#include "afinternal.h" +#include "audiofile.h" +#include "util.h" +#include "byteorder.h" +#include "setup.h" +#include "track.h" +#include "marker.h" + +#include "ircam.h" + +/* + These magic numbers are fucking stupid. + + Here ircam_mips_magic refers to little-endian MIPS, not SGI IRIX, + which uses big-endian MIPS. +*/ +const u_int8_t _af_ircam_vax_magic[4] = {0x64, 0xa3, 0x01, 0x00}, + _af_ircam_sun_magic[4] = {0x64, 0xa3, 0x02, 0x00}, + _af_ircam_mips_magic[4] = {0x64, 0xa3, 0x03, 0x00}, + _af_ircam_next_magic[4] = {0x64, 0xa3, 0x04, 0x00}; + +_AFfilesetup _af_ircam_default_filesetup = +{ + _AF_VALID_FILESETUP, /* valid */ + AF_FILE_IRCAM, /* fileFormat */ + AF_TRUE, /* trackSet */ + AF_TRUE, /* instrumentSet */ + AF_TRUE, /* miscellaneousSet */ + 1, /* trackCount */ + NULL, /* tracks */ + 0, /* instrumentCount */ + NULL, /* instruments */ + 0, /* miscellaneousCount */ + NULL /* miscellaneous */ +}; + +bool _af_ircam_recognize (AFvirtualfile *fh) +{ + u_int8_t buffer[4]; + + af_fseek(fh, 0, SEEK_SET); + + if (af_fread(buffer, 4, 1, fh) != 1) + return AF_FALSE; + + /* Check to see if the file's magic number matches. */ + if (memcmp(buffer, _af_ircam_vax_magic, 4) == 0 || + memcmp(buffer, _af_ircam_sun_magic, 4) == 0 || + memcmp(buffer, _af_ircam_mips_magic, 4) == 0 || + memcmp(buffer, _af_ircam_next_magic, 4) == 0) + { + return AF_TRUE; + } + + return AF_FALSE; +} + +AFfilesetup _af_ircam_complete_setup (AFfilesetup setup) +{ + _TrackSetup *track; + + if (setup->trackSet && setup->trackCount != 1) + { + _af_error(AF_BAD_NUMTRACKS, "BICSF file must have 1 track"); + return AF_NULL_FILESETUP; + } + + track = &setup->tracks[0]; + + if (track->sampleFormatSet) + { + if (track->f.sampleFormat == AF_SAMPFMT_UNSIGNED) + { + _af_error(AF_BAD_SAMPFMT, + "BICSF format does not support unsigned data"); + return AF_NULL_FILESETUP; + } + + if (track->f.sampleFormat == AF_SAMPFMT_TWOSCOMP && + track->f.sampleWidth != 16) + { + _af_error(AF_BAD_WIDTH, + "BICSF format supports only 16-bit width for " + "two's complement audio data"); + return AF_NULL_FILESETUP; + } + + if (track->f.sampleFormat == AF_SAMPFMT_DOUBLE) + { + _af_error(AF_BAD_SAMPFMT, + "BICSF format does not support " + "double-precision floating-point data"); + return AF_NULL_FILESETUP; + } + } + + if (track->rateSet && track->f.sampleRate <= 0.0) + { + _af_error(AF_BAD_RATE, + "invalid sample rate %.30g for BICSF file", + track->f.sampleRate); + return AF_NULL_FILESETUP; + } + + if (track->channelCountSet && track->f.channelCount != 1 && + track->f.channelCount != 2 && track->f.channelCount != 4) + { + _af_error(AF_BAD_CHANNELS, + "invalid channel count (%d) for BICSF format " + "(1, 2, or 4 channels only)", + track->f.channelCount); + return AF_NULL_FILESETUP; + } + + if (track->compressionSet && + track->f.compressionType != AF_COMPRESSION_NONE) + { + _af_error(AF_BAD_NOT_IMPLEMENTED, + "BICSF format does not support compression"); + return AF_NULL_FILESETUP; + } + + if (track->aesDataSet) + { + _af_error(AF_BAD_FILESETUP, "BICSF file cannot have AES data"); + return AF_NULL_FILESETUP; + } + + if (track->markersSet && track->markerCount != 0) + { + _af_error(AF_BAD_NUMMARKS, "BICSF format does not support markers"); + return AF_NULL_FILESETUP; + } + + if (setup->instrumentSet && setup->instrumentCount != 0) + { + _af_error(AF_BAD_NUMINSTS, "BICSF format does not support instruments"); + return AF_NULL_FILESETUP; + } + + /* XXXmpruett: We don't support miscellaneous chunks for now. */ + if (setup->miscellaneousSet && setup->miscellaneousCount != 0) + { + _af_error(AF_BAD_NOT_IMPLEMENTED, "BICSF format does not currently support miscellaneous chunks"); + return AF_NULL_FILESETUP; + } + + return _af_filesetup_copy(setup, &_af_ircam_default_filesetup, AF_TRUE); +} + +status _af_ircam_read_init (AFfilesetup setup, AFfilehandle handle) +{ + _Track *track; + u_int8_t magic[4]; + float rate; + u_int32_t channels; + u_int32_t packMode; + + float maxAmp = 1.0; + + bool isSwapped, isLittleEndian; + + handle->instruments = NULL; + handle->instrumentCount = 0 ; + handle->miscellaneous = NULL; + handle->miscellaneousCount = 0; + + handle->tracks = NULL; + handle->trackCount = 1; + + af_fseek(handle->fh, 0, SEEK_SET); + + if (af_fread(magic, 4, 1, handle->fh) != 1) + { + _af_error(AF_BAD_READ, "Could not read BICSF file header"); + return AF_FAIL; + } + + if (memcmp(magic, _af_ircam_vax_magic, 4) != 0 && + memcmp(magic, _af_ircam_sun_magic, 4) != 0 && + memcmp(magic, _af_ircam_mips_magic, 4) != 0 && + memcmp(magic, _af_ircam_next_magic, 4) != 0) + { + _af_error(AF_BAD_FILEFMT, + "file is not a BICSF file (bad magic number)"); + return AF_FAIL; + } + + /* + If the file's magic number is that for VAX or MIPS, + the file is little endian. + */ + isLittleEndian = (memcmp(magic, _af_ircam_vax_magic, 4) == 0 || + memcmp(magic, _af_ircam_mips_magic, 4) == 0); + +#ifdef WORDS_BIGENDIAN + isSwapped = isLittleEndian; +#else + isSwapped = !isLittleEndian; +#endif + + af_fread(&rate, 4, 1, handle->fh); + af_fread(&channels, 4, 1, handle->fh); + af_fread(&packMode, 4, 1, handle->fh); + + if (isSwapped) + { + rate = _af_byteswap_float32(rate); + channels = _af_byteswap_int32(channels); + packMode = _af_byteswap_int32(packMode); + } + + if ((handle->tracks = _af_track_new()) == NULL) + return AF_FAIL; + + track = &handle->tracks[0]; + + track->f.sampleRate = rate; + track->f.compressionType = AF_COMPRESSION_NONE; + + switch (packMode) + { + case SF_SHORT: + track->f.sampleFormat = AF_SAMPFMT_TWOSCOMP; + track->f.sampleWidth = 16; + break; + case SF_FLOAT: + track->f.sampleFormat = AF_SAMPFMT_FLOAT; + track->f.sampleWidth = 32; + break; + default: + _af_error(AF_BAD_NOT_IMPLEMENTED, + "BICSF data format %d not supported", packMode); + return AF_FAIL; + } + + track->f.channelCount = channels; + if (channels != 1 && channels != 2 && channels != 4) + { + _af_error(AF_BAD_FILEFMT, "invalid channel count (%d) " + "for BICSF format (1, 2, or 4 only)", + channels); + return AF_FAIL; + } + + if (isLittleEndian) + track->f.byteOrder = AF_BYTEORDER_LITTLEENDIAN; + else + track->f.byteOrder = AF_BYTEORDER_BIGENDIAN; + + if (_af_set_sample_format(&track->f, track->f.sampleFormat, + track->f.sampleWidth) == AF_FAIL) + { + return AF_FAIL; + } + + if (track->f.sampleFormat == AF_SAMPFMT_FLOAT) + track->f.pcm.slope = maxAmp; + + track->data_size = af_flength(handle->fh) - SIZEOF_BSD_HEADER; + + /* + Only uncompressed data formats are supported for IRCAM + files right now. The following line would need to be + changed if compressed data formats were supported. + */ + track->totalfframes = track->data_size / + _af_format_frame_size(&track->f, AF_FALSE); + + track->fpos_first_frame = SIZEOF_BSD_HEADER; + track->nextfframe = 0; + track->fpos_next_frame = track->fpos_first_frame; + + handle->formatSpecific = NULL; + + return AF_SUCCEED; +} diff --git a/libaudiofile/ircam.h b/libaudiofile/ircam.h new file mode 100644 index 0000000..98b70d1 --- /dev/null +++ b/libaudiofile/ircam.h @@ -0,0 +1,59 @@ +/* + Audio File Library + + Copyright (C) 2001, Silicon Graphics, Inc. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307 USA. +*/ + +/* + ircam.h + + This file contains constants and function prototypes related to + the Berkeley/IRCAM/CARL Sound File format. +*/ + +#ifndef IRCAM_H +#define IRCAM_H + +bool _af_ircam_recognize (AFvirtualfile *fh); + +status _af_ircam_read_init (AFfilesetup, AFfilehandle); + +status _af_ircam_write_init (AFfilesetup, AFfilehandle); + +AFfilesetup _af_ircam_complete_setup (AFfilesetup); + +status _af_ircam_update (AFfilehandle); + +#define SF_SHORT 2 +#define SF_FLOAT 4 + +#define SF_MAXCHAN 4 +#define SF_MAXCOMMENT 512 +#define SF_MINCOMMENT 256 + +enum +{ + SF_END, + SF_MAXAMP, + SF_COMMENT, + SF_LINKCODE +}; + +#define SIZEOF_BSD_HEADER 1024 + +#endif /* IRCAM_H */ diff --git a/libaudiofile/ircamwrite.c b/libaudiofile/ircamwrite.c new file mode 100644 index 0000000..17fccd5 --- /dev/null +++ b/libaudiofile/ircamwrite.c @@ -0,0 +1,129 @@ +/* + Audio File Library + Copyright (C) 2001, Silicon Graphics, Inc. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307 USA. +*/ + +/* + ircamwrite.c + + This file contains routines for writing to Berkeley/IRCAM/CARL + format files. +*/ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <assert.h> +#include <sys/types.h> +#include <stdio.h> +#include <string.h> + +#ifdef HAVE_INTTYPES_H +#include <inttypes.h> +#endif + +#include "afinternal.h" +#include "audiofile.h" +#include "util.h" +#include "byteorder.h" +#include "setup.h" +#include "track.h" +#include "marker.h" + +#include "ircam.h" + +/* + These magic numbers are fucking stupid. + + Here ircam_mips_magic refers to little-endian MIPS, not SGI IRIX, + which uses big-endian MIPS. +*/ +extern const u_int8_t _af_ircam_vax_magic[4], _af_ircam_sun_magic[4], + _af_ircam_mips_magic[4], _af_ircam_next_magic[4]; + +/* We write IRCAM files using the native byte order. */ +status _af_ircam_write_init (AFfilesetup setup, AFfilehandle handle) +{ + _Track *track; + const u_int8_t *magic; + float rate; + u_int32_t channels; + u_int32_t packMode; + u_int32_t dataOffset; + u_int8_t zeros[SIZEOF_BSD_HEADER]; + + float maxAmp = 1.0; + + bool isSwapped, isLittleEndian; + + assert(handle->fileFormat == AF_FILE_IRCAM); + + if (_af_filesetup_make_handle(setup, handle) == AF_FAIL) + return AF_FAIL; + + dataOffset = SIZEOF_BSD_HEADER; + + track = &handle->tracks[0]; + track->totalfframes = 0; + track->fpos_first_frame = dataOffset; + track->nextfframe = 0; + track->fpos_next_frame = track->fpos_first_frame; + + handle->formatSpecific = NULL; + + /* Choose the magic number appropriate for the byte order. */ +#ifdef WORDS_BIGENDIAN + magic = _af_ircam_sun_magic; +#else + magic = _af_ircam_vax_magic; +#endif + + channels = track->f.channelCount; + rate = track->f.sampleRate; + + assert(track->f.compressionType == AF_COMPRESSION_NONE); + + if (track->f.sampleFormat == AF_SAMPFMT_TWOSCOMP) + { + assert(track->f.sampleWidth == 16); + packMode = SF_SHORT; + } + else if (track->f.sampleFormat == AF_SAMPFMT_FLOAT) + { + assert(track->f.sampleWidth == 32); + packMode = SF_FLOAT; + } + + af_fseek(handle->fh, 0, SEEK_SET); + af_fwrite(magic, 4, 1, handle->fh); + af_fwrite(&rate, 4, 1, handle->fh); + af_fwrite(&channels, 4, 1, handle->fh); + af_fwrite(&packMode, 4, 1, handle->fh); + + /* Zero the entire description block. */ + memset(zeros, 0, SIZEOF_BSD_HEADER); + af_fwrite(zeros, SIZEOF_BSD_HEADER - 4*4, 1, handle->fh); + + return AF_SUCCEED; +} + +status _af_ircam_update (AFfilehandle file) +{ + return AF_SUCCEED; +} diff --git a/libaudiofile/loop.c b/libaudiofile/loop.c new file mode 100644 index 0000000..c0964c0 --- /dev/null +++ b/libaudiofile/loop.c @@ -0,0 +1,352 @@ +/* + Audio File Library + Copyright (C) 1998-2000, Michael Pruett <michael@68k.org> + Copyright (C) 2000, Silicon Graphics, Inc. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307 USA. +*/ + +/* + loop.c + + All routines that operate on loops. +*/ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include "audiofile.h" +#include "afinternal.h" +#include "util.h" +#include "setup.h" +#include "instrument.h" + +void afInitLoopIDs (AFfilesetup setup, int instid, int *loopids, int nloops) +{ + int instno; + + if (!_af_filesetup_ok(setup)) + return; + + if (!_af_unique_ids(loopids, nloops, "loop", AF_BAD_LOOPID)) + return; + + if ((instno = _af_setup_instrument_index_from_id(setup, instid)) == -1) + return; + + _af_setup_free_loops(setup, instno); + + setup->instruments[instno].loopCount = nloops; + setup->instruments[instno].loopSet = AF_TRUE; + + if (nloops == 0) + setup->instruments[instno].loops = NULL; + else + { + int i; + + if ((setup->instruments[instno].loops = _af_calloc(nloops, sizeof (_LoopSetup))) == NULL) + return; + + for (i=0; i < nloops; i++) + setup->instruments[instno].loops[i].id = loopids[i]; + } +} + +int afGetLoopIDs (AFfilehandle file, int instid, int *loopids) +{ + int instno; + int i; + + if (!_af_filehandle_ok(file)) + return AF_FAIL; + + if ((instno = _af_handle_instrument_index_from_id(file, instid)) == -1) + return AF_FAIL; + + if (loopids) + for (i=0; i < file->instruments[instno].loopCount; i++) + loopids[i] = file->instruments[instno].loops[i].id; + + return file->instruments[instno].loopCount; +} + +int _af_handle_loop_index_from_id (AFfilehandle file, int instno, int loopid) +{ + int i; + for (i=0; i<file->instruments[instno].loopCount; i++) + if (file->instruments[instno].loops[i].id == loopid) + return i; + + _af_error(AF_BAD_LOOPID, "no loop with id %d for instrument %d", + loopid, file->instruments[instno].id); + + return -1; +} + +/* + getLoop returns pointer to requested loop if it exists, and if + mustWrite is true, only if handle is writable. +*/ + +static _Loop *getLoop (AFfilehandle handle, int instid, int loopid, + bool mustWrite) +{ + int loopno, instno; + + if (!_af_filehandle_ok(handle)) + return NULL; + + if (mustWrite && !_af_filehandle_can_write(handle)) + return NULL; + + if ((instno = _af_handle_instrument_index_from_id(handle, instid)) == -1) + return NULL; + + if ((loopno = _af_handle_loop_index_from_id(handle, instno, loopid)) == -1) + return NULL; + + return &handle->instruments[instno].loops[loopno]; +} + +/* + Set loop mode (as in AF_LOOP_MODE_...). +*/ +void afSetLoopMode (AFfilehandle file, int instid, int loopid, int mode) +{ + _Loop *loop = getLoop(file, instid, loopid, AF_TRUE); + + if (!loop) + return; + + if (mode != AF_LOOP_MODE_NOLOOP && + mode != AF_LOOP_MODE_FORW && + mode != AF_LOOP_MODE_FORWBAKW) + { + _af_error(AF_BAD_LOOPMODE, "unrecognized loop mode %d", mode); + return; + } + + loop->mode = mode; +} + +/* + Get loop mode (as in AF_LOOP_MODE_...). +*/ +int afGetLoopMode (AFfilehandle file, int instid, int loopid) +{ + _Loop *loop = getLoop(file, instid, loopid, AF_FALSE); + + if (loop == NULL) + return -1; + + return loop->mode; +} + +/* + Set loop count. +*/ +int afSetLoopCount (AFfilehandle file, int instid, int loopid, int count) +{ + _Loop *loop = getLoop(file, instid, loopid, AF_TRUE); + + if (loop == NULL) + return AF_FAIL; + + if (count < 1) + { + _af_error(AF_BAD_LOOPCOUNT, "invalid loop count: %d", count); + return AF_FAIL; + } + + loop->count = count; + return AF_SUCCEED; +} + +/* + Get loop count. +*/ +int afGetLoopCount(AFfilehandle file, int instid, int loopid) +{ + _Loop *loop = getLoop(file, instid, loopid, AF_FALSE); + + if (loop == NULL) + return -1; + + return loop->count; +} + +/* + Set loop start marker id in the file structure +*/ +void +afSetLoopStart(AFfilehandle file, int instid, int loopid, int markid) +{ + _Loop *loop = getLoop(file, instid, loopid, AF_TRUE); + + if (!loop) + return; + + loop->beginMarker = markid; +} + +/* + Get loop start marker id. +*/ +int afGetLoopStart (AFfilehandle file, int instid, int loopid) +{ + _Loop *loop = getLoop(file, instid, loopid, AF_FALSE); + + if (loop == NULL) + return -1; + + return loop->beginMarker; +} + +/* + Set loop start frame in the file structure. +*/ +int afSetLoopStartFrame (AFfilehandle file, int instid, int loopid, AFframecount startFrame) +{ + int trackid, beginMarker; + _Loop *loop = getLoop(file, instid, loopid, AF_TRUE); + + if (loop == NULL) + return -1; + + if (startFrame < 0) + { + _af_error(AF_BAD_FRAME, "loop start frame must not be negative"); + return AF_FAIL; + } + + trackid = loop->trackid; + beginMarker = loop->beginMarker; + + afSetMarkPosition(file, trackid, beginMarker, startFrame); + return AF_SUCCEED; +} + +/* + Get loop start frame. +*/ +AFframecount afGetLoopStartFrame (AFfilehandle file, int instid, int loopid) +{ + int trackid, beginMarker; + _Loop *loop = getLoop(file, instid, loopid, AF_FALSE); + + if (loop == NULL) + return -1; + + trackid = loop->trackid; + beginMarker = loop->beginMarker; + + return afGetMarkPosition(file, trackid, beginMarker); +} + +/* + Set loop track id. +*/ +void afSetLoopTrack (AFfilehandle file, int instid, int loopid, int track) +{ + _Loop *loop = getLoop(file, instid, loopid, AF_TRUE); + + if (!loop) return; + + loop->trackid = track; +} + +/* + Get loop track. +*/ +int afGetLoopTrack (AFfilehandle file, int instid, int loopid) +{ + _Loop *loop = getLoop(file, instid, loopid, AF_FALSE); + + if (loop == NULL) + return -1; + + return loop->trackid; +} + +/* + Set loop end frame marker id. +*/ +void afSetLoopEnd (AFfilehandle file, int instid, int loopid, int markid) +{ + _Loop *loop = getLoop(file, instid, loopid, AF_TRUE); + + if (!loop) + return; + + loop->endMarker = markid; +} + +/* + Get loop end frame marker id. +*/ +int afGetLoopEnd (AFfilehandle file, int instid, int loopid) +{ + _Loop *loop = getLoop(file, instid, loopid, AF_FALSE); + + if (loop == NULL) + return -1; + + return loop->endMarker; +} + +/* + Set loop end frame. +*/ +int afSetLoopEndFrame (AFfilehandle file, int instid, int loopid, AFframecount endFrame) +{ + int trackid, endMarker; + _Loop *loop = getLoop(file, instid, loopid, AF_TRUE); + + if (loop == NULL) + return -1; + + if (endFrame < 0) + { + _af_error(AF_BAD_FRAME, "loop end frame must not be negative"); + return AF_FAIL; + } + + trackid = loop->trackid; + endMarker = loop->endMarker; + + afSetMarkPosition(file, trackid, endMarker, endFrame); + return AF_SUCCEED; +} + +/* + Get loop end frame. +*/ + +AFframecount afGetLoopEndFrame (AFfilehandle file, int instid, int loopid) +{ + int trackid, endMarker; + _Loop *loop = getLoop(file, instid, loopid, AF_FALSE); + + if (loop == NULL) + return -1; + + trackid = loop->trackid; + endMarker = loop->endMarker; + + return afGetMarkPosition(file, trackid, endMarker); +} diff --git a/libaudiofile/marker.c b/libaudiofile/marker.c new file mode 100644 index 0000000..aec297b --- /dev/null +++ b/libaudiofile/marker.c @@ -0,0 +1,306 @@ +/* + Audio File Library + Copyright (C) 1998-2000, Michael Pruett <michael@68k.org> + Copyright (C) 2000, Silicon Graphics, Inc. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307 USA. +*/ + +/* + marker.c + + This file contains routines for dealing with loop markers. +*/ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <string.h> +#include <stdlib.h> +#include <assert.h> + +#include "audiofile.h" +#include "afinternal.h" +#include "util.h" + +_Marker *_af_marker_find_by_id (_Track *track, int markerid) +{ + int i; + + assert(track); + + for (i=0; i<track->markerCount; i++) + if (track->markers[i].id == markerid) + return &track->markers[i]; + + _af_error(AF_BAD_MARKID, "no mark with id %d found in track %d", + markerid, track->id); + + return NULL; +} + +void afInitMarkIDs(AFfilesetup setup, int trackid, int markids[], int nmarks) +{ + int i; + _TrackSetup *track; + + if (!_af_filesetup_ok(setup)) + return; + + if ((track = _af_filesetup_get_tracksetup(setup, trackid)) == NULL) + return; + + if (track->markers != NULL) + { + for (i=0; i<track->markerCount; i++) + { + if (track->markers[i].name != NULL) + free(track->markers[i].name); + if (track->markers[i].comment != NULL) + free(track->markers[i].comment); + } + free(track->markers); + } + + track->markers = _af_calloc(nmarks, sizeof (struct _MarkerSetup)); + track->markerCount = nmarks; + + for (i=0; i<nmarks; i++) + { + track->markers[i].id = markids[i]; + track->markers[i].name = _af_strdup(""); + track->markers[i].comment = _af_strdup(""); + } + + track->markersSet = AF_TRUE; +} + +void afInitMarkName(AFfilesetup setup, int trackid, int markid, + const char *namestr) +{ + int markno; + int length; + + _TrackSetup *track = NULL; + + assert(setup); + assert(markid > 0); + + track = _af_filesetup_get_tracksetup(setup, trackid); + assert(track); + + if (track == NULL) + { + _af_error(AF_BAD_TRACKID, "bad track id"); + return; + } + + for (markno=0; markno<track->markerCount; markno++) + { + if (track->markers[markno].id == markid) + break; + } + + if (markno == track->markerCount) + { + _af_error(AF_BAD_MARKID, "no marker id %d for file setup", markid); + return; + } + + length = strlen(namestr); + if (length > 255) + { + _af_error(AF_BAD_STRLEN, + "warning: marker name truncated to 255 characters"); + length = 255; + } + + if (track->markers[markno].name) + free(track->markers[markno].name); + if ((track->markers[markno].name = _af_malloc(length+1)) == NULL) + return; + strncpy(track->markers[markno].name, namestr, length); + /* + The null terminator is not set by strncpy if + strlen(namestr) > length. Set it here. + */ + track->markers[markno].name[length] = '\0'; +} + +void afInitMarkComment(AFfilesetup setup, int trackid, int markid, + const char *commstr) +{ + int markno; + int length; + _TrackSetup *track = NULL; + + assert(setup); + assert(markid > 0); + + track = _af_filesetup_get_tracksetup(setup, trackid); + assert(track); + + if (track == NULL) + { + _af_error(AF_BAD_TRACKID, "bad track id"); + return; + } + + for (markno=0; markno<track->markerCount; markno++) + { + if (track->markers[markno].id == markid) + break; + } + + if (markno == track->markerCount) + { + _af_error(AF_BAD_MARKID, "no marker id %d for file setup", markid); + return; + } + + length = strlen(commstr); + + if (track->markers[markno].comment) + free(track->markers[markno].comment); + if ((track->markers[markno].comment = _af_malloc(length+1)) == NULL) + return; + strcpy(track->markers[markno].comment, commstr); +} + +char *afGetMarkName (AFfilehandle file, int trackid, int markid) +{ + _Track *track; + _Marker *marker; + + assert(file != NULL); + assert(markid > 0); + + if (!_af_filehandle_ok(file)) + return NULL; + + if ((track = _af_filehandle_get_track(file, trackid)) == NULL) + return NULL; + + if ((marker = _af_marker_find_by_id(track, markid)) == NULL) + return NULL; + + return marker->name; +} + +char *afGetMarkComment (AFfilehandle file, int trackid, int markid) +{ + _Track *track; + _Marker *marker; + + assert(file != NULL); + assert(markid > 0); + + if (!_af_filehandle_ok(file)) + return NULL; + + if ((track = _af_filehandle_get_track(file, trackid)) == NULL) + return NULL; + + if ((marker = _af_marker_find_by_id(track, markid)) == NULL) + return NULL; + + return marker->comment; +} + +void afSetMarkPosition (AFfilehandle file, int trackid, int markid, + AFframecount pos) +{ + _Track *track; + _Marker *marker; + + assert(file != NULL); + assert(markid > 0); + + if (!_af_filehandle_ok(file)) + return; + + if (!_af_filehandle_can_write(file)) + return; + + if ((track = _af_filehandle_get_track(file, trackid)) == NULL) + return; + + if ((marker = _af_marker_find_by_id(track, markid)) == NULL) + return; + + if (pos < 0) + { + _af_error(AF_BAD_MARKPOS, "invalid marker position %d", pos); + pos = 0; + } + + marker->position = pos; +} + +int afGetMarkIDs (AFfilehandle file, int trackid, int markids[]) +{ + _Track *track; + + assert(file); + + if (!_af_filehandle_ok(file)) + return -1; + + if ((track = _af_filehandle_get_track(file, trackid)) == NULL) + return -1; + + if (markids != NULL) + { + int i; + + for (i=0; i<track->markerCount; i++) + { + markids[i] = track->markers[i].id; + } + } + + return track->markerCount; +} + +AFframecount afGetMarkPosition (AFfilehandle file, int trackid, int markid) +{ + _Track *track; + _Marker *marker; + + assert(file); + assert(markid > 0); + + if (!_af_filehandle_ok(file)) + return 0L; + + if ((track = _af_filehandle_get_track(file, trackid)) == NULL) + return 0L; + + if ((marker = _af_marker_find_by_id(track, markid)) == NULL) + return 0L; + + return marker->position; +} + +_Marker *_af_marker_new (int count) +{ + _Marker *markers = _af_calloc(count, sizeof (_Marker)); + if (markers == NULL) + return NULL; + + return markers; +} diff --git a/libaudiofile/marker.h b/libaudiofile/marker.h new file mode 100644 index 0000000..94d51a0 --- /dev/null +++ b/libaudiofile/marker.h @@ -0,0 +1,27 @@ +/* + Audio File Library + Copyright (C) 2000, Silicon Graphics, Inc. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307 USA. +*/ + +#ifndef MARKER_H +#define MARKER_H + +_Marker *_af_marker_new (int count); +_Marker *_af_marker_find_by_id (_Track *track, int id); + +#endif /* MARKER_H */ diff --git a/libaudiofile/misc.c b/libaudiofile/misc.c new file mode 100644 index 0000000..c931110 --- /dev/null +++ b/libaudiofile/misc.c @@ -0,0 +1,286 @@ +/* + Audio File Library + Copyright (C) 1998, Michael Pruett <michael@68k.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307 USA. +*/ + +/* + misc.c + + This file contains routines for dealing with the Audio File + Library's internal miscellaneous data types. +*/ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <sys/types.h> +#include <stdlib.h> +#include <string.h> + +#include "audiofile.h" +#include "afinternal.h" +#include "util.h" + +static _Miscellaneous *find_misc_by_id (AFfilehandle file, int id) +{ + int i; + + for (i=0; i<file->miscellaneousCount; i++) + { + if (file->miscellaneous[i].id == id) + return &file->miscellaneous[i]; + } + + _af_error(AF_BAD_MISCID, "bad miscellaneous id %d", id); + + return NULL; +} + +static _MiscellaneousSetup *find_miscsetup_by_id (AFfilesetup setup, int id) +{ + int i; + + for (i=0; i<setup->miscellaneousCount; i++) + { + if (setup->miscellaneous[i].id == id) + return &setup->miscellaneous[i]; + } + + _af_error(AF_BAD_MISCID, "bad miscellaneous id %d", id); + + return NULL; +} + +void afInitMiscIDs (AFfilesetup setup, int *ids, int nids) +{ + int i; + + if (!_af_filesetup_ok(setup)) + return; + + if (setup->miscellaneous != NULL) + { + free(setup->miscellaneous); + } + + setup->miscellaneousCount = nids; + + if (nids == 0) + setup->miscellaneous = NULL; + else + { + setup->miscellaneous = _af_calloc(nids, + sizeof (_Miscellaneous)); + + if (setup->miscellaneous == NULL) + return; + + for (i=0; i<nids; i++) + { + setup->miscellaneous[i].id = ids[i]; + setup->miscellaneous[i].type = 0; + setup->miscellaneous[i].size = 0; + } + } + + setup->miscellaneousSet = AF_TRUE; +} + +int afGetMiscIDs (AFfilehandle file, int *ids) +{ + int i; + + if (!_af_filehandle_ok(file)) + return -1; + + if (ids != NULL) + { + for (i=0; i<file->miscellaneousCount; i++) + { + ids[i] = file->miscellaneous[i].id; + } + } + + return file->miscellaneousCount; +} + +void afInitMiscType (AFfilesetup setup, int miscellaneousid, int type) +{ + _MiscellaneousSetup *miscellaneous; + + if (!_af_filesetup_ok(setup)) + return; + + miscellaneous = find_miscsetup_by_id(setup, miscellaneousid); + + if (miscellaneous) + miscellaneous->type = type; + else + _af_error(AF_BAD_MISCID, "bad miscellaneous id"); +} + +int afGetMiscType (AFfilehandle file, int miscellaneousid) +{ + _Miscellaneous *miscellaneous; + + if (!_af_filehandle_ok(file)) + return -1; + + miscellaneous = find_misc_by_id(file, miscellaneousid); + + if (miscellaneous) + { + return miscellaneous->type; + } + else + { + _af_error(AF_BAD_MISCID, "bad miscellaneous id"); + return -1; + } +} + +void afInitMiscSize (AFfilesetup setup, int miscellaneousid, int size) +{ + _MiscellaneousSetup *miscellaneous; + + if (!_af_filesetup_ok(setup)) + return; + + miscellaneous = find_miscsetup_by_id(setup, miscellaneousid); + + if (miscellaneous) + { + miscellaneous->size = size; + } + else + _af_error(AF_BAD_MISCID, "bad miscellaneous id"); +} + +int afGetMiscSize (AFfilehandle file, int miscellaneousid) +{ + _Miscellaneous *miscellaneous; + + if (!_af_filehandle_ok(file)) + return -1; + + miscellaneous = find_misc_by_id(file, miscellaneousid); + + if (miscellaneous) + { + return miscellaneous->size; + } + else + { + _af_error(AF_BAD_MISCID, "bad miscellaneous id"); + return -1; + } +} + +int afWriteMisc (AFfilehandle file, int miscellaneousid, void *buf, int bytes) +{ + _Miscellaneous *miscellaneous; + int localsize; + + if (!_af_filehandle_ok(file)) + return -1; + + if (!_af_filehandle_can_write(file)) + return -1; + + if ((miscellaneous = find_misc_by_id(file, miscellaneousid)) == NULL) + return -1; + + if (bytes <= 0) + { + _af_error(AF_BAD_MISCSIZE, "invalid size (%d) for miscellaneous chunk", bytes); + } + + if (miscellaneous->buffer == NULL && miscellaneous->size != 0) + { + miscellaneous->buffer = _af_malloc(miscellaneous->size); + memset(miscellaneous->buffer, 0, miscellaneous->size); + if (miscellaneous->buffer == NULL) + return -1; + } + + if (bytes + miscellaneous->position > miscellaneous->size) + localsize = miscellaneous->size - miscellaneous->position; + else + localsize = bytes; + + memcpy((char *) miscellaneous->buffer + miscellaneous->position, + buf, localsize); + miscellaneous->position += localsize; + return localsize; +} + +int afReadMisc (AFfilehandle file, int miscellaneousid, void *buf, int bytes) +{ + int localsize; + _Miscellaneous *miscellaneous; + + if (!_af_filehandle_ok(file)) + return -1; + + if (!_af_filehandle_can_read(file)) + return -1; + + if ((miscellaneous = find_misc_by_id(file, miscellaneousid)) == NULL) + return -1; + + if (bytes <= 0) + { + _af_error(AF_BAD_MISCSIZE, "invalid size (%d) for miscellaneous chunk", bytes); + return -1; + } + + if (bytes + miscellaneous->position > miscellaneous->size) + localsize = miscellaneous->size - miscellaneous->position; + else + localsize = bytes; + + memcpy(buf, (char *) miscellaneous->buffer + miscellaneous->position, + localsize); + miscellaneous->position += localsize; + return localsize; +} + +int afSeekMisc (AFfilehandle file, int miscellaneousid, int offset) +{ + _Miscellaneous *miscellaneous; + + if (!_af_filehandle_ok(file)) + return -1; + + if ((miscellaneous = find_misc_by_id(file, miscellaneousid)) == NULL) + return -1; + + if (offset >= miscellaneous->size) + { + _af_error(AF_BAD_MISCSEEK, + "offset %d too big for miscellaneous chunk %d " + "(%d data bytes)", + offset, miscellaneousid, miscellaneous->size); + return -1; + } + + miscellaneous->position = offset; + + return offset; +} diff --git a/libaudiofile/modules.c b/libaudiofile/modules.c new file mode 100644 index 0000000..1154550 --- /dev/null +++ b/libaudiofile/modules.c @@ -0,0 +1,2744 @@ +/* + Audio File Library + Copyright (C) 2000, Silicon Graphics, Inc. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307 USA. +*/ + +/* + modules.c +*/ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <math.h> + +#include <assert.h> + +#include <audiofile.h> +#include "afinternal.h" +#include "modules.h" +#include "pcm.h" +#include "util.h" +#include "units.h" +#include "compression.h" +#include "byteorder.h" +#include "print.h" +#include "debug.h" + +#include "modules/rebuffer.h" + +#ifdef DEBUG +#define CHNK(X) X +#define DEBG(X) X +#else +#define CHNK(X) +#define DEBG(X) +#endif + +#define NULLMODULEPARAM + +extern _PCMInfo _af_default_signed_integer_pcm_mappings[]; +extern _PCMInfo _af_default_unsigned_integer_pcm_mappings[]; +extern _PCMInfo _af_default_float_pcm_mapping; +extern _PCMInfo _af_default_double_pcm_mapping; + +extern _CompressionUnit _af_compression[]; + +/* Define rebuffering modules. */ +extern _AFmodule int2rebufferv2f, int2rebufferf2v; + +/* + module utility routines +*/ + +/* + _AFnewmodinst creates a module instance from a module. + It returns a structure, not a pointer to a structure. +*/ +_AFmoduleinst _AFnewmodinst (_AFmodule *mod) +{ + _AFmoduleinst ret; + + ret.inc = ret.outc = NULL; + ret.modspec = NULL; + ret.u.pull.source = NULL; + ret.mod = mod; + ret.free_on_close = AF_FALSE; + ret.valid = AF_FALSE; + + return(ret); +} + +/* + _AFfreemodspec: useful routine for mod.free function pointer +*/ +void _AFfreemodspec (_AFmoduleinst *i) +{ + if (i->modspec) + free(i->modspec); + i->modspec = NULL; +} + +/* + _AFpull: used a lot -- see comments in README.modules +*/ +AFframecount _AFpull (_AFmoduleinst *i, AFframecount nframes2pull) +{ + _AFmoduleinst *src = i->u.pull.source; + + i->inc->nframes = nframes2pull; + CHNK(printf("%s pulling %" AF_FRAMECOUNT_PRINT_FMT " frames from %s\n", + i->mod->name, i->inc->nframes, src->mod->name)); + (*src->mod->run_pull)(src); + CHNK(_af_print_chunk(i->inc)); + + CHNK(printf("%s received %" AF_FRAMECOUNT_PRINT_FMT " frames from %s\n", + i->mod->name, i->inc->nframes, src->mod->name)); + return i->inc->nframes; +} + +/* + _AFsimplemodrun +*/ +void _AFsimplemodrun_pull (_AFmoduleinst *i) +{ + _AFpull(i, i->outc->nframes); + (*i->mod->run)(i->inc, i->outc, i->modspec); +} + +/* + _AFpush +*/ +void _AFpush (_AFmoduleinst *i, AFframecount nframes2push) +{ + _AFmoduleinst *snk = i->u.push.sink; + i->outc->nframes = nframes2push; + CHNK(printf("%s pushing %" AF_FRAMECOUNT_PRINT_FMT " frames into %s\n", + i->mod->name, i->outc->nframes, snk->mod->name)); + CHNK(_af_print_chunk(i->outc)); + (*(snk->mod->run_push))(snk); +} + +/* + _AFpushat +*/ +void _AFpushat (_AFmoduleinst *i, AFframecount startframe, bool stretchint, + AFframecount nframes2push) +{ + _AFmoduleinst *snk = i->u.push.sink; + + void *saved_buf = i->outc->buf; + i->outc->buf = ((char *)i->outc->buf) + + (_af_format_frame_size_uncompressed(&i->outc->f,stretchint) * startframe); + + i->outc->nframes = nframes2push; + CHNK(printf("%s pushing %" AF_FRAMECOUNT_PRINT_FMT " frames into %s " + "with OFFSET %" AF_FILEOFFSET_PRINT_FMT " frames\n", + i->mod->name, i->outc->nframes, snk->mod->name, startframe)); + CHNK(_af_print_chunk(i->outc)); + (*(snk->mod->run_push))(snk); + + i->outc->buf = saved_buf; +} + +/* + _AFsimplemodrun +*/ +void _AFsimplemodrun_push (_AFmoduleinst *i) +{ + i->outc->nframes = i->inc->nframes; + (*(i->mod->run))(i->inc, i->outc, i->modspec); + _AFpush(i, i->outc->nframes); +} + +/* + These macros each declare a module. + + The module uses _AFsimplemodrun_pull and _AFsimplemodrun_push + (see comments in README.modules). Thus we only have to define + one routine that does the actual processing. + + The arguments to the macros are as follows: + + name - name of module + desc - code for module's "describe" function (see README.modules) + intype - type of elements of input buffer + outtype - type of elements of output buffer + action - action to take in inner loop -- indexes "ip" and "op" with "i" + + modspectype - (MODULEM) this will initialize a pointer "m" + to this instance's modspec data, which is of type modspectype + + Don't use _MODULE directly. + + The code in "desc" is executed once, after the module is + initialized. It can reference "i->modspec" and should modify + "i->outc->f". A pointer "f" initialized to "&i->outc->f" is + emitted prior to "desc"; use this to to keep the code cleaner. + + Note that the generated "run" routine shouldn't set outc->nframes since + + * outc->nframes is set to inc->nframes by _AFsimplemodrun_push + * inc->nframes is set to outc->nframes by _AFsimplemodrun_pull + + The whole point of the simplified "run" routine is that you don't + have to worry about push or pull. + + See README.modules for more info on how modules work. +*/ + +#define _MODULE( name, desc, \ + intype, outtype, chans, preamble, action, postamble )\ +static void name##run(_AFchunk *inc, _AFchunk *outc, void *modspec)\ +{\ + intype *ip = inc->buf;\ + outtype *op = outc->buf;\ + int count = inc->nframes * (chans);\ + int i;\ + \ + preamble;\ + for(i=0; i < count; ++i) \ + action;\ + postamble;\ +}\ +\ +static void name##describe(struct _AFmoduleinst *i)\ +{\ + _AudioFormat *f = &i->outc->f; \ + desc;\ +}\ +\ +static _AFmodule name =\ +{ \ + #name,\ + name##describe, \ + AF_NULL, AF_NULL, \ + _AFsimplemodrun_pull, AF_NULL, AF_NULL, \ + _AFsimplemodrun_push, AF_NULL, AF_NULL, \ + name##run, \ + _AFfreemodspec \ +}; + +#define MODULE(name, desc, intype, outtype, action)\ + _MODULE(name, desc, intype, outtype, inc->f.channelCount, \ + NULLMODULEPARAM, action, NULLMODULEPARAM) + +#define MODULEM(name, desc, intype, outtype, modspectype, action)\ + _MODULE(name, desc, intype, outtype, inc->f.channelCount, \ + modspectype *m = (modspectype *) modspec, action, NULLMODULEPARAM) + +/* + Byte-order-swapping modules. +*/ + +#define MODULESWAP(name, type, action) \ +MODULE(name, \ + f->byteOrder = (f->byteOrder==AF_BYTEORDER_LITTLEENDIAN) ?\ + AF_BYTEORDER_BIGENDIAN : AF_BYTEORDER_LITTLEENDIAN,\ + type, type,\ + action) + +MODULESWAP(swap2, uchar2, + { char3u u; uchar1 c; u.uchar2.s0 = ip[i]; + c = u.uchar1.c1; u.uchar1.c1 = u.uchar1.c0; u.uchar1.c0 = c; + op[i] = u.uchar2.s0; }) + +MODULESWAP(swap3, real_char3, + { char3u u; uchar1 c; u.real_char3_low.c3 = ip[i]; + c = u.uchar1.c3; u.uchar1.c3 = u.uchar1.c1; u.uchar1.c1 = c; + op[i] = u.real_char3_low.c3; }) + +MODULESWAP(swap4, uchar4, + { char3u u; uchar1 c; u.uchar4.i = ip[i]; + c = u.uchar1.c3; u.uchar1.c3 = u.uchar1.c0; u.uchar1.c0 = c; + c = u.uchar1.c1; u.uchar1.c1 = u.uchar1.c2; u.uchar1.c2 = c; + op[i] = u.uchar4.i; }) + +MODULESWAP(swap8, real_char8, + { real_char8 *i8 = &ip[i]; real_char8 *o8 = &op[i]; + o8->c0 = i8->c7; + o8->c1 = i8->c6; + o8->c2 = i8->c5; + o8->c3 = i8->c4; + o8->c4 = i8->c3; + o8->c5 = i8->c2; + o8->c6 = i8->c1; + o8->c7 = i8->c0; }) + +/* + modules for dealing with 3-byte integers +*/ + +/* convert 0xaabbcc to 0xssaabbcc */ +#ifdef WORDS_BIGENDIAN +MODULE(real_char3_to_schar3, f /* NOTUSED */, real_char3, schar3, + { + char3u u; + u.real_char3_high.c3 = ip[i]; + u.real_char3_high.pad = 0; + op[i] = u.schar3.i >> 8; + }) +#else +MODULE(real_char3_to_schar3, f /* NOTUSED */, real_char3, schar3, + { + char3u u; + u.real_char3_low.c3 = ip[i]; + u.real_char3_low.pad = 0; + op[i] = u.schar3.i >> 8; + }) +#endif + +/* convert 0xaabbcc to 0x00aabbcc */ +#ifdef WORDS_BIGENDIAN +MODULE(real_char3_to_uchar3, f /* NOTUSED */, real_char3, uchar3, + { + char3u u; + u.real_char3_high.c3 = ip[i]; + u.real_char3_high.pad = 0; + op[i] = u.uchar3.i >> 8; + }) +#else +MODULE(real_char3_to_uchar3, f /* NOTUSED */, real_char3, uchar3, + { + char3u u; + u.real_char3_low.c3 = ip[i]; + u.real_char3_low.pad = 0; + op[i] = u.uchar3.i >> 8; + }) +#endif + +/* convert 0x??aabbcc to 0xaabbcc */ +#ifdef WORDS_BIGENDIAN +MODULE(char3_to_real_char3, f /* NOTUSED */, uchar3, real_char3, + { + char3u u; + u.uchar3.i = ip[i]; + op[i] = u.real_char3_low.c3; + }) +#else +MODULE(char3_to_real_char3, f /* NOTUSED */, uchar3, real_char3, + { + char3u u; + u.uchar3.i = ip[i]; + op[i] = u.real_char3_high.c3; + }) +#endif + +/* + float <--> double ; CASTS +*/ + +MODULE(float2double, f->sampleFormat = AF_SAMPFMT_DOUBLE, + float, double, op[i] = ip[i] ) +MODULE(double2float, f->sampleFormat = AF_SAMPFMT_FLOAT, + double, float, op[i] = ip[i] ) + +/* + int2floatN - expects 8N-bit 2's comp ints, outputs floats ; CASTS +*/ + +MODULE(int2float1, f->sampleFormat = AF_SAMPFMT_FLOAT, + schar1, float, op[i] = ip[i]) +MODULE(int2float2, f->sampleFormat = AF_SAMPFMT_FLOAT, + schar2, float, op[i] = ip[i]) +MODULE(int2float3, f->sampleFormat = AF_SAMPFMT_FLOAT, + schar3, float, op[i] = ip[i]) +MODULE(int2float4, f->sampleFormat = AF_SAMPFMT_FLOAT, + schar4, float, op[i] = ip[i]) + +/* + int2doubleN - expects 8N-bit 2's comp ints, outputs doubles ; CASTS +*/ + +MODULE(int2double1, f->sampleFormat = AF_SAMPFMT_DOUBLE, + schar1, double, op[i] = ip[i]) +MODULE(int2double2, f->sampleFormat = AF_SAMPFMT_DOUBLE, + schar2, double, op[i] = ip[i]) +MODULE(int2double3, f->sampleFormat = AF_SAMPFMT_DOUBLE, + schar3, double, op[i] = ip[i]) +MODULE(int2double4, f->sampleFormat = AF_SAMPFMT_DOUBLE, + schar4, double, op[i] = ip[i]) + +/* + The following modules perform the transformation between one + pcm mapping and another. + + The modules all use MODULETRANS; some of them also perform + clipping. + + Use initpcmmod() to create an instance of any of these modules. + initpcmmod() takes an _PCMInfo describing the desired output + pcm mapping. +*/ + +typedef struct pcmmodspec +{ + /* These are the computed parameters of the transformation. */ + double m, b; + double maxv, minv; + + /* This is what goes in i->outc->f. */ + _PCMInfo output_mapping; +} pcmmodspec; + +/* + initpcmmod +*/ +static _AFmoduleinst initpcmmod (_AFmodule *mod, + _PCMInfo *input_mapping, _PCMInfo *output_mapping) +{ + _AFmoduleinst ret = _AFnewmodinst(mod); + pcmmodspec *m = _af_malloc(sizeof (pcmmodspec)); + ret.modspec = m; + + /* Remember output mapping for use in the describe function. */ + m->output_mapping = *output_mapping; + + /* + Compute values needed to perform transformation if the module + being initialized does a transformation.. + */ + if (input_mapping) + { + m->m = output_mapping->slope / input_mapping->slope; + m->b = output_mapping->intercept - m->m * input_mapping->intercept; + } + + /* Remember clip values. */ + m->minv = output_mapping->minClip; + m->maxv = output_mapping->maxClip; + return ret; +} + +#define MODULETRANS( name, xtradesc, intype, outtype, action ) \ +MODULEM(name, \ + { \ + f->pcm = ((pcmmodspec *) i->modspec)->output_mapping; \ + xtradesc; \ + }, \ + intype, outtype, pcmmodspec, \ + action) + + +MODULETRANS(floattransform, NULLMODULEPARAM, float, float, \ + op[i]=(m->b + m->m * ip[i])) +MODULETRANS(doubletransform, NULLMODULEPARAM, double, double, \ + op[i]=(m->b + m->m * ip[i])) + +/* + float2intN_clip - expects floats, + outputs CLIPped, 8N-bit, transformed 2's comp ints + double2intN_clip - same deal with doubles +*/ + +#define TRANS_CLIP(type) \ +{\ + double d=(m->b + m->m * ip[i]); \ + op[i] = \ + (((type)((d>(m->maxv)) ? (m->maxv) : ((d<(m->minv))?(m->minv):d)))); \ +} + +MODULETRANS(float2int1_clip, + { f->sampleFormat = AF_SAMPFMT_TWOSCOMP; f->sampleWidth = 8; }, + float, schar1, TRANS_CLIP(schar1)) +MODULETRANS(float2int2_clip, + { f->sampleFormat = AF_SAMPFMT_TWOSCOMP; f->sampleWidth = 16; }, + float, schar2, TRANS_CLIP(schar2)) +MODULETRANS(float2int3_clip, + { f->sampleFormat = AF_SAMPFMT_TWOSCOMP; f->sampleWidth = 24; }, + float, schar3, TRANS_CLIP(schar3)) +MODULETRANS(float2int4_clip, + { f->sampleFormat = AF_SAMPFMT_TWOSCOMP; f->sampleWidth = 32; }, + float, schar4, TRANS_CLIP(schar4)) + +MODULETRANS(double2int1_clip, + { f->sampleFormat = AF_SAMPFMT_TWOSCOMP; f->sampleWidth = 8; }, + double, schar1, TRANS_CLIP(schar1)) +MODULETRANS(double2int2_clip, + { f->sampleFormat = AF_SAMPFMT_TWOSCOMP; f->sampleWidth = 16; }, + double, schar2, TRANS_CLIP(schar2)) +MODULETRANS(double2int3_clip, + { f->sampleFormat = AF_SAMPFMT_TWOSCOMP; f->sampleWidth = 24; }, + double, schar3, TRANS_CLIP(schar3)) +MODULETRANS(double2int4_clip, + { f->sampleFormat = AF_SAMPFMT_TWOSCOMP; f->sampleWidth = 32; }, + double, schar4, TRANS_CLIP(schar4)) + +/* + clipping modules - use initpcmmod() to make one of these + + clips to range given as argument to init function. +*/ + +#define MODULECLIP(name, type)\ +MODULEM(name, \ + { f->pcm = ((pcmmodspec *)i->modspec)->output_mapping; }, \ + type, type, pcmmodspec, \ + { \ + type d=ip[i]; \ + type min=(type)(m->minv); \ + type max=(type)(m->maxv); \ + op[i] = ((d>max) ? max : ((d<min) ? min : d)); \ + } ) + +MODULECLIP(clipfloat, float) +MODULECLIP(clipdouble, double) +MODULECLIP(clip1, schar1) +MODULECLIP(clip2, schar2) +MODULECLIP(clip3, schar3) +MODULECLIP(clip4, schar4) + +/* + unsigned2signedN - expects 8N-bit unsigned ints, outputs 2's comp +*/ + +MODULE(unsigned2signed1, + { + double shift = (double) MIN_INT8; + f->sampleFormat = AF_SAMPFMT_TWOSCOMP; + f->pcm.intercept += shift; + f->pcm.minClip += shift; + f->pcm.maxClip += shift; + }, + uchar1, schar1, + op[i] = ip[i] + MIN_INT8) +MODULE(unsigned2signed2, + { + double shift = (double) MIN_INT16; + f->sampleFormat = AF_SAMPFMT_TWOSCOMP; + f->pcm.intercept += shift; + f->pcm.minClip += shift; + f->pcm.maxClip += shift; + }, + uchar2, schar2, + op[i] = ip[i] + MIN_INT16) +MODULE(unsigned2signed3, + { + double shift = (double) MIN_INT24; + f->sampleFormat = AF_SAMPFMT_TWOSCOMP; + f->pcm.intercept += shift; + f->pcm.minClip += shift; + f->pcm.maxClip += shift; + }, + uchar3, schar3, + op[i] = ip[i] + MIN_INT24) +MODULE(unsigned2signed4, + { + double shift = (double) MIN_INT32; + f->sampleFormat = AF_SAMPFMT_TWOSCOMP; + f->pcm.intercept += shift; + f->pcm.minClip += shift; + f->pcm.maxClip += shift; + }, + uchar4, schar4, + op[i] = ip[i] + MIN_INT32) + +/* !! unsigned2signed4 shouldn't work, but it does !! */ + + +/* + signed2unsignedN - expects 8N-bit 2's comp ints, outputs unsigned +*/ + +MODULE(signed2unsigned1, + { + double shift = -(double) MIN_INT8; + f->sampleFormat = AF_SAMPFMT_UNSIGNED; + f->pcm.intercept += shift; + f->pcm.minClip += shift; + f->pcm.maxClip += shift; + }, + schar1, uchar1, + op[i] = ip[i] - MIN_INT8) +MODULE(signed2unsigned2, + { + double shift = -(double) MIN_INT16; + f->sampleFormat = AF_SAMPFMT_UNSIGNED; + f->pcm.intercept += shift; + f->pcm.minClip += shift; + f->pcm.maxClip += shift; + }, + schar2, uchar2, + op[i] = ip[i] - MIN_INT16) +MODULE(signed2unsigned3, + { + double shift = -(double) MIN_INT24; + f->sampleFormat = AF_SAMPFMT_UNSIGNED; + f->pcm.intercept += shift; + f->pcm.minClip += shift; + f->pcm.maxClip += shift; + }, + schar3, uchar3, + op[i] = ip[i] - MIN_INT24) +MODULE(signed2unsigned4, + { + double shift = -(double) MIN_INT32; + f->sampleFormat = AF_SAMPFMT_UNSIGNED; + f->pcm.intercept += shift; + f->pcm.minClip += shift; + f->pcm.maxClip += shift; + }, + schar4, uchar4, + op[i] = ip[i] - MIN_INT32) + +/* !! signed2unsigned4 shouldn't work, but it does !! */ + + +/* + These convert between different 2's complement integer formats + with no roundoff/asymmetric errors. They should also work faster + than converting integers to floats and back to other integers. + + They are only meant to be used when the input and output integers + have the default PCM mapping; otherwise, arrangemodules will + make the conversion go through floating point and these modules + will not be used. +*/ + +#define intmap _af_default_signed_integer_pcm_mappings /* shorthand */ + +MODULE(int1_2, { f->sampleWidth = 16; f->pcm=intmap[2]; }, + schar1, schar2, op[i] = ip[i] << 8) +MODULE(int1_3, { f->sampleWidth = 24; f->pcm=intmap[3]; }, + schar1, schar3, op[i] = ip[i] << 16) +MODULE(int1_4, { f->sampleWidth = 32; f->pcm=intmap[4]; }, + schar1, schar4, op[i] = ip[i] << 24) + +MODULE(int2_1, { f->sampleWidth = 8; f->pcm=intmap[1]; }, + schar2, schar1, op[i] = ip[i] >> 8) +MODULE(int2_3, { f->sampleWidth = 24; f->pcm=intmap[3]; }, + schar2, schar3, op[i] = ip[i] << 8) +MODULE(int2_4, { f->sampleWidth = 32; f->pcm=intmap[4]; }, + schar2, schar4, op[i] = ip[i] << 16) + +MODULE(int3_1, { f->sampleWidth = 8; f->pcm=intmap[1]; }, + schar3, schar1, op[i] = ip[i] >> 16) +MODULE(int3_2, { f->sampleWidth = 16; f->pcm=intmap[2]; }, + schar3, schar2, op[i] = ip[i] >> 8) +MODULE(int3_4, { f->sampleWidth = 32; f->pcm=intmap[4]; }, + schar3, schar4, op[i] = ip[i] << 8) + +MODULE(int4_1, { f->sampleWidth = 8; f->pcm=intmap[1]; }, + schar4, schar1, op[i] = ip[i] >> 24) +MODULE(int4_2, { f->sampleWidth = 16; f->pcm=intmap[2]; }, + schar4, schar2, op[i] = ip[i] >> 16) +MODULE(int4_3, { f->sampleWidth = 24; f->pcm=intmap[3]; }, + schar4, schar3, op[i] = ip[i] >> 8) + +#undef intmap + +/* + channel changer modules - convert channels using channel matrix + + The channel matrix is a two-dimensional array of doubles, the rows + of which correspond to the virtual format, and the columns + of which correspond to the file format. + + If the channel matrix is null (unspecified), then the default + behavior occurs (see initchannelchange). + + Internally, the module holds a copy of the matrix in which the + rows correspond to the output format, and the columns correspond + to the input format (therefore, if reading==AF_FALSE, the matrix + is transposed as it is copied). +*/ + +typedef struct channelchangedata +{ + int outchannels; + double minClip; + double maxClip; + double *matrix; +} channelchangedata; + +/* + channelchangefree +*/ +static void channelchangefree (struct _AFmoduleinst *i) +{ + channelchangedata *d = i->modspec; + + assert(d); + assert(d->matrix); + + free(d->matrix); + free(d); + + i->modspec = AF_NULL; +} + +/* + channelchangedescribe +*/ +static void channelchangedescribe (struct _AFmoduleinst *i) +{ + channelchangedata *m = (channelchangedata *) i->modspec; + i->outc->f.channelCount = m->outchannels; + i->outc->f.pcm.minClip = m->minClip; + i->outc->f.pcm.maxClip = m->maxClip; +} + +#define CHANNELMOD( name, type, zero_op, action, afteraction ) \ +static void name##run(_AFchunk *inc, _AFchunk *outc, void *modspec) \ +{ \ + type *ip = inc->buf; \ + type *op = outc->buf; \ + double *matrix = ((channelchangedata *)modspec)->matrix; \ + double *m; \ + int frame, inch, outch; \ + \ + for (frame=0; frame < outc->nframes; frame++) \ + { \ + type *ipsave; \ + \ + m = matrix; \ + ipsave = ip; \ + \ + for (outch = 0; outch < outc->f.channelCount; outch++) \ + { \ + zero_op; \ + ip = ipsave; \ + \ + for (inch = 0; inch < inc->f.channelCount; inch++) \ + action;\ + \ + afteraction; \ + op++;\ + }\ + }\ +}\ +\ +static _AFmodule name =\ +{ \ + #name, \ + channelchangedescribe, \ + AF_NULL, AF_NULL, \ + _AFsimplemodrun_pull, AF_NULL, AF_NULL, \ + _AFsimplemodrun_push, AF_NULL, AF_NULL, \ + name##run, \ + channelchangefree \ +}; + +CHANNELMOD(channelchangefloat, float, *op = 0.0, *op += *ip++ * *m++, \ + NULLMODULEPARAM) +CHANNELMOD(channelchangedouble, double, *op = 0.0, *op += *ip++ * *m++, \ + NULLMODULEPARAM) + +#define CHANNELINTMOD(name, type) \ + CHANNELMOD(name, type, \ + double d=0.0, \ + d += *ip++ * *m++, \ + { \ + double minv=outc->f.pcm.minClip; \ + double maxv=outc->f.pcm.maxClip; \ + *op = (type) ((d>maxv) ? maxv : ((d<minv) ? minv : d)); \ + } ) + +CHANNELINTMOD(channelchange1, schar1) +CHANNELINTMOD(channelchange2, schar2) +CHANNELINTMOD(channelchange3, schar3) +CHANNELINTMOD(channelchange4, schar4) + +/* + initchannelchange +*/ +static _AFmoduleinst initchannelchange (_AFmodule *mod, + double *matrix, _PCMInfo *outpcm, + int inchannels, int outchannels, + bool reading) +{ + _AFmoduleinst ret; + channelchangedata *d; + int i, j; + + ret = _AFnewmodinst(mod); + + d = _af_malloc(sizeof (channelchangedata)); + ret.modspec = d; + d->outchannels = outchannels; + d->minClip = outpcm->minClip; + d->maxClip = outpcm->maxClip; + d->matrix = _af_malloc(sizeof (double) * inchannels * outchannels); + + /* + Set d->matrix to a default matrix if a matrix was not specified. + */ + if (!matrix) + { + bool special=AF_FALSE; + + /* Handle many common special cases. */ + + if (inchannels==1 && outchannels==2) + { + static double m[]={1,1}; + matrix=m; + special=AF_TRUE; + } + else if (inchannels==1 && outchannels==4) + { + static double m[]={1,1,0,0}; + matrix=m; + special=AF_TRUE; + } + else if (inchannels==2 && outchannels==1) + { + static double m[]={.5,.5}; + matrix=m; + special=AF_TRUE; + } + else if (inchannels==2 && outchannels==4) + { + static double m[]={1,0,0,1,0,0,0,0}; + matrix=m; + special=AF_TRUE; + } + else if (inchannels==4 && outchannels==1) + { + static double m[]={.5,.5,.5,.5}; + matrix=m; + special=AF_TRUE; + } + else if (inchannels==4 && outchannels==2) + { + static double m[]={1,0,1,0,0,1,0,1}; + matrix=m; + special=AF_TRUE; + } + else + { + /* + Each input channel from 1 to N + maps to output channel 1 to N where + N=min(inchannels, outchannels). + */ + + for(i=0; i < inchannels; i++) + for(j=0; j < outchannels; j++) + d->matrix[j*inchannels + i] = + (i==j) ? 1.0 : 0.0; + } + + if (special) + memcpy(d->matrix, matrix, + sizeof (double) * inchannels * outchannels); + } + /* Otherwise transfer matrix into d->matrix. */ + else + { + /* reading: copy matrix */ + if (reading) + { + memcpy(d->matrix, matrix, sizeof (double) * inchannels * outchannels); + } + /* writing: transpose matrix */ + else + { + for (i=0; i < inchannels; i++) + for (j=0; j < outchannels; j++) + d->matrix[j*inchannels + i] = + matrix[i*outchannels + j]; + } + } + + DEBG(printf("channelchange d->matrix=")); + DEBG(_af_print_channel_matrix(d->matrix, inchannels, outchannels)); + DEBG(printf("\n")); + + return(ret); +} + +/* just used here */ +typedef struct current_state +{ + _AFmoduleinst *modinst; /* current mod instance we're creating */ + _AFchunk *inchunk; /* current input chunk */ + _AFchunk *outchunk; /* current output chunk */ +} current_state; + +/* + addmod is called once per added module instance. It does the + work of putting the module instance in the list and assigning + it an input and output chunk. +*/ +static void addmod (current_state *current, _AFmoduleinst modinst) +{ + *(current->modinst) = modinst; + current->modinst->valid = AF_TRUE; /* at this point mod must be valid */ + + /* Assign the new module instance an input and an output chunk. */ + + current->modinst->inc = current->inchunk; + current->modinst->outc = current->outchunk; + + /* + The output chunk has the same format and number of frames + as input chunk, except in whatever way the 'describe' + method tells us (see README.modules). + */ + + *(current->outchunk) = *(current->inchunk); + + if (current->modinst->mod->describe) + (*current->modinst->mod->describe)(current->modinst); + + /* + Advance to next module and next chunks. Note that next + moduleinst will have this module's out chunk as input. + */ + + current->modinst++; + current->inchunk = current->outchunk; + current->outchunk++; +} + +/* + initfilemods: + + Functions that deal with extended-lifetime file read / file write + modules and their extended-lifetime rebuffer modules. + called once in the lifetime of an AFfilehandle. + + If h->access == _AF_READ_ACCESS: + + Create the module which will be the first module in the chain, + the one which reads the file. This module does the decompression + if necessary, or it could just be a PCM file reader. + + If h->access == _AF_WRITE_ACCESS: + + Create the module which will be the last module in the chain, + the one which writes the file. This module does the compression + if necessary, or it could just be a PCM file writer. + + Also creates a rebuffer module for these modules if necessary. +*/ +static status initfilemods (_Track *track, AFfilehandle h) +{ + int compressionIndex; + _CompressionUnit *compunit; + AFframecount chunkframes; + + compressionIndex = _af_compression_index_from_id(track->f.compressionType); + compunit = &_af_compression[compressionIndex]; + + /* Invalidate everything. */ + + track->ms.filemodinst.valid = AF_FALSE; + track->ms.filemod_rebufferinst.valid = AF_FALSE; + + /* + Seek to beginning of sound data in the track. + + This is needed ONLY for those modules which have to + read/write some kind of pre-data header or table in the + sound data chunk of the file (such as aware). This is NOT + the seek that sets the file at the beginning of the data. + */ + /* XXXmpruett -- we currently don't set seekok. + if (h->seekok && af_fseek(h->fh, track->fpos_first_frame, SEEK_SET) < 0) + */ + if (af_fseek(h->fh, track->fpos_first_frame, SEEK_SET) < 0) + { + _af_error(AF_BAD_LSEEK, "unable to position file handle at beginning of sound data"); + return AF_FAIL; + } + + /* Create file read/write module. */ + + track->filemodhappy = AF_TRUE; + + if (h->access == _AF_READ_ACCESS) + track->ms.filemodinst = + (*compunit->initdecompress)(track, h->fh, h->seekok, + (h->fileFormat==AF_FILE_RAWDATA), &chunkframes); + else + track->ms.filemodinst = + (*compunit->initcompress)(track, h->fh, h->seekok, + (h->fileFormat==AF_FILE_RAWDATA), &chunkframes); + + if (!track->filemodhappy) + return AF_FAIL; + + track->ms.filemodinst.valid = AF_TRUE; + + /* + WHEN DOES THE FILE GET LSEEKED ? + + Somebody sometime has got to lseek the file to the + beginning of the audio data. Similarly, somebody + has to lseek the file at the time of reset or sync. + Furthermore, we have to make sure that we operate + correctly if more than one track is being read or written. + This is handled differently based on whether we are + reading or writing, and whether the ONE_TRACK_ONLY lseek + optimization is engaged. + + READING: + + If we are reading, the file needs to be positioned once + before we start reading and then once per seek. + + If ONE_TRACK_ONLY is not defined, then there can + be multiple tracks in the file. Thus any call to + afReadFrames could cause the file pointer to be + put anywhere: we can not rely on the file pointer + tracking only one track in the file, thus we must seek + to the current position in track N whenever we begin + an AFreadframes on track N. Thus the lseek is done in + afReadFrames. When a reset occurs (including the initial + one), we merely set trk->fpos_next_frame, and the next + afReadFrames will seek the file there before proceeding. + + If ONE_TRACK_ONLY is defined, meaning there can only + be 1 track in the file, we do not need to ever seek + during normal sequential operation, because the file + read module is the only module which ever accesses the + file after _afOpenFile returns. In this case, we do + not need to do the expensive lseek at the beginning of + every AFreadframes call. We need only seek once when the + file is first opened and once when the file is seeked. + At both of these times, we reset the modules. So we + can do the lseek in resetmodules() right after it has + called all of the modules' reset2 methods. + + WRITING: + + If we are writing, the file needs to be positioned once + before we start writing and it needs to be positioned + after every complete sync operation on the file. + + If ONE_TRACK_ONLY is not defined, then there can be + multiple tracks in the file. This assumes space for + the tracks has been preallocated. Thus any call to + AFwriteframes could cause the file pointer to be + put anywhere: we can not rely on the file pointer + tracking only one track in the file, thus we must seek + to the current position in track n whenever we begin + an AFwriteframes on track n. Thus the lseek is done + in AFwriteframes. When we first start, and when a sync + occurs, we merely set trk->fpos_next_frame, and the next + AFwriteframes will seek the file there before proceeding. + + If ONE_TRACK_ONLY is defined, meaning there can only + be 1 track in the file, we do not need to ever seek + during normal sequential operation, because the file + write module is the only module which ever accesses + the file after _AFopenfile returns. In this case, we + do not need to do the expensive lseek at the beginning + of every AFwriteframes call. We can do the lseek for + the initial case right here (that's what you see below), + and we can do the lseek for syncs in _AFsyncmodules right + after it has called all of the modules' sync2 methods. + + One annoying exceptional case is _AFeditrate, which + can get called at any time. But it saves and restores + the file position at its beginning and end, so it's + no problem. + + WHY THE F(*#&@ DON'T YOU JUST HAVE MULTIPLE FILE DESCRIPTORS? + + The obviously and blatantly better way to do this would be + to simply open one fd per track and then all the problems + go away! Too bad we offer afOpenFD() in the API, which + makes it impossible for the AF to get more than 1 fd + for the file. afOpenFD() is unfortunately used far + more often than afOpenFile(), so the benefit of doing + the optimization the "right" way in the cases where + afOpenFile() are used are not currently too great. + But one day we will have to phase out afOpenFD(). + Too bad, it seemed like such a great idea when we put + it in. + */ + +#ifdef ONE_TRACK_ONLY + if (h->access == _AF_WRITE_ACCESS) + { + if (h->seekok && af_fseek(h->fh, track->fpos_next_frame, SEEK_SET) < 0) + { + _af_error(AF_BAD_LSEEK, + "unable to position write ptr at first data frame"); + return AF_FAIL; + } + } +#endif + + /* Create its rebuffer module. */ + + if (compunit->needsRebuffer) + { + /* We assume the following for now. */ + assert(compunit->nativeSampleFormat == AF_SAMPFMT_TWOSCOMP); + assert(compunit->nativeSampleWidth == 16); + + if (h->access == _AF_WRITE_ACCESS) + track->ms.filemod_rebufferinst = + _af_initint2rebufferv2f(chunkframes*track->f.channelCount, + compunit->multiple_of); + else + track->ms.filemod_rebufferinst = + _af_initint2rebufferf2v(chunkframes*track->f.channelCount, + compunit->multiple_of); + + track->ms.filemod_rebufferinst.valid = AF_TRUE; + } + else + track->ms.filemod_rebufferinst.valid = AF_FALSE; + + /* + These modules should not get freed until the file handle + is destroyed (i.e. the file is closed). + */ + + track->ms.filemodinst.free_on_close = AF_TRUE; + track->ms.filemod_rebufferinst.free_on_close = AF_TRUE; + + return AF_SUCCEED; +} + +/* + addfilereadmods: called once per setup of the modules + for a given AFfilehandle +*/ +static status addfilereadmods (current_state *current, _Track *track, + AFfilehandle h) +{ + assert(track->ms.filemodinst.valid); + + /* Fail in case code is broken and NDEBUG is defined. */ + if (!track->ms.filemodinst.valid) + return AF_FAIL; + + addmod(current, track->ms.filemodinst); + if (track->ms.filemod_rebufferinst.valid) + addmod(current, track->ms.filemod_rebufferinst); + + return AF_SUCCEED; +} + +/* + addfilewritemods is called once per setup of the modules + for a given AFfilehandle. +*/ +static status addfilewritemods (current_state *current, _Track *track, + AFfilehandle h) +{ + assert(track->ms.filemodinst.valid); + + /* Fail in case code is broken and NDEBUG is defined. */ + if (!track->ms.filemodinst.valid) + return(AF_FAIL); + + if (track->ms.filemod_rebufferinst.valid) + addmod(current, track->ms.filemod_rebufferinst); + + addmod(current, track->ms.filemodinst); + + return(AF_SUCCEED); +} + +/* + disposefilemods: called once in the lifetime of an AFfilehandle +*/ +static status disposefilemods (_Track *track) +{ + if (track->ms.filemodinst.valid && + track->ms.filemodinst.mod->free) + (*track->ms.filemodinst.mod->free)(&track->ms.filemodinst); + + track->ms.filemodinst.valid = AF_FALSE; + + if (track->ms.filemod_rebufferinst.valid && + track->ms.filemod_rebufferinst.mod->free) + (*track->ms.filemod_rebufferinst.mod->free)(&track->ms.filemod_rebufferinst); + + track->ms.filemod_rebufferinst.valid = AF_FALSE; + + return AF_SUCCEED; +} + +/* + useAP: rate conversion AP decision maker and warner and kludger +*/ +static bool useAP (double inrate, double outrate, + double *inratep, double *outratep) +{ + bool instandard = + (inrate==8000 || inrate==11025 || inrate==16000 || + inrate==22050 || inrate==32000 || inrate==44100 || + inrate==48000); + bool outstandard = + (outrate==8000 || outrate==11025 || outrate==16000 || + outrate==22050 || outrate==32000 || outrate==44100 || + outrate==48000); + bool incodec; + bool outcodec; + + incodec = (inrate==_AF_SRATE_CODEC || inrate==(long)_AF_SRATE_CODEC); + outcodec = (outrate==_AF_SRATE_CODEC || outrate==(long)_AF_SRATE_CODEC); + + *inratep = inrate; + *outratep = outrate; + + if (instandard && outstandard) return AF_TRUE; + if (incodec && outstandard && outrate != 8000.00) + { + _af_error(AF_WARNING_CODEC_RATE, + "WARNING using input rate 8 kHz instead of %.30g Hz " + "to allow high-quality rate conversion", + inrate); + *inratep = 8000.00; + return AF_TRUE; + } + if (instandard && inrate != 8000.00 && outcodec) + { + _af_error(AF_WARNING_CODEC_RATE, + "WARNING using output rate 8 kHz instead of %.30g Hz " + "to allow high-quality rate conversion", + outrate); + *outratep = 8000.00; + return AF_TRUE; + } + + if (!instandard && !outstandard) + _af_error(AF_WARNING_RATECVT, + "WARNING using lower quality rate conversion due to " + "rates %.30g and %.30g -- " + "output file may contain audible artifacts", + inrate, outrate); + else if (!instandard) + _af_error(AF_WARNING_RATECVT, + "WARNING using lower quality rate conversion due to " + "input rate %.30g -- " + "output file may contain audible artifacts", + inrate); + else /* !outstandard */ + _af_error(AF_WARNING_RATECVT, + "WARNING using lower quality rate conversion due to " + "output rate %.30g -- " + "output file may contain audible artifacts", + outrate); + + return AF_FALSE; +} + +/* + initrateconvertmods handles the extended-life rate conversion + module and its extended-life rebuffer module called once in the + lifetime of an AFfilehandle. +*/ +static void initrateconvertmods (bool reading, _Track *track) +{ + /* no rate conversion initially */ + track->ms.rateconvertinst.valid = AF_FALSE; + track->ms.rateconvert_rebufferinst.valid = AF_FALSE; +} + +static void disposerateconvertmods (_Track *); + +/* XXXmpruett rate conversion is disabled for now */ +#if 0 +/* + addrateconvertmods: called once per setup of the modules + for a given AFfilehandle +*/ +static void addrateconvertmods (current_state *current, int nchannels, + double inrate, double outrate, + bool reading, _Track *track) +{ + AFframecount inframes, outframes; + + /* Check if we are no longer rate converting. */ + if (inrate == outrate) + { + disposerateconvertmods(track); + track->ratecvt_filter_params_set = AF_FALSE; /* XXX HACK */ + } + else + { + /* + We need new rateconverter if we didn't have one + or if rate has changed or rate conversion params + have changed. + */ + if (!track->ms.rateconvertinst.valid || + inrate != track->ms.rateconvert_inrate || + outrate != track->ms.rateconvert_outrate || + track->ratecvt_filter_params_set /* HACK */) + { + bool usingAP = useAP(inrate, outrate, &inrate, &outrate); + + disposerateconvertmods(track); + track->ratecvt_filter_params_set = AF_FALSE; /* HACK */ + + if (usingAP) + { + track->ms.rateconvertinst = InitAFRateConvert(inrate, outrate, + nchannels, + track->taper, track->dynamic_range, + &inframes, &outframes, + track, reading); + + if (!reading) + track->ms.rateconvert_rebufferinst = + initfloatrebufferv2f(inframes*nchannels, AF_FALSE); + else + track->ms.rateconvert_rebufferinst = + initfloatrebufferf2v(outframes*nchannels, AF_FALSE); + + track->ms.rateconvertinst.valid = AF_TRUE; + track->ms.rateconvert_rebufferinst.valid = AF_TRUE; + } + else + { + track->ms.rateconvertinst = initpolyratecvt(track, + inrate, outrate, + nchannels, reading); + + track->ms.rateconvertinst.valid = AF_TRUE; + track->ms.rateconvert_rebufferinst.valid = AF_FALSE; + } + + track->ms.rateconvert_inrate = inrate; + track->ms.rateconvert_outrate = outrate; + + track->ms.rateconvertinst.free_on_close = AF_TRUE; + track->ms.rateconvert_rebufferinst.free_on_close = AF_TRUE; + } + + /* Add the rate conversion modules. */ + + if (!reading && track->ms.rateconvert_rebufferinst.valid) + addmod(current, track->ms.rateconvert_rebufferinst); + + addmod(current, track->ms.rateconvertinst); + + if (reading && track->ms.rateconvert_rebufferinst.valid) + addmod(current, track->ms.rateconvert_rebufferinst); + } +} + +/* + disposerateconvertmods is called once in the lifetime of an + AFfilehandle. +*/ +static void disposerateconvertmods (_Track *track) +{ + /* + Neither module is necessarily valid--there could have been + an error, or the modules could possibly never have been set up. + */ + if (track->ms.rateconvertinst.valid && + track->ms.rateconvertinst.mod->free) + { + (*track->ms.rateconvertinst.mod->free) + (&track->ms.rateconvertinst); + } + + track->ms.rateconvertinst.valid = AF_FALSE; + + if (track->ms.rateconvert_rebufferinst.valid && + track->ms.rateconvert_rebufferinst.mod->free) + { + (*track->ms.rateconvert_rebufferinst.mod->free) + (&track->ms.rateconvert_rebufferinst); + } + + track->ms.rateconvert_rebufferinst.valid = AF_FALSE; +} +#endif /* XXXmpruett rate conversion is disabled for now */ + +/* -------------------------------------------------------------- */ + +/* The stuff in this section is used by arrangemodules(). */ + +static _AFmodule *unsigned2signed[5] = +{ + NULL, + &unsigned2signed1, &unsigned2signed2, + &unsigned2signed3, &unsigned2signed4 +}; + +static _AFmodule *signed2unsigned[5] = +{ + NULL, + &signed2unsigned1, &signed2unsigned2, + &signed2unsigned3, &signed2unsigned4 +}; + +static _AFmodule *swapbytes[9] = +{ + NULL, NULL, &swap2, &swap3, &swap4, + NULL, NULL, NULL, &swap8 +}; + +/* don't forget int24_fmt is really 24 bits right-justified in 32 bits */ + +typedef enum format_code +{ + int8_fmt, + int16_fmt, + int24_fmt, + int32_fmt, + float_fmt, + double_fmt +} format_code; + +#define isinteger(fc) ((fc) <= int32_fmt) +#define isfloating(fc) ((fc) >= float_fmt) + +/* + get_format_code +*/ +static format_code get_format_code (_AudioFormat *fmt) +{ + if (fmt->sampleFormat == AF_SAMPFMT_FLOAT) + return float_fmt; + if (fmt->sampleFormat == AF_SAMPFMT_DOUBLE) + return double_fmt; + + if (fmt->sampleFormat == AF_SAMPFMT_TWOSCOMP || + fmt->sampleFormat == AF_SAMPFMT_UNSIGNED) + { + switch (_af_format_sample_size_uncompressed(fmt, AF_FALSE)) + { + case 1: return int8_fmt; + case 2: return int16_fmt; + case 3: return int24_fmt; + case 4: return int32_fmt; + } + } + + /* NOTREACHED */ + assert(0); + return -1; +} + +static _AFmodule *to_flt[6] = +{ + &int2float1, &int2float2, &int2float3, &int2float4, + NULL, &double2float +}; + +static _AFmodule *to_dbl[6] = +{ + &int2double1, &int2double2, &int2double3, &int2double4, + &float2double, NULL +}; + +static _AFmodule *clip[6] = +{ + &clip1, &clip2, &clip3, &clip4, + &clipfloat, &clipdouble +}; + +static _AFmodule *channelchanges[6] = +{ + &channelchange1, &channelchange2, &channelchange3, &channelchange4, + &channelchangefloat, &channelchangedouble +}; + +/* indices are of type format_code: matrix[infmtcode][outfmtcode] */ +static _AFmodule *convertmatrix[6][6] = +{ + /* TO: + { + int8_fmt, int16_fmt, + int24_fmt, int32_fmt, + float_fmt, double_fmt + } + */ + + /* FROM int8_fmt */ + { + NULL, &int1_2, + &int1_3, &int1_4, + &int2float1, &int2double1 + }, + + /* FROM int16_fmt */ + { + &int2_1, NULL, + &int2_3, &int2_4, + &int2float2, &int2double2 + }, + + /* FROM int24_fmt */ + { + &int3_1, &int3_2, + NULL, &int3_4, + &int2float3, &int2double3 + }, + + /* FROM int32_fmt */ + { + &int4_1, &int4_2, + &int4_3, NULL, + &int2float4, &int2double4 + }, + + /* FROM float_fmt */ + { + &float2int1_clip, &float2int2_clip, + &float2int3_clip, &float2int4_clip, + NULL, &float2double + }, + + /* FROM double_fmt */ + { + &double2int1_clip, &double2int2_clip, + &double2int3_clip, &double2int4_clip, + &double2float, NULL + } +}; + +static _PCMInfo *intmappings[6] = +{ + &_af_default_signed_integer_pcm_mappings[1], + &_af_default_signed_integer_pcm_mappings[2], + &_af_default_signed_integer_pcm_mappings[3], + &_af_default_signed_integer_pcm_mappings[4], + AF_NULL, AF_NULL +}; + +/* + trivial_int_clip +*/ +static bool trivial_int_clip (_AudioFormat *f, format_code code) +{ + return (intmappings[code] != NULL && + f->pcm.minClip == intmappings[code]->minClip && + f->pcm.maxClip == intmappings[code]->maxClip); +} + +/* + trivial_int_mapping +*/ +static bool trivial_int_mapping (_AudioFormat *f, format_code code) +{ + return (intmappings[code] != NULL && + f->pcm.slope == intmappings[code]->slope && + f->pcm.intercept == intmappings[code]->intercept); +} + +/* + arrangemodules decides which modules to use and creates instances + of them. +*/ +static status arrangemodules (_AFfilehandle *h, _Track *track) +{ + bool reading = (h->access == _AF_READ_ACCESS); + + current_state current; + + bool rateconverting, transforming; + bool already_clipped_output, already_transformed_output; + + int insampbytes, outsampbytes; + int chans; + + format_code infc, outfc; + + /* + in and out are the formats at the start and end of the + chain of modules, respectively. + */ + + _AudioFormat in, out; + + /* in==FILE, out==virtual (user) */ + if (reading) + { + in = track->f; + out = track->v; + } + /* in==virtual (user), out==FILE */ + else + { + in = track->v; + out = track->f; + } + + infc = get_format_code(&in); + outfc = get_format_code(&out); + + /* flags */ + + rateconverting = (in.sampleRate != out.sampleRate); + + /* + throughout routine: + + current.modinst points to current module + current.inchunk points to current in chunk, always outchunk-1 + current.outchunk points to current out chunk + + The addmod() function does most of the work. It calls the + "describe" module function, during which a module looks + at inc->f and writes the format it will output in outc->f. + */ + + current.modinst = track->ms.module; + + current.inchunk = track->ms.chunk; + current.outchunk = track->ms.chunk + 1; + + current.inchunk->f = in; + + /* + max # of modules that could be needed together + may need to change this if you change this function + */ + #define MAX_MODULES 17 + + /* Actually arrange the modules. Call addmod() to add one. */ + + /* Add file reader and possibly a decompressor. */ + + if (reading) + if (AF_FAIL == addfilereadmods(¤t, track, h)) + return AF_FAIL; + + /* Make data native-endian. */ + + if (in.byteOrder != _AF_BYTEORDER_NATIVE) + { + int bytes_per_samp = _af_format_sample_size_uncompressed(&in, !reading); + + if (bytes_per_samp > 1 && + in.compressionType == AF_COMPRESSION_NONE) + { + assert(swapbytes[bytes_per_samp]); + addmod(¤t, _AFnewmodinst(swapbytes[bytes_per_samp])); + } + else + in.byteOrder = _AF_BYTEORDER_NATIVE; + } + + /* Handle nasty 3-byte input cases. */ + + insampbytes = _af_format_sample_size_uncompressed(&in, AF_FALSE); + + if (isinteger(infc) && insampbytes == 3) + { + if (reading || in.compressionType != AF_COMPRESSION_NONE) + { + /* + We're reading 3-byte ints from a file. + At this point stretch them to 4-byte ints + by sign-extending or adding a zero-valued + most significant byte. We could also + be reading/writing 3-byte samples output + from a decompressor. + */ + if (in.sampleFormat == AF_SAMPFMT_UNSIGNED) + addmod(¤t, _AFnewmodinst(&real_char3_to_uchar3)); + else + addmod(¤t, _AFnewmodinst(&real_char3_to_schar3)); + } + else /* writing, non-compressed */ + { + /* + We're processing 3-byte ints from the + user, which come in as sign-extended + 4-byte quantities. How convenient: + this is what we want. + */ + } + } + + /* Make data signed. */ + + if (in.sampleFormat == AF_SAMPFMT_UNSIGNED) + { + addmod(¤t, _AFnewmodinst(unsigned2signed[insampbytes])); + } + + /* Standardize pcm mapping of "in" and "out". */ + + /* + Since they are used to compute transformations in the + inner section of this routine (inside of sign conversion), + we need in.pcm and out.pcm in terms of AF_SAMPFMT_TWOSCOMP + numbers. + */ + in.pcm = current.inchunk->f.pcm; /* "in" is easy */ + + if (out.sampleFormat == AF_SAMPFMT_UNSIGNED) /* "out": undo the unsigned shift */ + { + double shift = intmappings[outfc]->minClip; + out.pcm.intercept += shift; + out.pcm.minClip += shift; + out.pcm.maxClip += shift; + } + + /* ------ CLIP user's input samples if necessary */ + + if (in.pcm.minClip < in.pcm.maxClip && !trivial_int_clip(&in, infc)) + addmod(¤t, initpcmmod(clip[infc], AF_NULL, &in.pcm)); + + /* + At this point, we assume we can have doubles, floats, + and 1-, 2-, and 4-byte signed integers on the input and + on the output (or 4-byte integers with 24 significant + (low) bits, int24_fmt). + + Now we handle rate conversion and pcm transformation. + */ + + /* If rate conversion will happen, we must have floats. */ + /* + This may result in loss of precision. This bug must be + fixed eventually. + */ + if (rateconverting && infc != float_fmt) + { + addmod(¤t, _AFnewmodinst(to_flt[infc])); + infc = float_fmt; + } + + /* + We must make sure the output samples will get clipped + to SOMETHING reasonable if we are rateconverting. + The user cannot possibly expect to need to clip values + just because rate conversion is on. + */ + + if (out.pcm.minClip >= out.pcm.maxClip && rateconverting) + { + out.pcm.minClip = out.pcm.intercept - out.pcm.slope; + out.pcm.maxClip = out.pcm.intercept + out.pcm.slope; + } + + already_clipped_output = AF_FALSE; + already_transformed_output = AF_FALSE; + + /* + We need to perform a transformation (in floating point) + if the input and output PCM mappings are different. + + The only exceptions are the trivial integer conversions + (i.e., full-range integers of one # of bytes to full-range + integers to another # of bytes). + */ + + transforming = (in.pcm.slope != out.pcm.slope || + in.pcm.intercept != out.pcm.intercept) && + !(trivial_int_mapping(&in, infc) && + trivial_int_mapping(&out,outfc)); + + /* + If we have ints on input and the user is performing a + change of mapping other than a trivial one, we must go + to floats or doubles. + */ + + if (isinteger(infc) && transforming) + { + /* + Use doubles if either the in or out format has + that kind of precision. + */ + if (infc == int32_fmt || + outfc == double_fmt || outfc == int32_fmt) + { + addmod(¤t, _AFnewmodinst(to_dbl[infc])); + infc = double_fmt; + } + else + { + addmod(¤t, _AFnewmodinst(to_flt[infc])); + infc = float_fmt; + } + } + + DEBG(printf("arrangemodules in="); _af_print_audioformat(&in);); + DEBG(printf("arrangemodules out="); _af_print_audioformat(&out);); + DEBG(printf("arrangemodules transforming=%d\n", transforming)); + DEBG(printf("arrangemodules infc=%d outfc=%d\n", infc, outfc)); + + /* + invariant: + + At this point, if infc is an integer format, then we are + not rate converting, nor are we perfoming any change of + mapping other than possibly a trivial int->int conversion. + */ + + /* ----- convert format infc to format outfc */ + + /* change channels if appropriate now */ + + if (in.channelCount != out.channelCount && + (infc > outfc || (infc==outfc && out.channelCount < in.channelCount))) + { + addmod(¤t, + initchannelchange(channelchanges[infc], + track->channelMatrix, &in.pcm, + in.channelCount, out.channelCount, + reading)); + chans = out.channelCount; + } + else + chans = in.channelCount; + + /* Transform floats if appropriate now. */ + + if (transforming && + infc==double_fmt && isfloating(outfc)) + { + addmod(¤t, initpcmmod(&doubletransform, &in.pcm, &out.pcm)); + } + +#if 0 /* XXXmpruett */ + /* + Handle rate conversion (will do the right thing if + not rate converting). + */ + + addrateconvertmods(¤t, chans, in.sampleRate, out.sampleRate, reading, track); +#endif + + /* Add format conversion, if needed */ + + if (convertmatrix[infc][outfc]) + { + /* + for float/double -> int conversions, the module + we use here also does the transformation and + clipping. + + We use initpcmmod() in any case because it is harmless + for the other modules in convertmatrix[][]. + */ + if (isfloating(infc) && isinteger(outfc)) /* "float"->"int" */ + { + already_clipped_output = AF_TRUE; + already_transformed_output = AF_TRUE; + } + addmod(¤t, initpcmmod(convertmatrix[infc][outfc], + &in.pcm, &out.pcm)); + } + + /* Transform floats if appropriate now. */ + + if (transforming && !already_transformed_output && infc != double_fmt) + { + if (outfc==double_fmt) + addmod(¤t, initpcmmod(&doubletransform, + &in.pcm, &out.pcm)); + else if (outfc==float_fmt) + addmod(¤t, initpcmmod(&floattransform, + &in.pcm, &out.pcm)); + } + + /* Change channels if appropriate now. */ + + if (in.channelCount != out.channelCount && + (outfc > infc || (infc==outfc && in.channelCount < out.channelCount))) + { + addmod(¤t, + initchannelchange(channelchanges[outfc], + track->channelMatrix, &out.pcm, + in.channelCount, out.channelCount, + reading)); + } + + /* ------ CLIP user's output samples if needed */ + + if (!already_clipped_output) + { + if (out.pcm.minClip < out.pcm.maxClip && + !trivial_int_clip(&out, outfc)) + { + addmod(¤t, initpcmmod(clip[outfc], NULL, &out.pcm)); + } + } + + /* Make data unsigned if neccessary. */ + + outsampbytes = _af_format_sample_size_uncompressed(&out, AF_FALSE); + + if (out.sampleFormat == AF_SAMPFMT_UNSIGNED) + addmod(¤t, _AFnewmodinst(signed2unsigned[outsampbytes])); + + /* Handle nasty 3-byte output cases. */ + + if (isinteger(outfc) && outsampbytes == 3) + { + if (!reading || out.compressionType != AF_COMPRESSION_NONE) + { + /* + We're writing 3-byte ints into a file. + We have 4-byte ints. Squish them to + 3 by truncating the high byte off. + we could also be reading/writing ints + into a compressor. note this works for + signed and unsigned, and has to. + */ + addmod(¤t, _AFnewmodinst(&char3_to_real_char3)); + } + else /* reading, not compressed */ + { + /* + We're reading 3-byte ints into the + user's buffer. + + The user expects + 1. 4-byte sign-extended ints (3 bytes + sign extended in 4 bytes) or + 2. 4-byte unsigned ints (3 bytes in 4 bytes). + + How convenient: this is just what we have. + */ + } + } + + if (out.byteOrder != _AF_BYTEORDER_NATIVE) + { + int bytes_per_samp = _af_format_sample_size_uncompressed(&out, reading); + + if (bytes_per_samp > 1 && out.compressionType == AF_COMPRESSION_NONE) + { + assert(swapbytes[bytes_per_samp]); + addmod(¤t, _AFnewmodinst(swapbytes[bytes_per_samp])); + } + } + + /* Add file writer, possibly a compressor. */ + + if (!reading) + if (AF_FAIL == addfilewritemods(¤t, track, h)) + return(AF_FAIL); + + /* Now all modules are arranged! */ + + track->ms.nmodules = current.modinst - track->ms.module; + +#ifdef UNLIMITED_CHUNK_NVFRAMES + /* + OPTIMIZATION: normally, when we set up the modules, AFreadframes + and AFwriteframes must pull and push chunks of size at most + _AF_ATOMIC_NVFRAMES. + + For the simplest configurations of modules (1 module, no + compression), no buffering at all needs to be done by the + module system. In these cases, afReadFrames/afWriteFrames + can pull/push as many virtual frames as they want + in one call. This flag tells tells afReadFrames and + afWriteFrames whether they can do so. + + Note that if this flag is set, file modules cannot rely + on the intermediate working buffer which _AFsetupmodules + usually allocates for them in their input or output chunk + (for reading or writing, respectively). This is why if + we are reading/writing compressed data, this optimization + is turned off. + + There are warnings to this effect in the pcm + (uncompressed) file read/write module. If you want to + apply this optimization to other types, be sure to put + similar warnings in the code. + */ + if (track->ms.nmodules == 1 && + track->v.compressionType == AF_COMPRESSION_NONE && + track->f.compressionType == AF_COMPRESSION_NONE) + track->ms.mustuseatomicnvframes = AF_FALSE; + else + track->ms.mustuseatomicnvframes = AF_TRUE; +#else + track->ms.mustuseatomicnvframes = AF_TRUE; +#endif + + return AF_SUCCEED; +} + +/* + disposemodules will free old buffers and free old modules, except + those marked with free_on_close. + + The modules existing before we dispose them could be: + + 1. none (we may have only called _AFinitmodules and not _AFsetupmodules) + 2. some invalid PARTIALLY ALLOCATED ones (e.g. the last _AFsetupmodules + had an error) or + 3. a perfectly valid set of modules. + + disposemodules will deal with all three cases. +*/ +static void disposemodules (_Track *track) +{ + if (track->ms.module) + { + int i; + + for (i=0; i < MAX_MODULES; i++) + { + _AFmoduleinst *mod = &track->ms.module[i]; + +#ifdef AF_DEBUG + if (!mod->valid && i < track->ms.nmodules) + printf("disposemodules: WARNING in-range invalid module found '%s'\n", mod->mod->name); +#endif + + if (mod->valid && !mod->free_on_close && mod->mod->free) + { + (*mod->mod->free)(mod); + mod->valid = AF_FALSE; + } + } + + free(track->ms.module); + track->ms.module = AF_NULL; + } + track->ms.nmodules = 0; + + if (track->ms.chunk) + { + free(track->ms.chunk); + track->ms.chunk = AF_NULL; + } + + if (track->ms.buffer) + { + int i; + for (i=0; i < (MAX_MODULES+1); i++) + { + if (track->ms.buffer[i] != AF_NULL) + { + free(track->ms.buffer[i]); + track->ms.buffer[i] = AF_NULL; + } + } + free(track->ms.buffer); + track->ms.buffer = AF_NULL; + } +} + +/* + resetmodules: see advanced section in README.modules for more info +*/ +static status resetmodules (_AFfilehandle *h, _Track *track) +{ + int i; + + /* + We should already have called _AFsetupmodules. + (Actually this is called from the end of _AFsetupmodules + but whatever). + */ + + assert(!track->ms.modulesdirty); + + /* Assume all is well with track. */ + track->filemodhappy = AF_TRUE; + + CHNK(printf("resetmodules running reset1 routines\n")); + + /* Reset all modules. */ + for (i=track->ms.nmodules-1; i >= 0; i--) + { + /* reset1 */ + if (track->ms.module[i].mod->reset1 != AF_NULL) + (*track->ms.module[i].mod->reset1)(&track->ms.module[i]); + } + + /* Clear out frames2ignore here; the modules will increment it. */ + track->frames2ignore = 0; + + if (!track->filemodhappy) + return AF_FAIL; + + CHNK(printf("resetmodules running reset2 routines\n")); + + for (i=0; i < track->ms.nmodules; i++) + { + /* reset2 */ + if (track->ms.module[i].mod->reset2 != AF_NULL) + (*track->ms.module[i].mod->reset2)(&track->ms.module[i]); + } + + CHNK(printf("resetmodules completed\n")); + + if (!track->filemodhappy) + return AF_FAIL; + +#ifdef ONE_TRACK_ONLY + /* + For an explanation of this, see the comment in + initfilemods which explains how and when the file is + lseek'ed. + */ + if (h->seekok) + if (lseek(h->fd, track->fpos_next_frame, SEEK_SET) < 0) + { + _af_error(AF_BAD_LSEEK, + "unable to position read pointer at next data frame"); + return AF_FAIL; + } +#endif + + return AF_SUCCEED; +} + +/* + _AFsyncmodules +*/ +status _AFsyncmodules (AFfilehandle h, _Track *track) +{ + int i; + + /* We should already have called _AFsetupmodules. */ + assert(!track->ms.modulesdirty); + + /* Assume all is well with track. */ + track->filemodhappy = AF_TRUE; + + CHNK(printf("_AFsyncmodules running sync1 routines\n")); + + /* Sync all modules. */ + for(i=track->ms.nmodules-1; i >= 0; i-- ) + { + /* sync1 */ + if (AF_NULL != track->ms.module[i].mod->sync1) + (*track->ms.module[i].mod->sync1)(&track->ms.module[i]); + } + + if (!track->filemodhappy) + return AF_FAIL; + + CHNK(printf("_AFsyncmodules running sync2 routines\n")); + + for (i=0; i < track->ms.nmodules; i++) + { + /* sync2 */ + if (AF_NULL != track->ms.module[i].mod->sync2) + (*track->ms.module[i].mod->sync2)(&track->ms.module[i]); + } + + CHNK(printf("_AFsyncmodules completed\n")); + + if (!track->filemodhappy) + return AF_FAIL; + +#ifdef ONE_TRACK_ONLY + /* + For an explanation of this, see the comment in + initfilemods which explains how and when the file is + lseek'ed. + */ + if (h->seekok) + if (lseek( h->fd, track->fpos_next_frame, SEEK_SET) < 0 ) + { + _af_error(AF_BAD_LSEEK, + "unable to position write ptr at next data frame"); + return(AF_FAIL); + } +#endif + + return AF_SUCCEED; +} + +/* + _AFsetupmodules: + - frees any old modules, chunks, and buffers + - looks at the input and output format and sets up a whole new + set of input and output modules (using arrangemodules()) + - assigns those modules chunks + - allocates buffers and assigns the buffers to the chunks + - initializes various track fields pertaining to the module system + + It returns AF_FAIL on any kind of error. + + It sets modulesdirty to AF_FALSE if it was able to clean the + modules (although an error still could have occurred after + cleaning them). +*/ +status _AFsetupmodules (AFfilehandle h, _Track *track) +{ + _AFmoduleinst *modules; + _AFchunk *chunks; + void **buffers; + int maxbufsize, bufsize, i; + double rateratiof2v, fframepos; + + /* + The purpose of this function is to "clean" the modules: + + * All of the fields in trk->ms are completely set + and valid. + + * track->totalvframes and track->next[fv]frame are set + and valid and trk->modulesdirty will be set to AF_FALSE + if this function succeeds. + + This function also resets the modules on files open for read. + it will return AF_FAIL if either cleaning the modules fails, + or this reset fails. + + The comments will tell you which part does what. + */ + + /* + NOTE: we cannot trust any value in track->ms until we + have called disposemodules(), at which time things are + cleared to reasonable "zero" values. + + It is possible for track->ms to be in an illegal state + at this point, if the last _AFsetupmodules failed with + an error. + + We can trust track->totalvframes and track->next[fv]frame + because they are only modified after successfully building + the modules. + */ + + /* + Disallow compression in virtual format for now. + */ + if (track->v.compressionType != AF_COMPRESSION_NONE) + { + _af_error(AF_BAD_NOT_IMPLEMENTED, + "library does not support compression in virtual format yet"); + return AF_FAIL; + } + + /* + Check that virtual compression parameters are ok. + */ + { + int idx = _af_compression_index_from_id(track->v.compressionType); + if ((*_af_compression[idx].fmtok)(&track->v) == AF_FALSE) + { + return AF_FAIL; + } + } + + /* + track->nextvframe and track->nextfframe: + + At this point, only track->nextvframe contains useful + information, since track->nextfframe may be swayed by + currently buffered frames. + + Also track->nextvframe is currently in the scale of the + old sampling rate, not the new one we are setting up. + + So at this point we remember where we are in the file + (in floating point) in terms of the file sampling rate. + + We will use this later in this function to set both + track->nextfframe (for reading) and track->nextvframe + (for reading and writing). + + We must be careful to use the old rates, not the ones + in track->{f,v}. + */ + + /* If modules have been set up at all */ + if (track->ms.old_v_rate > 0) + { + assert(track->ms.old_f_rate > 0); + rateratiof2v = track->ms.old_f_rate / track->ms.old_v_rate; + fframepos = track->nextvframe * rateratiof2v; + } + else + /* We start at frame zero. */ + fframepos = 0; + + /* + Dispose the existing modules (except extended-life ones). + + See the function for info on what the module state could + be at this time. + */ + + disposemodules(track); + + /* + Here we allocate the highest number of module instances + (and chunks) chained together we could possibly need. + + This is how the chunks are used: + + module[n]'s input chunk is chunk[n] + module[n]'s output chunk is chunk[n+1] + + chunk[n]'s buffer, if it is not the user's buffer, is buffer[n]. + + For reading chunk[0] is not usually used. + For writing chunk[nmodules] is not usually used. + + We allocate a buffer for chunk[0] on reading and + chunk[nmodules] when writing because the file reading + or file writing module, if it does compression or + decompression, may need extra space in which to place + the result of its processing before reading or writing it. + + Also note that chunk[0].f and chunk[nmodules].f are used in + arrangemodules(). + */ + modules = _af_malloc(sizeof (_AFmoduleinst) * MAX_MODULES); + if (modules == AF_NULL) + return AF_FAIL; + for (i=0; i < MAX_MODULES; i++) + modules[i].valid = AF_FALSE; + + chunks = _af_malloc(sizeof (_AFchunk) * (MAX_MODULES+1)); + if (chunks == AF_NULL) + return AF_FAIL; + + buffers = _af_malloc(sizeof (void *) * (MAX_MODULES+1)); + if (buffers == AF_NULL) + return AF_FAIL; + /* + It is very important to initialize each buffers[i] to NULL; + dispose frees them all if !NULL. + */ + + for (i=0; i < (MAX_MODULES+1); i++) + buffers[i] = AF_NULL; + + track->ms.module = modules; + /* + nmodules is a bogus value here, set just for sanity + (in case of broken code). + */ + + track->ms.nmodules = 0; + track->ms.chunk = chunks; + track->ms.buffer = buffers; + + /* + Figure out the best modules to use to convert the + data and initialize instances of those modules. + Fills "track->ms.module" and most of "track->ms.chunk" + arrays (all but the buffers) as it goes. Sets + "track->ms.nmodules" As a side benefit, this function + also leaves information about the data format at each + stage in the "f" field of each chunk. + */ + if (arrangemodules(h, track) == AF_FAIL) + { + /* + At this point the modules are in an incompletely + initialized and probably illegal state. nmodules + could be meaningful or not. Things are nasty. + + But as long as any API call that uses the + modules calls _AFsetupmodules() first (which + then calls disposemodules(), which can handle + this nastiness), we can restore the modules to + a sane initial state and things will be ok. + */ + + return AF_FAIL; + } + + /* + At this point modules and nmodules are almost completely + filled in (modules aren't actually connected to one + another), but buffer[n] and chunk[n].buf are still in + a null state. + + track->totalvframes and track->next[fv]frame have not yet been + set to a valid state. + */ + + /* + Now go through the modules: + + 1. Connect up the source/sink fields properly. + 2. Use the information left in the _AudioFormat field + of each chunk by setupmodules() along with the + "max_pull"/"max_push" module function to figure + out the biggest buffer size that could be needed. + */ + + /* filemod reports error here */ + track->filemodhappy = AF_TRUE; + maxbufsize = 0; + + if (h->access == _AF_READ_ACCESS) + { + track->ms.chunk[track->ms.nmodules].nframes = _AF_ATOMIC_NVFRAMES; + + for (i=track->ms.nmodules-1; i >= 0; i--) + { + _AFchunk *inc = &track->ms.chunk[i]; + _AFchunk *outc = &track->ms.chunk[i+1]; + + /* check bufsize needed for current output chunk */ + + bufsize = outc->nframes * _af_format_frame_size(&outc->f, AF_TRUE); + if (bufsize > maxbufsize) + maxbufsize = bufsize; + + if (i != 0) + { + /* Connect source pointer for this module. */ + + track->ms.module[i].u.pull.source = &track->ms.module[i-1]; + } + + /* + Determine inc->nframes from outc->nframes. + If the max_pull function is present, we use it, + otherwise we assume module does no weird + buffering or rate conversion. + */ + if (track->ms.module[i].mod->max_pull) + (*track->ms.module[i].mod->max_pull)(&track->ms.module[i]); + else + inc->nframes = outc->nframes; + } + + if (!track->filemodhappy) + return AF_FAIL; + + /* + Check bufsize needed for filemod's input chunk + (intermediate buffer) based on an uncompressed + (output chunk) framesize. + */ + + { + _AFmoduleinst *filemod = &track->ms.module[0]; + bufsize = filemod->inc->nframes * + _af_format_frame_size(&filemod->outc->f, AF_TRUE); + if (bufsize > maxbufsize) + maxbufsize = bufsize; + } + } + else + { + track->ms.chunk[0].nframes = _AF_ATOMIC_NVFRAMES; + + for (i=0; i < track->ms.nmodules; i++) + { + _AFchunk *inc = &track->ms.chunk[i]; + _AFchunk *outc = &track->ms.chunk[i+1]; + + /* Check bufsize needed for current input chunk. */ + + bufsize = inc->nframes * _af_format_frame_size(&inc->f, AF_TRUE); + if (bufsize > maxbufsize) + maxbufsize = bufsize; + + if (i != track->ms.nmodules-1) + { + /* Connect sink pointer. */ + + track->ms.module[i].u.push.sink = &track->ms.module[i+1]; + } + + /* + Determine outc->nframes from inc->nframes. + If the max_push function is present, we use it, + otherwise we assume module does no weird + buffering or rate conversion. + */ + if (track->ms.module[i].mod->max_push) + (*track->ms.module[i].mod->max_push)(&track->ms.module[i]); + else + outc->nframes = inc->nframes; + } + + if (!track->filemodhappy) + return AF_FAIL; + + /* + Check bufsize needed for filemod's output chunk + (intermediate buffer) based on an uncompressed (input + chunk) framesize. + */ + + { + _AFmoduleinst *filemod = &track->ms.module[track->ms.nmodules-1]; + bufsize = filemod->outc->nframes * + _af_format_frame_size(&filemod->inc->f, AF_TRUE); + if (bufsize > maxbufsize) + maxbufsize = bufsize; + } + } + + /* + At this point everything is totally set up with the + modules except that the chunk buffers have not been + allocated, and thus buffer[n] and chunk[n].buf have + not been set. But now we know how big they should be + (maxbufsize). + + track->totalvframes and track->next[fv]frame have not + yet been set to a valid state. + */ + DEBG(printf("_AFsetupmodules: maxbufsize=%d\n", maxbufsize)); + + /* + One of these will get overwritten to point to user's + buffer. The other one will be allocated below (for file + read/write module). + */ + + track->ms.chunk[track->ms.nmodules].buf = AF_NULL; + track->ms.chunk[0].buf = AF_NULL; + + /* + One of these will be allocated for the file read/write + module The other will be completely unused. + */ + track->ms.buffer[track->ms.nmodules] = AF_NULL; + track->ms.buffer[0] = AF_NULL; + + /* + Now that we know how big buffers have to be, allocate + buffers and assign them to the module instances. + + Note that track->ms.chunk[nmodules].buf (reading) or + track->ms.chunk[0].buf (writing) will get overwritten + in _AFreadframes or _AFwriteframes to point to the + user's buffer. + + We allocate a buffer for track->ms.chunk[0].buf (reading) + or track->ms.chunk[nmodules].buf (writing) not because + it is needed for the modules to work, but as a working + buffer for the file reading / file writing modules. + + Also note that some modules may change their inc->buf or + outc->buf to point to something internal to the module + before calling their source or sink. + + So module code must be careful not to assume that a buffer + address will not change. Only for chunk[nmodules] + (reading) or chunk[0] (writing) is such trickery + disallowed. + */ + + if (h->access == _AF_READ_ACCESS) + for (i=track->ms.nmodules-1; i >= 0; i--) + { + if ((track->ms.buffer[i] = _af_malloc(maxbufsize)) == AF_NULL) + return AF_FAIL; + track->ms.chunk[i].buf = track->ms.buffer[i]; + } + else + for (i=1; i <= track->ms.nmodules; i++) + { + if ((track->ms.buffer[i] = _af_malloc(maxbufsize)) == AF_NULL) + return AF_FAIL; + track->ms.chunk[i].buf = track->ms.buffer[i]; + } + + /* + Hooray! The modules are now in a completely valid state. + But we can't set track->ms.modulesdirty to AF_FALSE yet... + + track->totalvframes and track->next[fv]frame have not yet been + set to a valid state. + */ + if (h->access == _AF_READ_ACCESS) + { + /* + Set total number of virtual frames based on new rate. + */ + if (track->totalfframes == -1) + track->totalvframes = -1; + else + track->totalvframes = track->totalfframes * + (track->v.sampleRate / track->f.sampleRate); + + /* + track->nextvframe and track->nextfframe: + + Currently our only indication of where we were + in the file is the variable fframepos, which + contains (in floating point) our offset in file + frames based on the old track->nextvframe. + + Now we get as close as we can to that original + position, given the new sampling rate. + */ + + track->nextfframe = (AFframecount) fframepos; + track->nextvframe = (AFframecount) (fframepos * (track->v.sampleRate / track->f.sampleRate)); + + /* + Now we can say the module system is in a + clean state. Any errors we get from here on + are reported but not critical. + */ + + track->ms.modulesdirty = AF_FALSE; + + /* Set up for next time. */ + track->ms.old_f_rate = track->f.sampleRate; + track->ms.old_v_rate = track->v.sampleRate; + + /* + Now we reset all the modules. + + If we are here because the user did afSeekFrame, + the actual seek will be performed here. + Otherwise this reset will set things up so that + we are at the same file offset we were at before + (or as close as possible given a change in + rate conversion). + */ + + /* Report error, but we're still clean. */ + if (AF_SUCCEED != resetmodules(h, track)) + return AF_FAIL; + } + /* Handle the case of _AF_WRITE_ACCESS. */ + else + { + /* + Don't mess with track->nextfframe or + track->totalfframes. Scale virtual frame position + relative to old virtual position. + */ + + track->nextvframe = track->totalvframes = + (AFframecount) (fframepos * (track->v.sampleRate / track->f.sampleRate)); + + /* + Now we can say the module system is in a + clean state. Any errors we get from here on + are reported but not critical. + */ + + track->ms.modulesdirty = AF_FALSE; + + /* Set up for next time. */ + track->ms.old_f_rate = track->f.sampleRate; + track->ms.old_v_rate = track->v.sampleRate; + } + + DEBG(_af_print_filehandle(h)); + +#ifdef DEBUG + for (i=track->ms.nmodules-1; i >= 0; i--) + { + _AFmoduleinst *inst = &track->ms.module[i]; + } + + { + /* Print format summary. */ + + printf("%s ->\n", (h->access == _AF_READ_ACCESS) ? "file" : "user"); + for (i=0; i < track->ms.nmodules; i++) + { + _AFmoduleinst *inst = &track->ms.module[i]; + _af_print_audioformat(&inst->inc->f); + printf(" -> %s(%d) ->\n", inst->mod->name, i); + } + _af_print_audioformat(&track->ms.chunk[track->ms.nmodules].f); + printf(" -> %s\n", (h->access != _AF_READ_ACCESS) ? "file" : "user"); + } +#endif + + /* + If we get here, then not only are the modules clean, but + whatever we did after the modules became clean succeeded. + So we gloat about our success. + */ + return AF_SUCCEED; +} + +/* + _AFinitmodules: this routine sets the initial value of the module- + related fields of the track when the track is first created. + + It also initializes the file read or file write modules. + See README.modules for info on this. + + Set "modulesdirty" flag on each track, so that the first + read/write/seek will set up the modules. +*/ +status _AFinitmodules (AFfilehandle h, _Track *track) +{ + track->channelMatrix = NULL; + + /* HACK: see private.h for a description of this hack */ + track->taper = 10; + track->dynamic_range = 100; + track->ratecvt_filter_params_set = AF_TRUE; + + track->ms.nmodules = 0; + track->ms.module = NULL; + track->ms.chunk = NULL; + track->ms.buffer = NULL; + + track->ms.modulesdirty = AF_TRUE; + + track->ms.filemodinst.valid = AF_FALSE; + track->ms.filemod_rebufferinst.valid = AF_FALSE; + + track->ms.rateconvertinst.valid = AF_FALSE; + track->ms.rateconvert_rebufferinst.valid = AF_FALSE; + + /* bogus value in case of bad code */ + track->ms.mustuseatomicnvframes = AF_TRUE; + + /* old_f_rate and old_v_rate MUST be set to <= 0 here. */ + track->ms.old_f_rate = -1; + track->ms.old_v_rate = -1; + + /* + Initialize extended-life file read or file write modules. + */ + if (AF_FAIL == initfilemods(track, h)) + return AF_FAIL; + + /* + Initialize extended-life rate convert modules (to NULL). + */ + initrateconvertmods(h->access == _AF_READ_ACCESS, track); + + /* + NOTE: Only now that we have initialized filemods is + track->totalfframes guaranteed to be ready. (The unit + cannot always tell how many frames are in the file.) + */ + + /* totalfframes could be -1. */ + track->totalvframes = track->totalfframes; + track->nextvframe = 0; + track->frames2ignore = 0; + + return AF_SUCCEED; +} + +/* + _AFfreemodules: + called once when filehandle is being freed + opposite of initmodules + free all modules, even the active ones +*/ +void _AFfreemodules (_Track *track) +{ + disposemodules(track); + disposefilemods(track); +#if 0 /* XXXmpruett rate conversion is deactivated for now */ + disposerateconvertmods(track); +#endif +} diff --git a/libaudiofile/modules.h b/libaudiofile/modules.h new file mode 100644 index 0000000..b06729d --- /dev/null +++ b/libaudiofile/modules.h @@ -0,0 +1,112 @@ +/* + Audio File Library + Copyright (C) 2000, Silicon Graphics, Inc. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307 USA. +*/ + +/* + modules.h +*/ + +#ifndef MODULES_H +#define MODULES_H + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#ifdef HAVE_INTTYPES_H +#include <inttypes.h> +#endif + +#include <sys/types.h> + +#include "audiofile.h" +#include "afinternal.h" + +typedef u_int8_t uchar1; +typedef u_int16_t uchar2; +typedef u_int32_t uchar3; +typedef u_int32_t uchar4; + +typedef int8_t schar1; +typedef int16_t schar2; +typedef int32_t schar3; +typedef int32_t schar4; + +typedef struct real_char3 { uchar1 c0; uchar1 c1; uchar1 c2; } real_char3; +typedef union char3u +{ + struct { schar4 i; } schar4; + struct { uchar4 i; } uchar4; + struct { schar3 i; } schar3; + struct { uchar3 i; } uchar3; + struct { real_char3 c3; schar1 pad; } real_char3_high; + struct { schar1 pad; real_char3 c3; } real_char3_low; + struct { uchar2 s0; uchar2 s1; } uchar2; + struct { schar2 s0; schar2 s1; } schar2; + struct { uchar1 c0; uchar1 c1; uchar1 c2; uchar1 c3; } uchar1; + struct { schar1 c0; schar1 c1; schar1 c2; schar1 c3; } schar1; +} char3u; + +typedef struct real_char8 +{ + uchar1 c0, c1, c2, c3, c4, c5, c6, c7; +} real_char8; + +typedef union char8u +{ + struct { schar4 i0, i1; } schar4; + struct { uchar4 i0, i1; } uchar4; + struct { schar2 s0, s1, s2, s3; } schar2; + struct { uchar2 s0, s1, s2, s3; } uchar2; + struct { schar1 c0, c1, c2, c3, c4, c5, c6, c7; } schar1; + struct { uchar1 c0, c1, c2, c3, c4, c5, c6, c7; } uchar1; +} char8u; + +#define AF_NULL ((void *) 0) + +/* + _AF_ATOMIC_NVFRAMES is NOT the maximum number of frames a module + can be requested to produce. + + This IS the maximum number of virtual (user) frames that will + be produced or processed per run of the modules. + + Modules can be requested more frames than this because of rate + conversion and rebuffering. +*/ + +#define _AF_ATOMIC_NVFRAMES 1024 + +AFframecount _AFpull (_AFmoduleinst *i, AFframecount nframes2pull); +void _AFpush (_AFmoduleinst *i, AFframecount nframes2push); +void _AFpushat (_AFmoduleinst *i, AFframecount startframe, bool stretchint, + AFframecount nframes2push); +void _AFsimplemodrun_pull (_AFmoduleinst *i); +void _AFsimplemodrun_push (_AFmoduleinst *i); +void _AFfreemodspec (_AFmoduleinst *i); + +/* _AFnewmodinst returns a structure, not a pointer. */ +_AFmoduleinst _AFnewmodinst (_AFmodule *mod); + +status _AFinitmodules (AFfilehandle h, _Track *trk); +status _AFsetupmodules (AFfilehandle h, _Track *trk); +status _AFsyncmodules (AFfilehandle h, _Track *trk); +void _AFfreemodules (_Track *trk); + +#endif /* MODULES_H */ diff --git a/libaudiofile/modules/Makefile.am b/libaudiofile/modules/Makefile.am new file mode 100644 index 0000000..02b8f91 --- /dev/null +++ b/libaudiofile/modules/Makefile.am @@ -0,0 +1,18 @@ +noinst_LTLIBRARIES = libmodules.la + +INCLUDES = -I$(srcdir)/.. + +libmodules_la_SOURCES = \ + g711.c g711.h \ + pcm.c pcm.h \ + msadpcm.c msadpcm.h \ + ima.c ima.h adpcm.c adpcm.h \ + rebuffer.c rebuffer.h \ + rebuffer.template + +# GNU gcc +# AM_CFLAGS = -Wall -g +# SGI MIPSpro cc +# AM_CFLAGS = -fullwarn -g +# No debugging. +AM_CFLAGS = -DNDEBUG diff --git a/libaudiofile/modules/Makefile.in b/libaudiofile/modules/Makefile.in new file mode 100644 index 0000000..7909c05 --- /dev/null +++ b/libaudiofile/modules/Makefile.in @@ -0,0 +1,337 @@ +# Makefile.in generated automatically by automake 1.5 from Makefile.am. + +# Copyright 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001 +# Free Software Foundation, Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +SHELL = @SHELL@ + +srcdir = @srcdir@ +top_srcdir = @top_srcdir@ +VPATH = @srcdir@ +prefix = @prefix@ +exec_prefix = @exec_prefix@ + +bindir = @bindir@ +sbindir = @sbindir@ +libexecdir = @libexecdir@ +datadir = @datadir@ +sysconfdir = @sysconfdir@ +sharedstatedir = @sharedstatedir@ +localstatedir = @localstatedir@ +libdir = @libdir@ +infodir = @infodir@ +mandir = @mandir@ +includedir = @includedir@ +oldincludedir = /usr/include +pkgdatadir = $(datadir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +top_builddir = ../.. + +ACLOCAL = @ACLOCAL@ +AUTOCONF = @AUTOCONF@ +AUTOMAKE = @AUTOMAKE@ +AUTOHEADER = @AUTOHEADER@ + +INSTALL = @INSTALL@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_HEADER = $(INSTALL_DATA) +transform = @program_transform_name@ +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +host_alias = @host_alias@ +host_triplet = @host@ +AMTAR = @AMTAR@ +AS = @AS@ +AUDIOFILE_MAJOR_VERSION = @AUDIOFILE_MAJOR_VERSION@ +AUDIOFILE_MICRO_VERSION = @AUDIOFILE_MICRO_VERSION@ +AUDIOFILE_MINOR_VERSION = @AUDIOFILE_MINOR_VERSION@ +AUDIOFILE_VERSION = @AUDIOFILE_VERSION@ +AUDIOFILE_VERSION_INFO = @AUDIOFILE_VERSION_INFO@ +AUDIO_LIB = @AUDIO_LIB@ +AWK = @AWK@ +CC = @CC@ +DEPDIR = @DEPDIR@ +DLLTOOL = @DLLTOOL@ +ECHO = @ECHO@ +EXEEXT = @EXEEXT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +LIBTOOL = @LIBTOOL@ +LN_S = @LN_S@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +PACKAGE = @PACKAGE@ +RANLIB = @RANLIB@ +STRIP = @STRIP@ +TEST_BIN = @TEST_BIN@ +VERSION = @VERSION@ +am__include = @am__include@ +am__quote = @am__quote@ +install_sh = @install_sh@ + +noinst_LTLIBRARIES = libmodules.la + +INCLUDES = -I$(srcdir)/.. + +libmodules_la_SOURCES = \ + g711.c g711.h \ + pcm.c pcm.h \ + msadpcm.c msadpcm.h \ + ima.c ima.h adpcm.c adpcm.h \ + rebuffer.c rebuffer.h \ + rebuffer.template + + +# GNU gcc +# AM_CFLAGS = -Wall -g +# SGI MIPSpro cc +# AM_CFLAGS = -fullwarn -g +# No debugging. +AM_CFLAGS = -DNDEBUG +subdir = libaudiofile/modules +mkinstalldirs = $(SHELL) $(top_srcdir)/mkinstalldirs +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +LTLIBRARIES = $(noinst_LTLIBRARIES) + +libmodules_la_LDFLAGS = +libmodules_la_LIBADD = +am_libmodules_la_OBJECTS = g711.lo pcm.lo msadpcm.lo ima.lo adpcm.lo \ + rebuffer.lo +libmodules_la_OBJECTS = $(am_libmodules_la_OBJECTS) + +DEFS = @DEFS@ +DEFAULT_INCLUDES = -I. -I$(srcdir) -I$(top_builddir) +CPPFLAGS = @CPPFLAGS@ +LDFLAGS = @LDFLAGS@ +LIBS = @LIBS@ +depcomp = $(SHELL) $(top_srcdir)/depcomp +@AMDEP_TRUE@DEP_FILES = $(DEPDIR)/adpcm.Plo $(DEPDIR)/g711.Plo \ +@AMDEP_TRUE@ $(DEPDIR)/ima.Plo $(DEPDIR)/msadpcm.Plo \ +@AMDEP_TRUE@ $(DEPDIR)/pcm.Plo $(DEPDIR)/rebuffer.Plo +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +LTCOMPILE = $(LIBTOOL) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) \ + $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +CCLD = $(CC) +LINK = $(LIBTOOL) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(AM_LDFLAGS) $(LDFLAGS) -o $@ +CFLAGS = @CFLAGS@ +DIST_SOURCES = $(libmodules_la_SOURCES) +DIST_COMMON = Makefile.am Makefile.in +SOURCES = $(libmodules_la_SOURCES) + +all: all-am + +.SUFFIXES: +.SUFFIXES: .c .lo .o .obj + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs + +distclean-libtool: + -rm -f libtool +$(srcdir)/Makefile.in: Makefile.am $(top_srcdir)/configure.in $(ACLOCAL_M4) + cd $(top_srcdir) && \ + $(AUTOMAKE) --gnu libaudiofile/modules/Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + cd $(top_builddir) && \ + CONFIG_HEADERS= CONFIG_LINKS= \ + CONFIG_FILES=$(subdir)/$@ $(SHELL) ./config.status + +clean-noinstLTLIBRARIES: + -test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES) +libmodules.la: $(libmodules_la_OBJECTS) $(libmodules_la_DEPENDENCIES) + $(LINK) $(libmodules_la_LDFLAGS) $(libmodules_la_OBJECTS) $(libmodules_la_LIBADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) core *.core + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/adpcm.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/g711.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/ima.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/msadpcm.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/pcm.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/rebuffer.Plo@am__quote@ + +distclean-depend: + -rm -rf $(DEPDIR) + +.c.o: +@AMDEP_TRUE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@ depfile='$(DEPDIR)/$*.Po' tmpdepfile='$(DEPDIR)/$*.TPo' @AMDEPBACKSLASH@ +@AMDEP_TRUE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ + $(COMPILE) -c `test -f $< || echo '$(srcdir)/'`$< + +.c.obj: +@AMDEP_TRUE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@ depfile='$(DEPDIR)/$*.Po' tmpdepfile='$(DEPDIR)/$*.TPo' @AMDEPBACKSLASH@ +@AMDEP_TRUE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ + $(COMPILE) -c `cygpath -w $<` + +.c.lo: +@AMDEP_TRUE@ source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@ depfile='$(DEPDIR)/$*.Plo' tmpdepfile='$(DEPDIR)/$*.TPlo' @AMDEPBACKSLASH@ +@AMDEP_TRUE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ + $(LTCOMPILE) -c -o $@ `test -f $< || echo '$(srcdir)/'`$< +CCDEPMODE = @CCDEPMODE@ +uninstall-info-am: + +tags: TAGS + +ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) + list='$(SOURCES) $(HEADERS) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + mkid -fID $$unique $(LISP) + +TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + tags=; \ + here=`pwd`; \ + list='$(SOURCES) $(HEADERS) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + test -z "$(ETAGS_ARGS)$$unique$(LISP)$$tags" \ + || etags $(ETAGS_ARGS) $$tags $$unique $(LISP) + +GTAGS: + here=`CDPATH=: && cd $(top_builddir) && pwd` \ + && cd $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) $$here + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH + +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) + +top_distdir = ../.. +distdir = $(top_distdir)/$(PACKAGE)-$(VERSION) + +distdir: $(DISTFILES) + @for file in $(DISTFILES); do \ + if test -f $$file; then d=.; else d=$(srcdir); fi; \ + dir=`echo "$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test "$$dir" != "$$file" && test "$$dir" != "."; then \ + $(mkinstalldirs) "$(distdir)/$$dir"; \ + fi; \ + if test -d $$d/$$file; then \ + cp -pR $$d/$$file $(distdir) \ + || exit 1; \ + else \ + test -f $(distdir)/$$file \ + || cp -p $$d/$$file $(distdir)/$$file \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +all-am: Makefile $(LTLIBRARIES) + +installdirs: + +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + `test -z '$(STRIP)' || \ + echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -rm -f Makefile $(CONFIG_CLEAN_FILES) stamp-h stamp-h[0-9]* + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-am + +clean-am: clean-generic clean-libtool clean-noinstLTLIBRARIES \ + mostlyclean-am + +distclean: distclean-am + +distclean-am: clean-am distclean-compile distclean-depend \ + distclean-generic distclean-libtool distclean-tags + +dvi: dvi-am + +dvi-am: + +info: info-am + +info-am: + +install-data-am: + +install-exec-am: + +install-info: install-info-am + +install-man: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool + +uninstall-am: uninstall-info-am + +.PHONY: GTAGS all all-am check check-am clean clean-generic \ + clean-libtool clean-noinstLTLIBRARIES distclean \ + distclean-compile distclean-depend distclean-generic \ + distclean-libtool distclean-tags distdir dvi dvi-am info \ + info-am install install-am install-data install-data-am \ + install-exec install-exec-am install-info install-info-am \ + install-man install-strip installcheck installcheck-am \ + installdirs maintainer-clean maintainer-clean-generic \ + mostlyclean mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool tags uninstall uninstall-am \ + uninstall-info-am + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/libaudiofile/modules/adpcm.c b/libaudiofile/modules/adpcm.c new file mode 100644 index 0000000..9ca7305 --- /dev/null +++ b/libaudiofile/modules/adpcm.c @@ -0,0 +1,248 @@ +/*********************************************************** +Copyright 1992 by Stichting Mathematisch Centrum, Amsterdam, The +Netherlands. + + All Rights Reserved + +Permission to use, copy, modify, and distribute this software and its +documentation for any purpose and without fee is hereby granted, +provided that the above copyright notice appear in all copies and that +both that copyright notice and this permission notice appear in +supporting documentation, and that the names of Stichting Mathematisch +Centrum or CWI not be used in advertising or publicity pertaining to +distribution of the software without specific, written prior permission. + +STICHTING MATHEMATISCH CENTRUM DISCLAIMS ALL WARRANTIES WITH REGARD TO +THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS, IN NO EVENT SHALL STICHTING MATHEMATISCH CENTRUM 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. + +******************************************************************/ + +/* +** Intel/DVI ADPCM coder/decoder. +** +** The algorithm for this coder was taken from the IMA Compatability Project +** proceedings, Vol 2, Number 2; May 1992. +** +** Version 1.2, 18-Dec-92. +** +** Change log: +** - Fixed a stupid bug, where the delta was computed as +** stepsize*code/4 in stead of stepsize*(code+0.5)/4. +** - There was an off-by-one error causing it to pick +** an incorrect delta once in a blue moon. +** - The NODIVMUL define has been removed. Computations are now always done +** using shifts, adds and subtracts. It turned out that, because the standard +** is defined using shift/add/subtract, you needed bits of fixup code +** (because the div/mul simulation using shift/add/sub made some rounding +** errors that real div/mul don't make) and all together the resultant code +** ran slower than just using the shifts all the time. +** - Changed some of the variable names to be more meaningful. +*/ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <sys/types.h> +#include <stdio.h> /*DBG*/ + +#include "adpcm.h" + +/* Intel ADPCM step variation table */ +static const int indexTable[16] = +{ + -1, -1, -1, -1, 2, 4, 6, 8, + -1, -1, -1, -1, 2, 4, 6, 8, +}; + +static const int stepsizeTable[89] = +{ + 7, 8, 9, 10, 11, 12, 13, 14, 16, 17, + 19, 21, 23, 25, 28, 31, 34, 37, 41, 45, + 50, 55, 60, 66, 73, 80, 88, 97, 107, 118, + 130, 143, 157, 173, 190, 209, 230, 253, 279, 307, + 337, 371, 408, 449, 494, 544, 598, 658, 724, 796, + 876, 963, 1060, 1166, 1282, 1411, 1552, 1707, 1878, 2066, + 2272, 2499, 2749, 3024, 3327, 3660, 4026, 4428, 4871, 5358, + 5894, 6484, 7132, 7845, 8630, 9493, 10442, 11487, 12635, 13899, + 15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794, 32767 +}; + +void _af_adpcm_coder (int16_t *indata, u_int8_t *outdata, int len, + struct adpcm_state *state) +{ + int16_t *inp; /* Input buffer pointer */ + u_int8_t *outp; /* Output buffer pointer */ + int val; /* Current input sample value */ + int sign; /* Current adpcm sign bit */ + int delta; /* Current adpcm output value */ + int diff; /* Difference between val and valprev */ + int step; /* Stepsize */ + int valpred; /* Predicted output value */ + int vpdiff; /* Current change to valpred */ + int index; /* Current step change index */ + int outputbuffer; /* place to keep previous 4-bit value */ + int bufferstep; /* toggle between outputbuffer/output */ + + outp = outdata; + inp = indata; + + valpred = state->valprev; + index = state->index; + step = stepsizeTable[index]; + + bufferstep = 1; + + for ( ; len > 0 ; len-- ) { + val = *inp++; + + /* Step 1 - compute difference with previous value */ + diff = val - valpred; + sign = (diff < 0) ? 8 : 0; + if ( sign ) diff = (-diff); + + /* Step 2 - Divide and clamp */ + /* Note: + ** This code *approximately* computes: + ** delta = diff*4/step; + ** vpdiff = (delta+0.5)*step/4; + ** but in shift step bits are dropped. The net result of this is + ** that even if you have fast mul/div hardware you cannot put it to + ** good use since the fixup would be too expensive. + */ + delta = 0; + vpdiff = (step >> 3); + + if ( diff >= step ) { + delta = 4; + diff -= step; + vpdiff += step; + } + step >>= 1; + if ( diff >= step ) { + delta |= 2; + diff -= step; + vpdiff += step; + } + step >>= 1; + if ( diff >= step ) { + delta |= 1; + vpdiff += step; + } + + /* Step 3 - Update previous value */ + if ( sign ) + valpred -= vpdiff; + else + valpred += vpdiff; + + /* Step 4 - Clamp previous value to 16 bits */ + if ( valpred > 32767 ) + valpred = 32767; + else if ( valpred < -32768 ) + valpred = -32768; + + /* Step 5 - Assemble value, update index and step values */ + delta |= sign; + + index += indexTable[delta]; + if ( index < 0 ) index = 0; + if ( index > 88 ) index = 88; + step = stepsizeTable[index]; + + /* Step 6 - Output value */ + if ( bufferstep ) { + outputbuffer = delta & 0x0f; + } else { + *outp++ = ((delta << 4) & 0xf0) | outputbuffer; + } + bufferstep = !bufferstep; + } + + /* Output last step, if needed */ + if ( !bufferstep ) + *outp++ = outputbuffer; + + state->valprev = valpred; + state->index = index; +} + +void _af_adpcm_decoder (u_int8_t *indata, int16_t *outdata, int len, + struct adpcm_state *state) +{ + u_int8_t *inp; /* Input buffer pointer */ + int16_t *outp; /* output buffer pointer */ + int sign; /* Current adpcm sign bit */ + int delta; /* Current adpcm output value */ + int step; /* Stepsize */ + int valpred; /* Predicted value */ + int vpdiff; /* Current change to valpred */ + int index; /* Current step change index */ + int inputbuffer; /* place to keep next 4-bit value */ + int bufferstep; /* toggle between inputbuffer/input */ + + outp = outdata; + inp = indata; + + valpred = state->valprev; + index = state->index; + step = stepsizeTable[index]; + + bufferstep = 0; + + for ( ; len > 0 ; len-- ) { + + /* Step 1 - get the delta value */ + if ( bufferstep ) { + delta = (inputbuffer >> 4) & 0xf; + } else { + inputbuffer = *inp++; + delta = inputbuffer & 0xf; + } + bufferstep = !bufferstep; + + /* Step 2 - Find new index value (for later) */ + index += indexTable[delta]; + if ( index < 0 ) index = 0; + if ( index > 88 ) index = 88; + + /* Step 3 - Separate sign and magnitude */ + sign = delta & 8; + delta = delta & 7; + + /* Step 4 - Compute difference and new predicted value */ + /* + ** Computes 'vpdiff = (delta+0.5)*step/4', but see comment + ** in adpcm_coder. + */ + vpdiff = step >> 3; + if ( delta & 4 ) vpdiff += step; + if ( delta & 2 ) vpdiff += step>>1; + if ( delta & 1 ) vpdiff += step>>2; + + if ( sign ) + valpred -= vpdiff; + else + valpred += vpdiff; + + /* Step 5 - clamp output value */ + if ( valpred > 32767 ) + valpred = 32767; + else if ( valpred < -32768 ) + valpred = -32768; + + /* Step 6 - Update step value */ + step = stepsizeTable[index]; + + /* Step 7 - Output value */ + *outp++ = valpred; + } + + state->valprev = valpred; + state->index = index; +} diff --git a/libaudiofile/modules/adpcm.h b/libaudiofile/modules/adpcm.h new file mode 100644 index 0000000..885893c --- /dev/null +++ b/libaudiofile/modules/adpcm.h @@ -0,0 +1,26 @@ +/* +** adpcm.h - include file for adpcm coder. +** +** Version 1.0, 7-Jul-92. +*/ + +#ifndef ADPCM_H +#define ADPCM_H + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#ifdef HAVE_INTTYPES_H +#include <inttypes.h> +#endif + +struct adpcm_state { + short valprev; /* Previous output value */ + char index; /* Index into stepsize table */ +}; + +void _af_adpcm_coder (int16_t [], u_int8_t [], int, struct adpcm_state *); +void _af_adpcm_decoder (u_int8_t [], int16_t [], int, struct adpcm_state *); + +#endif /* ADPCM_H */ diff --git a/libaudiofile/modules/g711.c b/libaudiofile/modules/g711.c new file mode 100644 index 0000000..d48ffda --- /dev/null +++ b/libaudiofile/modules/g711.c @@ -0,0 +1,346 @@ +/* + Audio File Library + Copyright (C) 2000-2001, Silicon Graphics, Inc. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307 USA. +*/ + +/* + g711.c +*/ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <errno.h> +#include <string.h> +#include <assert.h> + +#include <audiofile.h> + +#include "afinternal.h" +#include "modules.h" +#include "units.h" +#include "compression.h" +#include "g711.h" +#include "byteorder.h" +#include "util.h" + +#include "../g711.h" + +#define CHNK(X) + +static void ulaw2linear_buf (unsigned char *ulaw, signed short int *linear, + int nsamples) +{ + int i; + for (i=0; i < nsamples; i++) + { + linear[i] = _af_ulaw2linear(ulaw[i]); + } +} + +static void linear2ulaw_buf (signed short int *linear, unsigned char *ulaw, + int nsamples) +{ + int i; + for (i=0; i < nsamples; i++) + { + ulaw[i] = _af_linear2ulaw(linear[i]); + } +} + +static void alaw2linear_buf (unsigned char *alaw, signed short int *linear, + int nsamples) +{ + int i; + for (i=0; i < nsamples; i++) + { + linear[i] = _af_alaw2linear(alaw[i]); + } +} + +static void linear2alaw_buf (signed short int *linear, unsigned char *alaw, + int nsamples) +{ + int i; + for (i=0; i < nsamples; i++) + { + alaw[i] = _af_linear2alaw(linear[i]); + } +} + +bool _af_g711_format_ok (_AudioFormat *f) +{ + if (f->sampleFormat != AF_SAMPFMT_TWOSCOMP || f->sampleWidth != 16) + { + _af_error(AF_BAD_COMPRESSION, + "G711 compression requires 16-bit signed integer format"); + f->sampleFormat = AF_SAMPFMT_TWOSCOMP; + f->sampleWidth = 16; + /* non-fatal */ + } + + if (f->byteOrder != AF_BYTEORDER_BIGENDIAN) + { + _af_error(AF_BAD_COMPRESSION, + "G711 compression requires big endian format"); + f->byteOrder = AF_BYTEORDER_BIGENDIAN; + /* non-fatal */ + } + + return AF_TRUE; +} + +static _AFmodule g711compress, g711decompress; + +typedef unsigned char g711samp; + +typedef struct g711_data +{ + _Track *trk; + AFvirtualfile *fh; + bool seekok; + + /* saved_fpos_next_frame and saved_nextfframe apply only to writing. */ + int saved_fpos_next_frame; + int saved_nextfframe; +} g711_data; + +static void g711compressdescribe (_AFmoduleinst *i) +{ + g711_data *d = (g711_data *)i->modspec; + i->outc->f.compressionType = d->trk->f.compressionType; +} + +_AFmoduleinst _AFg711initcompress (_Track *trk, AFvirtualfile *fh, bool seekok, + bool headerless, AFframecount *chunkframes) +{ + _AFmoduleinst ret = _AFnewmodinst(&g711compress); + g711_data *d; + + d = (g711_data *) _af_malloc(sizeof (g711_data)); + + d->trk = trk; + d->fh = fh; + d->seekok = seekok; + + d->trk->fpos_next_frame = d->trk->fpos_first_frame; + + ret.modspec = d; + return ret; +} + +static void g711run_push (_AFmoduleinst *i) +{ + g711_data *d = (g711_data *)i->modspec; + AFframecount frames2write = i->inc->nframes; + AFframecount samps2write = i->inc->nframes * i->inc->f.channelCount; + int framesize = sizeof (g711samp) * (i->inc->f.channelCount); + AFframecount nfr; + + assert(d->trk->f.compressionType == AF_COMPRESSION_G711_ULAW || + d->trk->f.compressionType == AF_COMPRESSION_G711_ALAW); + + /* Compress frames into i->outc. */ + + if (d->trk->f.compressionType == AF_COMPRESSION_G711_ULAW) + linear2ulaw_buf(i->inc->buf, i->outc->buf, samps2write); + else + linear2alaw_buf(i->inc->buf, i->outc->buf, samps2write); + + /* Write the compressed data. */ + + nfr = af_fwrite(i->outc->buf, framesize, frames2write, d->fh); + + CHNK(printf("writing %d frames to g711 file\n", frames2write)); + + if (nfr != frames2write) + { + /* report error if we haven't already */ + if (d->trk->filemodhappy) + { + /* i/o error */ + if (nfr < 0) + _af_error(AF_BAD_WRITE, + "unable to write data (%s) -- " + "wrote %d out of %d frames", + strerror(errno), + d->trk->nextfframe + nfr, + d->trk->nextfframe + frames2write); + + /* usual disk full error */ + else + _af_error(AF_BAD_WRITE, + "unable to write data (disk full) -- " + "wrote %d out of %d frames", + d->trk->nextfframe + nfr, + d->trk->nextfframe + frames2write); + + d->trk->filemodhappy = AF_FALSE; + } + } + + d->trk->nextfframe += nfr; + d->trk->totalfframes = d->trk->nextfframe; + d->trk->fpos_next_frame += (nfr>0) ? nfr*framesize : 0; + + assert(!d->seekok || (af_ftell(d->fh) == d->trk->fpos_next_frame)); +} + +static void g711sync1 (_AFmoduleinst *i) +{ + g711_data *d = (g711_data *)i->modspec; + + d->saved_fpos_next_frame = d->trk->fpos_next_frame; + d->saved_nextfframe = d->trk->nextfframe; +} + +static void g711sync2 (_AFmoduleinst *i) +{ + g711_data *d = (g711_data *) i->modspec; + + /* sanity check. */ + assert(!d->seekok || (af_ftell(d->fh) == d->trk->fpos_next_frame)); + + /* We can afford to do an lseek just in case because sync2 is rare. */ + d->trk->fpos_after_data = af_ftell(d->fh); + + d->trk->fpos_next_frame = d->saved_fpos_next_frame; + d->trk->nextfframe = d->saved_nextfframe; +} + +static void g711decompressdescribe(_AFmoduleinst *i) +{ +/* XXXmpruett this is probably the correct way to go, but other things + need to be changed first. + + i->outc->f.byteOrder = _AF_BYTEORDER_NATIVE; +*/ + i->outc->f.compressionType = AF_COMPRESSION_NONE; + i->outc->f.compressionParams = AU_NULL_PVLIST; +} + +_AFmoduleinst _AFg711initdecompress (_Track *trk, AFvirtualfile *fh, + bool seekok, bool headerless, AFframecount *chunkframes) +{ + _AFmoduleinst ret = _AFnewmodinst(&g711decompress); + g711_data *d; + + d = (g711_data *) _af_malloc(sizeof(g711_data)); + + d->trk = trk; + d->fh = fh; + d->seekok = seekok; + + d->trk->f.compressionParams = AU_NULL_PVLIST; + + d->trk->frames2ignore = 0; + d->trk->fpos_next_frame = d->trk->fpos_first_frame; + + ret.modspec = d; + return ret; +} + +static void g711run_pull (_AFmoduleinst *i) +{ + g711_data *d = (g711_data *) i->modspec; + AFframecount frames2read = i->outc->nframes; + AFframecount samps2read = i->outc->nframes * i->outc->f.channelCount; + int framesize = sizeof (g711samp) * (i->outc->f.channelCount); + AFframecount nfr; + + /* Read the compressed frames. */ + + nfr = af_fread(i->inc->buf, framesize, frames2read, d->fh); + + /* Decompress into i->outc. */ + + if (d->trk->f.compressionType == AF_COMPRESSION_G711_ULAW) + ulaw2linear_buf(i->inc->buf, i->outc->buf, samps2read); + else + alaw2linear_buf(i->inc->buf, i->outc->buf, samps2read); + + CHNK(printf("reading %d frames from g711 file (got %d)\n", + frames2read, nfr)); + + d->trk->nextfframe += nfr; + d->trk->fpos_next_frame += (nfr>0) ? nfr*framesize : 0; + assert(!d->seekok || (af_ftell(d->fh) == d->trk->fpos_next_frame)); + + /* + If we got EOF from read, then we return the actual amount read. + + Complain only if there should have been more frames in the file. + */ + + if (d->trk->totalfframes != -1 && nfr != frames2read) + { + /* Report error if we haven't already */ + if (d->trk->filemodhappy) + { + _af_error(AF_BAD_READ, + "file missing data -- read %d frames, should be %d", + d->trk->nextfframe, + d->trk->totalfframes); + d->trk->filemodhappy = AF_FALSE; + } + } + + i->outc->nframes = nfr; +} + +static void g711reset1 (_AFmoduleinst *i) +{ +#ifdef DONE + g711_data *d = (g711_data *) i->modspec; +#endif + /* This function is supposed to be empty to fit into design. */ +} + +static void g711reset2 (_AFmoduleinst *i) +{ + g711_data *d = (g711_data *) i->modspec; + int framesize = sizeof (g711samp) * (i->inc->f.channelCount); + + d->trk->fpos_next_frame = + d->trk->fpos_first_frame + framesize * d->trk->nextfframe; + + d->trk->frames2ignore = 0; +} + +static _AFmodule g711compress = +{ + "g711compress", + g711compressdescribe, + AF_NULL, AF_NULL, + AF_NULL, AF_NULL, AF_NULL, + g711run_push, g711sync1, g711sync2, + AF_NULL, + _AFfreemodspec +}; + +static _AFmodule g711decompress = +{ + "g711decompress", + g711decompressdescribe, + AF_NULL, AF_NULL, + g711run_pull, g711reset1, g711reset2, + AF_NULL, AF_NULL, AF_NULL, + AF_NULL, + _AFfreemodspec +}; diff --git a/libaudiofile/modules/g711.h b/libaudiofile/modules/g711.h new file mode 100644 index 0000000..df10615 --- /dev/null +++ b/libaudiofile/modules/g711.h @@ -0,0 +1,39 @@ +/* + Audio File Library + Copyright (C) 2000, Silicon Graphics, Inc. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307 USA. +*/ + +/* + g711.h +*/ + +#ifndef MODULES_G711_H +#define MODULES_G711_H + +#include <audiofile.h> +#include "afinternal.h" + +bool _af_g711_format_ok (_AudioFormat *f); + +_AFmoduleinst _AFg711initcompress (_Track *trk, AFvirtualfile *fh, bool seekok, + bool headerless, AFframecount *chunkframes); + +_AFmoduleinst _AFg711initdecompress (_Track *trk, AFvirtualfile *fh, + bool seekok, bool headerless, AFframecount *chunkframes); + +#endif /* MODULES_G711_H */ diff --git a/libaudiofile/modules/ima.c b/libaudiofile/modules/ima.c new file mode 100644 index 0000000..a1cc391 --- /dev/null +++ b/libaudiofile/modules/ima.c @@ -0,0 +1,269 @@ +/* + Audio File Library + Copyright (C) 2001, Silicon Graphics, Inc. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307 USA. +*/ + +/* + ima.c + + This module implements IMA ADPCM compression for the Audio File + Library. +*/ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <errno.h> +#include <string.h> +#include <assert.h> + +#include <audiofile.h> + +#include "afinternal.h" +#include "modules.h" +#include "units.h" +#include "compression.h" +#include "byteorder.h" +#include "util.h" + +#include "adpcm.h" + +#define CHNK(X) + +static _AFmodule ima_adpcm_decompress; + +typedef struct +{ + _Track *track; + AFvirtualfile *fh; + + int blockAlign, samplesPerBlock; + AFframecount framesToIgnore; +} ima_adpcm_data; + +static int ima_adpcm_decode_block (ima_adpcm_data *ima, u_int8_t *encoded, + int16_t *decoded) +{ + int outputLength; + + struct adpcm_state state; + + outputLength = ima->samplesPerBlock * sizeof (int16_t) * + ima->track->f.channelCount; + + state.valprev = (encoded[1]<<8) | encoded[0]; + if (encoded[1] & 0x80) + state.valprev -= 0x10000; + + state.index = encoded[2]; + + *decoded++ = state.valprev; + + encoded += 4; + + _af_adpcm_decoder(encoded, decoded, ima->samplesPerBlock - 1, &state); + + return outputLength; +} + +bool _af_ima_adpcm_format_ok (_AudioFormat *f) +{ + if (f->channelCount != 1) + { + _af_error(AF_BAD_COMPRESSION, + "IMA ADPCM compression requires 1 channel"); + return AF_FALSE; + } + + if (f->sampleFormat != AF_SAMPFMT_TWOSCOMP || f->sampleWidth != 16) + { + _af_error(AF_BAD_COMPRESSION, + "IMA ADPCM compression requires 16-bit signed integer format"); + f->sampleFormat = AF_SAMPFMT_TWOSCOMP; + f->sampleWidth = 16; + /* non-fatal */ + } + + if (f->byteOrder != AF_BYTEORDER_BIGENDIAN) + { + _af_error(AF_BAD_COMPRESSION, + "IMA ADPCM compression requires big endian format"); + f->byteOrder = AF_BYTEORDER_BIGENDIAN; + /* non-fatal */ + } + + return AF_TRUE; +} + +static void ima_adpcm_decompress_describe (_AFmoduleinst *i) +{ +/* XXXmpruett this is probably the correct way to go, but other things + need to be changed first. + + i->outc->f.byteOrder = _AF_BYTEORDER_NATIVE; +*/ + i->outc->f.compressionType = AF_COMPRESSION_NONE; + i->outc->f.compressionParams = AU_NULL_PVLIST; +} + +_AFmoduleinst _af_ima_adpcm_init_decompress (_Track *track, AFvirtualfile *fh, + bool seekok, bool headerless, AFframecount *chunkframes) +{ + _AFmoduleinst ret = _AFnewmodinst(&ima_adpcm_decompress); + ima_adpcm_data *d; + AUpvlist pv; + int i; + long l; + void *v; + + assert(af_ftell(fh) == track->fpos_first_frame); + + d = (ima_adpcm_data *) _af_malloc(sizeof (ima_adpcm_data)); + + d->track = track; + d->fh = fh; + + d->track->frames2ignore = 0; + d->track->fpos_next_frame = d->track->fpos_first_frame; + + pv = d->track->f.compressionParams; + + if (_af_pv_getlong(pv, _AF_SAMPLES_PER_BLOCK, &l)) + d->samplesPerBlock = l; + else + _af_error(AF_BAD_CODEC_CONFIG, "samples per block not set"); + + if (_af_pv_getlong(pv, _AF_BLOCK_SIZE, &l)) + d->blockAlign = l; + else + _af_error(AF_BAD_CODEC_CONFIG, "block size not set"); + + *chunkframes = d->samplesPerBlock / d->track->f.channelCount; + + ret.modspec = d; + return ret; +} + +static void ima_adpcm_run_pull (_AFmoduleinst *module) +{ + ima_adpcm_data *d = (ima_adpcm_data *) module->modspec; + AFframecount frames2read = module->outc->nframes; + AFframecount nframes = 0; + int i, framesPerBlock, blockCount; + ssize_t blocksRead, bytesDecoded; + + framesPerBlock = d->samplesPerBlock / d->track->f.channelCount; + assert(module->outc->nframes % framesPerBlock == 0); + blockCount = module->outc->nframes / framesPerBlock; + + /* Read the compressed frames. */ + blocksRead = af_fread(module->inc->buf, d->blockAlign, blockCount, d->fh); + + /* This condition would indicate that the file is bad. */ + if (blocksRead < 0) + { + if (d->track->filemodhappy) + { + _af_error(AF_BAD_READ, "file missing data"); + d->track->filemodhappy = AF_FALSE; + } + } + + if (blocksRead < blockCount) + blockCount = blocksRead; + + /* Decompress into module->outc. */ + for (i=0; i<blockCount; i++) + { + bytesDecoded = ima_adpcm_decode_block(d, + (u_int8_t *) module->inc->buf + i * d->blockAlign, + (int16_t *) module->outc->buf + i * d->samplesPerBlock); + + nframes += framesPerBlock; + } + + d->track->nextfframe += nframes; + + if (blocksRead > 0) + d->track->fpos_next_frame += blocksRead * d->blockAlign; + + assert(af_ftell(d->fh) == d->track->fpos_next_frame); + + /* + If we got EOF from read, then we return the actual amount read. + + Complain only if there should have been more frames in the file. + */ + + if (d->track->totalfframes != -1 && nframes != frames2read) + { + /* Report error if we haven't already */ + if (d->track->filemodhappy) + { + _af_error(AF_BAD_READ, + "file missing data -- read %d frames, should be %d", + d->track->nextfframe, + d->track->totalfframes); + d->track->filemodhappy = AF_FALSE; + } + } + + module->outc->nframes = nframes; +} + +static void ima_adpcm_reset1 (_AFmoduleinst *i) +{ + ima_adpcm_data *d = (ima_adpcm_data *) i->modspec; + AFframecount nextTrackFrame; + int framesPerBlock; + + framesPerBlock = d->samplesPerBlock / d->track->f.channelCount; + + nextTrackFrame = d->track->nextfframe; + d->track->nextfframe = (nextTrackFrame / framesPerBlock) * + framesPerBlock; + + d->framesToIgnore = nextTrackFrame - d->track->nextfframe; + /* postroll = frames2ignore */ +} + +static void ima_adpcm_reset2 (_AFmoduleinst *i) +{ + ima_adpcm_data *d = (ima_adpcm_data *) i->modspec; + int framesPerBlock; + + framesPerBlock = d->samplesPerBlock / d->track->f.channelCount; + + d->track->fpos_next_frame = d->track->fpos_first_frame + + d->blockAlign * (d->track->nextfframe / framesPerBlock); + d->track->frames2ignore += d->framesToIgnore; + + assert(d->track->nextfframe % framesPerBlock == 0); +} + +static _AFmodule ima_adpcm_decompress = +{ + "ima_adpcm_decompress", + ima_adpcm_decompress_describe, + AF_NULL, AF_NULL, + ima_adpcm_run_pull, ima_adpcm_reset1, ima_adpcm_reset2, + AF_NULL, AF_NULL, AF_NULL, + AF_NULL, + _AFfreemodspec +}; diff --git a/libaudiofile/modules/ima.h b/libaudiofile/modules/ima.h new file mode 100644 index 0000000..3a2a351 --- /dev/null +++ b/libaudiofile/modules/ima.h @@ -0,0 +1,40 @@ +/* + Audio File Library + Copyright (C) 2001, Silicon Graphics, Inc. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307 USA. +*/ + +/* + afima.h + + This module declares the interface for the IMA ADPCM + compression module. +*/ + +#ifndef IMA_H +#define IMA_H + +#include <audiofile.h> + +#include "afinternal.h" + +bool _af_ima_adpcm_format_ok (_AudioFormat *f); + +_AFmoduleinst _af_ima_adpcm_init_decompress (_Track *track, AFvirtualfile *fh, + bool seekok, bool headerless, AFframecount *chunkframes); + +#endif /* IMA_H */ diff --git a/libaudiofile/modules/msadpcm.c b/libaudiofile/modules/msadpcm.c new file mode 100644 index 0000000..d6ed11c --- /dev/null +++ b/libaudiofile/modules/msadpcm.c @@ -0,0 +1,388 @@ +/* + Audio File Library + Copyright (C) 2001, Silicon Graphics, Inc. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307 USA. +*/ + +/* + msadpcm.c + + This module implements Microsoft ADPCM compression. +*/ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <errno.h> +#include <string.h> +#include <assert.h> + +#include <audiofile.h> + +#include "afinternal.h" +#include "modules.h" +#include "units.h" +#include "compression.h" +#include "byteorder.h" +#include "util.h" + +#include "msadpcm.h" + +#define CHNK(X) + +static _AFmodule ms_adpcm_decompress; + +typedef struct ms_adpcm_state +{ + u_int8_t predictor; + u_int16_t delta; + int16_t sample1, sample2; +} ms_adpcm_state; + +typedef struct ms_adpcm_data +{ + _Track *track; + AFvirtualfile *fh; + + /* + We set framesToIgnore during a reset1 and add it to + framesToIgnore during a reset2. + */ + AFframecount framesToIgnore; + + int blockAlign, samplesPerBlock; + + /* a is an array of numCoefficients ADPCM coefficient pairs. */ + int numCoefficients; + int16_t coefficients[256][2]; +} ms_adpcm_data; + +/* + Compute a linear PCM value from the given differential coded + value. +*/ +static int16_t ms_adpcm_decode_sample (struct ms_adpcm_state *state, + u_int8_t code, const int16_t *coefficient) +{ + const int32_t MAX_INT16 = 32767, MIN_INT16 = -32768; + const int32_t adaptive[] = + { + 230, 230, 230, 230, 307, 409, 512, 614, + 768, 614, 512, 409, 307, 230, 230, 230 + }; + int32_t linearSample, delta; + + linearSample = ((state->sample1 * coefficient[0]) + + (state->sample2 * coefficient[1])) / 256; + + if (code & 0x08) + linearSample += state->delta * (code-0x10); + else + linearSample += state->delta * code; + + /* Clamp linearSample to a signed 16-bit value. */ + if (linearSample < MIN_INT16) + linearSample = MIN_INT16; + else if (linearSample > MAX_INT16) + linearSample = MAX_INT16; + + delta = ((int32_t) state->delta * adaptive[code])/256; + if (delta < 16) + { + delta = 16; + } + + state->delta = delta; + state->sample2 = state->sample1; + state->sample1 = linearSample; + + /* + Because of earlier range checking, new_sample will be + in the range of an int16_t. + */ + return (int16_t) linearSample; +} + +/* Decode one block of MS ADPCM data. */ +static int ms_adpcm_decode_block (ms_adpcm_data *msadpcm, u_int8_t *encoded, + int16_t *decoded) +{ + int i, outputLength, samplesRemaining; + int channelCount; + int16_t *coefficient[2]; + ms_adpcm_state decoderState[2]; + ms_adpcm_state *state[2]; + + /* Calculate the number of bytes needed for decoded data. */ + outputLength = msadpcm->samplesPerBlock * sizeof (int16_t) * + msadpcm->track->f.channelCount; + + channelCount = msadpcm->track->f.channelCount; + + state[0] = &decoderState[0]; + if (channelCount == 2) + state[1] = &decoderState[1]; + else + state[1] = &decoderState[0]; + + /* Initialize predictor. */ + for (i=0; i<channelCount; i++) + { + state[i]->predictor = *encoded++; + assert(state[i]->predictor < msadpcm->numCoefficients); + } + + /* Initialize delta. */ + for (i=0; i<channelCount; i++) + { + state[i]->delta = (encoded[1]<<8) | encoded[0]; + encoded += sizeof (u_int16_t); + } + + /* Initialize first two samples. */ + for (i=0; i<channelCount; i++) + { + state[i]->sample1 = (encoded[1]<<8) | encoded[0]; + encoded += sizeof (u_int16_t); + } + + for (i=0; i<channelCount; i++) + { + state[i]->sample2 = (encoded[1]<<8) | encoded[0]; + encoded += sizeof (u_int16_t); + } + + coefficient[0] = msadpcm->coefficients[state[0]->predictor]; + coefficient[1] = msadpcm->coefficients[state[1]->predictor]; + + for (i=0; i<channelCount; i++) + *decoded++ = state[i]->sample2; + + for (i=0; i<channelCount; i++) + *decoded++ = state[i]->sample1; + + /* + The first two samples have already been 'decoded' in + the block header. + */ + samplesRemaining = (msadpcm->samplesPerBlock - 2) * + msadpcm->track->f.channelCount; + + while (samplesRemaining > 0) + { + u_int8_t code; + int16_t newSample; + + code = *encoded >> 4; + newSample = ms_adpcm_decode_sample(state[0], code, + coefficient[0]); + *decoded++ = newSample; + + code = *encoded & 0x0f; + newSample = ms_adpcm_decode_sample(state[1], code, + coefficient[1]); + *decoded++ = newSample; + + encoded++; + samplesRemaining -= 2; + } + + return outputLength; +} + +bool _af_ms_adpcm_format_ok (_AudioFormat *f) +{ + if (f->channelCount != 1 && f->channelCount != 2) + { + _af_error(AF_BAD_COMPRESSION, + "MS ADPCM compression requires 1 or 2 channels"); + return AF_FALSE; + } + + if (f->sampleFormat != AF_SAMPFMT_TWOSCOMP || f->sampleWidth != 16) + { + _af_error(AF_BAD_COMPRESSION, + "MS ADPCM compression requires 16-bit signed integer format"); + f->sampleFormat = AF_SAMPFMT_TWOSCOMP; + f->sampleWidth = 16; + /* non-fatal */ + } + + if (f->byteOrder != AF_BYTEORDER_BIGENDIAN) + { + _af_error(AF_BAD_COMPRESSION, + "MS ADPCM compression requires big endian format"); + f->byteOrder = AF_BYTEORDER_BIGENDIAN; + /* non-fatal */ + } + + return AF_TRUE; +} + +static void ms_adpcm_decompress_describe (_AFmoduleinst *i) +{ +/* XXXmpruett this is probably the correct way to go, but other things + need to be changed first. + + i->outc->f.byteOrder = _AF_BYTEORDER_NATIVE; +*/ + i->outc->f.compressionType = AF_COMPRESSION_NONE; + i->outc->f.compressionParams = AU_NULL_PVLIST; +} + +_AFmoduleinst _af_ms_adpcm_init_decompress (_Track *track, AFvirtualfile *fh, + bool seekok, bool headerless, AFframecount *chunkframes) +{ + _AFmoduleinst ret = _AFnewmodinst(&ms_adpcm_decompress); + ms_adpcm_data *d; + AUpvlist pv; + int i; + long l; + void *v; + + assert(af_ftell(fh) == track->fpos_first_frame); + + d = (ms_adpcm_data *) _af_malloc(sizeof (ms_adpcm_data)); + + d->track = track; + d->fh = fh; + + d->track->frames2ignore = 0; + d->track->fpos_next_frame = d->track->fpos_first_frame; + + pv = d->track->f.compressionParams; + if (_af_pv_getlong(pv, _AF_MS_ADPCM_NUM_COEFFICIENTS, &l)) + d->numCoefficients = l; + else + _af_error(AF_BAD_CODEC_CONFIG, "number of coefficients not set"); + + if (_af_pv_getptr(pv, _AF_MS_ADPCM_COEFFICIENTS, &v)) + memcpy(d->coefficients, v, sizeof (int16_t) * 256 * 2); + else + _af_error(AF_BAD_CODEC_CONFIG, "coefficient array not set"); + + if (_af_pv_getlong(pv, _AF_SAMPLES_PER_BLOCK, &l)) + d->samplesPerBlock = l; + else + _af_error(AF_BAD_CODEC_CONFIG, "samples per block not set"); + + if (_af_pv_getlong(pv, _AF_BLOCK_SIZE, &l)) + d->blockAlign = l; + else + _af_error(AF_BAD_CODEC_CONFIG, "block size not set"); + + *chunkframes = d->samplesPerBlock / d->track->f.channelCount; + + ret.modspec = d; + return ret; +} + +static void ms_adpcm_run_pull (_AFmoduleinst *module) +{ + ms_adpcm_data *d = (ms_adpcm_data *) module->modspec; + AFframecount frames2read = module->outc->nframes; + AFframecount nframes = 0; + int i, framesPerBlock, blockCount; + ssize_t blocksRead, bytesDecoded; + + framesPerBlock = d->samplesPerBlock / d->track->f.channelCount; + assert(module->outc->nframes % framesPerBlock == 0); + blockCount = module->outc->nframes / framesPerBlock; + + /* Read the compressed frames. */ + blocksRead = af_fread(module->inc->buf, d->blockAlign, blockCount, d->fh); + + /* Decompress into module->outc. */ + for (i=0; i<blockCount; i++) + { + bytesDecoded = ms_adpcm_decode_block(d, + (u_int8_t *) module->inc->buf + i * d->blockAlign, + (int16_t *) module->outc->buf + i * d->samplesPerBlock); + + nframes += framesPerBlock; + } + + d->track->nextfframe += nframes; + + if (blocksRead > 0) + d->track->fpos_next_frame += blocksRead * d->blockAlign; + + assert(af_ftell(d->fh) == d->track->fpos_next_frame); + + /* + If we got EOF from read, then we return the actual amount read. + + Complain only if there should have been more frames in the file. + */ + + if (d->track->totalfframes != -1 && nframes != frames2read) + { + /* Report error if we haven't already */ + if (d->track->filemodhappy) + { + _af_error(AF_BAD_READ, + "file missing data -- read %d frames, should be %d", + d->track->nextfframe, + d->track->totalfframes); + d->track->filemodhappy = AF_FALSE; + } + } + + module->outc->nframes = nframes; +} + +static void ms_adpcm_reset1 (_AFmoduleinst *i) +{ + ms_adpcm_data *d = (ms_adpcm_data *) i->modspec; + AFframecount nextTrackFrame; + int framesPerBlock; + + framesPerBlock = d->samplesPerBlock / d->track->f.channelCount; + + nextTrackFrame = d->track->nextfframe; + d->track->nextfframe = (nextTrackFrame / framesPerBlock) * + framesPerBlock; + + d->framesToIgnore = nextTrackFrame - d->track->nextfframe; + /* postroll = frames2ignore */ +} + +static void ms_adpcm_reset2 (_AFmoduleinst *i) +{ + ms_adpcm_data *d = (ms_adpcm_data *) i->modspec; + int framesPerBlock; + + framesPerBlock = d->samplesPerBlock / d->track->f.channelCount; + + d->track->fpos_next_frame = d->track->fpos_first_frame + + d->blockAlign * (d->track->nextfframe / framesPerBlock); + d->track->frames2ignore += d->framesToIgnore; + + assert(d->track->nextfframe % framesPerBlock == 0); +} + +static _AFmodule ms_adpcm_decompress = +{ + "ms_adpcm_decompress", + ms_adpcm_decompress_describe, + AF_NULL, AF_NULL, + ms_adpcm_run_pull, ms_adpcm_reset1, ms_adpcm_reset2, + AF_NULL, AF_NULL, AF_NULL, + AF_NULL, + _AFfreemodspec +}; diff --git a/libaudiofile/modules/msadpcm.h b/libaudiofile/modules/msadpcm.h new file mode 100644 index 0000000..b9b751e --- /dev/null +++ b/libaudiofile/modules/msadpcm.h @@ -0,0 +1,40 @@ +/* + Audio File Library + Copyright (C) 2001, Silicon Graphics, Inc. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307 USA. +*/ + +/* + msadpcm.h + + This module declares the interface for the Microsoft ADPCM + compression module. +*/ + +#ifndef MSADPCM_H +#define MSADPCM_H + +#include <audiofile.h> + +#include "afinternal.h" + +bool _af_ms_adpcm_format_ok (_AudioFormat *f); + +_AFmoduleinst _af_ms_adpcm_init_decompress (_Track *track, AFvirtualfile *fh, + bool seekok, bool headerless, AFframecount *chunkframes); + +#endif /* MSADPCM_H */ diff --git a/libaudiofile/modules/pcm.c b/libaudiofile/modules/pcm.c new file mode 100644 index 0000000..4691d90 --- /dev/null +++ b/libaudiofile/modules/pcm.c @@ -0,0 +1,274 @@ +/* + Audio File Library + Copyright (C) 2000, Silicon Graphics, Inc. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307 USA. +*/ + +/* + pcm.c - read and file write module for uncompressed data +*/ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <errno.h> +#include <assert.h> +#include <string.h> +#include <math.h> + +#include <audiofile.h> +#include "afinternal.h" +#include "compression.h" +#include "modules.h" +#include "util.h" +#include "print.h" + +#ifdef DEBUG +#define CHNK(X) (X) +#else +#define CHNK(X) +#endif + +bool _af_pcm_format_ok (_AudioFormat *f) +{ + assert(!isnan(f->pcm.slope)); + assert(!isnan(f->pcm.intercept)); + assert(!isnan(f->pcm.minClip)); + assert(!isnan(f->pcm.maxClip)); + + return AF_TRUE; +} + +/* + The pcm module does both reading and writing. +*/ + +static _AFmodule pcm; + +typedef struct pcm_data +{ + _Track *trk; + AFvirtualfile *fh; + bool seekok; + int bytes_per_frame; + + /* saved_fpos_next_frame and saved_nextfframe apply only to writing. */ + int saved_fpos_next_frame; + int saved_nextfframe; +} pcm_data; + +_AFmoduleinst _AFpcminitcompress (_Track *trk, AFvirtualfile *fh, bool seekok, + bool headerless, AFframecount *chunkframes) +{ + _AFmoduleinst ret = _AFnewmodinst(&pcm); + pcm_data *d; + + d = (pcm_data *) _af_malloc(sizeof (pcm_data)); + + d->trk = trk; + d->fh = fh; + d->seekok = seekok; + d->bytes_per_frame = _af_format_frame_size(&trk->f, AF_FALSE); + + d->trk->fpos_next_frame = d->trk->fpos_first_frame; + + ret.modspec = d; + return ret; +} + +static void pcmrun_push (_AFmoduleinst *i) +{ + pcm_data *d = (pcm_data *) i->modspec; + AFframecount frames2write = i->inc->nframes; + AFframecount n; + + /* + WARNING: due to the optimization explained at the end + of arrangemodules(), the pcm file module cannot depend + on the presence of the intermediate working buffer + which _AFsetupmodules usually allocates for file + modules in their input or output chunk (for reading or + writing, respectively). + + Fortunately, the pcm module has no need for such a buffer. + */ + + n = af_fwrite(i->inc->buf, d->bytes_per_frame, frames2write, d->fh); + + CHNK(printf("writing %" AF_FRAMECOUNT_PRINT_FMT " frames to pcm file\n", + frames2write)); + + if (n != frames2write) + { + /* Report error if we haven't already. */ + if (d->trk->filemodhappy) + { + /* I/O error */ + if (n < 0) + _af_error(AF_BAD_WRITE, + "unable to write data (%s) -- " + "wrote %d out of %d frames", + strerror(errno), + d->trk->nextfframe + n, + d->trk->nextfframe + frames2write); + /* usual disk full error */ + else + _af_error(AF_BAD_WRITE, + "unable to write data (disk full) -- " + "wrote %d out of %d frames", + d->trk->nextfframe + n, + d->trk->nextfframe + frames2write); + d->trk->filemodhappy = AF_FALSE; + } + } + + d->trk->nextfframe += n; + d->trk->totalfframes = d->trk->nextfframe; + d->trk->fpos_next_frame += (n>0) ? n*d->bytes_per_frame : 0; + assert(!d->seekok || (af_ftell(d->fh) == d->trk->fpos_next_frame)); +} + +static void pcmsync1 (_AFmoduleinst *i) +{ + pcm_data *d = (pcm_data *)i->modspec; + + d->saved_fpos_next_frame = d->trk->fpos_next_frame; + d->saved_nextfframe = d->trk->nextfframe; +} + +static void pcmsync2 (_AFmoduleinst *i) +{ + pcm_data *d = (pcm_data *)i->modspec; + + /* sanity check */ + assert(!d->seekok || (af_ftell(d->fh) == d->trk->fpos_next_frame)); + + /* We can afford to do an lseek just in case cuz sync2 is rare. */ + d->trk->fpos_after_data = af_ftell(d->fh); + + d->trk->fpos_next_frame = d->saved_fpos_next_frame; + d->trk->nextfframe = d->saved_nextfframe; +} + +_AFmoduleinst _AFpcminitdecompress (_Track *trk, AFvirtualfile *fh, bool seekok, + bool headerless, AFframecount *chunkframes) +{ + _AFmoduleinst ret = _AFnewmodinst(&pcm); + pcm_data *d; + + d = (pcm_data *) _af_malloc(sizeof (pcm_data)); + + d->trk = trk; + d->fh = fh; + d->seekok = seekok; + + d->trk->f.compressionParams = AU_NULL_PVLIST; + + d->bytes_per_frame = _af_format_frame_size(&trk->f, AF_FALSE); + + ret.modspec = d; + return ret; +} + +static void pcmrun_pull (_AFmoduleinst *i) +{ + pcm_data *d = (pcm_data *) i->modspec; + AFframecount n, frames2read = i->outc->nframes; + + /* + WARNING: Due to the optimization explained at the end of + arrangemodules(), the pcm file module cannot depend on + the presence of the intermediate working buffer which + _AFsetupmodules usually allocates for file modules in + their input or output chunk (for reading or writing, + respectively). + + Fortunately, the pcm module has no need for such a buffer. + */ + + /* + Limit the number of frames to be read to the number of + frames left in the track. + */ + if (d->trk->totalfframes != -1 && + d->trk->nextfframe + frames2read > d->trk->totalfframes) + { + frames2read = d->trk->totalfframes - d->trk->nextfframe; + } + + n = af_fread(i->outc->buf, d->bytes_per_frame, frames2read, d->fh); + + CHNK(printf("reading %" AF_FRAMECOUNT_PRINT_FMT " frames from pcm file " + "(got %" AF_FRAMECOUNT_PRINT_FMT ")\n", + frames2read, n)); + + d->trk->nextfframe += n; + d->trk->fpos_next_frame += (n>0) ? n*d->bytes_per_frame : 0; + assert(!d->seekok || (af_ftell(d->fh) == d->trk->fpos_next_frame)); + + /* + If we got EOF from read, then we return the actual amount read. + + Complain only if there should have been more frames in the file. + */ + + if (n != frames2read && d->trk->totalfframes != -1) + { + /* Report error if we haven't already. */ + if (d->trk->filemodhappy) + { + _af_error(AF_BAD_READ, + "file missing data -- read %d frames, " + "should be %d", + d->trk->nextfframe, + d->trk->totalfframes); + d->trk->filemodhappy = AF_FALSE; + } + } + + i->outc->nframes = n; +} + +static void pcmreset1 (_AFmoduleinst *i) +{ +#ifdef DONE + pcm_data *d = (pcm_data *) i->modspec; +#endif + /* This function is supposed to be empty to fit into design. */ +} + +static void pcmreset2 (_AFmoduleinst *i) +{ + pcm_data *d = (pcm_data *) i->modspec; + + d->trk->fpos_next_frame = d->trk->fpos_first_frame + + d->bytes_per_frame * d->trk->nextfframe; + + d->trk->frames2ignore = 0; +} + +static _AFmodule pcm = +{ + "pcm", + AF_NULL, + AF_NULL, AF_NULL, + pcmrun_pull, pcmreset1, pcmreset2, + pcmrun_push, pcmsync1, pcmsync2, + AF_NULL, + _AFfreemodspec +}; diff --git a/libaudiofile/modules/pcm.h b/libaudiofile/modules/pcm.h new file mode 100644 index 0000000..0c9a006 --- /dev/null +++ b/libaudiofile/modules/pcm.h @@ -0,0 +1,41 @@ +/* + Audio File Library + Copyright (C) 2000, Silicon Graphics, Inc. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307 USA. +*/ + +/* + pcm.h +*/ + +#ifndef MODULES_PCM_H +#define MODULES_PCM_H + +#include <audiofile.h> +#include "afinternal.h" +#include "compression.h" +#include "modules.h" + +bool _af_pcm_format_ok (_AudioFormat *f); + +_AFmoduleinst _AFpcminitcompress (_Track *trk, AFvirtualfile *fh, bool seekok, + bool headerless, AFframecount *chunkframes); + +_AFmoduleinst _AFpcminitdecompress (_Track *trk, AFvirtualfile *fh, bool seekok, + bool headerless, AFframecount *chunkframes); + +#endif /* MODULES_PCM_H */ diff --git a/libaudiofile/modules/rebuffer.c b/libaudiofile/modules/rebuffer.c new file mode 100644 index 0000000..ee9a187 --- /dev/null +++ b/libaudiofile/modules/rebuffer.c @@ -0,0 +1,58 @@ +/* + rebuffer.c +*/ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <math.h> + +#include <assert.h> + +#include <audiofile.h> +#include "afinternal.h" +#include "modules.h" +#include "pcm.h" +#include "util.h" +#include "units.h" + +#define CHNK(X) +#define DEBG(X) + +#ifndef min +#define min(a,b) ((a)<(b) ? (a) : (b)) +#endif + +#ifndef max +#define max(a,b) ((a)>(b) ? (a) : (b)) +#endif + +/* ===== REBUFFERING modules (use templates) */ + +/* defines module floatrebufferv2f and floatrebufferf2v */ + +#define PRFX(word) float ## word +#define INITFUNC(word) _af_initfloat ## word +#define NAMESTRING(word) "float" #word +#define TYPE float +#include "rebuffer.template" +#undef PRFX +#undef INITFUNC +#undef NAMESTRING +#undef TYPE + +/* defines module int2rebufferv2f and int2rebufferf2v */ + +#define PRFX(word) int2 ## word +#define INITFUNC(word) _af_initint2 ## word +#define NAMESTRING(word) "int2" #word +#define TYPE schar2 +#include "rebuffer.template" +#undef PRFX +#undef INITFUNC +#undef NAMESTRING +#undef TYPE diff --git a/libaudiofile/modules/rebuffer.h b/libaudiofile/modules/rebuffer.h new file mode 100644 index 0000000..4c6fae1 --- /dev/null +++ b/libaudiofile/modules/rebuffer.h @@ -0,0 +1,10 @@ +#ifndef REBUFFER_H +#define REBUFFER_H + +_AFmoduleinst _af_initfloatrebufferv2f (AFframecount nsamps, bool multiple_of); +_AFmoduleinst _af_initfloatrebufferf2v (AFframecount nsamps, bool multiple_of); + +_AFmoduleinst _af_initint2rebufferv2f (AFframecount nsamps, bool multiple_of); +_AFmoduleinst _af_initint2rebufferf2v (AFframecount nsamps, bool multiple_of); + +#endif /* REBUFFER_H */ diff --git a/libaudiofile/modules/rebuffer.template b/libaudiofile/modules/rebuffer.template new file mode 100644 index 0000000..ae994ca --- /dev/null +++ b/libaudiofile/modules/rebuffer.template @@ -0,0 +1,555 @@ +/* + rebuffer.template -- this is a TEMPLATE for a rebuffer module + + since C has no templates I use this method to get a type-independent + rebuffering module. This avoids enormous amounts of duplicated code. + + to use this file, #include it with the following tokens defined: + + #define PRFX(word) XXXX ## word + #define INITFUNC(word) initXXXX ## word + #define NAMESTRING(word) "XXXX" #word + #define TYPE YYYY + + where XXXX is replaced with the prefix you want for the module + name and YYYY is replaced with the type the routine works with. + All the other stuff above stays the same. Here is an example: + + #define PRFX(word) int2 ## word + #define INITFUNC(word) initint2 ## word + #define NAMESTRING(word) "int2" #word + #define TYPE signed short int + #include "rebuffer.template" + + I would just have you set two variables and do the above myself, + but the C preprocessor is so brain dead that it does not evaluate + actual arguments to preprocessor functions until after they are + substituted in! (and thus concatenated, and thus unable to be + substituted). + + The alternative to this (other than to use C++, which would make this + whole damn library so much easier to write and to read) is to have + large numbers of almost identical copies of the rebuffering routines + with a few type fields different. This is to be avoided. + + This code will then define a module called (using above example) + int2rebufferv2f and another called int2rebufferf2v. +*/ + +/* instance data of rebuffering modules */ + +typedef struct PRFX(rebuffer_data) +{ + bool multiple_of; /* TRUE=buffer to multiple-of not exactly... */ + AFframecount nsamps; /* # of fixed samples / multiple-of */ + TYPE *buf; /* buf of nsamps samples */ + long offset; /* see code for meaning */ + bool eof; /* (pull only) end of input stream reached */ + bool sent_short_chunk; /* (pull only) end of output stream indicated */ + + TYPE *saved_buf; /* (push only) saved buffer */ + long saved_offset; /* (push only) saved buffer offset */ +} PRFX(rebuffer_data); + +#define REBUFFER_DATA PRFX(rebuffer_data) + +/* REBUFFER variable to fixed -- PUSH ONLY */ + +static void PRFX(rebufferv2fmax_push)(struct _AFmoduleinst *i) +{ + REBUFFER_DATA *d = i->modspec; + int nframes = d->nsamps/i->inc->f.channelCount; + + if (d->multiple_of) + /* actually, it can be less, but this is ok */ + i->outc->nframes = i->inc->nframes + nframes; + else + i->outc->nframes = nframes; +} + +static void PRFX(rebufferv2frun_push)(struct _AFmoduleinst *i) +{ + REBUFFER_DATA *d = i->modspec; + long samples2push = i->inc->nframes * i->inc->f.channelCount; + TYPE *inbufp = i->inc->buf; + + /* d->offset could be from 0 to nsamps. 0 is no saved samples, + nsamps is all saved samples (should never be nsamps though) + offset thus contains a count of the valid data samples */ + + assert(d->offset >= 0 && d->offset < d->nsamps); + + /* check that we will be able to push even one block */ + if (d->offset + samples2push >= d->nsamps) + { + /* push the currently buffered samples through */ + if (d->offset != 0) + memcpy(i->outc->buf, d->buf, sizeof(TYPE)*d->offset); + + if (d->multiple_of) + { + /* round down to nearest d->nsamps */ + int n = (((d->offset+samples2push) / d->nsamps) * d->nsamps); + + /* + Example with d->nsamps==5, d->offset==3, and samples2push=10. + B=a buffered sample + N=a new sample (pushed into module on this call) + Note that we group samples in groups of d->nsamps since they must + be pushed that way (any # of groups at a time): + + offset samples2push + |-||------------| + BBBNN NNNNN NNN.... + |-------| + n-offset + |----------| + n + + n is the number of samples we will push on this run. What's + left over (3 samples in this case) will get buffered for use + in the next run. + */ + + assert(n > d->offset); + memcpy((TYPE *)i->outc->buf + d->offset, inbufp, + sizeof(TYPE)*(n - d->offset)); /* fill in rest of outbuf */ + + _AFpush(i, n / i->outc->f.channelCount); + + inbufp += n - d->offset; + samples2push -= n - d->offset; + assert(samples2push >= 0); + d->offset = 0; /* the buffer is now empty */ + } + else + { + while (d->offset + samples2push >= d->nsamps) + { + int n = d->nsamps - d->offset; + /* + Same example as above. Everything is the same except now + we can only push one group of d->nsamps samples at a time, + so we must loop through groups, and n takes on several + values: + + offset samples2push + |-||------------| + BBBNN NNNNN NNN.... + || |---| + n n + + n says how many samples to take from the input chunk. + n first has the value d->nsamps - d->offset, and then it + always has the value d->nsamps. + */ + memcpy((TYPE *)i->outc->buf + d->offset, inbufp, + sizeof(TYPE)*n); /* fill in rest of outbuf */ + + _AFpush(i, d->nsamps / i->outc->f.channelCount); + + inbufp += n; + samples2push -= n; + assert(samples2push >= 0); + d->offset = 0; /* clear out the buffer */ + } + } + + /* if we pushed blocks, then we must have used all stored samples */ + assert(d->offset == 0); + } + + /* at this point: guaranteed that d->offset + samples2push < d->nsamps */ + assert(d->offset + samples2push < d->nsamps); + + /* save remaining samples in buffer */ + if ( samples2push != 0 ) + { + memcpy(d->buf+d->offset, inbufp, sizeof(TYPE)*samples2push); + d->offset += samples2push; + } + + assert(d->offset >= 0 && d->offset < d->nsamps); +} + +static void PRFX(rebufferv2fsync1)(struct _AFmoduleinst *i) +{ + REBUFFER_DATA *d = i->modspec; + + assert(d->offset >= 0 && d->offset < d->nsamps); + + /* + save all the samples and the offset so we can + restore our state later. + */ + memcpy(d->saved_buf, d->buf, sizeof(TYPE)*d->nsamps); + d->saved_offset = d->offset; +} + +static void PRFX(rebufferv2fsync2)(struct _AFmoduleinst *i) +{ + REBUFFER_DATA *d = i->modspec; + + /* d->offset could be from 0 to nsamps. 0 is no saved samples, + nsamps is all saved samples (should never be nsamps though) + offset thus contains a count of the valid data samples */ + + assert(d->offset >= 0 && d->offset < d->nsamps); + + /* + push the currently buffered samples through--even if there + are none! + + in other words, push a SHORT CHUNK -- see modules.c TOF for more info ! + */ + if (d->offset != 0) + { + memcpy(i->outc->buf, d->buf, sizeof(TYPE)*d->offset); + } + + _AFpush(i, d->offset / i->outc->f.channelCount); /* SHORT CHUNK */ + + /* restore state saved in sync1 */ + + memcpy(d->buf, d->saved_buf, sizeof(TYPE)*d->nsamps); + d->offset = d->saved_offset; + + assert(d->offset >= 0 && d->offset < d->nsamps); +} + +static void PRFX(rebufferv2ffree)(struct _AFmoduleinst *i) +{ + REBUFFER_DATA *d = i->modspec; + if (d->buf) + free(d->buf); + if (d->saved_buf) + free(d->saved_buf); +} + +static _AFmodule PRFX(rebufferv2f) = +{ + NAMESTRING(rebufferv2f), + AF_NULL, + AF_NULL, PRFX(rebufferv2fmax_push), + AF_NULL, AF_NULL, AF_NULL, + PRFX(rebufferv2frun_push), PRFX(rebufferv2fsync1), PRFX(rebufferv2fsync2), + AF_NULL, + PRFX(rebufferv2ffree) +}; + +_AFmoduleinst INITFUNC(rebufferv2f)(AFframecount nsamps, bool multiple_of) +{ + _AFmoduleinst ret = _AFnewmodinst(&PRFX(rebufferv2f)); + REBUFFER_DATA *d = _af_malloc(sizeof (REBUFFER_DATA)); + d->nsamps = nsamps; + d->offset = 0; + d->buf = _af_malloc(sizeof (TYPE) * nsamps); + d->multiple_of = multiple_of; + d->saved_buf = _af_malloc(sizeof (TYPE) * nsamps); + ret.modspec = d; + + DEBG(printf("%s nsamps=%d multiple_of=%d\n", NAMESTRING(rebufferv2f), + nsamps, multiple_of)); + + return(ret); +} + +/* ===== REBUFFER fixed to variable -- PULL ONLY */ + +static void PRFX(rebufferf2vmax_pull)(struct _AFmoduleinst *i) +{ + REBUFFER_DATA *d = i->modspec; + long nframes = d->nsamps / i->inc->f.channelCount; + + if (d->multiple_of) + /* actually, it can be less, but this is ok */ + i->inc->nframes = i->outc->nframes + nframes; + else + i->inc->nframes = nframes; +} + +static void PRFX(rebufferf2vrun_pull)(struct _AFmoduleinst *i) +{ + REBUFFER_DATA *d = i->modspec; + long samples2pull = i->outc->nframes * i->outc->f.channelCount; + TYPE *outbufp = i->outc->buf; + + /* d->offset could be from 0 to nsamps. 0=saved all samples, + nsamps=saved no samples (should never be 0 though) + offset is thus offset of first valid sample in buffer + */ + assert(d->offset > 0 && d->offset <= d->nsamps); + + /* + as explained TOF modules.c, a module should not pull more + frames from its input after receiving the short chunk, + otherwise it is an error. This checks if the next module + in the chain is attempting to do so: + */ + assert(!d->sent_short_chunk); + + /* + Here is a way of thinking about the stream of data being pulled + from this module (say d->nsamps=5, d->offset=2, samples2pull=11). + We arrange samples in groups of d->nsamps as they arrived from the + input stream (since the chunk size constraint comes only from + that side): + + X=old data long gone + B=data we got from d->buf + N=new data we have to pull from our input module + + samples2pull=11 + >-------------| + XXXXX XXBBB NNNNN NNNNN + |---| |---| |---| |---| + nsamps nsamps nsamps nsamps + + Think of samples2pull as a distance which shrinks as we satisfy the + request. The way it shrinks is that the left side of it (shown as '>' + above) moves gradually left until it meets the fixed right side + (shown as '|'), at which time we have completely satisfied the + output's request. + + As the diagram indicates, some of those samples are buffered up from + the last run (BBB), and some of those samples have to be pulled + from our input right now. + + If the '>' moves PAST the '|' to the right, then this means we have pulled + more samples that we needed for the output's request, and so we should + buffer up those samples for the next run_pull. + */ + + + /* ----- first try and use currently buffered samples */ + + CHNK(printf("%*.*s%s buffer holds %d samples\n", + i->margin, i->margin, "", i->mod->name, + d->nsamps-d->offset)); + + if (d->offset != d->nsamps) + { + int buffered = d->nsamps - d->offset; + int n = min(samples2pull, buffered); + memcpy(outbufp, d->buf+d->offset, sizeof(TYPE)*n); + CHNK(printf("%*.*s%s taking %d currently buffered samples\n", + i->margin, i->margin, "", i->mod->name, + n)); + /* + this may make samples2pull negative and outbufp invalid: + that's ok, it means we have some samples left over after + satisfying the output's request + */ + outbufp += buffered; + samples2pull -= buffered; + /* + we have taken n samples from the buffer so we indicate + that the buffer is that much smaller + */ + d->offset += n; + } + + CHNK(printf("%*.*s%s now samples2pull=%d\n", + i->margin, i->margin, "", i->mod->name, + samples2pull)); + + /* + at this point the picture from above looks something like: + + samples2pull=8 + >--------| + XXXXX XXBBB NNNNN NNNNN + |---| |---| |---| |---| + nsamps nsamps nsamps nsamps + + note that samples2pull has shrunk by the number of samples we + had buffered up. + + If it happened that samples2pull was originally less than the + number of samples we have buffered up, as in + + samples2pull=2 + >| + XXXXX XXBBB + |---| |---| + nsamps nsamps + + Then at this point things look like: + + samples2pull=-1 + |> + XXXXX XXBBB + |---| |---| + nsamps nsamps + + Which is just fine. It means we should re-save that 1 sample for + later use. + */ + + /* ----- then try and pull more samples from the source */ + + while (!d->eof && samples2pull > 0) + { + int req, got; + + /* + req is the number of "N" frames (from the pictures above) which + we want to pull at a time. + */ + if (d->multiple_of) + /* round samples2pull up to nearest d->nsamps */ + req = ( ( ((samples2pull-1)/d->nsamps) + 1) * d->nsamps); + else + req = d->nsamps; + + assert(req > 0); + + /* pull chunk(s) of data from source */ + + _AFpull(i, req / i->inc->f.channelCount); + + got = i->inc->nframes * i->inc->f.channelCount; /* may be short chunk */ + + if (got != req) /* short chunk on input */ + d->eof = AF_TRUE; + + memcpy(outbufp, i->inc->buf, sizeof(TYPE)*min(samples2pull, got)); + + /* + this may make samples2pull negative and outbufp invalid: + that's ok, it means we have some samples left over after + satisfying the output's request + */ + outbufp += got; + samples2pull -= got; + + /* we should only do loop once for multiple_of */ + + if (d->multiple_of) + assert(d->eof || samples2pull <= 0); + + if (samples2pull < 0) + { + /* + we pulled more frames than needed for the user's request + (and the loop is about to exit) so save the remaining samples + in d->buf. + */ + assert(d->offset==d->nsamps); /* if we pulled, buffer must be used up */ + + /* samples2pull is -(the number of samples the next mod didn't want) */ + d->offset = d->nsamps + samples2pull; + + assert(d->offset > 0 && d->offset <= d->nsamps); + + memcpy(d->buf+d->offset, (TYPE *)i->inc->buf+d->offset, + sizeof(TYPE)*(d->nsamps-d->offset)); + } + else + { + assert(d->offset==d->nsamps); /* if we pulled, buffer must be used up */ + } + } + + CHNK(printf("%*.*s%s ... and now samples2pull=%d\n", + i->margin, i->margin, "", i->mod->name, + samples2pull)); + + /* ----- + at this point we have done all we can to try and satisfy the output's + request. We may have hit eof on input. + + If samples2pull <= 0, we have pulled enough samples to satisfy the + request. + + If d->eof==AF_TRUE, then we hit EOF on input. + + If we did not hit end of file, then we must have satisfied the request. + If we did hit end of file, then we may or may not have satisfied the request. + + If we hit eof and we did not satisfy the request, then we push the + SHORT CHUNK, after which the module on our output is not allowed to + pull any more samples. + + Otherwise we save any samples left over into d->buf. + + Note that just because we hit EOF on input doesn't mean we're going to + push the short chunk on output right away. Because we buffer up samples + internally, there could be any number of run_push calls between hitting + eof on input and sending the short chunk on output. d->eof indicates + the first condition, d->send_short_chunk indicates the second. + */ + + if (d->eof && samples2pull > 0) /* EOF reached and not enough data */ + { + i->outc->nframes -= samples2pull / i->inc->f.channelCount; /* SHORT CHUNK out */ + d->sent_short_chunk = AF_TRUE; + assert(d->offset == d->nsamps); /* we must have used all buffered frames */ + } + else + { + /* !d->eof && samples2pull > 0 is impossible */ + assert(samples2pull <= 0); + + /* samples2pull is -(the number of samples the next mod didn't want) */ + assert(d->offset == d->nsamps + samples2pull); + } + assert(d->offset > 0 && d->offset <= d->nsamps); + + CHNK(printf("%*.*s%s ... and now buffer holds %d samples\n", + i->margin, i->margin, "", i->mod->name, + d->nsamps-d->offset)); +} + +static void PRFX(rebufferf2vreset1)(struct _AFmoduleinst *i) +{ + REBUFFER_DATA *d = i->modspec; + d->offset = d->nsamps; + d->eof = AF_FALSE; + d->sent_short_chunk = AF_FALSE; + assert(d->offset > 0 && d->offset <= d->nsamps); +} + +static void PRFX(rebufferf2vreset2)(struct _AFmoduleinst *i) +{ +#ifdef AF_DEBUG + REBUFFER_DATA *d = i->modspec; + assert(d->offset > 0 && d->offset <= d->nsamps); +#endif +} + +static void PRFX(rebufferf2vfree)(struct _AFmoduleinst *i) +{ + REBUFFER_DATA *d = i->modspec; + if (d->buf) + free(d->buf); +} + +static _AFmodule PRFX(rebufferf2v) = +{ + NAMESTRING(rebufferf2v), + AF_NULL, + PRFX(rebufferf2vmax_pull), AF_NULL, + PRFX(rebufferf2vrun_pull), PRFX(rebufferf2vreset1), PRFX(rebufferf2vreset2), + AF_NULL, AF_NULL, AF_NULL, + AF_NULL, + PRFX(rebufferf2vfree) +}; + +_AFmoduleinst INITFUNC(rebufferf2v)(AFframecount nsamps, bool multiple_of) +{ + _AFmoduleinst ret = _AFnewmodinst(&PRFX(rebufferf2v)); + REBUFFER_DATA *d = _af_malloc(sizeof (REBUFFER_DATA)); + d->nsamps = nsamps; + d->offset = d->nsamps; + d->buf = _af_malloc(sizeof (TYPE) * nsamps); + d->multiple_of = multiple_of; + d->eof = AF_FALSE; + d->sent_short_chunk = AF_FALSE; + ret.modspec = d; + + DEBG(printf("%s nsamps=%d multiple_of=%d\n", NAMESTRING(rebufferf2v), + nsamps, multiple_of)); + + return(ret); +} diff --git a/libaudiofile/next.c b/libaudiofile/next.c new file mode 100644 index 0000000..e8f9bca --- /dev/null +++ b/libaudiofile/next.c @@ -0,0 +1,266 @@ +/* + Audio File Library + Copyright (C) 1998-2000, Michael Pruett <michael@68k.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307 USA. +*/ + +/* + next.c + + This file contains routines for parsing NeXT/Sun .snd format sound + files. +*/ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <assert.h> +#include <sys/types.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#ifdef HAVE_INTTYPES_H +#include <inttypes.h> +#endif + +#include "next.h" +#include "audiofile.h" +#include "afinternal.h" +#include "track.h" +#include "util.h" +#include "setup.h" +#include "byteorder.h" + +int _af_next_compression_types[_AF_NEXT_NUM_COMPTYPES] = +{ + AF_COMPRESSION_G711_ULAW, + AF_COMPRESSION_G711_ALAW +}; + +_AFfilesetup _af_next_default_filesetup = +{ + _AF_VALID_FILESETUP, /* valid */ + AF_FILE_NEXTSND, /* fileFormat */ + AF_TRUE, /* trackSet */ + AF_TRUE, /* instrumentSet */ + AF_TRUE, /* miscellaneousSet */ + 1, /* trackCount */ + NULL, /* tracks */ + 0, /* instrumentCount */ + NULL, /* instruments */ + 0, /* miscellaneousCount */ + NULL /* miscellaneous */ +}; + +status _af_next_read_init (AFfilesetup setup, AFfilehandle file) +{ + u_int32_t id, offset, length, encoding, sampleRate, channelCount; + _Track *track; + + assert(file != NULL); + assert(file->fh != NULL); + + file->formatSpecific = NULL; + + file->instruments = NULL; + file->instrumentCount = 0; + + file->miscellaneous = NULL; + file->miscellaneousCount = 0; + + file->tracks = NULL; /* Allocate this later. */ + file->trackCount = 1; + + af_fseek(file->fh, 0, SEEK_SET); + + af_fread(&id, 4, 1, file->fh); + assert(!memcmp(&id, ".snd", 4)); + + af_fread(&offset, 4, 1, file->fh); + af_fread(&length, 4, 1, file->fh); + af_fread(&encoding, 4, 1, file->fh); + af_fread(&sampleRate, 4, 1, file->fh); + af_fread(&channelCount, 4, 1, file->fh); + + offset = BENDIAN_TO_HOST_INT32(offset); + length = BENDIAN_TO_HOST_INT32(length); + encoding = BENDIAN_TO_HOST_INT32(encoding); + sampleRate = BENDIAN_TO_HOST_INT32(sampleRate); + channelCount = BENDIAN_TO_HOST_INT32(channelCount); + +#ifdef DEBUG + printf("id, offset, length, encoding, sampleRate, channelCount:\n" + " %d %d %d %d %d %d\n", + id, offset, length, encoding, sampleRate, channelCount); +#endif + + if ((track = _af_track_new()) == NULL) + return AF_FAIL; + + file->tracks = track; + + track->f.byteOrder = AF_BYTEORDER_BIGENDIAN; + + /* Override the compression type later if necessary. */ + track->f.compressionType = AF_COMPRESSION_NONE; + + track->fpos_first_frame = offset; + track->data_size = af_flength(file->fh) - offset; + + switch (encoding) + { + case _AU_FORMAT_MULAW_8: + track->f.sampleWidth = 16; + track->f.sampleFormat = AF_SAMPFMT_TWOSCOMP; + track->f.compressionType = AF_COMPRESSION_G711_ULAW; + break; + case _AU_FORMAT_ALAW_8: + track->f.sampleWidth = 16; + track->f.sampleFormat = AF_SAMPFMT_TWOSCOMP; + track->f.compressionType = AF_COMPRESSION_G711_ALAW; + break; + case _AU_FORMAT_LINEAR_8: + track->f.sampleWidth = 8; + track->f.sampleFormat = AF_SAMPFMT_TWOSCOMP; + break; + case _AU_FORMAT_LINEAR_16: + track->f.sampleWidth = 16; + track->f.sampleFormat = AF_SAMPFMT_TWOSCOMP; + break; + case _AU_FORMAT_LINEAR_24: + track->f.sampleWidth = 24; + track->f.sampleFormat = AF_SAMPFMT_TWOSCOMP; + break; + case _AU_FORMAT_LINEAR_32: + track->f.sampleWidth = 32; + track->f.sampleFormat = AF_SAMPFMT_TWOSCOMP; + break; + case _AU_FORMAT_FLOAT: + track->f.sampleWidth = 32; + track->f.sampleFormat = AF_SAMPFMT_FLOAT; + break; + case _AU_FORMAT_DOUBLE: + track->f.sampleWidth = 64; + track->f.sampleFormat = AF_SAMPFMT_DOUBLE; + break; + + default: + /* + This encoding method is not recognized. + */ + _af_error(AF_BAD_SAMPFMT, "bad sample format"); + return AF_FAIL; + } + + _af_set_sample_format(&track->f, track->f.sampleFormat, track->f.sampleWidth); + + track->f.sampleRate = sampleRate; + track->f.channelCount = channelCount; + track->totalfframes = length / _af_format_frame_size(&track->f, AF_FALSE); + +#ifdef DEBUG + printf("_af_next_read_init\n"); + _af_print_filehandle(file); +#endif + + /* The file has been parsed successfully. */ + return AF_SUCCEED; +} + +bool _af_next_recognize (AFvirtualfile *fh) +{ + u_int8_t buffer[4]; + + af_fseek(fh, 0, SEEK_SET); + + if (af_fread(buffer, 1, 4, fh) != 4 || memcmp(buffer, ".snd", 4) != 0) + return AF_FALSE; + + return AF_TRUE; +} + +AFfilesetup _af_next_complete_setup (AFfilesetup setup) +{ + _TrackSetup *track; + + if (setup->trackSet && setup->trackCount != 1) + { + _af_error(AF_BAD_NUMTRACKS, "NeXT files must have exactly 1 track"); + return AF_NULL_FILESETUP; + } + + track = _af_filesetup_get_tracksetup(setup, AF_DEFAULT_TRACK); + if (track->f.sampleFormat == AF_SAMPFMT_UNSIGNED) + { + _af_error(AF_BAD_FILEFMT, "NeXT format does not support unsigned data"); + _af_set_sample_format(&track->f, AF_SAMPFMT_TWOSCOMP, track->f.sampleWidth); + } + + if (track->f.sampleFormat == AF_SAMPFMT_TWOSCOMP) + { + if (track->f.sampleWidth != 8 && + track->f.sampleWidth != 16 && + track->f.sampleWidth != 24 && + track->f.sampleWidth != 32) + { + _af_error(AF_BAD_WIDTH, "invalid sample width %d for NeXT file (only 8-, 16-, 24-, and 32-bit data are allowed)"); + return AF_NULL_FILESETUP; + } + } + + if (track->f.compressionType != AF_COMPRESSION_NONE && + track->f.compressionType != AF_COMPRESSION_G711_ULAW && + track->f.compressionType != AF_COMPRESSION_G711_ALAW) + { + _af_error(AF_BAD_NOT_IMPLEMENTED, "compression format not implemented for NeXT files"); + return AF_NULL_FILESETUP; + } + + if (track->f.byteOrder != AF_BYTEORDER_BIGENDIAN && track->byteOrderSet) + { + _af_error(AF_BAD_BYTEORDER, "NeXT format supports only big-endian data"); + track->f.byteOrder = AF_BYTEORDER_BIGENDIAN; + } + + if (track->aesDataSet) + { + _af_error(AF_BAD_FILESETUP, "NeXT files cannot have AES data"); + return AF_NULL_FILESETUP; + } + + if (track->markersSet && track->markerCount != 0) + { + _af_error(AF_BAD_FILESETUP, "NeXT format does not support markers"); + return AF_NULL_FILESETUP; + } + + if (setup->instrumentSet && setup->instrumentCount != 0) + { + _af_error(AF_BAD_FILESETUP, "NeXT format does not support instruments"); + return AF_NULL_FILESETUP; + } + + if (setup->miscellaneousSet && setup->miscellaneousCount != 0) + { + _af_error(AF_BAD_FILESETUP, "NeXT format does not support miscellaneous data"); + return AF_NULL_FILESETUP; + } + + return _af_filesetup_copy(setup, &_af_next_default_filesetup, AF_FALSE); +} diff --git a/libaudiofile/next.h b/libaudiofile/next.h new file mode 100644 index 0000000..a4dfcbd --- /dev/null +++ b/libaudiofile/next.h @@ -0,0 +1,74 @@ +/* + Audio File Library + Copyright (C) 1998-2000, Michael Pruett <michael@68k.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307 USA. +*/ + +/* + next.h + + This file contains headers and constants related to the NeXT/Sun + .snd audio file format. +*/ + +#include "afinternal.h" + +#ifndef NEXTSND_H +#define NEXTSND_H + +enum +{ + _AU_FORMAT_UNSPECIFIED = 0, + _AU_FORMAT_MULAW_8 = 1, /* CCITT G.711 mu-law 8-bit */ + _AU_FORMAT_LINEAR_8 = 2, + _AU_FORMAT_LINEAR_16 = 3, + _AU_FORMAT_LINEAR_24 = 4, + _AU_FORMAT_LINEAR_32 = 5, + _AU_FORMAT_FLOAT = 6, + _AU_FORMAT_DOUBLE = 7, + _AU_FORMAT_INDIRECT = 8, + _AU_FORMAT_NESTED = 9, + _AU_FORMAT_DSP_CORE = 10, + _AU_FORMAT_DSP_DATA_8 = 11, /* 8-bit fixed point */ + _AU_FORMAT_DSP_DATA_16 = 12, /* 16-bit fixed point */ + _AU_FORMAT_DSP_DATA_24 = 13, /* 24-bit fixed point */ + _AU_FORMAT_DSP_DATA_32 = 14, /* 32-bit fixed point */ + _AU_FORMAT_DISPLAY = 16, + _AU_FORMAT_MULAW_SQUELCH = 17, /* 8-bit mu-law, squelched */ + _AU_FORMAT_EMPHASIZED = 18, + _AU_FORMAT_COMPRESSED = 19, + _AU_FORMAT_COMPRESSED_EMPHASIZED = 20, + _AU_FORMAT_DSP_COMMANDS = 21, + _AU_FORMAT_DSP_COMMANDS_SAMPLES = 22, + _AU_FORMAT_ADPCM_G721 = 23, /* CCITT G.721 ADPCM 32 kbits/s */ + _AU_FORMAT_ADPCM_G722 = 24, /* CCITT G.722 ADPCM */ + _AU_FORMAT_ADPCM_G723_3 = 25, /* CCITT G.723 ADPCM 24 kbits/s */ + _AU_FORMAT_ADPCM_G723_5 = 26, /* CCITT G.723 ADPCM 40 kbits/s */ + _AU_FORMAT_ALAW_8 = 27, /* CCITT G.711 a-law */ + _AU_FORMAT_AES = 28, + _AU_FORMAT_DELTA_MULAW_8 = 29 +}; + +#define _AF_NEXT_NUM_COMPTYPES 2 + +bool _af_next_recognize (AFvirtualfile *fh); +status _af_next_read_init (AFfilesetup, AFfilehandle); +status _af_next_write_init (AFfilesetup, AFfilehandle); +status _af_next_update (AFfilehandle); +AFfilesetup _af_next_complete_setup (AFfilesetup); + +#endif diff --git a/libaudiofile/nextwrite.c b/libaudiofile/nextwrite.c new file mode 100644 index 0000000..a674ae0 --- /dev/null +++ b/libaudiofile/nextwrite.c @@ -0,0 +1,141 @@ +/* + Audio File Library + Copyright (C) 1998-2000, Michael Pruett <michael@68k.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307 USA. +*/ + +/* + nextwrite.c + + This file contains routines for writing NeXT/Sun format sound files. +*/ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <assert.h> + +#ifdef HAVE_INTTYPES_H +#include <inttypes.h> +#endif + +#include "audiofile.h" +#include "afinternal.h" +#include "next.h" +#include "byteorder.h" +#include "util.h" +#include "setup.h" + +status _af_next_update (AFfilehandle file); + +static u_int32_t nextencodingtype (_AudioFormat *format); +static status next_write_header (AFfilehandle file); + +/* A return value of zero indicates successful synchronisation. */ +status _af_next_update (AFfilehandle file) +{ + next_write_header(file); + return AF_SUCCEED; +} + +static status next_write_header (AFfilehandle file) +{ + _Track *track; + int frameSize; + u_int32_t offset, length, encoding, sampleRate, channelCount; + + track = _af_filehandle_get_track(file, AF_DEFAULT_TRACK); + + frameSize = _af_format_frame_size(&track->f, AF_FALSE); + + offset = HOST_TO_BENDIAN_INT32(track->fpos_first_frame); + length = HOST_TO_BENDIAN_INT32(track->totalfframes * frameSize); + encoding = HOST_TO_BENDIAN_INT32(nextencodingtype(&track->f)); + sampleRate = HOST_TO_BENDIAN_INT32(track->f.sampleRate); + channelCount = HOST_TO_BENDIAN_INT32(track->f.channelCount); + + if (af_fseek(file->fh, 0, SEEK_SET) != 0) + _af_error(AF_BAD_LSEEK, "bad seek"); + + af_fwrite(".snd", 4, 1, file->fh); + af_fwrite(&offset, 4, 1, file->fh); + af_fwrite(&length, 4, 1, file->fh); + af_fwrite(&encoding, 4, 1, file->fh); + af_fwrite(&sampleRate, 4, 1, file->fh); + af_fwrite(&channelCount, 4, 1, file->fh); + + return AF_SUCCEED; +} + +static u_int32_t nextencodingtype (_AudioFormat *format) +{ + u_int32_t encoding = 0; + + if (format->compressionType != AF_COMPRESSION_NONE) + { + if (format->compressionType == AF_COMPRESSION_G711_ULAW) + encoding = _AU_FORMAT_MULAW_8; + else if (format->compressionType == AF_COMPRESSION_G711_ALAW) + encoding = _AU_FORMAT_ALAW_8; + } + else if (format->sampleFormat == AF_SAMPFMT_TWOSCOMP) + { + if (format->sampleWidth == 8) + encoding = _AU_FORMAT_LINEAR_8; + else if (format->sampleWidth == 16) + encoding = _AU_FORMAT_LINEAR_16; + else if (format->sampleWidth == 24) + encoding = _AU_FORMAT_LINEAR_24; + else if (format->sampleWidth == 32) + encoding = _AU_FORMAT_LINEAR_32; + } + else if (format->sampleFormat == AF_SAMPFMT_FLOAT) + encoding = _AU_FORMAT_FLOAT; + else if (format->sampleFormat == AF_SAMPFMT_DOUBLE) + encoding = _AU_FORMAT_DOUBLE; + + return encoding; +} + +status _af_next_write_init (AFfilesetup setup, AFfilehandle filehandle) +{ + _Track *track; + + if (_af_filesetup_make_handle(setup, filehandle) == AF_FAIL) + return AF_FAIL; + + filehandle->formatSpecific = NULL; + + if (filehandle->miscellaneousCount > 0) + { + _af_error(AF_BAD_NUMMISC, "NeXT format supports no miscellaneous chunks"); + return AF_FAIL; + } + + next_write_header(filehandle); + + track = _af_filehandle_get_track(filehandle, AF_DEFAULT_TRACK); + track->fpos_first_frame = 28; + + track->f.byteOrder = AF_BYTEORDER_BIGENDIAN; + + return AF_SUCCEED; +} diff --git a/libaudiofile/nist.c b/libaudiofile/nist.c new file mode 100644 index 0000000..4872dec --- /dev/null +++ b/libaudiofile/nist.c @@ -0,0 +1,394 @@ +/* + Audio File Library + Copyright (C) 2004, Michael Pruett <michael@68k.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307 USA. +*/ + +/* + nist.c + + This file contains code for reading NIST SPHERE files. +*/ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <sys/types.h> +#include <stdio.h> +#include <string.h> +#include <assert.h> + +#ifdef HAVE_INTTYPES_H +#include <inttypes.h> +#endif + +#include "afinternal.h" +#include "audiofile.h" +#include "util.h" +#include "byteorder.h" +#include "setup.h" +#include "track.h" + +#include "nist.h" + +_AFfilesetup _af_nist_default_filesetup = +{ + _AF_VALID_FILESETUP, /* valid */ + AF_FILE_NIST_SPHERE, /* fileFormat */ + AF_TRUE, /* trackSet */ + AF_TRUE, /* instrumentSet */ + AF_TRUE, /* miscellaneousSet */ + 1, /* trackCount */ + NULL, /* tracks */ + 0, /* instrumentCount */ + NULL, /* instruments */ + 0, /* miscellaneousCount */ + NULL /* miscellaneous */ +}; + +bool _af_nist_recognize (AFvirtualfile *fh) +{ + u_int8_t buffer[16]; + + af_fseek(fh, 0, SEEK_SET); + + if (af_fread(buffer, 16, 1, fh) != 1) + return AF_FALSE; + + /* Check to see if the file's magic number matches. */ + if (memcmp(buffer, "NIST_1A\n 1024\n", 16) == 0) + return AF_TRUE; + + return AF_FALSE; +} + +AFfilesetup _af_nist_complete_setup (AFfilesetup setup) +{ + _TrackSetup *track; + + if (setup->trackSet && setup->trackCount != 1) + { + _af_error(AF_BAD_NUMTRACKS, "NIST SPHERE file must have 1 track"); + return AF_NULL_FILESETUP; + } + + track = &setup->tracks[0]; + + if (track->sampleFormatSet) + { + /* XXXmpruett: Currently we allow only 1-16 bit sample width. */ + if (track->f.sampleFormat == AF_SAMPFMT_TWOSCOMP && + (track->f.sampleWidth < 1 || track->f.sampleWidth > 16)) + { + _af_error(AF_BAD_WIDTH, + "invalid sample width %d bits for NIST SPHERE format"); + return AF_NULL_FILESETUP; + } + else if (track->f.sampleFormat == AF_SAMPFMT_UNSIGNED) + { + _af_error(AF_BAD_SAMPFMT, + "NIST SPHERE format does not support unsigned data"); + return AF_NULL_FILESETUP; + } + else if (track->f.sampleFormat == AF_SAMPFMT_FLOAT || + track->f.sampleFormat == AF_SAMPFMT_DOUBLE) + { + _af_error(AF_BAD_SAMPFMT, + "NIST SPHERE format does not support floating-point data"); + return AF_NULL_FILESETUP; + } + } + + if (track->rateSet && track->f.sampleRate <= 0.0) + { + _af_error(AF_BAD_RATE, + "invalid sample rate %.30g for NIST SPHERE file", + track->f.sampleRate); + return AF_NULL_FILESETUP; + } + + if (track->channelCountSet && track->f.channelCount < 1) + { + _af_error(AF_BAD_CHANNELS, + "invalid channel count (%d) for NIST SPHERE format", + track->f.channelCount); + return AF_NULL_FILESETUP; + } + + if (track->compressionSet && track->f.compressionType != AF_COMPRESSION_NONE && + track->f.compressionType != AF_COMPRESSION_G711_ULAW && + track->f.compressionType != AF_COMPRESSION_G711_ALAW) + { + _af_error(AF_BAD_NOT_IMPLEMENTED, + "NIST SPHERE format supports only G.711 u-law or A-law compression"); + return AF_NULL_FILESETUP; + } + + if (track->aesDataSet) + { + _af_error(AF_BAD_FILESETUP, "NIST SPHERE file cannot have AES data"); + return AF_NULL_FILESETUP; + } + + if (track->markersSet && track->markerCount != 0) + { + _af_error(AF_BAD_NUMMARKS, "NIST SPHERE format does not support markers"); + return AF_NULL_FILESETUP; + } + + if (setup->instrumentSet && setup->instrumentCount != 0) + { + _af_error(AF_BAD_NUMINSTS, "NIST SPHERE format does not support instruments"); + return AF_NULL_FILESETUP; + } + + /* XXXmpruett: We don't support miscellaneous chunks for now. */ + if (setup->miscellaneousSet && setup->miscellaneousCount != 0) + { + _af_error(AF_BAD_NOT_IMPLEMENTED, "NIST SPHERE format does not currently support miscellaneous chunks"); + return AF_NULL_FILESETUP; + } + + return _af_filesetup_copy(setup, &_af_nist_default_filesetup, AF_TRUE); +} + +static bool nist_header_read_int (char *header, char *key, int *val) +{ + char *cp; + char keystring[256], scanstring[256]; + + snprintf(keystring, 256, "\n%s -i", key); + + if ((cp = strstr(header, keystring)) != NULL) + { + snprintf(scanstring, 256, "\n%s -i %%d", key); + sscanf(cp, scanstring, val); + return AF_TRUE; + } + + return AF_FALSE; +} + +static bool nist_header_read_string (char *header, char *key, int *length, char *val) +{ + char *cp; + char keystring[256], scanstring[256]; + + snprintf(keystring, 256, "\n%s -s", key); + + if ((cp = strstr(header, keystring)) != NULL) + { + snprintf(scanstring, 256, "\n%s -s%%d %%79s", key); + sscanf(cp, scanstring, length, val); + return AF_TRUE; + } + + return AF_FALSE; +} + +status _af_nist_read_init (AFfilesetup setup, AFfilehandle handle) +{ + _Track *track; + char header[NIST_SPHERE_HEADER_LENGTH + 1]; + int intval; + char strval[NIST_SPHERE_MAX_FIELD_LENGTH]; + int sample_n_bytes; + + handle->instruments = NULL; + handle->instrumentCount = 0 ; + handle->miscellaneous = NULL; + handle->miscellaneousCount = 0; + + handle->tracks = NULL; + handle->trackCount = 1; + + af_fseek(handle->fh, 0, SEEK_SET); + + if (af_fread(header, NIST_SPHERE_HEADER_LENGTH, 1, handle->fh) != 1) + { + _af_error(AF_BAD_READ, "Could not read NIST SPHERE file header"); + return AF_FAIL; + } + + header[NIST_SPHERE_HEADER_LENGTH] = '\0'; + + if (memcmp(header, "NIST_1A\n 1024\n", 16) != 0) + { + _af_error(AF_BAD_FILEFMT, "Bad NIST SPHERE file header"); + return AF_FAIL; + } + + if ((handle->tracks = _af_track_new()) == NULL) + return AF_FAIL; + track = &handle->tracks[0]; + + /* Read number of bytes per sample. */ + if (!nist_header_read_int(header, "sample_n_bytes", &sample_n_bytes)) + { + _af_error(AF_BAD_HEADER, "bytes per sample not specified"); + return AF_FAIL; + } + + /* + Since some older NIST SPHERE files lack a sample_coding + field, if sample_n_bytes is 1, assume mu-law; + otherwise assume linear PCM. + */ + track->f.sampleFormat = AF_SAMPFMT_TWOSCOMP; + if (sample_n_bytes == 1) + { + track->f.compressionType = AF_COMPRESSION_G711_ULAW; + track->f.sampleWidth = 16; + } + else + { + track->f.compressionType = AF_COMPRESSION_NONE; + track->f.sampleWidth = sample_n_bytes * 8; + } + + if (nist_header_read_string(header, "sample_coding", &intval, strval)) + { + if (strcmp(strval, "pcm") == 0) + ; + else if (strcmp(strval, "ulaw") == 0 || strcmp(strval, "mu-law") == 0) + { + track->f.compressionType = AF_COMPRESSION_G711_ULAW; + track->f.sampleWidth = 16; + } + else if (strcmp(strval, "alaw") == 0) + { + track->f.compressionType = AF_COMPRESSION_G711_ALAW; + track->f.sampleWidth = 16; + } + else + { + _af_error(AF_BAD_SAMPFMT, + "unrecognized NIST SPHERE sample format %s", strval); + return AF_FAIL; + } + } + + /* Read channel count. */ + if (nist_header_read_int(header, "channel_count", &intval)) + { + if (intval < 1) + { + _af_error(AF_BAD_CHANNELS, "invalid number of channels %d", + intval); + return AF_FAIL; + } + track->f.channelCount = intval; + } + else + { + _af_error(AF_BAD_HEADER, "number of channels not specified"); + return AF_FAIL; + } + + /* Read string representing byte order. */ + if (nist_header_read_string(header, "sample_byte_format", &intval, strval)) + { + if (intval > 1) + { + if (strncmp(strval, "01", 2) == 0) + track->f.byteOrder = AF_BYTEORDER_LITTLEENDIAN; + else + track->f.byteOrder = AF_BYTEORDER_BIGENDIAN; + } + else + track->f.byteOrder = _AF_BYTEORDER_NATIVE; + } + else + { + /* + Fail if this field is not present and sample + width is more than one byte. + */ + if (track->f.compressionType == AF_COMPRESSION_NONE && + track->f.sampleWidth > 8) + { + _af_error(AF_BAD_HEADER, "sample byte order not specified"); + return AF_FAIL; + } + } + + /* Read significant bits per sample. */ + if (nist_header_read_int(header, "sample_sig_bits", &intval)) + { + if (intval < 1 || intval > 32) + { + _af_error(AF_BAD_WIDTH, "invalid sample width %d bits\n", + intval); + return AF_FAIL; + } + + /* + Use specified significant bits value as the + sample width for uncompressed data as long + as the number of bytes per sample remains + unchanged. + */ + if (track->f.compressionType == AF_COMPRESSION_NONE && + (intval + 7) / 8 == sample_n_bytes) + { + track->f.sampleWidth = intval; + } + } + + /* Read sample rate. */ + if (nist_header_read_int(header, "sample_rate", &intval)) + { + if (intval <= 0) + { + _af_error(AF_BAD_RATE, "invalid sample rate %d Hz\n", intval); + return AF_FAIL; + } + track->f.sampleRate = intval; + } + else + { + _af_error(AF_BAD_HEADER, "sample rate not specified"); + return AF_FAIL; + } + + /* Read sample count. */ + if (nist_header_read_int(header, "sample_count", &intval)) + { + track->totalfframes = intval / track->f.channelCount; + } + else + { + _af_error(AF_BAD_HEADER, "number of samples not specified"); + return AF_FAIL; + } + + if (_af_set_sample_format(&track->f, track->f.sampleFormat, + track->f.sampleWidth) == AF_FAIL) + { + return AF_FAIL; + } + + track->fpos_first_frame = NIST_SPHERE_HEADER_LENGTH; + track->data_size = af_flength(handle->fh) - NIST_SPHERE_HEADER_LENGTH; + track->nextfframe = 0; + track->fpos_next_frame = track->fpos_first_frame; + + handle->formatSpecific = NULL; + + return AF_SUCCEED; +} diff --git a/libaudiofile/nist.h b/libaudiofile/nist.h new file mode 100644 index 0000000..63c12a7 --- /dev/null +++ b/libaudiofile/nist.h @@ -0,0 +1,41 @@ +/* + Audio File Library + Copyright (C) 2004, Michael Pruett <michael@68k.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307 USA. +*/ + +/* + nist.h + + This file declares code for reading and writing NIST SPHERE files. +*/ + +#ifndef NIST_H +#define NIST_H + +#include "audiofile.h" + +#define NIST_SPHERE_HEADER_LENGTH 1024 +#define NIST_SPHERE_MAX_FIELD_LENGTH 80 + +bool _af_nist_recognize (AFvirtualfile *fh); +AFfilesetup _af_nist_complete_setup (AFfilesetup setup); +status _af_nist_read_init (AFfilesetup setup, AFfilehandle handle); +status _af_nist_write_init (AFfilesetup setup, AFfilehandle handle); +status _af_nist_update (AFfilehandle file); + +#endif /* NIST_H */ diff --git a/libaudiofile/nistwrite.c b/libaudiofile/nistwrite.c new file mode 100644 index 0000000..f26932e --- /dev/null +++ b/libaudiofile/nistwrite.c @@ -0,0 +1,146 @@ +/* + Audio File Library + Copyright (C) 2004, Michael Pruett <michael@68k.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307 USA. +*/ + +/* + nistwrite.c + + This file contains routines for writing to NIST SPHERE + format files. +*/ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <assert.h> +#include <sys/types.h> +#include <stdio.h> +#include <string.h> + +#ifdef HAVE_INTTYPES_H +#include <inttypes.h> +#endif + +#include "afinternal.h" +#include "audiofile.h" +#include "util.h" +#include "byteorder.h" +#include "setup.h" +#include "track.h" + +#include "nist.h" + +static char *sample_byte_format (_AudioFormat *fmt) +{ + int nbytes = _af_format_sample_size(fmt, AF_FALSE); + + assert(nbytes == 1 || nbytes == 2); + + if (nbytes == 1) + return "0"; + else if (nbytes == 2) + { + if (fmt->byteOrder == AF_BYTEORDER_BIGENDIAN) + return "10"; + else + return "01"; + } + + /* NOTREACHED */ + return NULL; +} + +static char *sample_coding (_AudioFormat *fmt) +{ + if (fmt->compressionType == AF_COMPRESSION_NONE) + return "pcm"; + else if (fmt->compressionType == AF_COMPRESSION_G711_ULAW) + return "ulaw"; + else if (fmt->compressionType == AF_COMPRESSION_G711_ALAW) + return "alaw"; + + /* NOTREACHED */ + return NULL; +} + +status WriteNISTHeader (AFfilehandle file) +{ + AFvirtualfile *fp = file->fh; + _Track *track; + char header[NIST_SPHERE_HEADER_LENGTH]; + int printed; + + track = _af_filehandle_get_track(file, AF_DEFAULT_TRACK); + + printed = snprintf(header, NIST_SPHERE_HEADER_LENGTH, + "NIST_1A\n 1024\n" + "channel_count -i %d\n" + "sample_count -i %d\n" + "sample_rate -i %d\n" + "sample_n_bytes -i %d\n" + "sample_byte_format -s%d %s\n" + "sample_sig_bits -i %d\n" + "sample_coding -s%d %s\n" + "end_head\n", + track->f.channelCount, + (int) (track->totalfframes * track->f.channelCount), + (int) track->f.sampleRate, + (int) _af_format_sample_size(&track->f, AF_FALSE), + (int) _af_format_sample_size(&track->f, AF_FALSE), sample_byte_format(&track->f), + track->f.sampleWidth, + (int) strlen(sample_coding(&track->f)), sample_coding(&track->f)); + + /* Fill the remaining space in the buffer with space characters. */ + if (printed < NIST_SPHERE_HEADER_LENGTH) + memset(header + printed, ' ', NIST_SPHERE_HEADER_LENGTH - printed); + + return af_fwrite(header, NIST_SPHERE_HEADER_LENGTH, 1, fp); +} + +status _af_nist_write_init (AFfilesetup setup, AFfilehandle handle) +{ + _Track *track; + + assert(handle->fileFormat == AF_FILE_NIST_SPHERE); + + if (_af_filesetup_make_handle(setup, handle) == AF_FAIL) + return AF_FAIL; + + track = &handle->tracks[0]; + track->totalfframes = 0; + track->fpos_first_frame = NIST_SPHERE_HEADER_LENGTH; + track->nextfframe = 0; + track->fpos_next_frame = track->fpos_first_frame; + + handle->formatSpecific = NULL; + + af_fseek(handle->fh, 0, SEEK_SET); + WriteNISTHeader(handle); + + return AF_SUCCEED; +} + +status _af_nist_update (AFfilehandle file) +{ + af_fseek(file->fh, 0, SEEK_SET); + WriteNISTHeader(file); + + return AF_SUCCEED; +} diff --git a/libaudiofile/openclose.c b/libaudiofile/openclose.c new file mode 100644 index 0000000..dd0a121 --- /dev/null +++ b/libaudiofile/openclose.c @@ -0,0 +1,648 @@ +/* + Audio File Library + Copyright (C) 2000-2001, Silicon Graphics, Inc. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307 USA. +*/ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <stdlib.h> +#include <assert.h> +#include <string.h> + +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif + +#include <audiofile.h> + +#include "afinternal.h" +#include "units.h" +#include "util.h" +#include "modules.h" + +#if defined(WIN32) || defined(__CYGWIN__) +#include <io.h> +#include <fcntl.h> +#define SETBINARYMODE(fp) _setmode(_fileno(fp), _O_BINARY) +#else +#define SETBINARYMODE(x) +#endif /* WIN32 || __CYGWIN__ */ + +extern _Unit _af_units[]; + +static void freeFileHandle (AFfilehandle filehandle); +static void freeInstParams (AFPVu *values, int fileFormat); +static status _afOpenFile (int access, AFvirtualfile *vf, const char *filename, + AFfilehandle *file, AFfilesetup filesetup); + +int _af_identify (AFvirtualfile *vf, int *implemented) +{ + AFfileoffset curpos; + int i; + + curpos = af_ftell(vf); + + for (i=0; i<_AF_NUM_UNITS; i++) + { + if (_af_units[i].read.recognize && + _af_units[i].read.recognize(vf)) + { + if (implemented != NULL) + *implemented = _af_units[i].implemented; + af_fseek(vf, curpos, SEEK_SET); + return _af_units[i].fileFormat; + } + } + + af_fseek(vf, curpos, SEEK_SET); + + if (implemented != NULL) + *implemented = AF_FALSE; + + return AF_FILE_UNKNOWN; +} + +int afIdentifyFD (int fd) +{ + FILE *fp; + AFvirtualfile *vf; + int result; + + /* + Duplicate the file descriptor since otherwise the + original file descriptor would get closed when we close + the virtual file below. + */ + fd = dup(fd); + + fp = fdopen(fd, "r"); + if (fp == NULL) + { + _af_error(AF_BAD_OPEN, "could not open file"); + return AF_FILE_UNKNOWN; + } + + SETBINARYMODE(fp); + + vf = af_virtual_file_new_for_file(fp); + if (vf == NULL) + { + _af_error(AF_BAD_OPEN, "could not open file"); + return AF_FILE_UNKNOWN; + } + + result = _af_identify(vf, NULL); + + af_fclose(vf); + + return result; +} + +int afIdentifyNamedFD (int fd, const char *filename, int *implemented) +{ + FILE *fp; + AFvirtualfile *vf; + int result; + + /* + Duplicate the file descriptor since otherwise the + original file descriptor would get closed when we close + the virtual file below. + */ + fd = dup(fd); + + fp = fdopen(fd, "r"); + if (fp == NULL) + { + _af_error(AF_BAD_OPEN, "could not open file '%s'", filename); + return AF_FILE_UNKNOWN; + } + + SETBINARYMODE(fp); + + vf = af_virtual_file_new_for_file(fp); + if (vf == NULL) + { + _af_error(AF_BAD_OPEN, "could not open file '%s'", filename); + return AF_FILE_UNKNOWN; + } + + result = _af_identify(vf, implemented); + + af_fclose(vf); + + return result; +} + +AFfilehandle afOpenFD (int fd, const char *mode, AFfilesetup setup) +{ + FILE *fp; + AFvirtualfile *vf; + AFfilehandle filehandle; + int access; + + if (mode == NULL) + { + _af_error(AF_BAD_ACCMODE, "null access mode"); + return AF_NULL_FILEHANDLE; + } + + if (mode[0] == 'r') + access = _AF_READ_ACCESS; + else if (mode[0] == 'w') + access = _AF_WRITE_ACCESS; + else + { + _af_error(AF_BAD_ACCMODE, "unrecognized access mode '%s'", mode); + return AF_NULL_FILEHANDLE; + } + + if ((fp = fdopen(fd, mode)) == NULL) + { + _af_error(AF_BAD_OPEN, "could not open file"); + return AF_NULL_FILEHANDLE; + } + + SETBINARYMODE(fp); + + vf = af_virtual_file_new_for_file(fp); + + if (_afOpenFile(access, vf, NULL, &filehandle, setup) != AF_SUCCEED) + af_fclose(vf); + + return filehandle; +} + +AFfilehandle afOpenNamedFD (int fd, const char *mode, AFfilesetup setup, + const char *filename) +{ + FILE *fp; + AFvirtualfile *vf; + AFfilehandle filehandle; + int access; + + if (mode == NULL) + { + _af_error(AF_BAD_ACCMODE, "null access mode"); + return AF_NULL_FILEHANDLE; + } + + if (mode[0] == 'r') + access = _AF_READ_ACCESS; + else if (mode[0] == 'w') + access = _AF_WRITE_ACCESS; + else + { + _af_error(AF_BAD_ACCMODE, "unrecognized access mode '%s'", mode); + return AF_NULL_FILEHANDLE; + } + + if ((fp = fdopen(fd, mode)) == NULL) + { + _af_error(AF_BAD_OPEN, "could not open file '%s'", filename); + return AF_NULL_FILEHANDLE; + } + + SETBINARYMODE(fp); + + vf = af_virtual_file_new_for_file(fp); + + if (_afOpenFile(access, vf, filename, &filehandle, setup) != AF_SUCCEED) + af_fclose(vf); + + return filehandle; +} + +AFfilehandle afOpenFile (const char *filename, const char *mode, AFfilesetup setup) +{ + FILE *fp; + AFvirtualfile *vf; + AFfilehandle filehandle; + int access; + + if (mode == NULL) + { + _af_error(AF_BAD_ACCMODE, "null access mode"); + return AF_NULL_FILEHANDLE; + } + + if (mode[0] == 'r') + access = _AF_READ_ACCESS; + else if (mode[0] == 'w') + access = _AF_WRITE_ACCESS; + else + { + _af_error(AF_BAD_ACCMODE, "unrecognized access mode '%s'", mode); + return AF_NULL_FILEHANDLE; + } + + if ((fp = fopen(filename, mode)) == NULL) + { + _af_error(AF_BAD_OPEN, "could not open file '%s'", filename); + return AF_NULL_FILEHANDLE; + } + + SETBINARYMODE(fp); + + vf = af_virtual_file_new_for_file(fp); + + if (_afOpenFile(access, vf, filename, &filehandle, setup) != AF_SUCCEED) + af_fclose(vf); + + return filehandle; +} + +AFfilehandle afOpenVirtualFile (AFvirtualfile *vfile, const char *mode, + AFfilesetup setup) +{ + AFfilehandle filehandle; + int access; + + if (vfile == NULL) + { + _af_error(AF_BAD_FILEHANDLE, "null virtual filehandle"); + return AF_NULL_FILEHANDLE; + } + + if (mode == NULL) + { + _af_error(AF_BAD_ACCMODE, "null access mode"); + return AF_NULL_FILEHANDLE; + } + + if (mode[0] == 'r') + access = _AF_READ_ACCESS; + else if (mode[0] == 'w') + access = _AF_WRITE_ACCESS; + else + { + _af_error(AF_BAD_ACCMODE, "unrecognized access mode '%s'", mode); + return AF_NULL_FILEHANDLE; + } + + if (_afOpenFile(access, vfile, NULL, &filehandle, setup) != AF_SUCCEED) + af_fclose(vfile); + + return filehandle; +} + +static status _afOpenFile (int access, AFvirtualfile *vf, const char *filename, + AFfilehandle *file, AFfilesetup filesetup) +{ + int fileFormat = AF_FILE_UNKNOWN; + bool implemented = AF_TRUE; + char *formatName; + status (*initfunc) (AFfilesetup, AFfilehandle); + + int userSampleFormat = 0; + double userSampleRate = 0.0; + _PCMInfo userPCM; + bool userFormatSet = AF_FALSE; + + int t; + + AFfilehandle filehandle = AF_NULL_FILEHANDLE; + AFfilesetup completesetup = AF_NULL_FILESETUP; + + *file = AF_NULL_FILEHANDLE; + + if (access == _AF_WRITE_ACCESS || filesetup != AF_NULL_FILESETUP) + { + if (!_af_filesetup_ok(filesetup)) + return AF_FAIL; + + fileFormat = filesetup->fileFormat; + if (access == _AF_READ_ACCESS && fileFormat != AF_FILE_RAWDATA) + { + _af_error(AF_BAD_FILESETUP, + "warning: opening file for read access: " + "ignoring file setup with non-raw file format"); + filesetup = AF_NULL_FILESETUP; + fileFormat = _af_identify(vf, &implemented); + } + } + else if (filesetup == AF_NULL_FILESETUP) + fileFormat = _af_identify(vf, &implemented); + + if (fileFormat == AF_FILE_UNKNOWN) + { + if (filename != NULL) + _af_error(AF_BAD_NOT_IMPLEMENTED, + "'%s': unrecognized audio file format", + filename); + else + _af_error(AF_BAD_NOT_IMPLEMENTED, + "unrecognized audio file format"); + return AF_FAIL; + } + + formatName = _af_units[fileFormat].name; + + if (implemented == AF_FALSE) + { + _af_error(AF_BAD_NOT_IMPLEMENTED, + "%s format not currently supported", formatName); + } + + assert(_af_units[fileFormat].completesetup != NULL); + assert(_af_units[fileFormat].read.init != NULL); + + if (access == _AF_WRITE_ACCESS && + _af_units[fileFormat].write.init == NULL) + { + _af_error(AF_BAD_NOT_IMPLEMENTED, + "%s format is currently supported for reading only", + formatName); + return AF_FAIL; + } + + completesetup = NULL; + if (filesetup != AF_NULL_FILESETUP) + { + userSampleFormat = filesetup->tracks[0].f.sampleFormat; + userPCM = filesetup->tracks[0].f.pcm; + userSampleRate = filesetup->tracks[0].f.sampleRate; + userFormatSet = AF_TRUE; + if ((completesetup = _af_units[fileFormat].completesetup(filesetup)) == NULL) + return AF_FAIL; + } + + filehandle = _af_malloc(sizeof (_AFfilehandle)); + if (filehandle == NULL) + { + if (completesetup) + afFreeFileSetup(completesetup); + return AF_FAIL; + } + memset(filehandle, 0, sizeof (_AFfilehandle)); + + filehandle->valid = _AF_VALID_FILEHANDLE; + filehandle->fh = vf; + filehandle->access = access; + if (filename != NULL) + filehandle->fileName = strdup(filename); + else + filehandle->fileName = NULL; + filehandle->fileFormat = fileFormat; + filehandle->formatSpecific = NULL; + + initfunc = (access == _AF_READ_ACCESS) ? + _af_units[fileFormat].read.init : _af_units[fileFormat].write.init; + + if (initfunc(completesetup, filehandle) != AF_SUCCEED) + { + freeFileHandle(filehandle); + filehandle = AF_NULL_FILEHANDLE; + if (completesetup) + afFreeFileSetup(completesetup); + return AF_FAIL; + } + + if (completesetup) + { + afFreeFileSetup(completesetup); + completesetup = NULL; + } + + /* + Initialize virtual format. + */ + for (t=0; t<filehandle->trackCount; t++) + { + _Track *track = &filehandle->tracks[t]; + + track->v = track->f; + + if (userFormatSet) + { + track->v.sampleFormat = userSampleFormat; + track->v.pcm = userPCM; + track->v.sampleRate = userSampleRate; + } + + track->v.compressionType = AF_COMPRESSION_NONE; + track->v.compressionParams = NULL; + +#if WORDS_BIGENDIAN + track->v.byteOrder = AF_BYTEORDER_BIGENDIAN; +#else + track->v.byteOrder = AF_BYTEORDER_LITTLEENDIAN; +#endif + + if (_AFinitmodules(filehandle, track) == AF_FAIL) + { + freeFileHandle(filehandle); + return AF_FAIL; + } + } + + *file = filehandle; + + return AF_SUCCEED; +} + +int afSyncFile (AFfilehandle handle) +{ + if (!_af_filehandle_ok(handle)) + return -1; + + if (handle->access == _AF_WRITE_ACCESS) + { + int filefmt = handle->fileFormat; + int trackno; + + /* Finish writes on all tracks. */ + for (trackno = 0; trackno < handle->trackCount; trackno++) + { + _Track *track = &handle->tracks[trackno]; + + if (track->ms.modulesdirty) + { + if (_AFsetupmodules(handle, track) == AF_FAIL) + return -1; + } + + if (_AFsyncmodules(handle, track) != AF_SUCCEED) + return -1; + } + + /* Update file headers. */ + if (_af_units[filefmt].write.update != NULL && + _af_units[filefmt].write.update(handle) != AF_SUCCEED) + return AF_FAIL; + } + else if (handle->access == _AF_READ_ACCESS) + { + /* Do nothing. */ + } + else + { + _af_error(AF_BAD_ACCMODE, "unrecognized access mode %d", + handle->access); + return AF_FAIL; + } + + return AF_SUCCEED; +} + +int afCloseFile (AFfilehandle file) +{ + int err; + + if (!_af_filehandle_ok(file)) + return -1; + + afSyncFile(file); + + err = af_fclose(file->fh); + if (err < 0) + _af_error(AF_BAD_CLOSE, "close returned %d", err); + + freeFileHandle(file); + + return 0; +} + +static void freeFileHandle (AFfilehandle filehandle) +{ + int fileFormat; + if (filehandle == NULL || filehandle->valid != _AF_VALID_FILEHANDLE) + { + _af_error(AF_BAD_FILEHANDLE, "bad filehandle"); + return; + } + + filehandle->valid = 0; + + if (filehandle->fileName != NULL) + free(filehandle->fileName); + + fileFormat = filehandle->fileFormat; + + if (filehandle->formatSpecific != NULL) + { + free(filehandle->formatSpecific); + filehandle->formatSpecific = NULL; + } + + if (filehandle->tracks) + { + int i; + for (i=0; i<filehandle->trackCount; i++) + { + /* Free the compression parameters. */ + if (filehandle->tracks[i].f.compressionParams) + { + AUpvfree(filehandle->tracks[i].f.compressionParams); + filehandle->tracks[i].f.compressionParams = AU_NULL_PVLIST; + } + + if (filehandle->tracks[i].v.compressionParams) + { + AUpvfree(filehandle->tracks[i].v.compressionParams); + filehandle->tracks[i].v.compressionParams = AU_NULL_PVLIST; + } + + /* Free the track's modules. */ + + _AFfreemodules(&filehandle->tracks[i]); + + if (filehandle->tracks[i].channelMatrix) + { + free(filehandle->tracks[i].channelMatrix); + filehandle->tracks[i].channelMatrix = NULL; + } + + if (filehandle->tracks[i].markers) + { + int j; + for (j=0; j<filehandle->tracks[i].markerCount; j++) + { + if (filehandle->tracks[i].markers[j].name) + { + free(filehandle->tracks[i].markers[j].name); + filehandle->tracks[i].markers[j].name = NULL; + } + if (filehandle->tracks[i].markers[j].comment) + { + free(filehandle->tracks[i].markers[j].comment); + filehandle->tracks[i].markers[j].comment = NULL; + } + + } + + free(filehandle->tracks[i].markers); + filehandle->tracks[i].markers = NULL; + } + } + + free(filehandle->tracks); + filehandle->tracks = NULL; + } + filehandle->trackCount = 0; + + if (filehandle->instruments) + { + int i; + for (i=0; i<filehandle->instrumentCount; i++) + { + if (filehandle->instruments[i].loops) + { + free(filehandle->instruments[i].loops); + filehandle->instruments[i].loops = NULL; + } + filehandle->instruments[i].loopCount = 0; + + if (filehandle->instruments[i].values) + { + freeInstParams(filehandle->instruments[i].values, fileFormat); + filehandle->instruments[i].values = NULL; + } + } + free(filehandle->instruments); + filehandle->instruments = NULL; + } + filehandle->instrumentCount = 0; + + if (filehandle->miscellaneous) + { + free(filehandle->miscellaneous); + filehandle->miscellaneous = NULL; + } + filehandle->miscellaneousCount = 0; + + memset(filehandle, 0, sizeof (_AFfilehandle)); + free(filehandle); +} + +static void freeInstParams (AFPVu *values, int fileFormat) +{ + int i; + int parameterCount = _af_units[fileFormat].instrumentParameterCount; + + for (i=0; i<parameterCount; i++) + { + if (_af_units[fileFormat].instrumentParameters[i].type == AU_PVTYPE_PTR) + if (values[i].v != NULL) + free(values[i].v); + } + + free(values); +} diff --git a/libaudiofile/pcm.c b/libaudiofile/pcm.c new file mode 100644 index 0000000..d2d0777 --- /dev/null +++ b/libaudiofile/pcm.c @@ -0,0 +1,173 @@ +/* + Audio File Library + Copyright (C) 1999-2000, Michael Pruett <michael@68k.org> + Copyright (C) 2000-2001, Silicon Graphics, Inc. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307 USA. +*/ + +/* + pcm.c + + This file declares default PCM mappings. +*/ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include "afinternal.h" +#include "pcm.h" +#include "util.h" + +_PCMInfo _af_default_signed_integer_pcm_mappings[] = +{ + {0, 0, 0, 0}, + {SLOPE_INT8, 0, MIN_INT8, MAX_INT8}, + {SLOPE_INT16, 0, MIN_INT16, MAX_INT16}, + {SLOPE_INT24, 0, MIN_INT24, MAX_INT24}, + {SLOPE_INT32, 0, MIN_INT32, MAX_INT32} +}; + +_PCMInfo _af_default_unsigned_integer_pcm_mappings[] = +{ + {0, 0, 0, 0}, + {SLOPE_INT8, INTERCEPT_U_INT8, 0, MAX_U_INT8}, + {SLOPE_INT16, INTERCEPT_U_INT16, 0, MAX_U_INT16}, + {SLOPE_INT24, INTERCEPT_U_INT24, 0, MAX_U_INT24}, + {SLOPE_INT32, INTERCEPT_U_INT32, 0, MAX_U_INT32} +}; + +_PCMInfo _af_default_float_pcm_mapping = +{1, 0, 0, 0}; + +_PCMInfo _af_default_double_pcm_mapping = +{1, 0, 0, 0}; + +/* + Initialize the PCM mapping for a given track. +*/ +void afInitPCMMapping (AFfilesetup setup, int trackid, + double slope, double intercept, double minClip, double maxClip) +{ + _TrackSetup *track; + + if (!_af_filesetup_ok(setup)) + return; + + if ((track = _af_filesetup_get_tracksetup(setup, trackid)) == NULL) + return; + + track->f.pcm.slope = slope; + track->f.pcm.intercept = intercept; + track->f.pcm.minClip = minClip; + track->f.pcm.maxClip = maxClip; +} + +int afSetVirtualPCMMapping (AFfilehandle file, int trackid, + double slope, double intercept, double minClip, double maxClip) +{ + _Track *track; + + if (!_af_filehandle_ok(file)) + return -1; + + if ((track = _af_filehandle_get_track(file, trackid)) == NULL) + return -1; + + track->v.pcm.slope = slope; + track->v.pcm.intercept = intercept; + track->v.pcm.minClip = minClip; + track->v.pcm.maxClip = maxClip; + + track->ms.modulesdirty = AF_TRUE; + + return 0; +} + +int afSetTrackPCMMapping (AFfilehandle file, int trackid, + double slope, double intercept, double minClip, double maxClip) +{ + _Track *track; + + if (!_af_filehandle_ok(file)) + return -1; + + if ((track = _af_filehandle_get_track(file, trackid)) == NULL) + return -1; + + /* + NOTE: this is highly unusual: we don't ordinarily + change track.f after the file is opened. + + PCM mapping is the exception because this information + is not encoded in sound files' headers using today's + formats, so the user will probably want to set this + information on a regular basis. The defaults may or + may not be what the user wants. + */ + + track->f.pcm.slope = slope; + track->f.pcm.intercept = intercept; + track->f.pcm.minClip = minClip; + track->f.pcm.maxClip = maxClip; + + track->ms.modulesdirty = AF_TRUE; + + return 0; +} + +void afGetPCMMapping (AFfilehandle file, int trackid, + double *slope, double *intercept, double *minClip, double *maxClip) +{ + _Track *track; + + if (!_af_filehandle_ok(file)) + return; + + if ((track = _af_filehandle_get_track(file, trackid)) == NULL) + return; + + if (slope) + *slope = track->f.pcm.slope; + if (intercept) + *intercept = track->f.pcm.intercept; + if (minClip) + *minClip = track->f.pcm.minClip; + if (maxClip) + *maxClip = track->f.pcm.maxClip; +} + +void afGetVirtualPCMMapping (AFfilehandle file, int trackid, + double *slope, double *intercept, double *minClip, double *maxClip) +{ + _Track *track; + + if (!_af_filehandle_ok(file)) + return; + + if ((track = _af_filehandle_get_track(file, trackid)) == NULL) + return; + + if (slope) + *slope = track->v.pcm.slope; + if (intercept) + *intercept = track->v.pcm.intercept; + if (minClip) + *minClip = track->v.pcm.minClip; + if (maxClip) + *maxClip = track->v.pcm.maxClip; +} diff --git a/libaudiofile/pcm.h b/libaudiofile/pcm.h new file mode 100644 index 0000000..413fdc2 --- /dev/null +++ b/libaudiofile/pcm.h @@ -0,0 +1,70 @@ +/* + Audio File Library + Copyright (C) 2000, Silicon Graphics, Inc. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307 USA. +*/ + +/* + pcm.h + + This file defines various constants for PCM mapping. +*/ + +#ifndef PCM_H +#define PCM_H + +/* + SLOPE_INTN = 2^(N-1) - 1 +*/ +#define SLOPE_INT8 (127.0) +#define SLOPE_INT16 (32767.0) +#define SLOPE_INT24 (8388607.0) +#define SLOPE_INT32 (2147483647.0) + +/* + INTERCEPT_U_INTN = 2^(N-1) +*/ +#define INTERCEPT_U_INT8 (128.0) +#define INTERCEPT_U_INT16 (32768.0) +#define INTERCEPT_U_INT24 (8388608.0) +#define INTERCEPT_U_INT32 (2147483648.0) + +/* + MIN_INTN = -(2^(N-1)) +*/ +#define MIN_INT8 (-128.0) +#define MIN_INT16 (-32768.0) +#define MIN_INT24 (-8388608.0) +#define MIN_INT32 (-2147483648.0) + +/* + MAX_INTN = 2^(N-1) - 1 +*/ +#define MAX_INT8 127.0 +#define MAX_INT16 32767.0 +#define MAX_INT24 8388607.0 +#define MAX_INT32 2147483647.0 + +/* + MAX_U_INTN = 2^N - 1 +*/ +#define MAX_U_INT8 255.0 +#define MAX_U_INT16 65535.0 +#define MAX_U_INT24 16777215.0 +#define MAX_U_INT32 4294967295.0 + +#endif /* PCM_H */ diff --git a/libaudiofile/print.h b/libaudiofile/print.h new file mode 100644 index 0000000..d67ca54 --- /dev/null +++ b/libaudiofile/print.h @@ -0,0 +1,55 @@ +/* + Audio File Library + Copyright (C) 2001, Silicon Graphics, Inc. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307 USA. +*/ + +/* + print.h + + Declare format specifiers for off_t and size_t for use with printf. +*/ + +#ifndef PRINT_H +#define PRINT_H + +#include <config.h> + +#if (SIZEOF_OFF_T == SIZEOF_LONG) +#define AF_OFF_T_PRINT_FMT "ld" +#endif +#if (SIZEOF_OFF_T > SIZEOF_LONG) +#define AF_OFF_T_PRINT_FMT "lld" +#endif +#if (SIZEOF_OFF_T < SIZEOF_LONG) +#define AF_OFF_T_PRINT_FMT "d" +#endif + +#if (SIZEOF_SIZE_T == SIZEOF_LONG) +#define AF_SIZE_T_PRINT_FMT "lu" +#endif +#if (SIZEOF_SIZE_T > SIZEOF_LONG) +#define AF_SIZE_T_PRINT_FMT "llu" +#endif +#if (SIZEOF_SIZE_T < SIZEOF_LONG) +#define AF_SIZE_T_PRINT_FMT "u" +#endif + +#define AF_FRAMECOUNT_PRINT_FMT AF_OFF_T_PRINT_FMT +#define AF_FILEOFFSET_PRINT_FMT AF_OFF_T_PRINT_FMT + +#endif /* PRINT_H */ diff --git a/libaudiofile/query.c b/libaudiofile/query.c new file mode 100644 index 0000000..18a0941 --- /dev/null +++ b/libaudiofile/query.c @@ -0,0 +1,478 @@ +/* + Audio File Library + Copyright (C) 1998-2000, Michael Pruett <michael@68k.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307 USA. +*/ + +/* + query.c + + This file contains the implementation of the Audio File Library's + query mechanism. Querying through the afQuery calls can allow the + programmer to determine the capabilities and characteristics of + the Audio File Library implementation and its supported formats. +*/ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <assert.h> +#include <stdlib.h> + +#include "audiofile.h" +#include "afinternal.h" +#include "aupvlist.h" +#include "error.h" +#include "util.h" +#include "units.h" +#include "compression.h" +#include "instrument.h" + +extern _Unit _af_units[]; +extern _CompressionUnit _af_compression[]; + +AUpvlist _afQueryFileFormat (int arg1, int arg2, int arg3, int arg4); +AUpvlist _afQueryInstrument (int arg1, int arg2, int arg3, int arg4); +AUpvlist _afQueryInstrumentParameter (int arg1, int arg2, int arg3, int arg4); +AUpvlist _afQueryLoop (int arg1, int arg2, int arg3, int arg4); +AUpvlist _afQueryMarker (int arg1, int arg2, int arg3, int arg4); +AUpvlist _afQueryMiscellaneous (int arg1, int arg2, int arg3, int arg4); +AUpvlist _afQueryCompression (int arg1, int arg2, int arg3, int arg4); +AUpvlist _afQueryCompressionParameter (int arg1, int arg2, int arg3, int arg4); + +AUpvlist afQuery (int querytype, int arg1, int arg2, int arg3, int arg4) +{ + switch (querytype) + { + case AF_QUERYTYPE_INST: + return _afQueryInstrument(arg1, arg2, arg3, arg4); + case AF_QUERYTYPE_INSTPARAM: + return _afQueryInstrumentParameter(arg1, arg2, arg3, arg4); + case AF_QUERYTYPE_LOOP: + return _afQueryLoop(arg1, arg2, arg3, arg4); + case AF_QUERYTYPE_FILEFMT: + return _afQueryFileFormat(arg1, arg2, arg3, arg4); + case AF_QUERYTYPE_COMPRESSION: + return _afQueryCompression(arg1, arg2, arg3, arg4); + case AF_QUERYTYPE_COMPRESSIONPARAM: + /* FIXME: This selector is not implemented. */ + return AU_NULL_PVLIST; + case AF_QUERYTYPE_MISC: + /* FIXME: This selector is not implemented. */ + return AU_NULL_PVLIST; + case AF_QUERYTYPE_MARK: + return _afQueryMarker(arg1, arg2, arg3, arg4); + default: + _af_error(AF_BAD_QUERYTYPE, "bad query type"); + return AU_NULL_PVLIST; + } + + /* NOTREACHED */ + return AU_NULL_PVLIST; +} + +/* ARGSUSED3 */ +AUpvlist _afQueryFileFormat (int arg1, int arg2, int arg3, int arg4) +{ + switch (arg1) + { + /* The following select only on arg1. */ + case AF_QUERY_ID_COUNT: + { + int count = 0, idx; + for (idx = 0; idx < _AF_NUM_UNITS; idx++) + if (_af_units[idx].implemented) + count++; + return _af_pv_long(count); + } + /* NOTREACHED */ + break; + + case AF_QUERY_IDS: + { + int count = 0, idx; + int *buffer; + + buffer = _af_calloc(_AF_NUM_UNITS, sizeof (int)); + if (buffer == NULL) + return AU_NULL_PVLIST; + + for (idx = 0; idx < _AF_NUM_UNITS; idx++) + if (_af_units[idx].implemented) + buffer[count++] = idx; + + if (count == 0) + { + free(buffer); + return AU_NULL_PVLIST; + } + + return _af_pv_pointer(buffer); + } + /* NOTREACHED */ + break; + + /* The following select on arg2. */ + case AF_QUERY_LABEL: + if (arg2 < 0 || arg2 >= _AF_NUM_UNITS) + return AU_NULL_PVLIST; + return _af_pv_pointer(_af_units[arg2].label); + + case AF_QUERY_NAME: + if (arg2 < 0 || arg2 >= _AF_NUM_UNITS) + return AU_NULL_PVLIST; + return _af_pv_pointer(_af_units[arg2].name); + + case AF_QUERY_DESC: + if (arg2 < 0 || arg2 >= _AF_NUM_UNITS) + return AU_NULL_PVLIST; + return _af_pv_pointer(_af_units[arg2].description); + + case AF_QUERY_IMPLEMENTED: + if (arg2 < 0 || arg2 >= _AF_NUM_UNITS) + return AU_NULL_PVLIST; + return _af_pv_long(_af_units[arg2].implemented); + + /* The following select on arg3. */ + case AF_QUERY_SAMPLE_FORMATS: + if (arg3 < 0 || arg3 >= _AF_NUM_UNITS) + return AU_NULL_PVLIST; + switch (arg2) + { + case AF_QUERY_DEFAULT: + return _af_pv_long(_af_units[arg3].defaultSampleFormat); + default: + return AU_NULL_PVLIST; + } + /* NOTREACHED */ + break; + + case AF_QUERY_SAMPLE_SIZES: + if (arg3 < 0 || arg3 >= _AF_NUM_UNITS) + return AU_NULL_PVLIST; + + switch (arg2) + { + case AF_QUERY_DEFAULT: + return _af_pv_long(_af_units[arg3].defaultSampleWidth); + default: + break; + } + /* NOTREACHED */ + break; + + case AF_QUERY_COMPRESSION_TYPES: + { + int idx, count; + int *buffer; + + if (arg3 < 0 || arg3 >= _AF_NUM_UNITS) + { + _af_error(AF_BAD_QUERY, + "unrecognized file format %d", arg3); + return AU_NULL_PVLIST; + } + + switch (arg2) + { + case AF_QUERY_VALUE_COUNT: + count = _af_units[arg3].compressionTypeCount; + return _af_pv_long(count); + + case AF_QUERY_VALUES: + count = _af_units[arg3].compressionTypeCount; + if (count == 0) + return AU_NULL_PVLIST; + + buffer = _af_calloc(count, sizeof (int)); + if (buffer == NULL) + return AU_NULL_PVLIST; + + for (idx = 0; idx < count; idx++) + { + buffer[idx] = _af_units[arg3].compressionTypes[idx]; + } + + return _af_pv_pointer(buffer); + } + } + break; + } + + return AU_NULL_PVLIST; +} + +long afQueryLong (int querytype, int arg1, int arg2, int arg3, int arg4) +{ + AUpvlist list; + int type; + long value; + + list = afQuery(querytype, arg1, arg2, arg3, arg4); + if (list == AU_NULL_PVLIST) + return -1; + AUpvgetvaltype(list, 0, &type); + if (type != AU_PVTYPE_LONG) + return -1; + AUpvgetval(list, 0, &value); + AUpvfree(list); + return value; +} + +double afQueryDouble (int querytype, int arg1, int arg2, int arg3, int arg4) +{ + AUpvlist list; + int type; + double value; + + list = afQuery(querytype, arg1, arg2, arg3, arg4); + if (list == AU_NULL_PVLIST) + return -1; + AUpvgetvaltype(list, 0, &type); + if (type != AU_PVTYPE_DOUBLE) + return -1; + AUpvgetval(list, 0, &value); + AUpvfree(list); + return value; +} + +void *afQueryPointer (int querytype, int arg1, int arg2, int arg3, int arg4) +{ + AUpvlist list; + int type; + void *value; + + list = afQuery(querytype, arg1, arg2, arg3, arg4); + if (list == AU_NULL_PVLIST) + return NULL; + AUpvgetvaltype(list, 0, &type); + if (type != AU_PVTYPE_PTR) + return NULL; + AUpvgetval(list, 0, &value); + AUpvfree(list); + return value; +} + +/* ARGSUSED3 */ +AUpvlist _afQueryInstrumentParameter (int arg1, int arg2, int arg3, int arg4) +{ + switch (arg1) + { + /* For the following query types, arg2 is the file format. */ + case AF_QUERY_SUPPORTED: + if (arg2 < 0 || arg2 >= _AF_NUM_UNITS) + return AU_NULL_PVLIST; + return _af_pv_long(_af_units[arg2].instrumentParameterCount != 0); + + case AF_QUERY_ID_COUNT: + if (arg2 < 0 || arg2 >= _AF_NUM_UNITS) + return AU_NULL_PVLIST; + return _af_pv_long(_af_units[arg2].instrumentParameterCount); + + case AF_QUERY_IDS: + { + int i, count; + int *buffer; + + if (arg2 < 0 || arg2 >= _AF_NUM_UNITS) + return AU_NULL_PVLIST; + count = _af_units[arg2].instrumentParameterCount; + if (count == 0) + return AU_NULL_PVLIST; + buffer = _af_calloc(count, sizeof (int)); + if (buffer == NULL) + return AU_NULL_PVLIST; + for (i=0; i<count; i++) + buffer[i] = _af_units[arg2].instrumentParameters[i].id; + return _af_pv_pointer(buffer); + } + /* NOTREACHED */ + break; + + /* + For the next few query types, arg2 is the file + format and arg3 is the instrument parameter id. + */ + case AF_QUERY_TYPE: + { + int idx; + + if (arg2 < 0 || arg2 >= _AF_NUM_UNITS) + return AU_NULL_PVLIST; + + idx = _af_instparam_index_from_id(arg2, arg3); + if (idx<0) + return AU_NULL_PVLIST; + return _af_pv_long(_af_units[arg2].instrumentParameters[idx].type); + } + + case AF_QUERY_NAME: + { + int idx; + + if (arg2 < 0 || arg2 >= _AF_NUM_UNITS) + return AU_NULL_PVLIST; + idx = _af_instparam_index_from_id(arg2, arg3); + if (idx < 0) + return AU_NULL_PVLIST; + return _af_pv_pointer(_af_units[arg2].instrumentParameters[idx].name); + } + + case AF_QUERY_DEFAULT: + { + int idx; + + if (arg2 < 0 || arg2 >= _AF_NUM_UNITS) + return AU_NULL_PVLIST; + idx = _af_instparam_index_from_id(arg2, arg3); + if (idx >= 0) + { + AUpvlist ret = AUpvnew(1); + AUpvsetparam(ret, 0, _af_units[arg2].instrumentParameters[idx].id); + AUpvsetvaltype(ret, 0, _af_units[arg2].instrumentParameters[idx].type); + AUpvsetval(ret, 0, &_af_units[arg2].instrumentParameters[idx].defaultValue); + return ret; + } + return AU_NULL_PVLIST; + } + + default: + break; + } + + return AU_NULL_PVLIST; +} + +/* ARGSUSED2 */ +AUpvlist _afQueryLoop (int arg1, int arg2, int arg3, int arg4) +{ + if (arg2 < 0 || arg2 >= _AF_NUM_UNITS) + return AU_NULL_PVLIST; + + switch (arg1) + { + case AF_QUERY_SUPPORTED: + return _af_pv_long(_af_units[arg2].loopPerInstrumentCount != 0); + case AF_QUERY_MAX_NUMBER: + return _af_pv_long(_af_units[arg2].loopPerInstrumentCount); + default: + break; + } + + return AU_NULL_PVLIST; +} + +/* ARGSUSED2 */ +AUpvlist _afQueryInstrument (int arg1, int arg2, int arg3, int arg4) +{ + switch (arg1) + { + case AF_QUERY_SUPPORTED: + if (arg2 < 0 || arg2 >= _AF_NUM_UNITS) + return AU_NULL_PVLIST; + return _af_pv_long(_af_units[arg2].instrumentCount != 0); + + case AF_QUERY_MAX_NUMBER: + if (arg2 < 0 || arg2 >= _AF_NUM_UNITS) + return AU_NULL_PVLIST; + return _af_pv_long(_af_units[arg2].instrumentCount); + + default: + break; + } + + return AU_NULL_PVLIST; +} + +/* ARGSUSED0 */ +AUpvlist _afQueryMiscellaneous (int arg1, int arg2, int arg3, int arg4) +{ + _af_error(AF_BAD_NOT_IMPLEMENTED, "not implemented yet"); + return AU_NULL_PVLIST; +} + +/* ARGSUSED2 */ +AUpvlist _afQueryMarker (int arg1, int arg2, int arg3, int arg4) +{ + switch (arg1) + { + case AF_QUERY_SUPPORTED: + return _af_pv_long(_af_units[arg2].markerCount != 0); + case AF_QUERY_MAX_NUMBER: + return _af_pv_long(_af_units[arg2].markerCount); + default: + _af_error(AF_BAD_QUERY, "bad query"); + return AU_NULL_PVLIST; + } + + /* NOTREACHED */ + return AU_NULL_PVLIST; +} + +/* ARGSUSED0 */ +AUpvlist _afQueryCompression (int arg1, int arg2, int arg3, int arg4) +{ + int count, i, index; + int *buf; + + switch (arg1) + { + case AF_QUERY_ID_COUNT: + count = 0; + for (i = 0; i < _AF_NUM_COMPRESSION; i++) + if (_af_compression[i].implemented == AF_TRUE) + count++; + return _af_pv_long(count); + + case AF_QUERY_IDS: + buf = _af_calloc(_AF_NUM_COMPRESSION, sizeof (int)); + if (!buf) + return AU_NULL_PVLIST; + + count = 0; + for (i = 0; i < _AF_NUM_COMPRESSION; i++) + { + if (_af_compression[i].implemented) + buf[count++] = _af_compression[i].compressionID; + } + return _af_pv_pointer(buf); + + case AF_QUERY_NATIVE_SAMPFMT: + index = _af_compression_index_from_id(arg2); + return _af_pv_long(_af_compression[index].nativeSampleFormat); + + case AF_QUERY_NATIVE_SAMPWIDTH: + index = _af_compression_index_from_id(arg2); + return _af_pv_long(_af_compression[_af_compression_index_from_id(arg2)].nativeSampleWidth); + + case AF_QUERY_LABEL: + index = _af_compression_index_from_id(arg2); + return _af_pv_pointer(_af_compression[index].label); + + case AF_QUERY_NAME: + index = _af_compression_index_from_id(arg2); + return _af_pv_pointer(_af_compression[index].shortname); + + case AF_QUERY_DESC: + index = _af_compression_index_from_id(arg2); + return _af_pv_pointer(_af_compression[index].name); + } + + _af_error(AF_BAD_QUERY, "unrecognized query selector %d\n", arg1); + return AU_NULL_PVLIST; +} diff --git a/libaudiofile/raw.c b/libaudiofile/raw.c new file mode 100644 index 0000000..5410086 --- /dev/null +++ b/libaudiofile/raw.c @@ -0,0 +1,188 @@ +/* + Audio File Library + Copyright (C) 2000, Silicon Graphics, Inc. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307 USA. +*/ + +/* + raw.c + + This file contains code for reading and writing raw audio + data files. +*/ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include "afinternal.h" +#include "audiofile.h" +#include "raw.h" +#include "util.h" +#include "setup.h" + +_AFfilesetup _af_raw_default_filesetup = +{ + _AF_VALID_FILESETUP, /* valid */ + AF_FILE_RAWDATA, /* fileFormat */ + AF_TRUE, /* trackSet */ + AF_TRUE, /* instrumentSet */ + AF_TRUE, /* miscellaneousSet */ + 1, /* trackCount */ + NULL, /* tracks */ + 0, /* instrumentCount */ + NULL, /* instruments */ + 0, /* miscellaneousCount */ + NULL /* miscellaneous */ +}; + +int _af_raw_compression_types[_AF_RAW_NUM_COMPTYPES] = +{ + AF_COMPRESSION_G711_ULAW, + AF_COMPRESSION_G711_ALAW +}; + +bool _af_raw_recognize (AFvirtualfile *fh) +{ + return AF_FALSE; +} + +status _af_raw_read_init (AFfilesetup filesetup, AFfilehandle filehandle) +{ + _Track *track; + + if (filesetup == NULL) + { + _af_error(AF_BAD_FILEHANDLE, "a valid AFfilesetup is required for reading raw data"); + return AF_FAIL; + } + + if (_af_filesetup_make_handle(filesetup, filehandle) == AF_FAIL) + return AF_FAIL; + + track = &filehandle->tracks[0]; + + /* Set the track's data offset. */ + if (filesetup->tracks[0].dataOffsetSet) + track->fpos_first_frame = filesetup->tracks[0].dataOffset; + else + track->fpos_first_frame = 0; + + /* Set the track's frame count. */ + if (filesetup->tracks[0].frameCountSet) + { + track->totalfframes = filesetup->tracks[0].frameCount; + } + else + { + AFfileoffset filesize; + filesize = af_flength(filehandle->fh); + if (filesize == -1) + track->totalfframes = -1; + else + { + /* Ensure that the data offset is valid. */ + if (track->fpos_first_frame > filesize) + { + _af_error(AF_BAD_FILESETUP, "data offset is larger than file size"); + return AF_FAIL; + } + + filesize -= track->fpos_first_frame; + track->totalfframes = filesize / _af_format_frame_size(&track->f, AF_FALSE); + } + track->data_size = filesize; + } + + return AF_SUCCEED; +} + +status _af_raw_write_init (AFfilesetup filesetup, AFfilehandle filehandle) +{ + _Track *track; + + if (_af_filesetup_make_handle(filesetup, filehandle) == AF_FAIL) + return AF_FAIL; + + track = &filehandle->tracks[0]; + track->totalfframes = 0; + if (filesetup->tracks[0].dataOffsetSet) + track->fpos_first_frame = filesetup->tracks[0].dataOffset; + else + track->fpos_first_frame = 0; + + return AF_SUCCEED; +} + +status _af_raw_update (AFfilehandle filehandle) +{ + return AF_SUCCEED; +} + +AFfilesetup _af_raw_complete_setup (AFfilesetup setup) +{ + AFfilesetup newSetup; + _TrackSetup *track; + + if (setup->trackSet && setup->trackCount != 1) + { + _af_error(AF_BAD_FILESETUP, "raw file must have exactly one track"); + return AF_NULL_FILESETUP; + } + + if ((track = _af_filesetup_get_tracksetup(setup, AF_DEFAULT_TRACK)) == NULL) + { + _af_error(AF_BAD_FILESETUP, "could not access track in file setup"); + return AF_NULL_FILESETUP; + } + + if (track->aesDataSet) + { + _af_error(AF_BAD_FILESETUP, "raw file cannot have AES data"); + return AF_NULL_FILESETUP; + } + + if (track->markersSet && track->markerCount != 0) + { + _af_error(AF_BAD_NUMMARKS, "raw file cannot have markers"); + return AF_NULL_FILESETUP; + } + + if (setup->instrumentSet && setup->instrumentCount != 0) + { + _af_error(AF_BAD_NUMINSTS, "raw file cannot have instruments"); + return AF_NULL_FILESETUP; + } + + if (setup->miscellaneousSet && setup->miscellaneousCount != 0) + { + _af_error(AF_BAD_NUMMISC, "raw file cannot have miscellaneous data"); + return AF_NULL_FILESETUP; + } + + newSetup = _af_malloc(sizeof (_AFfilesetup)); + *newSetup = _af_raw_default_filesetup; + + newSetup->tracks = _af_malloc(sizeof (_TrackSetup)); + newSetup->tracks[0] = setup->tracks[0]; + newSetup->tracks[0].f.compressionParams = NULL; + + newSetup->tracks[0].markerCount = 0; + newSetup->tracks[0].markers = NULL; + + return newSetup; +} diff --git a/libaudiofile/raw.h b/libaudiofile/raw.h new file mode 100644 index 0000000..a716b06 --- /dev/null +++ b/libaudiofile/raw.h @@ -0,0 +1,36 @@ +/* + Audio File Library + Copyright (C) 2000, Silicon Graphics, Inc. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307 USA. +*/ + +/* + raw.h +*/ + +#ifndef RAW_H +#define RAW_H + +bool _af_raw_recognize (AFvirtualfile *fh); +status _af_raw_read_init (AFfilesetup filesetup, AFfilehandle filehandle); +status _af_raw_write_init (AFfilesetup filesetup, AFfilehandle filehandle); +status _af_raw_update (AFfilehandle filehandle); +AFfilesetup _af_raw_complete_setup (AFfilesetup); + +#define _AF_RAW_NUM_COMPTYPES 2 + +#endif /* RAW_H */ diff --git a/libaudiofile/setup.c b/libaudiofile/setup.c new file mode 100644 index 0000000..8c3ab42 --- /dev/null +++ b/libaudiofile/setup.c @@ -0,0 +1,744 @@ +/* + Audio File Library + Copyright (C) 2000, Silicon Graphics, Inc. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307 USA. +*/ + +/* + setup.c +*/ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <stdlib.h> +#include <string.h> + +#include <audiofile.h> + +#include "afinternal.h" +#include "pcm.h" +#include "util.h" +#include "units.h" +#include "marker.h" + +extern _Unit _af_units[]; + +_AFfilesetup _af_default_file_setup = +{ + _AF_VALID_FILESETUP, /* valid */ +#if WORDS_BIGENDIAN + AF_FILE_AIFFC, /* file format */ +#else + AF_FILE_WAVE, /* file format */ +#endif + AF_FALSE, /* trackSet */ + AF_FALSE, /* instrumentSet */ + AF_FALSE, /* miscellaneousSet */ + 1, /* trackCount */ + NULL, /* tracks */ + 1, /* instrumentCount */ + NULL, /* instruments */ + 0, /* miscellaneousCount */ + NULL /* miscellaneous */ +}; + +_InstrumentSetup _af_default_instrumentsetup = +{ + 0, /* id */ + 2, /* loopCount */ + NULL, /* loops */ + AF_FALSE /* loopSet */ +}; + +_TrackSetup _af_default_tracksetup = +{ + 0, + { + 44100.0, + AF_SAMPFMT_TWOSCOMP, + 16, +#if WORDS_BIGENDIAN + AF_BYTEORDER_BIGENDIAN, +#else + AF_BYTEORDER_LITTLEENDIAN, +#endif + { SLOPE_INT16, 0, MIN_INT16, MAX_INT16 }, + 2, + AF_COMPRESSION_NONE, + NULL + }, + AF_FALSE, /* rateSet */ + AF_FALSE, /* sampleFormatSet */ + AF_FALSE, /* sampleWidthSet */ + AF_FALSE, /* byteOrderSet */ + AF_FALSE, /* channelCountSet */ + AF_FALSE, /* compressionSet */ + AF_FALSE, /* aesDataSet */ + AF_FALSE, /* markersSet */ + AF_FALSE, /* dataOffsetSet */ + AF_FALSE, /* frameCountSet */ + + 4, /* markerCount */ + NULL, /* markers */ + 0, /* dataOffset */ + 0 /* frameCount */ +}; + +_TrackSetup *_af_tracksetup_new (int trackCount) +{ + int i; + _TrackSetup *tracks; + + if (trackCount == 0) + return NULL; + + tracks = _af_calloc(trackCount, sizeof (_TrackSetup)); + if (tracks == NULL) + return NULL; + + for (i=0; i<trackCount; i++) + { + tracks[i] = _af_default_tracksetup; + + tracks[i].id = AF_DEFAULT_TRACK + i; + + /* XXXmpruett deal with compression */ + + _af_set_sample_format(&tracks[i].f, tracks[i].f.sampleFormat, + tracks[i].f.sampleWidth); + + if (tracks[i].markerCount == 0) + tracks[i].markers = NULL; + else + { + int j; + tracks[i].markers = _af_calloc(tracks[i].markerCount, + sizeof (_MarkerSetup)); + + if (tracks[i].markers == NULL) + return NULL; + + for (j=0; j<tracks[i].markerCount; j++) + { + tracks[i].markers[j].id = j+1; + + tracks[i].markers[j].name = _af_strdup(""); + if (tracks[i].markers[j].name == NULL) + return NULL; + + tracks[i].markers[j].comment = _af_strdup(""); + if (tracks[i].markers[j].comment == NULL) + return NULL; + } + } + } + + return tracks; +} + +_InstrumentSetup *_af_instsetup_new (int instrumentCount) +{ + int i; + + _InstrumentSetup *instruments; + + if (instrumentCount == 0) + return NULL; + instruments = _af_calloc(instrumentCount, sizeof (_InstrumentSetup)); + if (instruments == NULL) + return NULL; + + for (i=0; i<instrumentCount; i++) + { + instruments[i] = _af_default_instrumentsetup; + instruments[i].id = AF_DEFAULT_INST + i; + if (instruments[i].loopCount == 0) + instruments[i].loops = NULL; + else + { + int j; + instruments[i].loops = _af_calloc(instruments[i].loopCount, sizeof (_LoopSetup)); + if (instruments[i].loops == NULL) + return NULL; + + for (j=0; j<instruments[i].loopCount; j++) + instruments[i].loops[j].id = j+1; + } + } + + return instruments; +} + +AFfilesetup afNewFileSetup (void) +{ + AFfilesetup setup; + + setup = _af_malloc(sizeof (_AFfilesetup)); + if (setup == NULL) return AF_NULL_FILESETUP; + + *setup = _af_default_file_setup; + + setup->tracks = _af_tracksetup_new(setup->trackCount); + + setup->instruments = _af_instsetup_new(setup->instrumentCount); + + if (setup->miscellaneousCount == 0) + setup->miscellaneous = NULL; + else + { + int i; + + setup->miscellaneous = _af_calloc(setup->miscellaneousCount, + sizeof (_MiscellaneousSetup)); + for (i=0; i<setup->miscellaneousCount; i++) + { + setup->miscellaneous[i].id = i+1; + setup->miscellaneous[i].type = 0; + setup->miscellaneous[i].size = 0; + } + } + + return setup; +} + +/* + Free the specified track's markers and their subfields. +*/ +void _af_setup_free_markers (AFfilesetup setup, int trackno) +{ + if (setup->tracks[trackno].markerCount != 0) + { + int i; + for (i=0; i<setup->tracks[trackno].markerCount; i++) + { + free(setup->tracks[trackno].markers[i].name); + free(setup->tracks[trackno].markers[i].comment); + } + + free(setup->tracks[trackno].markers); + } + + setup->tracks[trackno].markers = NULL; + setup->tracks[trackno].markerCount = 0; +} + +/* + Free the specified setup's tracks and their subfields. +*/ +void _af_setup_free_tracks (AFfilesetup setup) +{ + int i; + + if (setup->tracks) + { + for (i=0; i<setup->trackCount; i++) + { + _af_setup_free_markers(setup, i); + } + + free(setup->tracks); + } + + setup->tracks = NULL; + setup->trackCount = 0; +} + +/* + Free the specified instrument's loops. +*/ +void _af_setup_free_loops (AFfilesetup setup, int instno) +{ + if (setup->instruments[instno].loops) + { + free(setup->instruments[instno].loops); + } + + setup->instruments[instno].loops = NULL; + setup->instruments[instno].loopCount = 0; +} + +/* + Free the specified setup's instruments and their subfields. +*/ +void _af_setup_free_instruments (AFfilesetup setup) +{ + int i; + + if (setup->instruments) + { + for (i = 0; i < setup->instrumentCount; i++) + _af_setup_free_loops(setup, i); + + free(setup->instruments); + } + + setup->instruments = NULL; + setup->instrumentCount = 0; +} + +void afFreeFileSetup (AFfilesetup setup) +{ + if (!_af_filesetup_ok(setup)) + return; + + _af_setup_free_tracks(setup); + + _af_setup_free_instruments(setup); + + if (setup->miscellaneousCount) + { + free(setup->miscellaneous); + setup->miscellaneous = NULL; + setup->miscellaneousCount = 0; + } + + memset(setup, 0, sizeof (_AFfilesetup)); + free(setup); +} + +void afInitFileFormat (AFfilesetup setup, int filefmt) +{ + if (!_af_filesetup_ok(setup)) + return; + + if (filefmt < 0 || filefmt > _AF_NUM_UNITS) + { + _af_error(AF_BAD_FILEFMT, "unrecognized file format %d", + filefmt); + return; + } + + if (_af_units[filefmt].implemented == AF_FALSE) + { + _af_error(AF_BAD_NOT_IMPLEMENTED, + "%s format not currently supported", + _af_units[filefmt].name); + return; + } + + setup->fileFormat = filefmt; +} + +void afInitChannels (AFfilesetup setup, int trackid, int channels) +{ + _TrackSetup *track; + + if (!_af_filesetup_ok(setup)) + return; + + if ((track = _af_filesetup_get_tracksetup(setup, trackid)) == NULL) + return; + + if (channels < 1) + { + _af_error(AF_BAD_CHANNELS, "invalid number of channels %d", + channels); + return; + } + + track->f.channelCount = channels; + track->channelCountSet = AF_TRUE; +} + +void afInitSampleFormat (AFfilesetup setup, int trackid, int sampfmt, int sampwidth) +{ + _TrackSetup *track; + + if (!_af_filesetup_ok(setup)) + return; + + if ((track = _af_filesetup_get_tracksetup(setup, trackid)) == NULL) + return; + + _af_set_sample_format(&track->f, sampfmt, sampwidth); + + track->sampleFormatSet = AF_TRUE; + track->sampleWidthSet = AF_TRUE; +} + +void afInitByteOrder (AFfilesetup setup, int trackid, int byteorder) +{ + _TrackSetup *track; + + if (!_af_filesetup_ok(setup)) + return; + + if ((track = _af_filesetup_get_tracksetup(setup, trackid)) == NULL) + return; + + if (byteorder != AF_BYTEORDER_BIGENDIAN && + byteorder != AF_BYTEORDER_LITTLEENDIAN) + { + _af_error(AF_BAD_BYTEORDER, "invalid byte order %d", byteorder); + return; + } + + track->f.byteOrder = byteorder; + track->byteOrderSet = AF_TRUE; +} + +void afInitRate (AFfilesetup setup, int trackid, double rate) +{ + _TrackSetup *track; + + if (!_af_filesetup_ok(setup)) + return; + + if ((track = _af_filesetup_get_tracksetup(setup, trackid)) == NULL) + return; + + if (rate <= 0.0) + { + _af_error(AF_BAD_RATE, "invalid sample rate %.30g", rate); + return; + } + + track->f.sampleRate = rate; + track->rateSet = AF_TRUE; +} + +/* + track data: data offset within the file (initialized for raw reading only) +*/ +void afInitDataOffset (AFfilesetup setup, int trackid, AFfileoffset offset) +{ + _TrackSetup *track; + + if (!_af_filesetup_ok(setup)) + return; + + if ((track = _af_filesetup_get_tracksetup(setup, trackid)) == NULL) + return; + + if (offset < 0) + { + _af_error(AF_BAD_DATAOFFSET, "invalid data offset %d", offset); + return; + } + + track->dataOffset = offset; + track->dataOffsetSet = AF_TRUE; +} + +/* + track data: data offset within the file (initialized for raw reading only) +*/ +void afInitFrameCount (AFfilesetup setup, int trackid, AFfileoffset count) +{ + _TrackSetup *track; + + if (!_af_filesetup_ok(setup)) + return; + + if ((track = _af_filesetup_get_tracksetup(setup, trackid)) == NULL) + return; + + if (count < 0) + { + _af_error(AF_BAD_FRAMECNT, "invalid frame count %d", count); + return; + } + + track->frameCount = count; + track->frameCountSet = AF_TRUE; +} + +#define alloccopy(type, n, var, copyfrom) \ +{ \ + if (n == 0) \ + var = NULL; \ + else \ + { \ + if ((var = _af_calloc(n, sizeof (type))) == NULL) \ + goto fail; \ + memcpy((var), (copyfrom), (n) * sizeof (type)); \ + } \ +} + +AFfilesetup _af_filesetup_copy (AFfilesetup setup, AFfilesetup defaultSetup, + bool copyMarks) +{ + AFfilesetup newsetup; + int i; + int instrumentCount, miscellaneousCount, trackCount; + + newsetup = _af_malloc(sizeof (_AFfilesetup)); + if (newsetup == AF_NULL_FILESETUP) + return AF_NULL_FILESETUP; + +#ifdef DEBUG + printf("default: trackset=%d instset=%d miscset=%d\n", + defaultSetup->trackSet, defaultSetup->instrumentSet, + defaultSetup->miscellaneousSet); + printf("setup: trackset=%d instset=%d miscset=%d\n", + setup->trackSet, setup->instrumentSet, setup->miscellaneousSet); +#endif + + *newsetup = *defaultSetup; + + newsetup->tracks = NULL; + newsetup->instruments = NULL; + newsetup->miscellaneous = NULL; + + /* Copy tracks. */ + trackCount = setup->trackSet ? setup->trackCount : + newsetup->trackSet ? newsetup->trackCount : 0; + alloccopy(_TrackSetup, trackCount, newsetup->tracks, setup->tracks); + newsetup->trackCount = trackCount; + + /* Copy instruments. */ + instrumentCount = setup->instrumentSet ? setup->instrumentCount : + newsetup->instrumentSet ? newsetup->instrumentCount : 0; + alloccopy(_InstrumentSetup, instrumentCount, newsetup->instruments, setup->instruments); + newsetup->instrumentCount = instrumentCount; + + /* Copy miscellaneous information. */ + miscellaneousCount = setup->miscellaneousSet ? setup->miscellaneousCount : + newsetup->miscellaneousSet ? newsetup->miscellaneousCount : 0; + alloccopy(_MiscellaneousSetup, miscellaneousCount, newsetup->miscellaneous, setup->miscellaneous); + newsetup->miscellaneousCount = miscellaneousCount; + + for (i=0; i<setup->trackCount; i++) + { + int j; + _TrackSetup *track = &newsetup->tracks[i]; + + /* XXXmpruett set compression information */ + + if (setup->tracks[i].markersSet == AF_FALSE && + copyMarks == AF_FALSE) + { + track->markers = NULL; + track->markerCount = 0; + continue; + } + + alloccopy(_MarkerSetup, setup->tracks[i].markerCount, + track->markers, setup->tracks[i].markers); + track->markerCount = setup->tracks[i].markerCount; + + for (j=0; j<setup->tracks[i].markerCount; j++) + { + track->markers[j].name = + _af_strdup(setup->tracks[i].markers[j].name); + if (track->markers[j].name == NULL) + goto fail; + + track->markers[j].comment = + _af_strdup(setup->tracks[i].markers[j].comment); + if (track->markers[j].comment == NULL) + goto fail; + } + } + + for (i=0; i<newsetup->instrumentCount; i++) + { + _InstrumentSetup *instrument = &newsetup->instruments[i]; + alloccopy(_LoopSetup, setup->instruments[i].loopCount, + instrument->loops, setup->instruments[i].loops); + } + + return newsetup; + + fail: + if (newsetup->miscellaneous) + free(newsetup->miscellaneous); + if (newsetup->instruments) + free(newsetup->instruments); + if (newsetup->tracks) + free(newsetup->tracks); + if (newsetup) + free(newsetup); + + return AF_NULL_FILESETUP; +} + +/* + Do all the non-file-format dependent things necessary to "convert" + a filesetup into a filehandle. + + This function assumes that the track count, instrument count, + etc. of given filesetup is okay for the file format. + + Called from write.init and raw read.init unit functions. + + This function does NOT SET ALL THE FIELDS of the filesetup. + You must be careful when writing a unit to set the fields + you are supposed to (described below). + + These fields are not set here, so are somebody else's problem: + - handle: valid, fd, access, filename, fileFormat, seekok (set in afOpenFile) + - handle: formatSpecific (UNIT MUST SET! (set to NULL if no data)) + + - track: virtual format v, modulesdirty, nmodules, module, chunk, + (buffers), totalvframes, nextvframe, channelmatrix, frames2ignore + (these are handled by _AFinitmodules and _AFsetupmodules) + + - track: totalfframes, nextfframe, fpos_first_frame, + fpos_next_frame, data_size (UNIT MUST SET!) + + - mark: fpos_position (UNIT MUST SET!) +*/ + +status _af_filesetup_make_handle (AFfilesetup setup, AFfilehandle handle) +{ + int i; + + handle->valid = _AF_VALID_FILEHANDLE; + + if ((handle->trackCount = setup->trackCount) == 0) + handle->tracks = NULL; + else + { + handle->tracks = _af_calloc(handle->trackCount, sizeof (_Track)); + if (handle->tracks == NULL) + return AF_FAIL; + + for (i=0; i<handle->trackCount; i++) + { + _Track *track = &handle->tracks[i]; + _TrackSetup *tracksetup = &setup->tracks[i]; + + track->id = tracksetup->id; + + track->f = tracksetup->f; + track->channelMatrix = NULL; + + /* XXXmpruett copy compression stuff too */ + + if ((track->markerCount = tracksetup->markerCount) == 0) + track->markers = NULL; + else + { + int j; + + track->markers = _af_marker_new(track->markerCount); + if (track->markers == NULL) + return AF_FAIL; + for (j=0; j<track->markerCount; j++) + { + track->markers[j].id = tracksetup->markers[j].id; + track->markers[j].name = + _af_strdup(tracksetup->markers[j].name); + if (track->markers[j].name == NULL) + return AF_FAIL; + + track->markers[j].comment = + _af_strdup(tracksetup->markers[j].comment); + if (track->markers[j].comment == NULL) + return AF_FAIL; + track->markers[j].position = 0; + } + } + + track->hasAESData = tracksetup->aesDataSet; + + track->ms.modulesdirty = AF_TRUE; + track->ms.nmodules = 0; + track->ms.chunk = NULL; + track->ms.module = NULL; + track->ms.buffer = NULL; + + track->ms.filemodinst.valid = AF_FALSE; + track->ms.filemod_rebufferinst.valid = AF_FALSE; + track->ms.rateconvertinst.valid = AF_FALSE; + track->ms.rateconvert_rebufferinst.valid = AF_FALSE; + } + } + + /* Copy instrument data. */ + if ((handle->instrumentCount = setup->instrumentCount) == 0) + handle->instruments = NULL; + else + { + handle->instruments = _af_calloc(handle->instrumentCount, + sizeof (_Instrument)); + if (handle->instruments == NULL) + return AF_FAIL; + + for (i=0; i<handle->instrumentCount; i++) + { + int instParamCount; + + handle->instruments[i].id = setup->instruments[i].id; + + /* Copy loops. */ + if ((handle->instruments[i].loopCount = + setup->instruments[i].loopCount) == 0) + handle->instruments[i].loops = NULL; + else + { + int j; + + handle->instruments[i].loops = _af_calloc(handle->instruments[i].loopCount, sizeof (_Loop)); + if (handle->instruments[i].loops == NULL) + return AF_FAIL; + for (j=0; j<handle->instruments[i].loopCount; j++) + { + _Loop *loop; + loop = &handle->instruments[i].loops[j]; + loop->id = setup->instruments[i].loops[j].id; + loop->mode = AF_LOOP_MODE_NOLOOP; + loop->count = 0; + loop->trackid = AF_DEFAULT_TRACK; + loop->beginMarker = 1 + (2*j); + loop->endMarker = 2 + (2*j); + } + } + + /* Copy instrument parameters. */ + if ((instParamCount = _af_units[setup->fileFormat].instrumentParameterCount) == 0) + handle->instruments[i].values = NULL; + else + { + int j; + handle->instruments[i].values = _af_calloc(instParamCount, sizeof (AFPVu)); + if (handle->instruments[i].values == NULL) + return AF_FAIL; + for (j=0; j<instParamCount; j++) + { + handle->instruments[i].values[j] = + _af_units[setup->fileFormat].instrumentParameters[j].defaultValue; + } + } + } + } + + /* Copy miscellaneous information. */ + + if ((handle->miscellaneousCount = setup->miscellaneousCount) == 0) + handle->miscellaneous = NULL; + else + { + handle->miscellaneous = _af_calloc(handle->miscellaneousCount, + sizeof (_Miscellaneous)); + if (handle->miscellaneous == NULL) + return AF_FAIL; + for (i=0; i<handle->miscellaneousCount; i++) + { + handle->miscellaneous[i].id = setup->miscellaneous[i].id; + handle->miscellaneous[i].type = setup->miscellaneous[i].type; + handle->miscellaneous[i].size = setup->miscellaneous[i].size; + handle->miscellaneous[i].position = 0; + handle->miscellaneous[i].buffer = NULL; + } + } + + return AF_SUCCEED; +} diff --git a/libaudiofile/setup.h b/libaudiofile/setup.h new file mode 100644 index 0000000..aed9edc --- /dev/null +++ b/libaudiofile/setup.h @@ -0,0 +1,37 @@ +/* + Audio File Library + Copyright (C) 1998-2000, Michael Pruett <michael@68k.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307 USA. +*/ + +#ifndef SETUP_H +#define SETUP_H + +AFfilesetup _af_filesetup_copy (AFfilesetup setup, AFfilesetup defaultSetup, + bool copyMarks); + +void _af_setup_free_markers (AFfilesetup setup, int trackno); +void _af_setup_free_tracks (AFfilesetup setup); +void _af_setup_free_loops (AFfilesetup setup, int instno); +void _af_setup_free_instruments (AFfilesetup setup); +AFfilesetup _af_filesetup_copy (AFfilesetup setup, AFfilesetup defaultSetup, + bool copyMarks); +status _af_filesetup_make_handle (AFfilesetup setup, AFfilehandle handle); + +_InstrumentSetup *_af_instsetup_new (int count); + +#endif /* SETUP_H */ diff --git a/libaudiofile/track.c b/libaudiofile/track.c new file mode 100644 index 0000000..53d5900 --- /dev/null +++ b/libaudiofile/track.c @@ -0,0 +1,97 @@ +/* + Audio File Library + Copyright (C) 1998, Michael Pruett <michael@68k.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307 USA. +*/ + +/* + track.c + + This file contains functions for dealing with tracks within an + audio file. +*/ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <stddef.h> +#include <string.h> +#include <assert.h> + +#include "audiofile.h" +#include "afinternal.h" +#include "util.h" + +void afInitTrackIDs (AFfilesetup file, int *trackids, int trackCount) +{ + assert(file); + assert(trackids); + assert(trackCount == 1); + assert(trackids[0] == AF_DEFAULT_TRACK); +} + +int afGetTrackIDs (AFfilehandle file, int *trackids) +{ + assert(file); + + if (trackids != NULL) + trackids[0] = AF_DEFAULT_TRACK; + + return 1; +} + +_Track *_af_track_new (void) +{ + _Track *t = _af_malloc(sizeof (_Track)); + + t->id = AF_DEFAULT_TRACK; + + t->f.compressionParams = NULL; + t->v.compressionParams = NULL; + + t->channelMatrix = NULL; + + t->markerCount = 0; + t->markers = NULL; + + t->hasAESData = AF_FALSE; + memset(t->aesData, 0, 24); + + t->totalfframes = 0; + t->nextfframe = 0; + t->frames2ignore = 0; + t->fpos_first_frame = 0; + t->fpos_next_frame = 0; + t->fpos_after_data = 0; + t->totalvframes = 0; + t->nextvframe = 0; + t->data_size = 0; + + t->ms.modulesdirty = AF_TRUE; + t->ms.nmodules = 0; + t->ms.chunk = NULL; + t->ms.module = NULL; + t->ms.buffer = NULL; + + t->ms.filemodinst.valid = AF_FALSE; + t->ms.filemod_rebufferinst.valid = AF_FALSE; + t->ms.rateconvertinst.valid = AF_FALSE; + t->ms.rateconvert_rebufferinst.valid = AF_FALSE; + + return t; +} diff --git a/libaudiofile/track.h b/libaudiofile/track.h new file mode 100644 index 0000000..d92d73c --- /dev/null +++ b/libaudiofile/track.h @@ -0,0 +1,30 @@ +/* + Audio File Library + Copyright (C) 2000, Silicon Graphics, Inc. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307 USA. +*/ + +/* + track.h +*/ + +#ifndef TRACK_H +#define TRACK_H + +_Track *_af_track_new (void); + +#endif /* TRACK_H */ diff --git a/libaudiofile/units.c b/libaudiofile/units.c new file mode 100644 index 0000000..8f8741c --- /dev/null +++ b/libaudiofile/units.c @@ -0,0 +1,291 @@ +/* + Audio File Library + Copyright (C) 2000-2001, Silicon Graphics, Inc. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307 USA. +*/ + +/* + units.c + + This file contains the file format units. +*/ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include "audiofile.h" +#include "afinternal.h" +#include "units.h" + +#include "raw.h" +#include "aiff.h" +#include "next.h" +#include "wave.h" +#include "ircam.h" +#include "avr.h" +#include "iff.h" +#include "nist.h" + +#include "compression.h" + +#include "modules/pcm.h" +#include "modules/g711.h" +#include "modules/ima.h" +#include "modules/msadpcm.h" + +extern _InstParamInfo _af_aiff_inst_params[]; +extern _InstParamInfo _af_wave_inst_params[]; + +extern int _af_raw_compression_types[]; +extern int _af_aiffc_compression_types[]; +extern int _af_next_compression_types[]; +extern int _af_wave_compression_types[]; + +_Unit _af_units[_AF_NUM_UNITS] = +{ + { + AF_FILE_RAWDATA, + "Raw Data", "Raw Sound Data", "raw", + AF_TRUE, NULL, _af_raw_complete_setup, + {_af_raw_recognize, _af_raw_read_init}, + {_af_raw_write_init, NULL, NULL}, + AF_SAMPFMT_TWOSCOMP, 16, + _AF_RAW_NUM_COMPTYPES, + _af_raw_compression_types, + 0, /* maximum marker count */ + 0, /* maximum instrument count */ + 0, /* maxium number of loops per instrument */ + 0, NULL, + }, + { + AF_FILE_AIFFC, + "AIFF-C", "AIFF-C File Format", "aifc", + AF_TRUE, _af_aifc_get_version, _af_aiff_complete_setup, + {_af_aifc_recognize, _af_aiff_read_init}, + {_af_aiff_write_init, _af_aiff_instparam_valid, _af_aiff_update}, + AF_SAMPFMT_TWOSCOMP, 16, + _AF_AIFFC_NUM_COMPTYPES, + _af_aiffc_compression_types, + 65535, /* maximum marker count */ + 1, /* maximum instrument count */ + 2, /* maximum number of loops per instrument */ + _AF_AIFF_NUM_INSTPARAMS, + _af_aiff_inst_params + }, + { + AF_FILE_AIFF, + "AIFF", "Audio Interchange File Format", "aiff", + AF_TRUE, NULL, _af_aiff_complete_setup, + {_af_aiff_recognize, _af_aiff_read_init}, + {_af_aiff_write_init, _af_aiff_instparam_valid, _af_aiff_update}, + AF_SAMPFMT_TWOSCOMP, 16, + 0, /* supported compression types */ + NULL, + 65535, /* maximum marker count */ + 1, /* maximum instrument count */ + 2, /* maximum number of loops per instrument */ + _AF_AIFF_NUM_INSTPARAMS, + _af_aiff_inst_params + }, + { + AF_FILE_NEXTSND, + "NeXT .snd/Sun .au", "NeXT .snd/Sun .au Format", "next", + AF_TRUE, NULL, _af_next_complete_setup, + {_af_next_recognize, _af_next_read_init}, + {_af_next_write_init, NULL, _af_next_update}, + AF_SAMPFMT_TWOSCOMP, 16, + _AF_NEXT_NUM_COMPTYPES, + _af_next_compression_types, + 0, /* maximum marker count */ + 0, /* maximum instrument count */ + 0, /* maximum number of loops per instrument */ + 0, NULL + }, + { + AF_FILE_WAVE, + "MS RIFF WAVE", "Microsoft RIFF WAVE Format", "wave", + AF_TRUE, NULL, _af_wave_complete_setup, + {_af_wave_recognize, _af_wave_read_init}, + {_af_wave_write_init, _af_wave_instparam_valid, _af_wave_update}, + AF_SAMPFMT_TWOSCOMP, 16, + _AF_WAVE_NUM_COMPTYPES, + _af_wave_compression_types, + AF_NUM_UNLIMITED, /* maximum marker count */ + 1, /* maximum instrument count */ + AF_NUM_UNLIMITED, /* maximum number of loops per instrument */ + _AF_WAVE_NUM_INSTPARAMS, + _af_wave_inst_params + }, + { + AF_FILE_IRCAM, + "BICSF", "Berkeley/IRCAM/CARL Sound Format", "bicsf", + AF_TRUE, NULL, _af_ircam_complete_setup, + {_af_ircam_recognize, _af_ircam_read_init}, + {_af_ircam_write_init, NULL, _af_ircam_update}, + AF_SAMPFMT_TWOSCOMP, 16, + 0, /* number of compression types */ + NULL, /* compression types */ + 0, /* maximum marker count */ + 0, /* maximum instrument count */ + 0, /* maximum number of loops per instrument */ + 0, /* number of instrument parameters */ + NULL /* instrument parameters */ + }, + { + AF_FILE_MPEG1BITSTREAM, + "MPEG", "MPEG Audio Bitstream", "mpeg", + AF_FALSE + }, + { + AF_FILE_SOUNDDESIGNER1, + "Sound Designer 1", "Sound Designer 1 File Format", "sd1", + AF_FALSE + }, + { + AF_FILE_SOUNDDESIGNER2, + "Sound Designer 2", "Sound Designer 2 File Format", "sd2", + AF_FALSE + }, + { + AF_FILE_AVR, + "AVR", "Audio Visual Research File Format", "avr", + AF_TRUE, NULL, _af_avr_complete_setup, + {_af_avr_recognize, _af_avr_read_init}, + {_af_avr_write_init, NULL, _af_avr_update}, + AF_SAMPFMT_TWOSCOMP, 16, + 0, /* number of compression types */ + NULL, /* compression types */ + 0, /* maximum marker count */ + 0, /* maximum instrument count */ + 0, /* maximum number of loops per instrument */ + 0, /* number of instrument parameters */ + }, + { + AF_FILE_IFF_8SVX, + "IFF/8SVX", "Amiga IFF/8SVX Sound File Format", "iff", + AF_TRUE, NULL, _af_iff_complete_setup, + {_af_iff_recognize, _af_iff_read_init}, + {_af_iff_write_init, NULL, _af_iff_update}, + AF_SAMPFMT_TWOSCOMP, 8, + 0, /* number of compression types */ + NULL, /* compression types */ + 0, /* maximum marker count */ + 0, /* maximum instrument count */ + 0, /* maximum number of loops per instrument */ + 0, /* number of instrument parameters */ + }, + { + AF_FILE_SAMPLEVISION, + "Sample Vision", "Sample Vision File Format", "smp", + AF_FALSE + }, + { + AF_FILE_VOC, + "VOC", "Creative Voice File Format", "voc", + AF_FALSE + }, + { + AF_FILE_NIST_SPHERE, + "NIST SPHERE", "NIST SPHERE File Format", "nist", + AF_TRUE, NULL, _af_nist_complete_setup, + {_af_nist_recognize, _af_nist_read_init}, + {_af_nist_write_init, NULL, _af_nist_update}, + AF_SAMPFMT_TWOSCOMP, 16, + 0, /* number of compression types */ + NULL, /* compression types */ + 0, /* maximum marker count */ + 0, /* maximum instrument count */ + 0, /* maximum number of loops per instrument */ + 0, /* number of instrument parameters */ + NULL /* instrument parameters */ + }, + { + AF_FILE_SOUNDFONT2, + "SoundFont 2", "SoundFont 2 File Format", "sf2", + AF_FALSE + } +}; + +_CompressionUnit _af_compression[_AF_NUM_COMPRESSION] = +{ + { + AF_COMPRESSION_NONE, + AF_TRUE, + "none", /* label */ + "none", /* short name */ + "not compressed", + 1.0, + AF_SAMPFMT_TWOSCOMP, 16, + AF_FALSE, /* needsRebuffer */ + AF_FALSE, /* multiple_of */ + _af_pcm_format_ok, + _AFpcminitcompress, _AFpcminitdecompress + }, + { + AF_COMPRESSION_G711_ULAW, + AF_TRUE, + "ulaw", /* label */ + "CCITT G.711 u-law", /* shortname */ + "CCITT G.711 u-law", + 2.0, + AF_SAMPFMT_TWOSCOMP, 16, + AF_FALSE, /* needsRebuffer */ + AF_FALSE, /* multiple_of */ + _af_g711_format_ok, + _AFg711initcompress, _AFg711initdecompress + }, + { + AF_COMPRESSION_G711_ALAW, + AF_TRUE, + "alaw", /* label */ + "CCITT G.711 A-law", /* short name */ + "CCITT G.711 A-law", + 2.0, + AF_SAMPFMT_TWOSCOMP, 16, + AF_FALSE, /* needsRebuffer */ + AF_FALSE, /* multiple_of */ + _af_g711_format_ok, + _AFg711initcompress, _AFg711initdecompress + }, + { + AF_COMPRESSION_IMA, + AF_TRUE, + "ima4", /* label */ + "IMA ADPCM", /* short name */ + "IMA DVI ADPCM", + 4.0, + AF_SAMPFMT_TWOSCOMP, 16, + AF_TRUE, /* needsRebuffer */ + AF_FALSE, /* multiple_of */ + _af_ima_adpcm_format_ok, + NULL, _af_ima_adpcm_init_decompress + }, + { + AF_COMPRESSION_MS_ADPCM, + AF_TRUE, + "msadpcm", /* label */ + "MS ADPCM", /* short name */ + "Microsoft ADPCM", + 4.0, + AF_SAMPFMT_TWOSCOMP, 16, + AF_TRUE, /* needsRebuffer */ + AF_FALSE, /* multiple_of */ + _af_ms_adpcm_format_ok, + NULL, _af_ms_adpcm_init_decompress + } +}; diff --git a/libaudiofile/units.h b/libaudiofile/units.h new file mode 100644 index 0000000..389afb5 --- /dev/null +++ b/libaudiofile/units.h @@ -0,0 +1,100 @@ +/* + Audio File Library + Copyright (C) 2000, Michael Pruett <michael@68k.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307 USA. +*/ + +/* + units.h + + This file defines the internal _Unit and _CompressionUnit + structures for the Audio File Library. +*/ + +#ifndef UNIT_H +#define UNIT_H + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include "audiofile.h" +#include "afinternal.h" + +typedef struct _Unit +{ + int fileFormat; /* AF_FILEFMT_... */ + char *name; /* a 2-3 word name of the file format */ + char *description; /* a more descriptive name for the format */ + char *label; /* a 4-character label for the format */ + bool implemented; /* if implemented */ + + int (*getversion) (AFfilehandle handle); + AFfilesetup (*completesetup) (AFfilesetup setup); + + struct + { + bool (*recognize) (AFvirtualfile *fh); + status (*init) (AFfilesetup, AFfilehandle); + } read; + + struct + { + status (*init) (AFfilesetup, AFfilehandle); + bool (*instparamvalid) (AFfilehandle, AUpvlist, int); + status (*update) (AFfilehandle); + } write; + + int defaultSampleFormat; + int defaultSampleWidth; + + int compressionTypeCount; + int *compressionTypes; + + int markerCount; + + int instrumentCount; + int loopPerInstrumentCount; + + int instrumentParameterCount; + _InstParamInfo *instrumentParameters; +} _Unit; + +typedef struct _CompressionUnit +{ + int compressionID; /* AF_COMPRESSION_... */ + bool implemented; + char *label; /* 4-character (approximately) label */ + char *shortname; /* short name in English */ + char *name; /* long name in English */ + double squishFactor; /* compression ratio */ + int nativeSampleFormat; /* AF_SAMPFMT_... */ + int nativeSampleWidth; /* sample width in bits */ + bool needsRebuffer; /* if there are chunk boundary requirements */ + bool multiple_of; /* can accept any multiple of chunksize */ + bool (*fmtok) (_AudioFormat *format); + + _AFmoduleinst (*initcompress) (_Track *track, AFvirtualfile *fh, + bool seekok, bool headerless, AFframecount *chunkframes); + _AFmoduleinst (*initdecompress) (_Track *track, AFvirtualfile *fh, + bool seekok, bool headerless, AFframecount *chunkframes); +} _CompressionUnit; + +#define _AF_NUM_UNITS 15 +#define _AF_NUM_COMPRESSION 5 + +#endif /* UNIT_H */ diff --git a/libaudiofile/util.c b/libaudiofile/util.c new file mode 100644 index 0000000..f18b3e4 --- /dev/null +++ b/libaudiofile/util.c @@ -0,0 +1,528 @@ +/* + Audio File Library + Copyright (C) 1998-2000, Michael Pruett <michael@68k.org> + Copyright (C) 2000, Silicon Graphics, Inc. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307 USA. +*/ + +/* + util.c + + This file contains general utility routines for the Audio File + Library. +*/ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <assert.h> + +#include "audiofile.h" +#include "aupvlist.h" + +#include "afinternal.h" +#include "util.h" +#include "units.h" +#include "compression.h" +#include "modules.h" +#include "byteorder.h" +#include "aupvinternal.h" + +extern _PCMInfo _af_default_signed_integer_pcm_mappings[]; +extern _PCMInfo _af_default_unsigned_integer_pcm_mappings[]; +extern _PCMInfo _af_default_float_pcm_mapping; +extern _PCMInfo _af_default_double_pcm_mapping; + +extern _CompressionUnit _af_compression[]; + +/* + _af_filesetup_ok and _af_filehandle_ok are sanity check routines + which are called at the beginning of every external subroutine. +*/ +bool _af_filesetup_ok (AFfilesetup setup) +{ + if (setup == AF_NULL_FILESETUP) + { + _af_error(AF_BAD_FILESETUP, "null file setup"); + return AF_FALSE; + } + if (setup->valid != _AF_VALID_FILESETUP) + { + _af_error(AF_BAD_FILESETUP, "invalid file setup"); + return AF_FALSE; + } + return AF_TRUE; +} + +bool _af_filehandle_can_read (AFfilehandle file) +{ + if (file->access != _AF_READ_ACCESS) + { + _af_error(AF_BAD_NOREADACC, "file not opened for read access"); + return AF_FALSE; + } + + return AF_TRUE; +} + +bool _af_filehandle_can_write (AFfilehandle file) +{ + if (file->access != _AF_WRITE_ACCESS) + { + _af_error(AF_BAD_NOWRITEACC, "file not opened for write access"); + return AF_FALSE; + } + + return AF_TRUE; +} + +bool _af_filehandle_ok (AFfilehandle file) +{ + if (file == AF_NULL_FILEHANDLE) + { + _af_error(AF_BAD_FILEHANDLE, "null file handle"); + return AF_FALSE; + } + if (file->valid != _AF_VALID_FILEHANDLE) + { + _af_error(AF_BAD_FILEHANDLE, "invalid file handle"); + return AF_FALSE; + } + return AF_TRUE; +} + +void *_af_malloc (size_t size) +{ + void *p; + + if (size <= 0) + { + _af_error(AF_BAD_MALLOC, "bad memory allocation size request %d", size); + return NULL; + } + + p = malloc(size); + +#ifdef AF_DEBUG + if (p) + memset(p, 0xff, size); +#endif + + if (p == NULL) + { + _af_error(AF_BAD_MALLOC, "allocation of %d bytes failed", size); + return NULL; + } + + return p; +} + +char *_af_strdup (char *s) +{ + char *p = malloc(strlen(s) + 1); + + if (p) + strcpy(p, s); + + return p; +} + +void *_af_realloc (void *p, size_t size) +{ + if (size <= 0) + { + _af_error(AF_BAD_MALLOC, "bad memory allocation size request %d", size); + return NULL; + } + + p = realloc(p, size); + + if (p == NULL) + { + _af_error(AF_BAD_MALLOC, "allocation of %d bytes failed", size); + return NULL; + } + + return p; +} + +void *_af_calloc (size_t nmemb, size_t size) +{ + void *p; + + if (nmemb <= 0 || size <= 0) + { + _af_error(AF_BAD_MALLOC, "bad memory allocation size request " + "%d elements of %d bytes each", nmemb, size); + return NULL; + } + + p = calloc(nmemb, size); + + if (p == NULL) + { + _af_error(AF_BAD_MALLOC, "allocation of %d bytes failed", + nmemb*size); + return NULL; + } + + return p; +} + +AUpvlist _af_pv_long (long val) +{ + AUpvlist ret = AUpvnew(1); + AUpvsetparam(ret, 0, 0); + AUpvsetvaltype(ret, 0, AU_PVTYPE_LONG); + AUpvsetval(ret, 0, &val); + return ret; +} + +AUpvlist _af_pv_double (double val) +{ + AUpvlist ret = AUpvnew(1); + AUpvsetparam(ret, 0, 0); + AUpvsetvaltype(ret, 0, AU_PVTYPE_DOUBLE); + AUpvsetval(ret, 0, &val); + return ret; +} + +AUpvlist _af_pv_pointer (void *val) +{ + AUpvlist ret = AUpvnew(1); + AUpvsetparam(ret, 0, 0); + AUpvsetvaltype(ret, 0, AU_PVTYPE_PTR); + AUpvsetval(ret, 0, &val); + return ret; +} + +bool _af_pv_getlong (AUpvlist pvlist, int param, long *l) +{ + int i; + + for (i=0; i<AUpvgetmaxitems(pvlist); i++) + { + int p, t; + + AUpvgetparam(pvlist, i, &p); + + if (p != param) + continue; + + AUpvgetvaltype(pvlist, i, &t); + + /* Ensure that this parameter is of type AU_PVTYPE_LONG. */ + if (t != AU_PVTYPE_LONG) + return AF_FALSE; + + AUpvgetval(pvlist, i, l); + return AF_TRUE; + } + + return AF_FALSE; +} + +bool _af_pv_getdouble (AUpvlist pvlist, int param, double *d) +{ + int i; + + for (i=0; i<AUpvgetmaxitems(pvlist); i++) + { + int p, t; + + AUpvgetparam(pvlist, i, &p); + + if (p != param) + continue; + + AUpvgetvaltype(pvlist, i, &t); + + /* Ensure that this parameter is of type AU_PVTYPE_DOUBLE. */ + if (t != AU_PVTYPE_DOUBLE) + return AF_FALSE; + + AUpvgetval(pvlist, i, d); + return AF_TRUE; + } + + return AF_FALSE; +} + +bool _af_pv_getptr (AUpvlist pvlist, int param, void **v) +{ + int i; + + for (i=0; i<AUpvgetmaxitems(pvlist); i++) + { + int p, t; + + AUpvgetparam(pvlist, i, &p); + + if (p != param) + continue; + + AUpvgetvaltype(pvlist, i, &t); + + /* Ensure that this parameter is of type AU_PVTYPE_PTR. */ + if (t != AU_PVTYPE_PTR) + return AF_FALSE; + + AUpvgetval(pvlist, i, v); + return AF_TRUE; + } + + return AF_FALSE; +} + +_TrackSetup *_af_filesetup_get_tracksetup (AFfilesetup setup, int trackid) +{ + int i; + for (i=0; i<setup->trackCount; i++) + { + if (setup->tracks[i].id == trackid) + return &setup->tracks[i]; + } + + _af_error(AF_BAD_TRACKID, "bad track id %d", trackid); + + return NULL; +} + +_Track *_af_filehandle_get_track (AFfilehandle file, int trackid) +{ + int i; + for (i=0; i<file->trackCount; i++) + { + if (file->tracks[i].id == trackid) + return &file->tracks[i]; + } + + _af_error(AF_BAD_TRACKID, "bad track id %d", trackid); + + return NULL; +} + +int _af_format_sample_size_uncompressed (_AudioFormat *format, bool stretch3to4) +{ + int size = 0; + + switch (format->sampleFormat) + { + case AF_SAMPFMT_FLOAT: + size = sizeof (float); + break; + case AF_SAMPFMT_DOUBLE: + size = sizeof (double); + break; + default: + size = (int) (format->sampleWidth + 7) / 8; + if (format->compressionType == AF_COMPRESSION_NONE && + size == 3 && stretch3to4) + size = 4; + break; + } + + return size; +} + +float _af_format_sample_size (_AudioFormat *fmt, bool stretch3to4) +{ + int compressionIndex; + float squishFactor; + + compressionIndex = _af_compression_index_from_id(fmt->compressionType); + squishFactor = _af_compression[compressionIndex].squishFactor; + + return _af_format_sample_size_uncompressed(fmt, stretch3to4) / + squishFactor; +} + +int _af_format_frame_size_uncompressed (_AudioFormat *fmt, bool stretch3to4) +{ + return _af_format_sample_size_uncompressed(fmt, stretch3to4) * + fmt->channelCount; +} + +float _af_format_frame_size (_AudioFormat *fmt, bool stretch3to4) +{ + int compressionIndex; + float squishFactor; + + compressionIndex = _af_compression_index_from_id(fmt->compressionType); + squishFactor = _af_compression[compressionIndex].squishFactor; + + return _af_format_frame_size_uncompressed(fmt, stretch3to4) / + squishFactor; +} + +/* + Set the sampleFormat and sampleWidth fields in f, and set the + PCM info to the appropriate default values for the given sample + format and sample width. +*/ +status _af_set_sample_format (_AudioFormat *f, int sampleFormat, int sampleWidth) +{ + switch (sampleFormat) + { + case AF_SAMPFMT_UNSIGNED: + case AF_SAMPFMT_TWOSCOMP: + if (sampleWidth < 1 || sampleWidth > 32) + { + _af_error(AF_BAD_SAMPFMT, + "illegal sample width %d for integer data", + sampleWidth); + return AF_FAIL; + } + else + { + int bytes; + + f->sampleFormat = sampleFormat; + f->sampleWidth = sampleWidth; + + bytes = _af_format_sample_size_uncompressed(f, AF_FALSE); + + if (sampleFormat == AF_SAMPFMT_TWOSCOMP) + f->pcm = _af_default_signed_integer_pcm_mappings[bytes]; + else + f->pcm = _af_default_unsigned_integer_pcm_mappings[bytes]; + } + break; + + case AF_SAMPFMT_FLOAT: + f->sampleFormat = sampleFormat; + f->sampleWidth = 32; + f->pcm = _af_default_float_pcm_mapping; + break; + case AF_SAMPFMT_DOUBLE: + f->sampleFormat = sampleFormat; + f->sampleWidth = 64; /*for convenience */ + f->pcm = _af_default_double_pcm_mapping; + break; + default: + _af_error(AF_BAD_SAMPFMT, "unknown sample format %d", + sampleFormat); + return AF_FAIL; + } + + return AF_SUCCEED; +} + +/* + Verify the uniqueness of the nids ids given. + + idname is the name of what the ids identify, as in "loop" + iderr is an error as in AF_BAD_LOOPID +*/ +bool _af_unique_ids (int *ids, int nids, char *idname, int iderr) +{ + int i; + + for (i = 0; i < nids; i++) + { + int j; + for (j = 0; j < i; j++) + if (ids[i] == ids[j]) + { + _af_error(iderr, "nonunique %s id %d", + idname, ids[i]); + return AF_FALSE; + } + } + + return AF_TRUE; +} + +status af_read_uint32_be (u_int32_t *value, AFvirtualfile *vf) +{ + u_int32_t v; + + if (af_fread(&v, sizeof (v), 1, vf) != 1) + return AF_FAIL; + *value = BENDIAN_TO_HOST_INT32(v); + return AF_SUCCEED; +} + +status af_read_uint32_le (u_int32_t *value, AFvirtualfile *vf) +{ + u_int32_t v; + + if (af_fread(&v, sizeof (v), 1, vf) != 1) + return AF_FAIL; + *value = LENDIAN_TO_HOST_INT32(v); + return AF_SUCCEED; +} + +status af_read_uint16_be (u_int16_t *value, AFvirtualfile *vf) +{ + u_int16_t v; + + if (af_fread(&v, sizeof (v), 1, vf) != 1) + return AF_FAIL; + *value = BENDIAN_TO_HOST_INT16(v); + return AF_SUCCEED; +} + +status af_read_uint16_le (u_int16_t *value, AFvirtualfile *vf) +{ + u_int16_t v; + + if (af_fread(&v, sizeof (v), 1, vf) != 1) + return AF_FAIL; + *value = LENDIAN_TO_HOST_INT16(v); + return AF_SUCCEED; +} + +status af_write_uint32_be (const u_int32_t *value, AFvirtualfile *vf) +{ + u_int32_t v; + v = HOST_TO_BENDIAN_INT32(*value); + if (af_fwrite(&v, sizeof (v), 1, vf) != 1) + return AF_FAIL; + return AF_SUCCEED; +} + +status af_write_uint32_le (const u_int32_t *value, AFvirtualfile *vf) +{ + u_int32_t v; + v = HOST_TO_LENDIAN_INT32(*value); + if (af_fwrite(&v, sizeof (v), 1, vf) != 1) + return AF_FAIL; + return AF_SUCCEED; +} + +status af_write_uint16_be (const u_int16_t *value, AFvirtualfile *vf) +{ + u_int16_t v; + v = HOST_TO_BENDIAN_INT16(*value); + if (af_fwrite(&v, sizeof (v), 1, vf) != 1) + return AF_FAIL; + return AF_SUCCEED; +} + +status af_write_uint16_le (const u_int16_t *value, AFvirtualfile *vf) +{ + u_int16_t v; + v = HOST_TO_LENDIAN_INT16(*value); + if (af_fwrite(&v, sizeof (v), 1, vf) != 1) + return AF_FAIL; + return AF_SUCCEED; +} diff --git a/libaudiofile/util.h b/libaudiofile/util.h new file mode 100644 index 0000000..4018427 --- /dev/null +++ b/libaudiofile/util.h @@ -0,0 +1,78 @@ +/* + Audio File Library + Copyright (C) 1998-2000, Michael Pruett <michael@68k.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307 USA. +*/ + +/* + util.h + + This file contains some general utility functions for the Audio + File Library. +*/ + +#ifndef UTIL_H +#define UTIL_H + +#include <sys/types.h> +#include "audiofile.h" +#include "afinternal.h" + +bool _af_filesetup_ok (AFfilesetup setup); +bool _af_filehandle_ok (AFfilehandle file); + +bool _af_filehandle_can_read (AFfilehandle file); + +void *_af_malloc (size_t size); +void *_af_realloc (void *ptr, size_t size); +void *_af_calloc (size_t nmemb, size_t size); +char *_af_strdup (char *s); + +AUpvlist _af_pv_long (long val); +AUpvlist _af_pv_double (double val); +AUpvlist _af_pv_pointer (void *val); + +bool _af_pv_getlong (AUpvlist pvlist, int param, long *l); +bool _af_pv_getdouble (AUpvlist pvlist, int param, double *d); +bool _af_pv_getptr (AUpvlist pvlist, int param, void **v); + +_TrackSetup *_af_filesetup_get_tracksetup (AFfilesetup setup, int trackid); +_Track *_af_filehandle_get_track (AFfilehandle file, int trackid); + +bool _af_unique_ids (int *ids, int nids, char *idname, int iderr); + +float _af_format_frame_size (_AudioFormat *format, bool stretch3to4); +int _af_format_frame_size_uncompressed (_AudioFormat *format, bool stretch3to4); +float _af_format_sample_size (_AudioFormat *format, bool stretch3to4); +int _af_format_sample_size_uncompressed (_AudioFormat *format, bool stretch3to4); + +status _af_set_sample_format (_AudioFormat *f, int sampleFormat, int sampleWidth); + +bool _af_filehandle_can_read (AFfilehandle file); +bool _af_filehandle_can_write (AFfilehandle file); + +status af_read_uint32_be (u_int32_t *value, AFvirtualfile *vf); +status af_read_uint32_le (u_int32_t *value, AFvirtualfile *vf); +status af_read_uint16_be (u_int16_t *value, AFvirtualfile *vf); +status af_read_uint16_le (u_int16_t *value, AFvirtualfile *vf); + +status af_write_uint32_be (const u_int32_t *value, AFvirtualfile *vf); +status af_write_uint32_le (const u_int32_t *value, AFvirtualfile *vf); +status af_write_uint16_be (const u_int16_t *value, AFvirtualfile *vf); +status af_write_uint16_le (const u_int16_t *value, AFvirtualfile *vf); + +#endif diff --git a/libaudiofile/wave.c b/libaudiofile/wave.c new file mode 100644 index 0000000..229b2b7 --- /dev/null +++ b/libaudiofile/wave.c @@ -0,0 +1,1036 @@ +/* + Audio File Library + Copyright (C) 1998-2000, Michael Pruett <michael@68k.org> + Copyright (C) 2000-2001, Silicon Graphics, Inc. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307 USA. +*/ + +/* + wave.c + + This file contains code for parsing RIFF WAVE format sound files. +*/ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <assert.h> +#include <sys/types.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <math.h> + +#ifdef HAVE_INTTYPES_H +#include <inttypes.h> +#endif + +#include "audiofile.h" +#include "util.h" +#include "afinternal.h" +#include "byteorder.h" +#include "wave.h" +#include "track.h" +#include "setup.h" +#include "marker.h" + +int _af_wave_compression_types[_AF_WAVE_NUM_COMPTYPES] = +{ + AF_COMPRESSION_G711_ULAW, + AF_COMPRESSION_G711_ALAW +}; + +_InstParamInfo _af_wave_inst_params[_AF_WAVE_NUM_INSTPARAMS] = +{ + { AF_INST_MIDI_BASENOTE, AU_PVTYPE_LONG, "MIDI base note", {60} }, + { AF_INST_NUMCENTS_DETUNE, AU_PVTYPE_LONG, "Detune in cents", {0} }, + { AF_INST_MIDI_LOVELOCITY, AU_PVTYPE_LONG, "Low velocity", {1} }, + { AF_INST_MIDI_HIVELOCITY, AU_PVTYPE_LONG, "High velocity", {127} }, + { AF_INST_MIDI_LONOTE, AU_PVTYPE_LONG, "Low note", {0} }, + { AF_INST_MIDI_HINOTE, AU_PVTYPE_LONG, "High note", {127} }, + { AF_INST_NUMDBS_GAIN, AU_PVTYPE_LONG, "Gain in dB", {0} } +}; + +_AFfilesetup _af_wave_default_filesetup = +{ + _AF_VALID_FILESETUP, /* valid */ + AF_FILE_WAVE, /* fileFormat */ + AF_TRUE, /* trackSet */ + AF_TRUE, /* instrumentSet */ + AF_TRUE, /* miscellaneousSet */ + 1, /* trackCount */ + NULL, /* tracks */ + 0, /* instrumentCount */ + NULL, /* instruments */ + 0, /* miscellaneousCount */ + NULL /* miscellaneous */ +}; + +static status ParseFrameCount (AFfilehandle filehandle, AFvirtualfile *fp, + u_int32_t id, size_t size) +{ + u_int32_t totalFrames; + _Track *track; + + track = _af_filehandle_get_track(filehandle, AF_DEFAULT_TRACK); + + af_fread(&totalFrames, 1, 4, fp); + + track->totalfframes = LENDIAN_TO_HOST_INT32(totalFrames); + + return AF_SUCCEED; +} + +static status ParseFormat (AFfilehandle filehandle, AFvirtualfile *fp, + u_int32_t id, size_t size) +{ + _Track *track; + u_int16_t formatTag, channelCount; + u_int32_t sampleRate, averageBytesPerSecond; + u_int16_t blockAlign; + _WAVEInfo *wave; + + assert(filehandle != NULL); + assert(fp != NULL); + assert(!memcmp(&id, "fmt ", 4)); + + track = _af_filehandle_get_track(filehandle, AF_DEFAULT_TRACK); + + assert(filehandle->formatSpecific != NULL); + wave = (_WAVEInfo *) filehandle->formatSpecific; + + af_fread(&formatTag, 1, 2, fp); + formatTag = LENDIAN_TO_HOST_INT16(formatTag); + + af_fread(&channelCount, 1, 2, fp); + channelCount = LENDIAN_TO_HOST_INT16(channelCount); + track->f.channelCount = channelCount; + + af_fread(&sampleRate, 1, 4, fp); + sampleRate = LENDIAN_TO_HOST_INT32(sampleRate); + track->f.sampleRate = sampleRate; + + af_fread(&averageBytesPerSecond, 1, 4, fp); + averageBytesPerSecond = LENDIAN_TO_HOST_INT32(averageBytesPerSecond); + + af_fread(&blockAlign, 1, 2, fp); + blockAlign = LENDIAN_TO_HOST_INT16(blockAlign); + + track->f.byteOrder = AF_BYTEORDER_LITTLEENDIAN; + + /* Default to uncompressed audio data. */ + track->f.compressionType = AF_COMPRESSION_NONE; + + switch (formatTag) + { + case WAVE_FORMAT_PCM: + { + u_int16_t bitsPerSample; + + af_fread(&bitsPerSample, 1, 2, fp); + bitsPerSample = LENDIAN_TO_HOST_INT16(bitsPerSample); + + track->f.sampleWidth = bitsPerSample; + + if (bitsPerSample == 0 || bitsPerSample > 32) + { + _af_error(AF_BAD_WIDTH, + "bad sample width of %d bits", + bitsPerSample); + return AF_FAIL; + } + + if (bitsPerSample <= 8) + track->f.sampleFormat = AF_SAMPFMT_UNSIGNED; + else + track->f.sampleFormat = AF_SAMPFMT_TWOSCOMP; + } + break; + + case WAVE_FORMAT_MULAW: + case IBM_FORMAT_MULAW: + track->f.sampleWidth = 16; + track->f.sampleFormat = AF_SAMPFMT_TWOSCOMP; + track->f.compressionType = AF_COMPRESSION_G711_ULAW; + break; + + case WAVE_FORMAT_ALAW: + case IBM_FORMAT_ALAW: + track->f.sampleWidth = 16; + track->f.sampleFormat = AF_SAMPFMT_TWOSCOMP; + track->f.compressionType = AF_COMPRESSION_G711_ALAW; + break; + + case WAVE_FORMAT_IEEE_FLOAT: + { + u_int16_t bitsPerSample; + + af_fread(&bitsPerSample, 1, 2, fp); + bitsPerSample = LENDIAN_TO_HOST_INT16(bitsPerSample); + + if (bitsPerSample == 64) + { + track->f.sampleWidth = 64; + track->f.sampleFormat = AF_SAMPFMT_DOUBLE; + } + else + { + track->f.sampleWidth = 32; + track->f.sampleFormat = AF_SAMPFMT_FLOAT; + } + } + break; + + case WAVE_FORMAT_ADPCM: + { + u_int16_t bitsPerSample, extraByteCount, + samplesPerBlock, numCoefficients; + int i; + AUpvlist pv; + long l; + void *v; + + if (track->f.channelCount != 1 && + track->f.channelCount != 2) + { + _af_error(AF_BAD_CHANNELS, + "WAVE file with MS ADPCM compression " + "must have 1 or 2 channels"); + } + + af_fread(&bitsPerSample, 1, 2, fp); + bitsPerSample = LENDIAN_TO_HOST_INT16(bitsPerSample); + + af_fread(&extraByteCount, 1, 2, fp); + extraByteCount = LENDIAN_TO_HOST_INT16(extraByteCount); + + af_fread(&samplesPerBlock, 1, 2, fp); + samplesPerBlock = LENDIAN_TO_HOST_INT16(samplesPerBlock); + + af_fread(&numCoefficients, 1, 2, fp); + numCoefficients = LENDIAN_TO_HOST_INT16(numCoefficients); + + /* numCoefficients should be at least 7. */ + assert(numCoefficients >= 7 && numCoefficients <= 255); + + for (i=0; i<numCoefficients; i++) + { + int16_t a0, a1; + + af_fread(&a0, 1, 2, fp); + af_fread(&a1, 1, 2, fp); + + a0 = LENDIAN_TO_HOST_INT16(a0); + a1 = LENDIAN_TO_HOST_INT16(a1); + + wave->msadpcmCoefficients[i][0] = a0; + wave->msadpcmCoefficients[i][1] = a1; + } + + track->f.sampleWidth = 16; + track->f.sampleFormat = AF_SAMPFMT_TWOSCOMP; + track->f.compressionType = AF_COMPRESSION_MS_ADPCM; + track->f.byteOrder = _AF_BYTEORDER_NATIVE; + + /* Create the parameter list. */ + pv = AUpvnew(4); + AUpvsetparam(pv, 0, _AF_MS_ADPCM_NUM_COEFFICIENTS); + AUpvsetvaltype(pv, 0, AU_PVTYPE_LONG); + l = numCoefficients; + AUpvsetval(pv, 0, &l); + + AUpvsetparam(pv, 1, _AF_MS_ADPCM_COEFFICIENTS); + AUpvsetvaltype(pv, 1, AU_PVTYPE_PTR); + v = wave->msadpcmCoefficients; + AUpvsetval(pv, 1, &v); + + AUpvsetparam(pv, 2, _AF_SAMPLES_PER_BLOCK); + AUpvsetvaltype(pv, 2, AU_PVTYPE_LONG); + l = samplesPerBlock; + AUpvsetval(pv, 2, &l); + + AUpvsetparam(pv, 3, _AF_BLOCK_SIZE); + AUpvsetvaltype(pv, 3, AU_PVTYPE_LONG); + l = blockAlign; + AUpvsetval(pv, 3, &l); + + track->f.compressionParams = pv; + } + break; + + case WAVE_FORMAT_DVI_ADPCM: + { + AUpvlist pv; + long l; + + u_int16_t bitsPerSample, extraByteCount, + samplesPerBlock; + + af_fread(&bitsPerSample, 1, 2, fp); + bitsPerSample = LENDIAN_TO_HOST_INT16(bitsPerSample); + + af_fread(&extraByteCount, 1, 2, fp); + extraByteCount = LENDIAN_TO_HOST_INT16(extraByteCount); + + af_fread(&samplesPerBlock, 1, 2, fp); + samplesPerBlock = LENDIAN_TO_HOST_INT16(samplesPerBlock); + + track->f.sampleWidth = 16; + track->f.sampleFormat = AF_SAMPFMT_TWOSCOMP; + track->f.compressionType = AF_COMPRESSION_IMA; + track->f.byteOrder = _AF_BYTEORDER_NATIVE; + + /* Create the parameter list. */ + pv = AUpvnew(2); + AUpvsetparam(pv, 0, _AF_SAMPLES_PER_BLOCK); + AUpvsetvaltype(pv, 0, AU_PVTYPE_LONG); + l = samplesPerBlock; + AUpvsetval(pv, 0, &l); + + AUpvsetparam(pv, 1, _AF_BLOCK_SIZE); + AUpvsetvaltype(pv, 1, AU_PVTYPE_LONG); + l = blockAlign; + AUpvsetval(pv, 1, &l); + + track->f.compressionParams = pv; + } + break; + + case WAVE_FORMAT_YAMAHA_ADPCM: + case WAVE_FORMAT_OKI_ADPCM: + case WAVE_FORMAT_CREATIVE_ADPCM: + case IBM_FORMAT_ADPCM: + _af_error(AF_BAD_NOT_IMPLEMENTED, "WAVE ADPCM data format 0x%x is not currently supported", formatTag); + return AF_FAIL; + break; + + case WAVE_FORMAT_MPEG: + _af_error(AF_BAD_NOT_IMPLEMENTED, "WAVE MPEG data format is not supported"); + return AF_FAIL; + break; + + case WAVE_FORMAT_MPEGLAYER3: + _af_error(AF_BAD_NOT_IMPLEMENTED, "WAVE MPEG layer 3 data format is not supported"); + return AF_FAIL; + break; + + default: + _af_error(AF_BAD_NOT_IMPLEMENTED, "WAVE file data format 0x%x not currently supported", formatTag); + return AF_FAIL; + break; + } + + _af_set_sample_format(&track->f, track->f.sampleFormat, track->f.sampleWidth); + + return AF_SUCCEED; +} + +static status ParseData (AFfilehandle filehandle, AFvirtualfile *fp, + u_int32_t id, size_t size) +{ + _Track *track; + + assert(filehandle != NULL); + assert(fp != NULL); + assert(!memcmp(&id, "data", 4)); + + track = _af_filehandle_get_track(filehandle, AF_DEFAULT_TRACK); + + track->fpos_first_frame = af_ftell(fp); + track->data_size = size; + + return AF_SUCCEED; +} + +static status ParsePlayList (AFfilehandle filehandle, AFvirtualfile *fp, + u_int32_t id, size_t size) +{ + _Instrument *instrument; + u_int32_t segmentCount; + int segment; + + af_fread(&segmentCount, 4, 1, fp); + segmentCount = LENDIAN_TO_HOST_INT32(segmentCount); + + if (segmentCount == 0) + { + filehandle->instrumentCount = 0; + filehandle->instruments = NULL; + return AF_SUCCEED; + } + + for (segment=0; segment<segmentCount; segment++) + { + u_int32_t startMarkID, loopLength, loopCount; + + af_fread(&startMarkID, 4, 1, fp); + startMarkID = LENDIAN_TO_HOST_INT32(startMarkID); + af_fread(&loopLength, 4, 1, fp); + loopLength = LENDIAN_TO_HOST_INT32(loopLength); + af_fread(&loopCount, 4, 1, fp); + loopCount = LENDIAN_TO_HOST_INT32(loopCount); + } + + return AF_SUCCEED; +} + +static status ParseCues (AFfilehandle filehandle, AFvirtualfile *fp, + u_int32_t id, size_t size) +{ + _Track *track; + u_int32_t markerCount; + int i; + + track = _af_filehandle_get_track(filehandle, AF_DEFAULT_TRACK); + + af_fread(&markerCount, 4, 1, fp); + markerCount = LENDIAN_TO_HOST_INT32(markerCount); + track->markerCount = markerCount; + + if (markerCount == 0) + { + track->markers = NULL; + return AF_SUCCEED; + } + + if ((track->markers = _af_marker_new(markerCount)) == NULL) + return AF_FAIL; + + for (i=0; i<markerCount; i++) + { + u_int32_t id, position, chunkid; + u_int32_t chunkByteOffset, blockByteOffset; + u_int32_t sampleFrameOffset; + _Marker *marker = &track->markers[i]; + + af_fread(&id, 4, 1, fp); + id = LENDIAN_TO_HOST_INT32(id); + + af_fread(&position, 4, 1, fp); + position = LENDIAN_TO_HOST_INT32(position); + + af_fread(&chunkid, 4, 1, fp); + chunkid = LENDIAN_TO_HOST_INT32(chunkid); + + af_fread(&chunkByteOffset, 4, 1, fp); + chunkByteOffset = LENDIAN_TO_HOST_INT32(chunkByteOffset); + + af_fread(&blockByteOffset, 4, 1, fp); + blockByteOffset = LENDIAN_TO_HOST_INT32(blockByteOffset); + + /* + sampleFrameOffset represents the position of + the mark in units of frames. + */ + af_fread(&sampleFrameOffset, 4, 1, fp); + sampleFrameOffset = LENDIAN_TO_HOST_INT32(sampleFrameOffset); + + marker->id = id; + marker->position = sampleFrameOffset; + marker->name = _af_strdup(""); + marker->comment = _af_strdup(""); + } + + return AF_SUCCEED; +} + +/* Parse an adtl sub-chunk within a LIST chunk. */ +static status ParseADTLSubChunk (AFfilehandle filehandle, AFvirtualfile *fp, + u_int32_t id, size_t size) +{ + _Track *track; + AFfileoffset endPos=af_ftell(fp)+size; + + track = _af_filehandle_get_track(filehandle, AF_DEFAULT_TRACK); + + while (af_ftell(fp) < endPos) + { + char chunkID[4]; + u_int32_t chunkSize; + + af_fread(chunkID, 4, 1, fp); + af_fread(&chunkSize, 4, 1, fp); + chunkSize = LENDIAN_TO_HOST_INT32(chunkSize); + + if (memcmp(chunkID, "labl", 4)==0 || memcmp(chunkID, "note", 4)==0) + { + _Marker *marker=NULL; + u_int32_t id; + long length=chunkSize-4; + char *p=_af_malloc(length); + + af_fread(&id, 4, 1, fp); + af_fread(p, length, 1, fp); + + id = LENDIAN_TO_HOST_INT32(id); + + marker = _af_marker_find_by_id(track, id); + + if (marker != NULL) + { + if (memcmp(chunkID, "labl", 4)==0) + { + free(marker->name); + marker->name = p; + } + else if (memcmp(chunkID, "note", 4)==0) + { + free(marker->comment); + marker->comment = p; + } + else + free(p); + } + else + free(p); + + /* + If chunkSize is odd, skip an extra byte + at the end of the chunk. + */ + if ((chunkSize % 2) != 0) + af_fseek(fp, 1, SEEK_CUR); + } + else + { + /* If chunkSize is odd, skip an extra byte. */ + af_fseek(fp, chunkSize + (chunkSize % 2), SEEK_CUR); + } + } + return AF_SUCCEED; +} + +/* Parse an INFO sub-chunk within a LIST chunk. */ +static status ParseINFOSubChunk (AFfilehandle filehandle, AFvirtualfile *fp, + u_int32_t id, size_t size) +{ + AFfileoffset endPos=af_ftell(fp)+size; + + while (af_ftell(fp) < endPos) + { + int misctype = AF_MISC_UNRECOGNIZED; + u_int32_t miscid, miscsize; + + af_fread(&miscid, 4, 1, fp); + af_fread(&miscsize, 4, 1, fp); + miscsize = LENDIAN_TO_HOST_INT32(miscsize); + + if (memcmp(&miscid, "IART", 4) == 0) + misctype = AF_MISC_AUTH; + else if (memcmp(&miscid, "INAM", 4) == 0) + misctype = AF_MISC_NAME; + else if (memcmp(&miscid, "ICOP", 4) == 0) + misctype = AF_MISC_COPY; + else if (memcmp(&miscid, "ICMT", 4) == 0) + misctype = AF_MISC_ICMT; + else if (memcmp(&miscid, "ICRD", 4) == 0) + misctype = AF_MISC_ICRD; + else if (memcmp(&miscid, "ISFT", 4) == 0) + misctype = AF_MISC_ISFT; + + if (misctype != AF_MISC_UNRECOGNIZED) + { + char *string = _af_malloc(miscsize); + + af_fread(string, miscsize, 1, fp); + + filehandle->miscellaneousCount++; + filehandle->miscellaneous = _af_realloc(filehandle->miscellaneous, sizeof (_Miscellaneous) * filehandle->miscellaneousCount); + + filehandle->miscellaneous[filehandle->miscellaneousCount-1].id = filehandle->miscellaneousCount; + filehandle->miscellaneous[filehandle->miscellaneousCount-1].type = misctype; + filehandle->miscellaneous[filehandle->miscellaneousCount-1].size = miscsize; + filehandle->miscellaneous[filehandle->miscellaneousCount-1].position = 0; + filehandle->miscellaneous[filehandle->miscellaneousCount-1].buffer = string; + } + else + { + af_fseek(fp, miscsize, SEEK_CUR); + } + + /* Make the current position an even number of bytes. */ + if (miscsize % 2 != 0) + af_fseek(fp, 1, SEEK_CUR); + } + return AF_SUCCEED; +} + +static status ParseList (AFfilehandle filehandle, AFvirtualfile *fp, + u_int32_t id, size_t size) +{ + u_int32_t typeID; + + af_fread(&typeID, 4, 1, fp); + size-=4; + + if (memcmp(&typeID, "adtl", 4) == 0) + { + /* Handle adtl sub-chunks. */ + return ParseADTLSubChunk(filehandle, fp, typeID, size); + } + else if (memcmp(&typeID, "INFO", 4) == 0) + { + /* Handle INFO sub-chunks. */ + return ParseINFOSubChunk(filehandle, fp, typeID, size); + } + else + { + /* Skip unhandled sub-chunks. */ + af_fseek(fp, size, SEEK_CUR); + return AF_SUCCEED; + } + return AF_SUCCEED; +} + +static status ParseInstrument (AFfilehandle filehandle, AFvirtualfile *fp, + u_int32_t id, size_t size) +{ + u_int8_t baseNote; + int8_t detune, gain; + u_int8_t lowNote, highNote, lowVelocity, highVelocity; + u_int8_t padByte; + + af_fread(&baseNote, 1, 1, fp); + af_fread(&detune, 1, 1, fp); + af_fread(&gain, 1, 1, fp); + af_fread(&lowNote, 1, 1, fp); + af_fread(&highNote, 1, 1, fp); + af_fread(&lowVelocity, 1, 1, fp); + af_fread(&highVelocity, 1, 1, fp); + af_fread(&padByte, 1, 1, fp); + + return AF_SUCCEED; +} + +bool _af_wave_recognize (AFvirtualfile *fh) +{ + u_int8_t buffer[8]; + + af_fseek(fh, 0, SEEK_SET); + + if (af_fread(buffer, 1, 8, fh) != 8 || memcmp(buffer, "RIFF", 4) != 0) + return AF_FALSE; + if (af_fread(buffer, 1, 4, fh) != 4 || memcmp(buffer, "WAVE", 4) != 0) + return AF_FALSE; + + return AF_TRUE; +} + +status _af_wave_read_init (AFfilesetup setup, AFfilehandle filehandle) +{ + _Track *track; + u_int32_t type, size, formtype; + u_int32_t index = 0; + bool hasFormat, hasData, hasCue, hasList, hasPlayList, hasFrameCount, + hasINST, hasINFO; + _WAVEInfo *wave = _af_malloc(sizeof (_WAVEInfo)); + + assert(filehandle != NULL); + assert(filehandle->fh != NULL); + + hasFormat = AF_FALSE; + hasData = AF_FALSE; + hasCue = AF_FALSE; + hasList = AF_FALSE; + hasPlayList = AF_FALSE; + hasFrameCount = AF_FALSE; + hasINST = AF_FALSE; + hasINFO = AF_FALSE; + + filehandle->formatSpecific = wave; + filehandle->instruments = NULL; + filehandle->instrumentCount = 0; + filehandle->miscellaneous = NULL; + filehandle->miscellaneousCount = 0; + + track = _af_track_new(); + filehandle->tracks = track; + filehandle->trackCount = 1; + + af_fseek(filehandle->fh, 0, SEEK_SET); + + af_fread(&type, 4, 1, filehandle->fh); + af_fread(&size, 4, 1, filehandle->fh); + size = LENDIAN_TO_HOST_INT32(size); + af_fread(&formtype, 4, 1, filehandle->fh); + + assert(!memcmp(&type, "RIFF", 4)); + assert(!memcmp(&formtype, "WAVE", 4)); + +#ifdef DEBUG + printf("size: %d\n", size); +#endif + + /* Include the offset of the form type. */ + index += 4; + + while (index < size) + { + u_int32_t chunkid = 0, chunksize = 0; + status result; + +#ifdef DEBUG + printf("index: %d\n", index); +#endif + af_fread(&chunkid, 4, 1, filehandle->fh); + + af_fread(&chunksize, 4, 1, filehandle->fh); + chunksize = LENDIAN_TO_HOST_INT32(chunksize); + +#ifdef DEBUG + _af_printid(BENDIAN_TO_HOST_INT32(chunkid)); + printf(" size: %d\n", chunksize); +#endif + + if (memcmp(&chunkid, "fmt ", 4) == 0) + { + result = ParseFormat(filehandle, filehandle->fh, chunkid, chunksize); + if (result == AF_FAIL) + return AF_FAIL; + + hasFormat = AF_TRUE; + } + else if (memcmp(&chunkid, "data", 4) == 0) + { + /* The format chunk must precede the data chunk. */ + if (!hasFormat) + { + _af_error(AF_BAD_HEADER, "missing format chunk in WAVE file"); + return AF_FAIL; + } + + result = ParseData(filehandle, filehandle->fh, chunkid, chunksize); + if (result == AF_FAIL) + return AF_FAIL; + + hasData = AF_TRUE; + } + else if (memcmp(&chunkid, "inst", 4) == 0) + { + result = ParseInstrument(filehandle, filehandle->fh, chunkid, chunksize); + if (result == AF_FAIL) + return AF_FAIL; + } + else if (memcmp(&chunkid, "fact", 4) == 0) + { + hasFrameCount = AF_TRUE; + result = ParseFrameCount(filehandle, filehandle->fh, chunkid, chunksize); + if (result == AF_FAIL) + return AF_FAIL; + } + else if (memcmp(&chunkid, "cue ", 4) == 0) + { + hasCue = AF_TRUE; + result = ParseCues(filehandle, filehandle->fh, chunkid, chunksize); + if (result == AF_FAIL) + return AF_FAIL; + } + else if (memcmp(&chunkid, "LIST", 4) == 0 || memcmp(&chunkid, "list", 4) == 0) + { + hasList = AF_TRUE; + result = ParseList(filehandle, filehandle->fh, chunkid, chunksize); + if (result == AF_FAIL) + return AF_FAIL; + } + else if (memcmp(&chunkid, "INST", 4) == 0) + { + hasINST = AF_TRUE; + result = ParseInstrument(filehandle, filehandle->fh, chunkid, chunksize); + if (result == AF_FAIL) + return AF_FAIL; + } + else if (memcmp(&chunkid, "plst", 4) == 0) + { + hasPlayList = AF_TRUE; + result = ParsePlayList(filehandle, filehandle->fh, chunkid, chunksize); + if (result == AF_FAIL) + return AF_FAIL; + } + + index += chunksize + 8; + + /* All chunks must be aligned on an even number of bytes */ + if ((index % 2) != 0) + index++; + + af_fseek(filehandle->fh, index + 8, SEEK_SET); + } + + /* The format chunk and the data chunk are required. */ + if (!hasFormat || !hasData) + { + return AF_FAIL; + } + + /* + At this point we know that the file has a format chunk + and a data chunk, so we can assume that track->f and + track->data_size have been initialized. + */ + if (hasFrameCount == AF_FALSE) + { + /* + Perform arithmetic in double-precision so as + to preserve accuracy. + */ + track->totalfframes = ceil((double) track->data_size / + _af_format_frame_size(&track->f, AF_FALSE)); + } + + if (track->f.compressionType != AF_COMPRESSION_NONE && + (track->f.compressionType == AF_COMPRESSION_G711_ULAW || + track->f.compressionType == AF_COMPRESSION_G711_ALAW)) + { + track->totalfframes = track->data_size / track->f.channelCount; + } + + /* + A return value of AF_SUCCEED indicates successful parsing. + */ + return AF_SUCCEED; +} + +AFfilesetup _af_wave_complete_setup (AFfilesetup setup) +{ + AFfilesetup newsetup; + _TrackSetup *track; + + if (setup->trackSet && setup->trackCount != 1) + { + _af_error(AF_BAD_NUMTRACKS, "WAVE file must have 1 track"); + return AF_NULL_FILESETUP; + } + + track = _af_filesetup_get_tracksetup(setup, AF_DEFAULT_TRACK); + + if (track->sampleFormatSet) + { + switch (track->f.sampleFormat) + { + case AF_SAMPFMT_FLOAT: + if (track->sampleWidthSet && + track->f.sampleWidth != 32) + { + _af_error(AF_BAD_WIDTH, + "Warning: invalid sample width for floating-point WAVE file: %d (must be 32 bits)\n", + track->f.sampleWidth); + _af_set_sample_format(&track->f, AF_SAMPFMT_FLOAT, 32); + } + break; + + case AF_SAMPFMT_DOUBLE: + _af_error(AF_BAD_SAMPFMT, "WAVE format does not support double-precision floating-point data"); + return AF_NULL_FILESETUP; + break; + + case AF_SAMPFMT_UNSIGNED: + if (track->sampleWidthSet) + { + if (track->f.sampleWidth < 1 || track->f.sampleWidth > 32) + { + _af_error(AF_BAD_WIDTH, "invalid sample width for WAVE file: %d (must be 1-32 bits)\n", track->f.sampleWidth); + return AF_NULL_FILESETUP; + } + if (track->f.sampleWidth > 8) + { + _af_error(AF_BAD_SAMPFMT, "WAVE integer data of more than 8 bits must be two's complement signed"); + _af_set_sample_format(&track->f, AF_SAMPFMT_TWOSCOMP, track->f.sampleWidth); + } + } + else + /* + If the sample width is not set but the user requests + unsigned data, set the width to 8 bits. + */ + _af_set_sample_format(&track->f, track->f.sampleFormat, 8); + break; + + case AF_SAMPFMT_TWOSCOMP: + if (track->sampleWidthSet) + { + if (track->f.sampleWidth < 1 || track->f.sampleWidth > 32) + { + _af_error(AF_BAD_WIDTH, "invalid sample width %d for WAVE file (must be 1-32)", track->f.sampleWidth); + return AF_NULL_FILESETUP; + } + else if (track->f.sampleWidth <= 8) + { + _af_error(AF_BAD_SAMPFMT, "Warning: WAVE format integer data of 1-8 bits must be unsigned; setting sample format to unsigned"); + _af_set_sample_format(&track->f, AF_SAMPFMT_UNSIGNED, track->f.sampleWidth); + } + } + else + /* + If no sample width was specified, we default to 16 bits + for signed integer data. + */ + _af_set_sample_format(&track->f, track->f.sampleFormat, 16); + break; + } + } + /* + Otherwise set the sample format depending on the sample + width or set completely to default. + */ + else + { + if (track->sampleWidthSet == AF_FALSE) + { + track->f.sampleWidth = 16; + track->f.sampleFormat = AF_SAMPFMT_TWOSCOMP; + } + else + { + if (track->f.sampleWidth < 1 || track->f.sampleWidth > 32) + { + _af_error(AF_BAD_WIDTH, "invalid sample width %d for WAVE file (must be 1-32)", track->f.sampleWidth); + return AF_NULL_FILESETUP; + } + else if (track->f.sampleWidth > 8) + /* Here track->f.sampleWidth is in {1..32}. */ + track->f.sampleFormat = AF_SAMPFMT_TWOSCOMP; + else + /* Here track->f.sampleWidth is in {1..8}. */ + track->f.sampleFormat = AF_SAMPFMT_UNSIGNED; + } + } + + if (track->f.compressionType != AF_COMPRESSION_NONE && + track->f.compressionType != AF_COMPRESSION_G711_ULAW && + track->f.compressionType != AF_COMPRESSION_G711_ALAW) + { + _af_error(AF_BAD_NOT_IMPLEMENTED, "compression format not supported in WAVE format"); + return AF_NULL_FILESETUP; + } + + if (track->byteOrderSet && + track->f.byteOrder != AF_BYTEORDER_LITTLEENDIAN && + track->f.compressionType == AF_COMPRESSION_NONE) + { + _af_error(AF_BAD_BYTEORDER, "WAVE format only supports little-endian data"); + return AF_NULL_FILESETUP; + } + + if (track->f.compressionType == AF_COMPRESSION_NONE) + track->f.byteOrder = AF_BYTEORDER_LITTLEENDIAN; + else + track->f.byteOrder = AF_BYTEORDER_BIGENDIAN; + + if (track->aesDataSet) + { + _af_error(AF_BAD_FILESETUP, "WAVE files cannot have AES data"); + return AF_NULL_FILESETUP; + } + + if (setup->instrumentSet) + { + if (setup->instrumentCount > 1) + { + _af_error(AF_BAD_NUMINSTS, "WAVE files can have 0 or 1 instrument"); + return AF_NULL_FILESETUP; + } + else if (setup->instrumentCount == 1) + { + if (setup->instruments[0].loopSet && + setup->instruments[0].loopCount > 0 && + (track->markersSet == AF_FALSE || track->markerCount == 0)) + { + _af_error(AF_BAD_NUMMARKS, "WAVE files with loops must contain at least 1 marker"); + return AF_NULL_FILESETUP; + } + } + } + + /* Make sure the miscellaneous data is of an acceptable type. */ + if (setup->miscellaneousSet) + { + int i; + for (i=0; i<setup->miscellaneousCount; i++) + { + switch (setup->miscellaneous[i].type) + { + case AF_MISC_COPY: + case AF_MISC_AUTH: + case AF_MISC_NAME: + case AF_MISC_ICRD: + case AF_MISC_ISFT: + case AF_MISC_ICMT: + break; + default: + _af_error(AF_BAD_MISCTYPE, "illegal miscellaneous type [%d] for WAVE file", setup->miscellaneous[i].type); + return AF_NULL_FILESETUP; + } + } + } + + /* + Allocate an AFfilesetup and make all the unset fields correct. + */ + newsetup = _af_filesetup_copy(setup, &_af_wave_default_filesetup, AF_FALSE); + + /* Make sure we do not copy loops if they are not specified in setup. */ + if (setup->instrumentSet && setup->instrumentCount > 0 && + setup->instruments[0].loopSet) + { + free(newsetup->instruments[0].loops); + newsetup->instruments[0].loopCount = 0; + } + + return newsetup; +} + +bool _af_wave_instparam_valid (AFfilehandle filehandle, AUpvlist list, int i) +{ + int param, type, lval; + + AUpvgetparam(list, i, ¶m); + AUpvgetvaltype(list, i, &type); + if (type != AU_PVTYPE_LONG) + return AF_FALSE; + + AUpvgetval(list, i, &lval); + + switch (param) + { + case AF_INST_MIDI_BASENOTE: + return ((lval >= 0) && (lval <= 127)); + + case AF_INST_NUMCENTS_DETUNE: + return ((lval >= -50) && (lval <= 50)); + + case AF_INST_MIDI_LOVELOCITY: + return ((lval >= 1) && (lval <= 127)); + + case AF_INST_MIDI_HIVELOCITY: + return ((lval >= 1) && (lval <= 127)); + + case AF_INST_MIDI_LONOTE: + return ((lval >= 0) && (lval <= 127)); + + case AF_INST_MIDI_HINOTE: + return ((lval >= 0) && (lval <= 127)); + + case AF_INST_NUMDBS_GAIN: + return AF_TRUE; + + default: + return AF_FALSE; + } + + return AF_TRUE; +} diff --git a/libaudiofile/wave.h b/libaudiofile/wave.h new file mode 100644 index 0000000..cf7a88a --- /dev/null +++ b/libaudiofile/wave.h @@ -0,0 +1,109 @@ +/* + Audio File Library + Copyright (C) 1998, Michael Pruett <michael@68k.org> + Copyright (C) 2000, Silicon Graphics, Inc. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307 USA. +*/ + +/* + wave.h + + This file contains structures and constants pertinent to the RIFF + WAVE file format. +*/ + +#ifndef WAVE_H +#define WAVE_H + +/* These constants are from RFC 2361. */ +enum +{ + WAVE_FORMAT_UNKNOWN = 0x0000, /* Microsoft Unknown Wave Format */ + WAVE_FORMAT_PCM = 0x0001, /* Microsoft PCM Format */ + WAVE_FORMAT_ADPCM = 0x0002, /* Microsoft ADPCM Format */ + WAVE_FORMAT_IEEE_FLOAT = 0x0003, /* IEEE Float */ + WAVE_FORMAT_VSELP = 0x0004, /* Compaq Computer's VSELP */ + WAVE_FORMAT_IBM_CVSD = 0x0005, /* IBM CVSD */ + WAVE_FORMAT_ALAW = 0x0006, /* Microsoft ALAW */ + WAVE_FORMAT_MULAW = 0x0007, /* Microsoft MULAW */ + WAVE_FORMAT_OKI_ADPCM = 0x0010, /* OKI ADPCM */ + WAVE_FORMAT_DVI_ADPCM = 0x0011, /* Intel's DVI ADPCM */ + WAVE_FORMAT_MEDIASPACE_ADPCM = 0x0012, /* Videologic's MediaSpace ADPCM */ + WAVE_FORMAT_SIERRA_ADPCM = 0x0013, /* Sierra ADPCM */ + WAVE_FORMAT_G723_ADPCM = 0x0014, /* G.723 ADPCM */ + WAVE_FORMAT_DIGISTD = 0x0015, /* DSP Solutions' DIGISTD */ + WAVE_FORMAT_DIGIFIX = 0x0016, /* DSP Solutions' DIGIFIX */ + WAVE_FORMAT_DIALOGIC_OKI_ADPCM = 0x0017, /* Dialogic OKI ADPCM */ + WAVE_FORMAT_MEDIAVISION_ADPCM = 0x0018, /* MediaVision ADPCM */ + WAVE_FORMAT_CU_CODEC = 0x0019, /* HP CU */ + WAVE_FORMAT_YAMAHA_ADPCM = 0x0020, /* Yamaha ADPCM */ + WAVE_FORMAT_SONARC = 0x0021, /* Speech Compression's Sonarc */ + WAVE_FORMAT_DSP_TRUESPEECH = 0x0022, /* DSP Group's True Speech */ + WAVE_FORMAT_ECHOSC1 = 0x0023, /* Echo Speech's EchoSC1 */ + WAVE_FORMAT_AUDIOFILE_AF36 = 0x0024, /* Audiofile AF36 */ + WAVE_FORMAT_APTX = 0x0025, /* APTX */ + WAVE_FORMAT_DOLBY_AC2 = 0x0030, /* Dolby AC2 */ + WAVE_FORMAT_GSM610 = 0x0031, /* GSM610 */ + WAVE_FORMAT_MSNAUDIO = 0x0032, /* MSNAudio */ + WAVE_FORMAT_ANTEX_ADPCME = 0x0033, /* Antex ADPCME */ + + WAVE_FORMAT_MPEG = 0x0050, /* MPEG */ + WAVE_FORMAT_MPEGLAYER3 = 0x0055, /* MPEG layer 3 */ + WAVE_FORMAT_LUCENT_G723 = 0x0059, /* Lucent G.723 */ + WAVE_FORMAT_G726_ADPCM = 0x0064, /* G.726 ADPCM */ + WAVE_FORMAT_G722_ADPCM = 0x0065, /* G.722 ADPCM */ + + IBM_FORMAT_MULAW = 0x0101, + IBM_FORMAT_ALAW = 0x0102, + IBM_FORMAT_ADPCM = 0x0103, + + WAVE_FORMAT_CREATIVE_ADPCM = 0x0200 +}; + +#define _AF_WAVE_NUM_INSTPARAMS 7 +#define _AF_WAVE_NUM_COMPTYPES 2 + +bool _af_wave_recognize (AFvirtualfile *fh); +status _af_wave_read_init (AFfilesetup, AFfilehandle); +status _af_wave_write_init (AFfilesetup, AFfilehandle); +bool _af_wave_update (AFfilehandle); +AFfilesetup _af_wave_complete_setup (AFfilesetup); +bool _af_wave_instparam_valid (AFfilehandle, AUpvlist, int); + +typedef struct _WAVEInfo +{ + AFfileoffset factOffset; /* start of fact (frame count) chunk */ + AFfileoffset miscellaneousStartOffset; + AFfileoffset totalMiscellaneousSize; + AFfileoffset markOffset; + AFfileoffset dataSizeOffset; + + /* + The following information is specified in the format + chunk and is for use with compressed data formats. + */ + u_int32_t blockAlign, samplesPerBlock; + + /* + The index into the coefficient array is of type + u_int8_t, so we can safely limit msadpcmCoefficients to + be 256 coefficient pairs. + */ + int16_t msadpcmCoefficients[256][2]; +} _WAVEInfo; + +#endif diff --git a/libaudiofile/wavewrite.c b/libaudiofile/wavewrite.c new file mode 100644 index 0000000..af9acda --- /dev/null +++ b/libaudiofile/wavewrite.c @@ -0,0 +1,551 @@ +/* + Audio File Library + Copyright (C) 1998-2000, Michael Pruett <michael@68k.org> + Copyright (C) 2000-2001, Silicon Graphics, Inc. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307 USA. +*/ + +/* + wavewrite.c + + This file contains routines which facilitate writing to WAVE files. +*/ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <sys/types.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <assert.h> + +#ifdef HAVE_INTTYPES_H +#include <inttypes.h> +#endif + +#include "audiofile.h" +#include "afinternal.h" +#include "byteorder.h" +#include "util.h" +#include "setup.h" +#include "wave.h" + +status _af_wave_update (AFfilehandle file); + +static status WriteFormat (AFfilehandle file); +static status WriteFrameCount (AFfilehandle file); +static status WriteMiscellaneous (AFfilehandle file); +static status WriteCues (AFfilehandle file); +static status WriteData (AFfilehandle file); + +_WAVEInfo *waveinfo_new (void) +{ + _WAVEInfo *waveinfo = _af_malloc(sizeof (_WAVEInfo)); + + waveinfo->factOffset = 0; + waveinfo->miscellaneousStartOffset = 0; + waveinfo->totalMiscellaneousSize = 0; + waveinfo->markOffset = 0; + waveinfo->dataSizeOffset = 0; + + return waveinfo; +} + +static status WriteFormat (AFfilehandle file) +{ + _Track *track = NULL; + + u_int16_t formatTag, channelCount; + u_int32_t sampleRate, averageBytesPerSecond; + u_int16_t blockAlign; + u_int32_t chunkSize; + u_int16_t bitsPerSample; + + assert(file != NULL); + + track = _af_filehandle_get_track(file, AF_DEFAULT_TRACK); + + af_fwrite("fmt ", 4, 1, file->fh); + + switch (track->f.compressionType) + { + case AF_COMPRESSION_NONE: + chunkSize = 16; + if (track->f.sampleFormat == AF_SAMPFMT_FLOAT) + { + formatTag = WAVE_FORMAT_IEEE_FLOAT; + } + else if (track->f.sampleFormat == AF_SAMPFMT_TWOSCOMP || + track->f.sampleFormat == AF_SAMPFMT_UNSIGNED) + { + formatTag = WAVE_FORMAT_PCM; + } + else + { + _af_error(AF_BAD_COMPTYPE, "bad sample format"); + return AF_FAIL; + } + + blockAlign = _af_format_frame_size(&track->f, AF_FALSE); + bitsPerSample = 8 * _af_format_sample_size(&track->f, AF_FALSE); + break; + + /* + G.711 compression uses eight bits per sample. + */ + case AF_COMPRESSION_G711_ULAW: + chunkSize = 18; + formatTag = IBM_FORMAT_MULAW; + blockAlign = track->f.channelCount; + bitsPerSample = 8; + break; + + case AF_COMPRESSION_G711_ALAW: + chunkSize = 18; + formatTag = IBM_FORMAT_ALAW; + blockAlign = track->f.channelCount; + bitsPerSample = 8; + break; + + default: + _af_error(AF_BAD_COMPTYPE, "bad compression type"); + return AF_FAIL; + } + + chunkSize = HOST_TO_LENDIAN_INT32(chunkSize); + af_fwrite(&chunkSize, 4, 1, file->fh); + + formatTag = HOST_TO_LENDIAN_INT16(formatTag); + af_fwrite(&formatTag, 2, 1, file->fh); + formatTag = LENDIAN_TO_HOST_INT16(formatTag); + + channelCount = track->f.channelCount; + channelCount = HOST_TO_LENDIAN_INT16(channelCount); + af_fwrite(&channelCount, 2, 1, file->fh); + + sampleRate = track->f.sampleRate; + sampleRate = HOST_TO_LENDIAN_INT32(sampleRate); + af_fwrite(&sampleRate, 4, 1, file->fh); + + averageBytesPerSecond = + track->f.sampleRate * _af_format_frame_size(&track->f, AF_FALSE); + averageBytesPerSecond = HOST_TO_LENDIAN_INT32(averageBytesPerSecond); + af_fwrite(&averageBytesPerSecond, 4, 1, file->fh); + + blockAlign = _af_format_frame_size(&track->f, AF_FALSE); + blockAlign = HOST_TO_LENDIAN_INT16(blockAlign); + af_fwrite(&blockAlign, 2, 1, file->fh); + + bitsPerSample = HOST_TO_LENDIAN_INT16(bitsPerSample); + af_fwrite(&bitsPerSample, 2, 1, file->fh); + + if (track->f.compressionType == AF_COMPRESSION_G711_ULAW || + track->f.compressionType == AF_COMPRESSION_G711_ALAW) + { + u_int16_t zero = 0; + af_fwrite(&zero, 2, 1, file->fh); + } + + return AF_SUCCEED; +} + +static status WriteFrameCount (AFfilehandle file) +{ + _Track *track = NULL; + _WAVEInfo *waveinfo = NULL; + u_int32_t factSize = 4; + u_int32_t totalFrameCount; + + assert(file != NULL); + + track = _af_filehandle_get_track(file, AF_DEFAULT_TRACK); + waveinfo = (_WAVEInfo *) file->formatSpecific; + + /* We only write the fact chunk for compressed audio. */ + if (track->f.compressionType == AF_COMPRESSION_NONE) + return AF_SUCCEED; + + /* + If the offset for the fact chunk hasn't been set yet, + set it to the file's current position. + */ + if (waveinfo->factOffset == 0) + waveinfo->factOffset = af_ftell(file->fh); + else + af_fseek(file->fh, waveinfo->factOffset, SEEK_SET); + + af_fwrite("fact", 4, 1, file->fh); + factSize = HOST_TO_LENDIAN_INT32(factSize); + af_fwrite(&factSize, 4, 1, file->fh); + + totalFrameCount = HOST_TO_LENDIAN_INT32(track->totalfframes); + af_fwrite(&totalFrameCount, 4, 1, file->fh); + + return AF_SUCCEED; +} + +static status WriteData (AFfilehandle file) +{ + _Track *track; + u_int32_t chunkSize; + _WAVEInfo *waveinfo; + + assert(file); + + waveinfo = file->formatSpecific; + track = _af_filehandle_get_track(file, AF_DEFAULT_TRACK); + + af_fwrite("data", 4, 1, file->fh); + waveinfo->dataSizeOffset = af_ftell(file->fh); + + chunkSize = _af_format_frame_size(&track->f, AF_FALSE) * + track->totalfframes; + + chunkSize = HOST_TO_LENDIAN_INT32(chunkSize); + af_fwrite(&chunkSize, 4, 1, file->fh); + track->fpos_first_frame = af_ftell(file->fh); + + return AF_SUCCEED; +} + +status _af_wave_update (AFfilehandle file) +{ + _Track *track; + _WAVEInfo *wave = (_WAVEInfo *) file->formatSpecific; + + track = _af_filehandle_get_track(file, AF_DEFAULT_TRACK); + + if (track->fpos_first_frame != 0) + { + u_int32_t dataLength, fileLength; + + /* Update the frame count chunk if present. */ + WriteFrameCount(file); + + /* Update the length of the data chunk. */ + af_fseek(file->fh, wave->dataSizeOffset, SEEK_SET); + + /* + We call _af_format_frame_size to calculate the + frame size of normal PCM data or compressed data. + */ + dataLength = (u_int32_t) track->totalfframes * + _af_format_frame_size(&track->f, AF_FALSE); + dataLength = HOST_TO_LENDIAN_INT32(dataLength); + af_fwrite(&dataLength, 4, 1, file->fh); + + /* Update the length of the RIFF chunk. */ + fileLength = (u_int32_t) af_flength(file->fh); + fileLength -= 8; + fileLength = HOST_TO_LENDIAN_INT32(fileLength); + + af_fseek(file->fh, 4, SEEK_SET); + af_fwrite(&fileLength, 4, 1, file->fh); + } + + /* + Write the actual data that was set after initializing + the miscellaneous IDs. The size of the data will be + unchanged. + */ + WriteMiscellaneous(file); + + /* Write the new positions; the size of the data will be unchanged. */ + WriteCues(file); + + return AF_SUCCEED; +} + +/* Convert an Audio File Library miscellaneous type to a WAVE type. */ +static status misc_type_to_wave (int misctype, u_int32_t *miscid) +{ + if (misctype == AF_MISC_AUTH) + memcpy(miscid, "IART", 4); + else if (misctype == AF_MISC_NAME) + memcpy(miscid, "INAM", 4); + else if (misctype == AF_MISC_COPY) + memcpy(miscid, "ICOP", 4); + else if (misctype == AF_MISC_ICMT) + memcpy(miscid, "ICMT", 4); + else if (misctype == AF_MISC_ICRD) + memcpy(miscid, "ICRD", 4); + else if (misctype == AF_MISC_ISFT) + memcpy(miscid, "ISFT", 4); + else + return AF_FAIL; + + return AF_SUCCEED; +} + +status WriteMiscellaneous (AFfilehandle filehandle) +{ + _WAVEInfo *wave = (_WAVEInfo *) filehandle->formatSpecific; + + if (filehandle->miscellaneousCount != 0) + { + int i; + u_int32_t miscellaneousBytes; + u_int32_t chunkSize; + + /* Start at 12 to account for 'LIST', size, and 'INFO'. */ + miscellaneousBytes = 12; + + /* Then calculate the size of the whole INFO chunk. */ + for (i=0; i<filehandle->miscellaneousCount; i++) + { + u_int32_t miscid; + + /* Skip miscellaneous data of an unsupported type. */ + if (misc_type_to_wave(filehandle->miscellaneous[i].type, + &miscid) == AF_FAIL) + continue; + + /* Account for miscellaneous type and size. */ + miscellaneousBytes += 8; + miscellaneousBytes += filehandle->miscellaneous[i].size; + + /* Add a pad byte if necessary. */ + if (filehandle->miscellaneous[i].size % 2 != 0) + miscellaneousBytes++; + + assert(miscellaneousBytes % 2 == 0); + } + + if (wave->miscellaneousStartOffset == 0) + wave->miscellaneousStartOffset = af_ftell(filehandle->fh); + else + af_fseek(filehandle->fh, wave->miscellaneousStartOffset, SEEK_SET); + + wave->totalMiscellaneousSize = miscellaneousBytes; + + /* + Write the data. On the first call to this + function (from _af_wave_write_init), the + data won't be available, af_fseek is used to + reserve space until the data has been provided. + On subseuent calls to this function (from + _af_wave_update), the data will really be written. + */ + + /* Write 'LIST'. */ + af_fwrite("LIST", 4, 1, filehandle->fh); + + /* Write the size of the following chunk. */ + chunkSize = miscellaneousBytes-8; + chunkSize = HOST_TO_LENDIAN_INT32(chunkSize); + af_fwrite(&chunkSize, sizeof (u_int32_t), 1, filehandle->fh); + + /* Write 'INFO'. */ + af_fwrite("INFO", 4, 1, filehandle->fh); + + /* Write each miscellaneous chunk. */ + for (i=0; i<filehandle->miscellaneousCount; i++) + { + u_int32_t miscsize = HOST_TO_LENDIAN_INT32(filehandle->miscellaneous[i].size); + u_int32_t miscid = 0; + + /* Skip miscellaneous data of an unsupported type. */ + if (misc_type_to_wave(filehandle->miscellaneous[i].type, + &miscid) == AF_FAIL) + continue; + + af_fwrite(&miscid, 4, 1, filehandle->fh); + af_fwrite(&miscsize, 4, 1, filehandle->fh); + if (filehandle->miscellaneous[i].buffer != NULL) + { + u_int8_t zero = 0; + + af_fwrite(filehandle->miscellaneous[i].buffer, filehandle->miscellaneous[i].size, 1, filehandle->fh); + + /* Pad if necessary. */ + if ((filehandle->miscellaneous[i].size%2) != 0) + af_fwrite(&zero, 1, 1, filehandle->fh); + } + else + { + int size; + size = filehandle->miscellaneous[i].size; + + /* Pad if necessary. */ + if ((size % 2) != 0) + size++; + af_fseek(filehandle->fh, size, SEEK_CUR); + } + } + } + + return AF_SUCCEED; +} + +static status WriteCues (AFfilehandle file) +{ + int i, *markids, markCount; + u_int32_t numCues, cueChunkSize, listChunkSize; + _Track *track = &file->tracks[0]; + _WAVEInfo *wave; + + assert(file); + + markCount = afGetMarkIDs(file, AF_DEFAULT_TRACK, NULL); + if (markCount == 0) + return AF_SUCCEED; + + wave = file->formatSpecific; + + if (wave->markOffset == 0) + wave->markOffset = af_ftell(file->fh); + else + af_fseek(file->fh, wave->markOffset, SEEK_SET); + + af_fwrite("cue ", 4, 1, file->fh); + + /* + The cue chunk consists of 4 bytes for the number of cue points + followed by 24 bytes for each cue point record. + */ + cueChunkSize = 4 + markCount * 24; + cueChunkSize = HOST_TO_LENDIAN_INT32(cueChunkSize); + af_fwrite(&cueChunkSize, sizeof (u_int32_t), 1, file->fh); + numCues = HOST_TO_LENDIAN_INT32(markCount); + af_fwrite(&numCues, sizeof (u_int32_t), 1, file->fh); + + markids = _af_calloc(markCount, sizeof (int)); + assert(markids != NULL); + afGetMarkIDs(file, AF_DEFAULT_TRACK, markids); + + /* Write each marker to the file. */ + for (i=0; i < markCount; i++) + { + u_int32_t identifier, position, chunkStart, blockStart; + u_int32_t sampleOffset; + AFframecount markposition; + + identifier = HOST_TO_LENDIAN_INT32(markids[i]); + af_fwrite(&identifier, sizeof (u_int32_t), 1, file->fh); + + position = HOST_TO_LENDIAN_INT32(i); + af_fwrite(&position, sizeof (u_int32_t), 1, file->fh); + + /* For now the RIFF id is always the first data chunk. */ + af_fwrite("data", 4, 1, file->fh); + + /* + For an uncompressed WAVE file which contains + only one data chunk, chunkStart and blockStart + are zero. + */ + chunkStart = 0; + af_fwrite(&chunkStart, sizeof (u_int32_t), 1, file->fh); + + blockStart = 0; + af_fwrite(&blockStart, sizeof (u_int32_t), 1, file->fh); + + markposition = afGetMarkPosition(file, AF_DEFAULT_TRACK, markids[i]); + + /* Sample offsets are stored in the WAVE file as frames. */ + sampleOffset = HOST_TO_LENDIAN_INT32(markposition); + af_fwrite(&sampleOffset, sizeof (u_int32_t), 1, file->fh); + } + + /* + Now write the cue names which is in a master list chunk + with a subchunk for each cue's name. + */ + + listChunkSize = 4; + for (i=0; i<markCount; i++) + { + const char *name; + + name = afGetMarkName(file, AF_DEFAULT_TRACK, markids[i]); + + /* + Each label chunk consists of 4 bytes for the + "labl" chunk ID, 4 bytes for the chunk data + size, 4 bytes for the cue point ID, and then + the length of the label as a Pascal-style string. + + In all, this is 12 bytes plus the length of the + string, its size byte, and a trailing pad byte + if the length of the chunk is otherwise odd. + */ + listChunkSize += 12 + (strlen(name) + 1) + + ((strlen(name) + 1) % 2); + } + + af_fwrite("LIST", 4, 1, file->fh); + listChunkSize = HOST_TO_LENDIAN_INT32(listChunkSize); + af_fwrite(&listChunkSize, sizeof (u_int32_t), 1, file->fh); + af_fwrite("adtl", 4, 1, file->fh); + + for (i=0; i<markCount; i++) + { + const char *name; + u_int32_t labelSize, cuePointID; + + name = afGetMarkName(file, AF_DEFAULT_TRACK, markids[i]); + + /* Make labelSize even if it is not already. */ + labelSize = 4+(strlen(name)+1) + ((strlen(name) + 1) % 2); + labelSize = HOST_TO_LENDIAN_INT32(labelSize); + cuePointID = HOST_TO_LENDIAN_INT32(markids[i]); + + af_fwrite("labl", 4, 1, file->fh); + af_fwrite(&labelSize, 4, 1, file->fh); + af_fwrite(&cuePointID, 4, 1, file->fh); + af_fwrite(name, strlen(name) + 1, 1, file->fh); + /* + If the name plus the size byte comprises an odd + length, add another byte to make the string an + even length. + */ + if (((strlen(name) + 1) % 2) != 0) + { + u_int8_t c=0; + af_fwrite(&c, 1, 1, file->fh); + } + } + + free(markids); + + return AF_SUCCEED; +} + +status _af_wave_write_init (AFfilesetup setup, AFfilehandle filehandle) +{ + u_int32_t zero = 0; + + if (_af_filesetup_make_handle(setup, filehandle) == AF_FAIL) + return AF_FAIL; + + filehandle->formatSpecific = waveinfo_new(); + + af_fseek(filehandle->fh, 0, SEEK_SET); + af_fwrite("RIFF", 4, 1, filehandle->fh); + af_fwrite(&zero, 4, 1, filehandle->fh); + af_fwrite("WAVE", 4, 1, filehandle->fh); + + WriteMiscellaneous(filehandle); + WriteCues(filehandle); + WriteFormat(filehandle); + WriteFrameCount(filehandle); + WriteData(filehandle); + + return AF_SUCCEED; +} |