diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..2bc5b87 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,12 @@ +# EditorConfig is awesome: https://EditorConfig.org + +# top-most EditorConfig file +root = true + +[*] +indent_style = tab +indent_size = 2 +end_of_line = crlf +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 0000000..ab90334 --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,278 @@ +name: Build and Release audiofile + +on: + push: + branches: + - master + + workflow_dispatch: + inputs: + release_type: + description: 'Set to "main" for full release; else pre-release' + required: false + default: '' + +permissions: + contents: write + +jobs: + build-linux-alsa: + name: Build Linux with ALSA + runs-on: ubuntu-latest + outputs: + linux-path: ./release/audiofile + steps: + - uses: actions/checkout@v5 + + - name: Install dependencies + run: | + sudo apt-get update + sudo apt-get install -y \ + build-essential autoconf automake autopoint libtool pkg-config \ + gettext intltool gtk-doc-tools \ + asciidoc docbook-xml docbook-xsl \ + libxml2-utils libasound2-dev + + - name: Generate configure & Build + run: | + chmod +x ./autogen.sh + ./autogen.sh + chmod +x ./configure + ./configure + make -j$(nproc) + + - name: Strip audiofile binary only + run: strip ./audiofile || true + + - name: Upload Linux build with ALSA artifact + uses: actions/upload-artifact@v4 + with: + name: audiofile-linux-alsa + path: ./audiofile + + - name: Upload Documentation artifact + uses: actions/upload-artifact@v4 + with: + name: audiofile-docs + path: ./docs/_build + + build-linux-portaudio: + name: Build Linux with PortAudio + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v5 + + - name: Install dependencies + run: | + sudo apt-get update + sudo apt-get install -y \ + build-essential autoconf automake autopoint libtool pkg-config \ + gettext intltool gtk-doc-tools \ + asciidoc docbook-xml docbook-xsl \ + libxml2-utils portaudio19-dev + + - name: Generate configure & Build audiofile + run: | + chmod +x ./autogen.sh + ./autogen.sh + chmod +x ./configure + ./configure --disable-docs + make -j$(nproc) + + - name: Strip audiofile binary only + run: strip ./audiofile || true + + - name: Upload Linux build with PortAudio artifact + uses: actions/upload-artifact@v4 + with: + name: audiofile-linux-portaudio + path: ./audiofile + + build-windows-x64: + name: Cross-build Windows x64 + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v5 + + - name: Install MinGW-w64 and autotools + run: | + sudo apt-get update + sudo apt-get install -y \ + gcc-mingw-w64-x86-64 g++-mingw-w64-x86-64 \ + make binutils-mingw-w64 automake autoconf autopoint libtool pkg-config \ + gettext intltool gtk-doc-tools asciidoc docbook-xml docbook-xsl libxml2-utils + + - name: Build PortAudio for Windows x64 + run: | + git clone https://github.com/PortAudio/portaudio.git + cd portaudio + ./configure --host=x86_64-w64-mingw32 --disable-shared --prefix=$(pwd)/build + make + make install + cd .. + + - name: Generate configure & Build audiofile + run: | + chmod +x ./autogen.sh + ./autogen.sh + chmod +x ./configure + ./configure CC=x86_64-w64-mingw32-gcc \ + CXX=x86_64-w64-mingw32-g++ \ + CPPFLAGS="-I$(pwd)/portaudio/build/include" \ + LDFLAGS="-L$(pwd)/portaudio/build/lib" \ + PKG_CONFIG_LIBDIR=$(pwd)/portaudio/build/lib/pkgconfig \ + --host=x86_64-w64-mingw32 --disable-docs + make -j$(nproc) + + - name: Strip audiofile.exe only + run: x86_64-w64-mingw32-strip ./audiofile.exe || true + + - name: Upload Windows x64 artifact + uses: actions/upload-artifact@v4 + with: + name: audiofile-windows-x64 + path: ./audiofile.exe + + build-windows-x86: + name: Cross-build Windows x86 + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v5 + + - name: Install MinGW-w64 and autotools + run: | + sudo apt-get update + sudo apt-get install -y \ + gcc-mingw-w64-i686 g++-mingw-w64-i686 \ + make binutils-mingw-w64 automake autoconf autopoint libtool pkg-config \ + gettext intltool gtk-doc-tools asciidoc docbook-xml docbook-xsl libxml2-utils + + - name: Build PortAudio for Windows x86 + run: | + git clone https://github.com/PortAudio/portaudio.git + cd portaudio + ./configure --host=i686-w64-mingw32 --disable-shared --prefix=$(pwd)/build + make + make install + cd .. + + - name: Generate configure & Build audiofile + run: | + chmod +x ./autogen.sh + ./autogen.sh + chmod +x ./configure + ./configure CC=i686-w64-mingw32-gcc \ + CXX=i686-w64-mingw32-g++ \ + CPPFLAGS="-I$(pwd)/portaudio/build/include" \ + LDFLAGS="-L$(pwd)/portaudio/build/lib" \ + PKG_CONFIG_LIBDIR=$(pwd)/portaudio/build/lib/pkgconfig \ + --host=i686-w64-mingw32 --disable-docs + make -j$(nproc) + + - name: Strip audiofile.exe only + run: i686-w64-mingw32-strip ./audiofile.exe || true + + - name: Upload Windows x86 artifact + uses: actions/upload-artifact@v4 + with: + name: audiofile-windows-x86 + path: ./audiofile.exe + + build-macos: + name: Build macOS + runs-on: macos-latest + steps: + - uses: actions/checkout@v5 + + - name: Install dependencies + shell: bash + run: | + brew install pkg-config automake libtool autoconf gettext intltool gtk-doc asciidoc docbook-xsl portaudio cpanminus libxml2 + echo "export PATH=$(brew --prefix libxml2)/bin:$PATH" >> $GITHUB_PATH + echo "export PKG_CONFIG_PATH=$(brew --prefix libxml2)/lib/pkgconfig" >> $GITHUB_ENV + echo "export LDFLAGS=-L$(brew --prefix libxml2)/lib" >> $GITHUB_ENV + echo "export CPPFLAGS=-I$(brew --prefix libxml2)/include" >> $GITHUB_ENV + + - name: Install XML parser + run: cpan XML::Parser + + - name: Generate configure & Build + run: | + export PERL_BAD_REGEX=0 + chmod +x ./autogen.sh + ./autogen.sh + chmod +x ./configure + ./configure --disable-docs + make -j$(sysctl -n hw.ncpu) + + - name: Strip audiofile binary only + run: strip ./audiofile || true + + - name: Upload macOS artifact + uses: actions/upload-artifact@v4 + with: + name: audiofile-macos + path: ./audiofile + + release: + name: Create GitHub Release + runs-on: ubuntu-latest + needs: + - build-linux-alsa + - build-linux-portaudio + - build-windows-x64 + - build-windows-x86 + - build-macos + steps: + - uses: actions/checkout@v5 + + - name: Download Linux ALSA artifact + uses: actions/download-artifact@v5 + with: + name: audiofile-linux-alsa + path: release/linux-alsa + + - name: Download Linux PortAudio artifact + uses: actions/download-artifact@v5 + with: + name: audiofile-linux-portaudio + path: release/linux-portaudio + + - name: Download Windows x64 artifact + uses: actions/download-artifact@v5 + with: + name: audiofile-windows-x64 + path: release/windows-x64 + + - name: Download Windows x86 artifact + uses: actions/download-artifact@v5 + with: + name: audiofile-windows-x86 + path: release/windows-x86 + + - name: Download macOS artifact + uses: actions/download-artifact@v5 + with: + name: audiofile-macos + path: release/macos + + - name: Download Documentation artifact + uses: actions/download-artifact@v5 + with: + name: audiofile-docs + path: release/docs + + - name: Create GitHub Release + uses: softprops/action-gh-release@v2 + with: + tag_name: build-${{ github.run_number }} + prerelease: ${{ github.event.inputs.release_type != 'main' }} + files: | + release/linux-alsa/audiofile + release/linux-portaudio/audiofile + release/windows-x64/audiofile.exe + release/windows-x86/audiofile.exe + release/macos/audiofile + release/docs/* + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.gitignore b/.gitignore index 3444160..2b5a7a6 100644 --- a/.gitignore +++ b/.gitignore @@ -22,11 +22,12 @@ compile config.cache config.guess config.h -config.h.in +config.h.in* config.log config.status config.sub -configure +config.rpath +configure* depcomp install-sh intl @@ -41,3 +42,5 @@ stamp-h.in stamp-h1 test-driver version.h +m4* +po diff --git a/Makefile.am b/Makefile.am index d4706e2..9d445ff 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,6 +1,6 @@ ## Process this file with automake to produce Makefile.in -SUBDIRS = gtest libaudiofile sfcommands test examples docs +SUBDIRS = gtest libaudiofile sfcommands test examples docs po EXTRA_DIST = \ ACKNOWLEDGEMENTS \ @@ -15,6 +15,8 @@ EXTRA_DIST = \ pkgconfig_DATA = audiofile.pc +# ACLOCAL_AMFLAGS = -I m4 + dist-hook: audiofile.spec cp audiofile.spec $(distdir) diff --git a/autogen.sh b/autogen.sh index c18bd7b..6b00be1 100755 --- a/autogen.sh +++ b/autogen.sh @@ -11,12 +11,17 @@ if test -z $AUTORECONF; then exit 1 fi +# Ensure POTFILES.in doesn't already exist before creating a blank one +if [ ! -f "$srcdir/po/POTFILES.in" ]; then + mkdir -p "$srcdir/po" + touch "$srcdir/po/POTFILES.in" +fi + rootme=`pwd` cd $srcdir -autoreconf --install --verbose || exit $? -cd $rootme +autoreconf --install --force --verbose || exit $? +cd "$rootme" $srcdir/configure "$@" -echo -echo "Now type 'make' to compile libaudiofile." +echo "\nNow type 'make' to compile libaudiofile." diff --git a/configure.ac b/configure.ac index 0628cc5..33fd41e 100644 --- a/configure.ac +++ b/configure.ac @@ -5,22 +5,37 @@ AC_CONFIG_SRCDIR([libaudiofile/AIFF.cpp]) dnl Set libtool version information. AUDIOFILE_VERSION_INFO=1:0:0 AUDIOFILE_VERSION=$PACKAGE_VERSION - AC_SUBST(AUDIOFILE_VERSION) AC_SUBST(AUDIOFILE_VERSION_INFO) -AM_INIT_AUTOMAKE -AC_CONFIG_HEADER([config.h]) +AM_INIT_AUTOMAKE([foreign]) +AC_CONFIG_MACRO_DIRS([m4]) dnl Only include local m4 folder +AC_CONFIG_HEADERS([config.h]) + +dnl --- Documentation / Internationalization support --- +AM_GNU_GETTEXT([external]) +AM_GNU_GETTEXT_REQUIRE_VERSION([0.20]) +IT_PROG_INTLTOOL([0.51]) dnl Intltool support +GTK_DOC_INIT dnl Checks for programs. -AC_PROG_CC_C99 +AC_PROG_CC AC_PROG_CXX AC_PROG_INSTALL -AM_PROG_LIBTOOL +LT_INIT + +dnl Ensure C99 support +AC_COMPILE_IFELSE( + [AC_LANG_PROGRAM([[ + #if !defined(__STDC_VERSION__) || __STDC_VERSION__ < 199901L + #error "C99 support required" + #endif + ]], [[return 0;]])], + [AC_MSG_RESULT([C99 support detected])], + [AC_MSG_ERROR([C99 support is required])]) dnl Checks for header files. -AC_HEADER_STDC -AC_CHECK_HEADERS(fcntl.h unistd.h) +AC_CHECK_HEADERS([fcntl.h unistd.h]) dnl Checks for typedefs, structures, and compiler characteristics. AC_C_CONST @@ -31,101 +46,64 @@ AC_SYS_LARGEFILE AC_TYPE_OFF_T AC_TYPE_SIZE_T -dnl Set up platform specific stuff +dnl Platform specific setup platform=none AC_MSG_CHECKING([for platform specific tests to compile]) case "$host_os" in - linux*) - TEST_BIN="linuxtest alsaplay" - platform=linux - ;; - irix5* | irix6*) - TEST_BIN="irixread irixtestloop" - platform=irix - ;; + linux*) TEST_BIN="linuxtest alsaplay"; platform=linux ;; + irix5*|irix6*) TEST_BIN="irixread irixtestloop"; platform=irix ;; darwin*) if test -e /System/Library/Frameworks/CoreAudio.framework; then TEST_BIN="osxplay" platform="Mac OS X" - fi - ;; + fi ;; esac AC_MSG_RESULT($platform) AC_SUBST(TEST_BIN) -AC_ARG_ENABLE(werror, - AS_HELP_STRING([--enable-werror], [treat compiler warnings as errors]), - [enable_werror=$enableval], - [enable_werror=no]) +dnl Compiler flags and options +AC_ARG_ENABLE(werror, AS_HELP_STRING([--enable-werror], [treat compiler warnings as errors]), + [enable_werror=$enableval], [enable_werror=no]) AM_CONDITIONAL(ENABLE_WERROR, [test "$enable_werror" = "yes"]) +AS_IF([test "$enable_werror" = "yes"], [WERROR_CFLAGS="-Werror"; AC_SUBST(WERROR_CFLAGS)]) -AS_IF([test "$enable_werror" = "yes"], - [WERROR_CFLAGS="-Werror" - AC_SUBST(WERROR_CFLAGS)]) - -AC_ARG_ENABLE(coverage, - AS_HELP_STRING([--enable-coverage], [enable code coverage]), - [enable_coverage=$enableval], - [enable_coverage=no]) +AC_ARG_ENABLE(coverage, AS_HELP_STRING([--enable-coverage], [enable code coverage]), + [enable_coverage=$enableval], [enable_coverage=no]) AM_CONDITIONAL(ENABLE_COVERAGE, [test "$enable_coverage" = "yes"]) - AS_IF([test "$enable_coverage" = "yes"], - [COVERAGE_CFLAGS="-fprofile-arcs -ftest-coverage" - COVERAGE_LIBS="-lgcov" - AC_SUBST(COVERAGE_CFLAGS) - AC_SUBST(COVERAGE_LIBS) - - AC_PATH_PROG(LCOV, lcov, :) - AC_PATH_PROG(GENHTML, genhtml, :) - AC_SUBST(LCOV) - AC_SUBST(GENHTML) - AS_IF([test "$LCOV" = :], - [AC_MSG_ERROR([lcov must be installed for code coverage: http://ltp.sourceforge.net/coverage/lcov.php])] - )] -) - -AC_ARG_ENABLE(valgrind, - AS_HELP_STRING([--enable-valgrind], [enable testing with Valgrind]), - [enable_valgrind=$enableval], - [enable_valgrind=no]) + [COVERAGE_CFLAGS="-fprofile-arcs -ftest-coverage" + COVERAGE_LIBS="-lgcov" + AC_SUBST(COVERAGE_CFLAGS) + AC_SUBST(COVERAGE_LIBS) + AC_PATH_PROG(LCOV, lcov, :) + AC_PATH_PROG(GENHTML, genhtml, :) + AC_SUBST(LCOV) + AC_SUBST(GENHTML) + AS_IF([test "$LCOV" = :], [AC_MSG_ERROR([lcov must be installed for code coverage])])]) + +AC_ARG_ENABLE(valgrind, AS_HELP_STRING([--enable-valgrind], [enable testing with Valgrind]), + [enable_valgrind=$enableval], [enable_valgrind=no]) AM_CONDITIONAL(ENABLE_VALGRIND, [test "$enable_valgrind" = "yes"]) - AS_IF([test "$enable_valgrind" = "yes"], - [AC_PATH_PROG(VALGRIND, valgrind, :) - AC_SUBST(VALGRIND) - AS_IF([test "$VALGRIND" = :], - [AC_MSG_ERROR([Could not find Valgrind.])] - )] -) - -AC_ARG_ENABLE(docs, - AS_HELP_STRING([--disable-docs], [disable documentation]), - [enable_documentation=$enableval], - [enable_documentation=yes]) + [AC_PATH_PROG(VALGRIND, valgrind, :) + AC_SUBST(VALGRIND) + AS_IF([test "$VALGRIND" = :], [AC_MSG_ERROR([Could not find Valgrind.])])]) +AC_ARG_ENABLE(docs, AS_HELP_STRING([--disable-docs], [disable documentation]), + [enable_documentation=$enableval], [enable_documentation=yes]) AM_CONDITIONAL(ENABLE_DOCUMENTATION, [test "$enable_documentation" = "yes"]) - AS_IF([test "$enable_documentation" = "yes"], - [AC_PATH_PROG(A2X, a2x, :) - AC_PATH_PROG(ASCIIDOC, asciidoc, :) - AS_IF([test "$A2X" = :], - [AC_MSG_WARN([Could not find a2x.])] - ) - AS_IF([test "$ASCIIDOC" = :], - [AC_MSG_WARN([Could not find asciidoc.])] - )] -) - -AC_ARG_ENABLE(examples, - AS_HELP_STRING([--disable-examples], [disable examples]), - [enable_examples=$enableval], - [enable_examples=yes]) + [AC_PATH_PROG(A2X, a2x, :) + AC_PATH_PROG(ASCIIDOC, asciidoc, :) + AS_IF([test "$A2X" = :], [AC_MSG_WARN([Could not find a2x.])]) + AS_IF([test "$ASCIIDOC" = :], [AC_MSG_WARN([Could not find asciidoc.])])]) + +AC_ARG_ENABLE(examples, AS_HELP_STRING([--disable-examples], [disable examples]), + [enable_examples=$enableval], [enable_examples=yes]) AS_IF([test "$enable_examples" != "yes"], [TEST_BIN=""]) -AC_ARG_ENABLE(flac, - AS_HELP_STRING([--disable-flac], [disable FLAC]), - [enable_flac=$enableval], - [enable_flac=yes]) +AC_ARG_ENABLE(flac, AS_HELP_STRING([--disable-flac], [disable FLAC]), + [enable_flac=$enableval], [enable_flac=yes]) PKG_PROG_PKG_CONFIG PKG_INSTALLDIR @@ -133,39 +111,79 @@ PKG_INSTALLDIR FLAC_CFLAGS="" FLAC_LIBS="" -if test -n "$PKG_CONFIG" ; then - if test "$enable_flac" = "yes" ; then - PKG_CHECK_MODULES([FLAC], [flac >= 1.2.1], [ac_cv_flac=yes], [ac_cv_flac=no]) - FLAC_CFLAGS=`echo $FLAC_CFLAGS | $SED "s:include/FLAC:include:"` - if test "$ac_cv_flac" = "no" ; then - enable_flac=no - fi - fi +if test -n "$PKG_CONFIG"; then + if test "$enable_flac" = "yes"; then + PKG_CHECK_MODULES([FLAC], [flac >= 1.2.1], [ac_cv_flac=yes], [ac_cv_flac=no]) + FLAC_CFLAGS=`echo $FLAC_CFLAGS | $SED "s:include/FLAC:include:"` + if test "$ac_cv_flac" = "no"; then enable_flac=no; fi + fi else - enable_flac=no + enable_flac=no fi AC_SUBST(FLAC_CFLAGS) AC_SUBST(FLAC_LIBS) - AM_CONDITIONAL(ENABLE_FLAC, [test "$enable_flac" = "yes"]) -if test "$enable_flac" = "yes" ; then - AC_DEFINE_UNQUOTED([ENABLE_FLAC], [1], [Whether FLAC is enabled.]) +if test "$enable_flac" = "yes"; then + AC_DEFINE_UNQUOTED([ENABLE_FLAC], [1], [Whether FLAC is enabled.]) +else + AC_DEFINE_UNQUOTED([ENABLE_FLAC], [0], [Whether FLAC is enabled.]) +fi + +dnl --- Check for ALSA library (Linux) --- +have_alsa=no +if test "$host_os" != "linux-gnu"; then + have_alsa=no +elif test "$cross_compiling" != yes; then + AC_CHECK_LIB([asound], [snd_pcm_open], [have_alsa=yes], [have_alsa=no]) +fi + +if test "$have_alsa" = yes; then + AC_DEFINE([HAVE_ALSA], [1], [Define if ALSA is available]) +else + AC_DEFINE([HAVE_ALSA], [0], [Define if ALSA is available]) +fi +AM_CONDITIONAL([HAVE_ALSA], [test "$have_alsa" = yes]) + +dnl --- Check for PortAudio library (all platforms) --- + +have_portaudio=no +have_portaudio_lib=no +have_portaudio_cond=no + +dnl If cross-compiling, just assume PortAudio exists if user provides headers/libs +if test "$cross_compiling" = yes; then + AC_MSG_CHECKING([for PortAudio library (cross-compile)]) + AC_MSG_RESULT([using provided build]) + have_portaudio=yes + have_portaudio_lib=yes +else + dnl Normal build: check for portaudio.h and link with libportaudio + AC_CHECK_HEADER([portaudio.h], [have_portaudio=yes], [have_portaudio=no]) + AC_CHECK_LIB([portaudio], [Pa_Initialize], [have_portaudio_lib=yes], [have_portaudio_lib=no]) +fi + +if test "$have_portaudio" = yes && test "$have_portaudio_lib" = yes; then + AC_DEFINE([HAVE_PORTAUDIO], [1], [Define if PortAudio is available]) + have_portaudio_cond=yes else - AC_DEFINE_UNQUOTED([ENABLE_FLAC], [0], [Whether FLAC is enabled.]) + AC_DEFINE([HAVE_PORTAUDIO], [0], [Define if PortAudio is available]) fi +AM_CONDITIONAL([HAVE_PORTAUDIO], [test "$have_portaudio_cond" = yes]) AC_CONFIG_FILES([ - audiofile.spec - audiofile.pc - audiofile-uninstalled.pc - sfcommands/Makefile - test/Makefile - gtest/Makefile - examples/Makefile - libaudiofile/Makefile - libaudiofile/alac/Makefile - libaudiofile/modules/Makefile - docs/Makefile - Makefile]) + audiofile.spec + audiofile.pc + audiofile-uninstalled.pc + po/Makefile.in + sfcommands/Makefile + test/Makefile + gtest/Makefile + examples/Makefile + libaudiofile/Makefile + libaudiofile/alac/Makefile + libaudiofile/modules/Makefile + docs/Makefile + Makefile +]) AC_OUTPUT diff --git a/docs/Makefile.am b/docs/Makefile.am index 327b394..7e08c36 100644 --- a/docs/Makefile.am +++ b/docs/Makefile.am @@ -66,6 +66,30 @@ A2XFLAGS = $(ASCIIDOCFLAGS) -d manpage -f manpage %.3: %.3.txt $(A2X) $(A2XFLAGS) --asciidoc-opts="-f asciidoc.conf" $< +afIdentifyNamedFD.3: afIdentifyFD.3.txt + $(A2X) $(A2XFLAGS) --asciidoc-opts="-f asciidoc.conf" $< + +afInitAESChannelData.3: afInitAESChannelDataTo.3.txt + $(A2X) $(A2XFLAGS) --asciidoc-opts="-f asciidoc.conf" $< + +afInitByteOrder.3 afInitChannels.3 afInitRate.3: afInitSampleFormat.3.txt + $(A2X) $(A2XFLAGS) --asciidoc-opts="-f asciidoc.conf" $< + +afGetDataOffset.3 afGetTrackBytes.3: afGetFrameCount.3.txt + $(A2X) $(A2XFLAGS) --asciidoc-opts="-f asciidoc.conf" $< + +afQueryLong.3 afQueryDouble.3 afQueryPointer.3: afQuery.3.txt + $(A2X) $(A2XFLAGS) --asciidoc-opts="-f asciidoc.conf" $< + +afSeekMisc.3 afWriteMisc.3: afReadMisc.3.txt + $(A2X) $(A2XFLAGS) --asciidoc-opts="-f asciidoc.conf" $< + +afSetVirtualByteOrder.3 afSetVirtualChannels.3 afSetVirtualPCMMapping.3: afSetVirtualSampleFormat.3.txt + $(A2X) $(A2XFLAGS) --asciidoc-opts="-f asciidoc.conf" $< + +afTellFrame.3: afSeekFrame.3.txt + $(A2X) $(A2XFLAGS) --asciidoc-opts="-f asciidoc.conf" $< + html: $(DOCS_HTML) CLEANFILES = *.1 *.3 *.html diff --git a/docs/asciidoc.conf b/docs/asciidoc.conf index 100932f..dee2852 100644 --- a/docs/asciidoc.conf +++ b/docs/asciidoc.conf @@ -7,7 +7,7 @@ # defined, else just show the command. [macros] -(?su)[\\]?(?Plinkaf):(?P\S*?)\[(?P.*?)\]= +(?su)[\\]?(?Plinkaf):(?P[^ \t\r\n]*?)\[(?P.*?)\]= ifdef::backend-docbook[] [linkaf-inlinemacro] diff --git a/examples/Makefile.am b/examples/Makefile.am index 22b0019..8c40a76 100644 --- a/examples/Makefile.am +++ b/examples/Makefile.am @@ -9,8 +9,21 @@ EXTRA_PROGRAMS = alsaplay irixread irixtestloop linuxtest osxplay LIBAUDIOFILE = $(top_builddir)/libaudiofile/libaudiofile.la +# ALSA playback for Linux and PortAudio playback elsewhere alsaplay_SOURCES = alsaplay.cpp -alsaplay_LDADD = $(LIBAUDIOFILE) -lasound +alsaplay_LDADD = $(LIBAUDIOFILE) + +# If ALSA is available, remove PortAudio and add ALSA +if HAVE_ALSA + alsaplay_LDADD := $(filter-out -lportaudio,$(alsaplay_LDADD)) + alsaplay_LDADD += -lasound +endif + +# If PortAudio is available, remove ALSA and add PortAudio +if HAVE_PORTAUDIO + alsaplay_LDADD := $(filter-out -lasound,$(alsaplay_LDADD)) + alsaplay_LDADD += -lportaudio +endif irixread_SOURCES = irixread.c sgi.c sgi.h irixread_LDADD = $(LIBAUDIOFILE) -laudio diff --git a/examples/alsaplay.cpp b/examples/alsaplay.cpp index 4c11f55..c3a20d4 100644 --- a/examples/alsaplay.cpp +++ b/examples/alsaplay.cpp @@ -32,10 +32,23 @@ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#include +/* + Audio File Library - Playback using ALSA on Linux or PortAudio elsewhere +*/ + +#include "../config.h" #include #include #include +#include + +#ifdef HAVE_ALSA +#include +#elif defined(HAVE_PORTAUDIO) +#include +#else +#error "No audio backend available!" +#endif int main(int argc, char **argv) { @@ -45,6 +58,7 @@ int main(int argc, char **argv) exit(EXIT_FAILURE); } + // Open audio file using Audio File Library AFfilehandle file = afOpenFile(argv[1], "r", AF_NULL_FILESETUP); if (!file) { @@ -54,10 +68,18 @@ int main(int argc, char **argv) int channels = afGetChannels(file, AF_DEFAULT_TRACK); double rate = afGetRate(file, AF_DEFAULT_TRACK); + + // Force 16-bit 2's complement output for simplicity afSetVirtualSampleFormat(file, AF_DEFAULT_TRACK, AF_SAMPFMT_TWOSCOMP, 16); + const int bufferFrames = 4096; + int16_t *buffer = new int16_t[bufferFrames * channels]; + +#if HAVE_ALSA + // --- ALSA playback for Linux --- int err; snd_pcm_t *handle; + if ((err = snd_pcm_open(&handle, "default", SND_PCM_STREAM_PLAYBACK, 0)) < 0) { fprintf(stderr, "Could not open audio output: %s\n", snd_strerror(err)); @@ -71,30 +93,82 @@ int main(int argc, char **argv) exit(EXIT_FAILURE); } - const int bufferFrames = 4096; - int16_t *buffer = new int16_t[bufferFrames * channels]; - while (true) { + // Read frames from the audio file AFframecount framesRead = afReadFrames(file, AF_DEFAULT_TRACK, buffer, bufferFrames); if (framesRead <= 0) break; - snd_pcm_sframes_t framesWritten = snd_pcm_writei(handle, buffer, bufferFrames); + // Write frames to ALSA device + snd_pcm_sframes_t framesWritten = snd_pcm_writei(handle, buffer, framesRead); if (framesWritten < 0) framesWritten = snd_pcm_recover(handle, framesWritten, 0); if (framesWritten < 0) { - fprintf(stderr, "Could not write audio data to output device: %s\n", - snd_strerror(err)); + fprintf(stderr, "Could not write audio data: %s\n", snd_strerror(framesWritten)); break; } } snd_pcm_drain(handle); snd_pcm_close(handle); - delete [] buffer; +#else + // --- PortAudio playback for non-Linux platforms --- + PaError err; + err = Pa_Initialize(); + if (err != paNoError) + { + fprintf(stderr, "PortAudio init failed: %s\n", Pa_GetErrorText(err)); + exit(EXIT_FAILURE); + } + + PaStream *stream; + err = Pa_OpenDefaultStream(&stream, + 0, // no input channels + channels, // output channels + paInt16, // 16-bit PCM + rate, + bufferFrames, + NULL, // no callback, blocking + NULL); + if (err != paNoError) + { + fprintf(stderr, "PortAudio open stream failed: %s\n", Pa_GetErrorText(err)); + Pa_Terminate(); + exit(EXIT_FAILURE); + } + + err = Pa_StartStream(stream); + if (err != paNoError) + { + fprintf(stderr, "PortAudio start stream failed: %s\n", Pa_GetErrorText(err)); + Pa_CloseStream(stream); + Pa_Terminate(); + exit(EXIT_FAILURE); + } + + while (true) + { + AFframecount framesRead = afReadFrames(file, AF_DEFAULT_TRACK, buffer, bufferFrames); + if (framesRead <= 0) + break; + + err = Pa_WriteStream(stream, buffer, framesRead); + if (err != paNoError) + { + fprintf(stderr, "PortAudio write failed: %s\n", Pa_GetErrorText(err)); + break; + } + } + + Pa_StopStream(stream); + Pa_CloseStream(stream); + Pa_Terminate(); +#endif + + delete[] buffer; afCloseFile(file); return 0; diff --git a/libaudiofile/Makefile.am b/libaudiofile/Makefile.am index d502cf2..b2a28c7 100644 --- a/libaudiofile/Makefile.am +++ b/libaudiofile/Makefile.am @@ -124,4 +124,6 @@ UnitTests_LDFLAGS = -static AM_CFLAGS = -DNDEBUG -Wall $(WERROR_CFLAGS) $(COVERAGE_CFLAGS) $(FLAC_CFLAGS) -fvisibility=hidden AM_CXXFLAGS = $(AM_CFLAGS) -fno-rtti -fno-exceptions -fvisibility-inlines-hidden +AUTOMAKE_OPTIONS = subdir-objects + CLEANFILES = *.gcda *.gcno