mirror of
https://github.com/mozilla/cubeb
synced 2024-11-21 14:29:11 -07:00
initial import (from 892ca582fe32577392a9843f14b8c5370df6ed78).
This commit is contained in:
commit
5045555f25
19 changed files with 4141 additions and 0 deletions
41
.gitignore
vendored
Normal file
41
.gitignore
vendored
Normal file
|
@ -0,0 +1,41 @@
|
|||
*.lo
|
||||
*.o
|
||||
*.swp
|
||||
*~
|
||||
.deps
|
||||
.dirstamp
|
||||
.libs
|
||||
Makefile
|
||||
Makefile.in
|
||||
_stdint.h
|
||||
aclocal.m4
|
||||
autom4te.cache
|
||||
compile
|
||||
config.guess
|
||||
config.h
|
||||
config.h.in
|
||||
config.log
|
||||
config.status
|
||||
config.sub
|
||||
configure
|
||||
depcomp
|
||||
docs/Doxyfile
|
||||
docs/doxygen-build.stamp
|
||||
docs/html
|
||||
install-sh
|
||||
libtool
|
||||
ltmain.sh
|
||||
m4/libtool.m4
|
||||
m4/ltoptions.m4
|
||||
m4/ltsugar.m4
|
||||
m4/ltversion.m4
|
||||
m4/lt~obsolete.m4
|
||||
missing
|
||||
cubeb-uninstalled.pc
|
||||
cubeb.pc
|
||||
src/.dirstamp
|
||||
src/libcubeb.la
|
||||
stamp-h1
|
||||
test/test
|
||||
test/test_sanity
|
||||
include/cubeb/cubeb-stdint.h
|
1
AUTHORS
Normal file
1
AUTHORS
Normal file
|
@ -0,0 +1 @@
|
|||
Matthew Gregan <kinetik@flim.org>
|
8
INSTALL
Normal file
8
INSTALL
Normal file
|
@ -0,0 +1,8 @@
|
|||
Build instructions for libcubeb
|
||||
===============================
|
||||
|
||||
0. Change directory into the source directory.
|
||||
1. Run |autoreconf --install| to generate configure.
|
||||
2. Run |./configure| to configure the build.
|
||||
3. Run |make| to build.
|
||||
4. Run |make check| to run the test suite.
|
13
LICENSE
Normal file
13
LICENSE
Normal file
|
@ -0,0 +1,13 @@
|
|||
Copyright © 2011 Mozilla Foundation
|
||||
|
||||
Permission to use, copy, modify, and distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, 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.
|
45
Makefile.am
Normal file
45
Makefile.am
Normal file
|
@ -0,0 +1,45 @@
|
|||
AUTOMAKE_OPTIONS = foreign 1.11 no-dist-gzip dist-xz subdir-objects
|
||||
ACLOCAL_AMFLAGS = -I m4
|
||||
|
||||
INCLUDES = -I$(top_srcdir)/include -I.
|
||||
AM_CFLAGS = -ansi -pedantic -std=c99 -Wall -Wextra -Wno-long-long -O0 -g
|
||||
|
||||
SUBDIRS = docs
|
||||
|
||||
EXTRA_DIST = \
|
||||
AUTHORS README LICENSE \
|
||||
cubeb-uninstalled.pc.in \
|
||||
m4/as-ac-expand.m4 \
|
||||
m4/pkg.m4 \
|
||||
m4/ax_create_stdint_h.m4
|
||||
|
||||
pkgconfigdir = $(libdir)/pkgconfig
|
||||
pkgconfig_DATA = cubeb.pc
|
||||
|
||||
cubebincludedir = $(includedir)/cubeb
|
||||
cubebinclude_HEADERS = include/cubeb/cubeb.h include/cubeb/cubeb-stdint.h
|
||||
|
||||
lib_LTLIBRARIES = src/libcubeb.la
|
||||
|
||||
src_libcubeb_la_SOURCES = \
|
||||
src/cubeb_pulse.c
|
||||
|
||||
src_libcubeb_la_LDFLAGS = -export-symbols-regex '^cubeb_'
|
||||
|
||||
check_PROGRAMS = test/test_sanity
|
||||
|
||||
test_test_sanity_SOURCES = test/test_sanity.c
|
||||
test_test_sanity_LDADD = -lpulse -lm src/libcubeb.la
|
||||
|
||||
TESTS = test/test_sanity
|
||||
|
||||
DISTCLEANFILES = include/cubeb/cubeb-stdint.h
|
||||
|
||||
dist-hook:
|
||||
find $(distdir) -type d -name '.git' | xargs rm -rf
|
||||
|
||||
debug:
|
||||
$(MAKE) all CFLAGS="@DEBUG@"
|
||||
|
||||
profile:
|
||||
$(MAKE) all CFLAGS="@PROFILE@"
|
3
README
Normal file
3
README
Normal file
|
@ -0,0 +1,3 @@
|
|||
See INSTALL for build instructions.
|
||||
|
||||
Licensed under an ISC-style license. See LICENSE for details.
|
219
TODO
Normal file
219
TODO
Normal file
|
@ -0,0 +1,219 @@
|
|||
TODO:
|
||||
- sync issues after seeking on osx
|
||||
- prefill on start is aggressive, fills all buffers... on playback start
|
||||
we (usually) have enough, but that's not true after seeking (why's it
|
||||
different?)
|
||||
- prefill timing behaviour is different on osx vs linux:
|
||||
linux takes ~6ms before prefill callback - actually seening it called
|
||||
immediately while stream_init waits on STREAM_READY
|
||||
osx prefills immediately from stream_init
|
||||
so it's likely that osx underruns when linux may not
|
||||
- prefill needs some amount of data, depending on latency (and maybe platform)
|
||||
- decoder needs to know how much before so it can decode ahead
|
||||
- or it can write silence and deal with it via underrun handling
|
||||
- except that means we have [audio ...] [inserted gap] [audio ...]
|
||||
which is stupid, so the caller needs to know how much is required for
|
||||
prefill
|
||||
- *also* need xrun handling for real xruns, where we've run out of data
|
||||
and must write silence and adjust the clock by it
|
||||
- deal with interaction between prefill and short files
|
||||
- allow callback to signal how much has been written as well as EOS
|
||||
- document thread safety
|
||||
- document what is and isn't safe inside callbacks, locking, blocking, cubeb calls
|
||||
- clarify samples vs frames vs bytes, introduce types or something
|
||||
- add silence insertion code
|
||||
- underrun handling for decoder
|
||||
- fix locking in callbacks
|
||||
- implement basic support for windows
|
||||
- get tests passing on windows
|
||||
- get pulse volume sane
|
||||
- test (and fix) small file handling
|
||||
- implement correct channel mapping
|
||||
- switch on channel count for simple formats, use fixed mapping
|
||||
- vorbis has documented mapping based on channel count (if mapping type == 0)
|
||||
http://xiph.org/vorbis/doc/Vorbis_I_spec.html#x1-800004.3.9
|
||||
1 -> M
|
||||
2 -> L, R
|
||||
3 -> L, C, R
|
||||
4 -> L, R, RL, RR
|
||||
5 -> L, C, R, RL, RR
|
||||
6 -> L, C, R, RL, RR, LFE
|
||||
7 -> L, C, R, SL, SR, RC, LFE
|
||||
8 -> L, C, R, SL, SR, RL, RR, LFE
|
||||
>8 -> application defined
|
||||
- wave files with channel count only
|
||||
3 -> L, R, C
|
||||
4 -> L, R, RL, RR
|
||||
5 -> L, R, C, RL, RR
|
||||
6 -> L, R, C, LFE, RL, RR
|
||||
7 -> L, R, C, LFE, RC, SL, SR
|
||||
8 -> L, R, C, LFE, RL, RR, SL, SR
|
||||
- wave files with WAVE_FORMAT_EXTENSIBLE have explicitly mappings, can extract these
|
||||
- fix audio api code
|
||||
- rework remote-audio code for android/fennec
|
||||
- document which calls may block, and when effects take effect
|
||||
(e.g. drain doesn't block, volume doesn't(?) and effect is delayed)
|
||||
|
||||
|
||||
NOTES:
|
||||
- osx drain: fill buffer in prefill, return EOS. listener set up for drain.
|
||||
then user starts playback.
|
||||
listener fires for playing==true.
|
||||
- AudioQueueGetCurrentTime can return negative mSampleTimes - why?
|
||||
|
||||
============================================================================
|
||||
|
||||
start with basic stuff:
|
||||
- callback to get data
|
||||
- preflight buffering (user needs to know how much to prepare first?)
|
||||
- per-stream volume
|
||||
- threading model
|
||||
- underrun handling model
|
||||
- vorbis channel mappings
|
||||
|
||||
|
||||
preflight - read a bunch of data before starting playback.
|
||||
- user needs to know how much to prepare.
|
||||
|
||||
channel mapping - per vorbis spec
|
||||
- what if particular mapping not available?
|
||||
http://www.xiph.org/vorbis/doc/Vorbis_I_spec.html#x1-800004.3.9
|
||||
- pick next one down and remix?
|
||||
|
||||
per-stream volume
|
||||
- if we can't provide, cubeb will implement in software
|
||||
|
||||
implement formats in software by resampling
|
||||
|
||||
callback states:
|
||||
preflight
|
||||
playing
|
||||
destroy
|
||||
|
||||
user can handle destroy as immediate or play-all-before-destroy by
|
||||
reacting in callback. actually doesn't work if latency is high so much
|
||||
audio is buffered--need a way to force immediately destroy. maybe destroy
|
||||
while playing = drain, destroy while stopped = immediate? one of stop or
|
||||
destroy must provide an immediate option.
|
||||
|
||||
all of the library calls should return immediately. this means any
|
||||
potentially blocking operation (drain) must be notified via a callback.
|
||||
should we reuse the audio callback for this, or use a new callback?
|
||||
possibly use new, but structure it so the user can reuse the same callback
|
||||
for both.
|
||||
|
||||
-> data callback, requests (or supplies -- for input) data
|
||||
-> state callback, indicates a state change when it happens
|
||||
|
||||
must make guarantees about when callbacks will and won't run. e.g. if
|
||||
stream destroyed, no more callbacks run. if a callback is currently
|
||||
running... what? block the destroy or return but let the callback
|
||||
complete?
|
||||
|
||||
callback timestamp is latest playback pos. user can store this for
|
||||
playback pos. but then depends on callback latency -- not good idea to
|
||||
callback as fast as we can provide position updates if it's unnecessary?
|
||||
|
||||
underrun handling?
|
||||
|
||||
basic api:
|
||||
|
||||
ctx = init(rate, channels, format, latency, data_callback)
|
||||
start(ctx)
|
||||
... data_callback runs on some thread ...
|
||||
ms = get_time(ctx)
|
||||
set_volume(ctx, v)
|
||||
stop(ctx, mode) : mode = now/drain
|
||||
... guarantee no data_callbacks after stop returns ...
|
||||
start(ctx)
|
||||
... data_callback runs on some thread ...
|
||||
ms = get_time(ctx)
|
||||
set_volume(ctx, v)
|
||||
stop(ctx, mode) : mode = now/drain
|
||||
destroy(ctx) (if running, calls stop)
|
||||
|
||||
mode now = pause and keep buffered data to play when started
|
||||
mode drain = pause after remaining buffers played
|
||||
mode flush = pause and drop buffered data
|
||||
|
||||
|
||||
introduce types for samples and frames
|
||||
|
||||
|
||||
============================================================================
|
||||
from bugzilla:
|
||||
============================================================================
|
||||
The plan is to build a small library that maps closely to modern sound APIs
|
||||
(PulseAudio on Linux, CoreAudio (AudioQueue) on OS X, and either DirectSound or
|
||||
XAudio2 on Windows). Rather than exposing a push-to-play model like
|
||||
sydneyaudio (which effectively requires a separate thread per playing media
|
||||
element to write audio), a callback will be called when more audio is required.
|
||||
|
||||
It may be necessary to provide an ALSA backend in addition to the PulseAudio
|
||||
one, depending on the minimum Linux distro requirements post Firefox 4.
|
||||
|
||||
Requirements:
|
||||
- remove necessity for 1-2 threads per active audio stream that we currently
|
||||
have (bug 592833)
|
||||
- underrun behavior/handling should be the same on each platform
|
||||
- low latency volume changes (bug 487504)
|
||||
- sharing top-level audio resources (bug 617852)
|
||||
- correct playback of sub-one-buffer length audio chunks (bug 615452)
|
||||
|
||||
Reviewing some important aspects of existing behaviour:
|
||||
|
||||
Playback start:
|
||||
- Linux: when start threshold reached
|
||||
- OS X: after first write
|
||||
- Win32: when block filled
|
||||
|
||||
With a callback based model, this problem mostly disappears. The library can
|
||||
decide what an appropriate initial buffer size is and explicitly request that
|
||||
the application fill it.
|
||||
|
||||
Underrun:
|
||||
- Linux: audio clock stops ticking
|
||||
- OS X: audio clock continues at normal rate (callback writes silence)
|
||||
- Win32: audio clock stops ticking
|
||||
|
||||
It's preferable for the clock to stop ticking. The only reason it doesn't on
|
||||
OS X is that I'm not aware of a way to stop it--the implementation is already
|
||||
using a callback model, and when a callback requests more data than is
|
||||
available, it's not obvious what you can do other than write silence.
|
||||
|
||||
API calls while underrun:
|
||||
- Linux: return error, usually return useless (initial state) values after
|
||||
recovery
|
||||
- OS X: work as there's effectively no underrun state
|
||||
- Win32: return frozen state at point of underrun
|
||||
|
||||
Ideally the underrun recovery should be dealt with in a single place, so other
|
||||
API calls should try to return sensible values when playback is stopped due to
|
||||
underrun.
|
||||
|
||||
Underrun recovery:
|
||||
- Linux: explicit, expensive, partial buffers lost
|
||||
- OS X: N/A
|
||||
- Win32: implicit (write more), cheap, no buffers lost
|
||||
|
||||
There's not much control available of this, it's an attribute of the OS's audio
|
||||
API.
|
||||
|
||||
Clock granularity:
|
||||
- Linux: unknown (but probably discoverable), often not more than one period
|
||||
length
|
||||
- OS X: dependent on callback buffer request size
|
||||
- Win32: unknown, seems fairly high (likely sample accurate)
|
||||
|
||||
Clock read cost:
|
||||
- Linux: medium to high, cross-DSO call, possibly involving IPC
|
||||
- OS X: low, reads a local counter protected by a lock
|
||||
- Win32: medium, cross-DSO call
|
||||
|
||||
In the current implementation, the caller may call the clock read function in a
|
||||
tight loop (with very short sleeps in between). In the older Firefox media
|
||||
playback engine, it was assumed that the clock was fine enough granularity that
|
||||
individual video frames could be timed based on the audio clock updates.
|
||||
Either the clock granularity must be explicit, high, and fast to read, or the
|
||||
API must be designed in such a way that the application can't easily make
|
||||
inappropriate assumptions about the clock's behaviour.
|
124
configure.ac
Normal file
124
configure.ac
Normal file
|
@ -0,0 +1,124 @@
|
|||
dnl ------------------------------------------------
|
||||
dnl Initialization and Versioning
|
||||
dnl ------------------------------------------------
|
||||
|
||||
AC_INIT(libcubeb,[0.1git])
|
||||
|
||||
AC_CANONICAL_HOST
|
||||
AC_CANONICAL_TARGET
|
||||
|
||||
AC_CONFIG_MACRO_DIR([m4])
|
||||
|
||||
AM_CONFIG_HEADER([config.h])
|
||||
AC_CONFIG_SRCDIR([src/cubeb_pulse.c])
|
||||
AM_INIT_AUTOMAKE
|
||||
|
||||
m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])])
|
||||
|
||||
dnl Library versioning
|
||||
dnl CURRENT, REVISION, AGE
|
||||
dnl - library source changed -> increment REVISION
|
||||
dnl - interfaces added/removed/changed -> increment CURRENT, REVISION = 0
|
||||
dnl - interfaces added -> increment AGE
|
||||
dnl - interfaces removed -> AGE = 0
|
||||
|
||||
CUBEB_CURRENT=0
|
||||
CUBEB_REVISION=0
|
||||
CUBEB_AGE=1
|
||||
AC_SUBST(CUBEB_CURRENT)
|
||||
AC_SUBST(CUBEB_REVISION)
|
||||
AC_SUBST(CUBEB_AGE)
|
||||
|
||||
|
||||
dnl --------------------------------------------------
|
||||
dnl Check for programs
|
||||
dnl --------------------------------------------------
|
||||
|
||||
dnl save $CFLAGS since AC_PROG_CC likes to insert "-g -O2"
|
||||
dnl if $CFLAGS is blank
|
||||
cflags_save="$CFLAGS"
|
||||
AC_PROG_CC
|
||||
AC_PROG_CPP
|
||||
CFLAGS="$cflags_save"
|
||||
|
||||
AM_PROG_CC_C_O
|
||||
AC_LIBTOOL_WIN32_DLL
|
||||
AM_PROG_LIBTOOL
|
||||
|
||||
dnl Check for doxygen
|
||||
AC_ARG_ENABLE([doc],
|
||||
AS_HELP_STRING([--enable-doc], [Build API documentation]),
|
||||
[ac_enable_doc=$enableval], [ac_enable_doc=auto])
|
||||
|
||||
if test "x$ac_enable_doc" != "xno"; then
|
||||
AC_CHECK_PROG(HAVE_DOXYGEN, doxygen, true, false)
|
||||
|
||||
if test "x$HAVE_DOXYGEN" = "xfalse" -a "x$ac_enable_doc" = "xyes"; then
|
||||
AC_MSG_ERROR([*** API documentation explicitly requested but Doxygen not found])
|
||||
fi
|
||||
else
|
||||
HAVE_DOXYGEN=false
|
||||
fi
|
||||
AM_CONDITIONAL(HAVE_DOXYGEN,$HAVE_DOXYGEN)
|
||||
if test $HAVE_DOXYGEN = "false"; then
|
||||
AC_MSG_WARN([*** doxygen not found, API documentation will not be built])
|
||||
fi
|
||||
|
||||
# Generate portable stdint.h replacement
|
||||
AX_CREATE_STDINT_H(include/cubeb/cubeb-stdint.h)
|
||||
|
||||
# Test whenever ld supports -version-script
|
||||
AC_PROG_LD
|
||||
AC_PROG_LD_GNU
|
||||
AC_MSG_CHECKING([how to control symbol export])
|
||||
|
||||
dnl --------------------------------------------------
|
||||
dnl Do substitutions
|
||||
dnl --------------------------------------------------
|
||||
|
||||
AC_SUBST(DEBUG)
|
||||
AC_SUBST(PROFILE)
|
||||
|
||||
AC_OUTPUT([
|
||||
Makefile
|
||||
docs/Makefile
|
||||
docs/Doxyfile
|
||||
cubeb.pc
|
||||
cubeb-uninstalled.pc
|
||||
])
|
||||
|
||||
AS_AC_EXPAND(LIBDIR, ${libdir})
|
||||
AS_AC_EXPAND(INCLUDEDIR, ${includedir})
|
||||
AS_AC_EXPAND(BINDIR, ${bindir})
|
||||
AS_AC_EXPAND(DOCDIR, ${docdir})
|
||||
|
||||
if test $HAVE_DOXYGEN = "false"; then
|
||||
doc_build="no"
|
||||
else
|
||||
doc_build="yes"
|
||||
fi
|
||||
|
||||
AC_MSG_RESULT([
|
||||
------------------------------------------------------------------------
|
||||
$PACKAGE $VERSION: Automatic configuration OK.
|
||||
|
||||
General configuration:
|
||||
|
||||
API Documentation: .......... ${doc_build}
|
||||
|
||||
Installation paths:
|
||||
|
||||
libcubeb: .................... ${LIBDIR}
|
||||
C header files: .............. ${INCLUDEDIR}/cubeb
|
||||
Documentation: ............... ${DOCDIR}
|
||||
|
||||
Building:
|
||||
|
||||
Type 'make' to compile $PACKAGE.
|
||||
|
||||
Type 'make install' to install $PACKAGE.
|
||||
|
||||
Example programs will be built but not installed.
|
||||
------------------------------------------------------------------------
|
||||
])
|
||||
|
13
cubeb-uninstalled.pc.in
Normal file
13
cubeb-uninstalled.pc.in
Normal file
|
@ -0,0 +1,13 @@
|
|||
# cubeb uninstalled pkg-config file
|
||||
|
||||
prefix=@prefix@
|
||||
exec_prefix=@exec_prefix@
|
||||
libdir=@libdir@
|
||||
includedir=@includedir@
|
||||
|
||||
Name: cubeb
|
||||
Description: WebM demuxer
|
||||
Version: @VERSION@
|
||||
Conflicts:
|
||||
Libs: -L${libdir} -lcubeb
|
||||
Cflags: -I${includedir}
|
13
cubeb.pc.in
Normal file
13
cubeb.pc.in
Normal file
|
@ -0,0 +1,13 @@
|
|||
# cubeb installed pkg-config file
|
||||
|
||||
prefix=@prefix@
|
||||
exec_prefix=@exec_prefix@
|
||||
libdir=@libdir@
|
||||
includedir=@includedir@
|
||||
|
||||
Name: cubeb
|
||||
Description: Portable audio API
|
||||
Version: @VERSION@
|
||||
Conflicts:
|
||||
Libs: -L${libdir} -lcubeb
|
||||
Cflags: -I${includedir}
|
1551
docs/Doxyfile.in
Normal file
1551
docs/Doxyfile.in
Normal file
File diff suppressed because it is too large
Load diff
38
docs/Makefile.am
Normal file
38
docs/Makefile.am
Normal file
|
@ -0,0 +1,38 @@
|
|||
doc_DATA = doxygen-build.stamp
|
||||
|
||||
EXTRA_DIST = Doxyfile.in
|
||||
|
||||
if HAVE_DOXYGEN
|
||||
doxygen-build.stamp: Doxyfile ../include/cubeb/cubeb.h
|
||||
doxygen
|
||||
touch doxygen-build.stamp
|
||||
else
|
||||
doxygen-build.stamp:
|
||||
echo "*** Warning: Doxygen not found; documentation will not be built."
|
||||
touch doxygen-build.stamp
|
||||
endif
|
||||
|
||||
dist_docdir = $(distdir)/libcubeb
|
||||
|
||||
dist-hook:
|
||||
if test -d html; then \
|
||||
mkdir $(dist_docdir); \
|
||||
echo -n "copying built documenation..."; \
|
||||
cp -rp html $(dist_docdir)/html; \
|
||||
echo "OK"; \
|
||||
fi
|
||||
|
||||
|
||||
install-data-local: doxygen-build.stamp
|
||||
$(mkinstalldirs) $(DESTDIR)$(docdir)
|
||||
if test -d html; then \
|
||||
cp -rp html $(DESTDIR)$(docdir)/html; \
|
||||
fi
|
||||
|
||||
uninstall-local:
|
||||
rm -rf $(DESTDIR)$(docdir)
|
||||
|
||||
clean-local:
|
||||
if test -d html; then rm -rf html; fi
|
||||
if test -f doxygen-build.stamp; then rm -f doxygen-build.stamp; fi
|
||||
|
202
include/cubeb.h
Normal file
202
include/cubeb.h
Normal file
|
@ -0,0 +1,202 @@
|
|||
/*
|
||||
* Copyright © 2011 Mozilla Foundation
|
||||
*
|
||||
* This program is made available under an ISC-style license. See the
|
||||
* accompanying file LICENSE for details.
|
||||
*/
|
||||
#ifndef CUBEB_c2f983e9_c96f_e71c_72c3_bbf62992a382
|
||||
#define CUBEB_c2f983e9_c96f_e71c_72c3_bbf62992a382
|
||||
|
||||
#include <cubeb/cubeb-stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/** @mainpage
|
||||
|
||||
@section intro Introduction
|
||||
|
||||
This is the documentation for the <tt>libcubeb</tt> C API.
|
||||
<tt>libcubeb</tt> is a callback-based audio API library allowing the
|
||||
authoring of portable multiplatform audio playback.
|
||||
|
||||
@section example Example code
|
||||
|
||||
@code
|
||||
cubeb * app_ctx;
|
||||
cubeb_init(&app_ctx, "Example Application");
|
||||
|
||||
cubeb_stream_params params;
|
||||
params.format = CUBEB_SAMPLE_S16LE;
|
||||
params.rate = 48000;
|
||||
params.channels = 2;
|
||||
|
||||
latency = params.rate / 4;
|
||||
|
||||
cubeb_stream * stm;
|
||||
cubeb_stream_init(app_ctx, &stm, "Example Stream 1", params,
|
||||
latency, data_cb, state_cb, NULL);
|
||||
|
||||
cubeb_start(stm);
|
||||
for (;;) {
|
||||
cubeb_get_time(stm, &ts);
|
||||
printf("time=%lu\n", ts);
|
||||
sleep(1);
|
||||
}
|
||||
cubeb_stop(stm);
|
||||
|
||||
cubeb_stream_destroy(stm);
|
||||
cubeb_destroy(app_ctx);
|
||||
@endcode
|
||||
|
||||
@code
|
||||
int data_cb(cubeb_stream * stm, void * user, void * buffer, size_t nframes)
|
||||
{
|
||||
short * buf = buffer;
|
||||
for (i = 0; i < nframes; ++i) {
|
||||
for (c = 0; c < params.channels; ++c) {
|
||||
buf[i][c] = 0;
|
||||
}
|
||||
}
|
||||
return CUBEB_OK;
|
||||
}
|
||||
@endcode
|
||||
|
||||
@code
|
||||
int state_cb(cubeb_stream * stm, void * user, cubeb_state state)
|
||||
{
|
||||
printf("state=%d\n", state);
|
||||
return CUBEB_OK;
|
||||
}
|
||||
@endcode
|
||||
*/
|
||||
|
||||
|
||||
/** @file
|
||||
The <tt>libcubeb</tt> C API. */
|
||||
|
||||
typedef struct cubeb cubeb; /**< Opaque handle referencing the application state. */
|
||||
typedef struct cubeb_stream cubeb_stream; /**< Opaque handle referencing the stream state. */
|
||||
|
||||
/** Sample format enumeration. */
|
||||
typedef enum {
|
||||
CUBEB_SAMPLE_U8, /**< 8-bit unsigned PCM. */
|
||||
CUBEB_SAMPLE_S16LE, /**< Little endian 16-bit signed PCM. */
|
||||
CUBEB_SAMPLE_FLOAT32LE /**< Little endian 32-bit IEEE floating point PCM. */
|
||||
} cubeb_sample_format;
|
||||
|
||||
/** Stream format initialization parameters. */
|
||||
typedef struct {
|
||||
cubeb_sample_format format; /**< Requested sample format. One of
|
||||
#CUBEB_SAMPLE_U8, #CUBEB_SAMPLE_S16LE, or
|
||||
#CUBEB_SAMPLE_FLOAT32LE. */
|
||||
unsigned int rate; /**< Requested sample rate. Valid range is [X, Y] (XXX: fix). */
|
||||
unsigned int channels; /**< Requested channel count. Valid range is
|
||||
[X, Y]. (XXX: fix and deal with channel
|
||||
mapping). */
|
||||
} cubeb_stream_params;
|
||||
|
||||
/** Stream states signaled via state_callback. */
|
||||
typedef enum {
|
||||
CUBEB_STATE_STARTED, /**< Stream started. */
|
||||
CUBEB_STATE_STOPPED, /**< Stream stopped. */
|
||||
CUBEB_STATE_DRAINED /**< Stream drained. */
|
||||
} cubeb_state;
|
||||
|
||||
/** Result code enumeration. */
|
||||
enum {
|
||||
CUBEB_OK = 0, /**< Success. */
|
||||
CUBEB_ERROR = -1, /**< Unclassified error. */
|
||||
CUBEB_ERROR_INVALID_FORMAT /**< Unsupported #cubeb_stream_params requested. */
|
||||
};
|
||||
|
||||
/** User supplied data callback.
|
||||
@param stream
|
||||
@param user_ptr
|
||||
@param buffer
|
||||
@param nframes
|
||||
@retval Number of frames written to buffer, which must equal nframes except at end of stream.
|
||||
@retval CUBEB_ERROR on error, in which case the data callback will stop
|
||||
and the stream will enter a shutdown state. */
|
||||
typedef long (* cubeb_data_callback)(cubeb_stream * stream,
|
||||
void * user_ptr,
|
||||
void * buffer,
|
||||
long nframes);
|
||||
|
||||
/** User supplied state callback.
|
||||
@param stream
|
||||
@param user_ptr
|
||||
@param state
|
||||
@retval CUBEB_OK
|
||||
@retval CUBEB_ERROR */
|
||||
typedef int (* cubeb_state_callback)(cubeb_stream * stream,
|
||||
void * user_ptr,
|
||||
cubeb_state state);
|
||||
|
||||
/** Initialize an application context. This will perform any library or
|
||||
application scoped initialization.
|
||||
@param context
|
||||
@param context_name
|
||||
@retval CUBEB_OK
|
||||
@retval CUBEB_ERROR */
|
||||
int cubeb_init(cubeb ** context, char const * context_name);
|
||||
|
||||
/** Destroy an application context.
|
||||
@param context */
|
||||
void cubeb_destroy(cubeb * context);
|
||||
|
||||
/** Initialize a stream associated with the supplied application context.
|
||||
@param context
|
||||
@param stream
|
||||
@param stream_name
|
||||
@param stream_params
|
||||
@param latency Approximate stream latency in frames.
|
||||
@param data_callback Will be called to preroll data before playback is
|
||||
started by cubeb_stream_start.
|
||||
@param state_callback
|
||||
@param user_ptr
|
||||
@retval CUBEB_OK
|
||||
@retval CUBEB_ERROR
|
||||
@retval CUBEB_ERROR_INVALID_FORMAT */
|
||||
int cubeb_stream_init(cubeb * context, cubeb_stream ** stream, char const * stream_name,
|
||||
cubeb_stream_params stream_params, unsigned int latency,
|
||||
cubeb_data_callback data_callback,
|
||||
cubeb_state_callback state_callback,
|
||||
void * user_ptr);
|
||||
|
||||
/** Destroy a stream.
|
||||
@param stream */
|
||||
void cubeb_stream_destroy(cubeb_stream * stream);
|
||||
|
||||
/** Start playback.
|
||||
@param stream
|
||||
@retval CUBEB_OK
|
||||
@retval CUBEB_ERROR */
|
||||
int cubeb_stream_start(cubeb_stream * stream);
|
||||
|
||||
/** Stop playback.
|
||||
@param stream
|
||||
@retval CUBEB_OK
|
||||
@retval CUBEB_ERROR */
|
||||
int cubeb_stream_stop(cubeb_stream * stream);
|
||||
|
||||
/** Get the current stream playback position.
|
||||
@param stream
|
||||
@param position Playback position in frames.
|
||||
@retval CUBEB_OK
|
||||
@retval CUBEB_ERROR */
|
||||
int cubeb_stream_get_position(cubeb_stream * stream, uint64_t * position);
|
||||
|
||||
/** Set the stream volume.
|
||||
@param stream
|
||||
@param volume Range [0.0, 1.0].
|
||||
@retval CUBEB_OK
|
||||
@retval CUBEB_ERROR */
|
||||
int cubeb_stream_set_volume(cubeb_stream * stream, float volume);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* CUBEB_c2f983e9_c96f_e71c_72c3_bbf62992a382 */
|
43
m4/as-ac-expand.m4
Normal file
43
m4/as-ac-expand.m4
Normal file
|
@ -0,0 +1,43 @@
|
|||
dnl as-ac-expand.m4 0.2.0
|
||||
dnl autostars m4 macro for expanding directories using configure's prefix
|
||||
dnl thomas@apestaart.org
|
||||
|
||||
dnl AS_AC_EXPAND(VAR, CONFIGURE_VAR)
|
||||
dnl example
|
||||
dnl AS_AC_EXPAND(SYSCONFDIR, $sysconfdir)
|
||||
dnl will set SYSCONFDIR to /usr/local/etc if prefix=/usr/local
|
||||
|
||||
AC_DEFUN([AS_AC_EXPAND],
|
||||
[
|
||||
EXP_VAR=[$1]
|
||||
FROM_VAR=[$2]
|
||||
|
||||
dnl first expand prefix and exec_prefix if necessary
|
||||
prefix_save=$prefix
|
||||
exec_prefix_save=$exec_prefix
|
||||
|
||||
dnl if no prefix given, then use /usr/local, the default prefix
|
||||
if test "x$prefix" = "xNONE"; then
|
||||
prefix="$ac_default_prefix"
|
||||
fi
|
||||
dnl if no exec_prefix given, then use prefix
|
||||
if test "x$exec_prefix" = "xNONE"; then
|
||||
exec_prefix=$prefix
|
||||
fi
|
||||
|
||||
full_var="$FROM_VAR"
|
||||
dnl loop until it doesn't change anymore
|
||||
while true; do
|
||||
new_full_var="`eval echo $full_var`"
|
||||
if test "x$new_full_var" = "x$full_var"; then break; fi
|
||||
full_var=$new_full_var
|
||||
done
|
||||
|
||||
dnl clean up
|
||||
full_var=$new_full_var
|
||||
AC_SUBST([$1], "$full_var")
|
||||
|
||||
dnl restore prefix and exec_prefix
|
||||
prefix=$prefix_save
|
||||
exec_prefix=$exec_prefix_save
|
||||
])
|
695
m4/ax_create_stdint_h.m4
Normal file
695
m4/ax_create_stdint_h.m4
Normal file
|
@ -0,0 +1,695 @@
|
|||
dnl @synopsis AX_CREATE_STDINT_H [( HEADER-TO-GENERATE [, HEDERS-TO-CHECK])]
|
||||
dnl
|
||||
dnl the "ISO C9X: 7.18 Integer types <stdint.h>" section requires the
|
||||
dnl existence of an include file <stdint.h> that defines a set of
|
||||
dnl typedefs, especially uint8_t,int32_t,uintptr_t. Many older
|
||||
dnl installations will not provide this file, but some will have the
|
||||
dnl very same definitions in <inttypes.h>. In other enviroments we can
|
||||
dnl use the inet-types in <sys/types.h> which would define the typedefs
|
||||
dnl int8_t and u_int8_t respectivly.
|
||||
dnl
|
||||
dnl This macros will create a local "_stdint.h" or the headerfile given
|
||||
dnl as an argument. In many cases that file will just "#include
|
||||
dnl <stdint.h>" or "#include <inttypes.h>", while in other environments
|
||||
dnl it will provide the set of basic 'stdint's definitions/typedefs:
|
||||
dnl
|
||||
dnl int8_t,uint8_t,int16_t,uint16_t,int32_t,uint32_t,intptr_t,uintptr_t
|
||||
dnl int_least32_t.. int_fast32_t.. intmax_t
|
||||
dnl
|
||||
dnl which may or may not rely on the definitions of other files, or
|
||||
dnl using the AC_CHECK_SIZEOF macro to determine the actual sizeof each
|
||||
dnl type.
|
||||
dnl
|
||||
dnl if your header files require the stdint-types you will want to
|
||||
dnl create an installable file mylib-int.h that all your other
|
||||
dnl installable header may include. So if you have a library package
|
||||
dnl named "mylib", just use
|
||||
dnl
|
||||
dnl AX_CREATE_STDINT_H(mylib-int.h)
|
||||
dnl
|
||||
dnl in configure.ac and go to install that very header file in
|
||||
dnl Makefile.am along with the other headers (mylib.h) - and the
|
||||
dnl mylib-specific headers can simply use "#include <mylib-int.h>" to
|
||||
dnl obtain the stdint-types.
|
||||
dnl
|
||||
dnl Remember, if the system already had a valid <stdint.h>, the
|
||||
dnl generated file will include it directly. No need for fuzzy
|
||||
dnl HAVE_STDINT_H things... (oops, GCC 4.2.x has deliberatly disabled
|
||||
dnl its stdint.h for non-c99 compilation and the c99-mode is not the
|
||||
dnl default. Therefore this macro will not use the compiler's stdint.h
|
||||
dnl - please complain to the GCC developers).
|
||||
dnl
|
||||
dnl @category C
|
||||
dnl @author Guido U. Draheim <guidod@gmx.de>
|
||||
dnl @version 2006-10-13
|
||||
dnl @license GPLWithACException
|
||||
|
||||
AC_DEFUN([AX_CHECK_DATA_MODEL],[
|
||||
AC_CHECK_SIZEOF(char)
|
||||
AC_CHECK_SIZEOF(short)
|
||||
AC_CHECK_SIZEOF(int)
|
||||
AC_CHECK_SIZEOF(long)
|
||||
AC_CHECK_SIZEOF(void*)
|
||||
ac_cv_char_data_model=""
|
||||
ac_cv_char_data_model="$ac_cv_char_data_model$ac_cv_sizeof_char"
|
||||
ac_cv_char_data_model="$ac_cv_char_data_model$ac_cv_sizeof_short"
|
||||
ac_cv_char_data_model="$ac_cv_char_data_model$ac_cv_sizeof_int"
|
||||
ac_cv_long_data_model=""
|
||||
ac_cv_long_data_model="$ac_cv_long_data_model$ac_cv_sizeof_int"
|
||||
ac_cv_long_data_model="$ac_cv_long_data_model$ac_cv_sizeof_long"
|
||||
ac_cv_long_data_model="$ac_cv_long_data_model$ac_cv_sizeof_voidp"
|
||||
AC_MSG_CHECKING([data model])
|
||||
case "$ac_cv_char_data_model/$ac_cv_long_data_model" in
|
||||
122/242) ac_cv_data_model="IP16" ; n="standard 16bit machine" ;;
|
||||
122/244) ac_cv_data_model="LP32" ; n="standard 32bit machine" ;;
|
||||
122/*) ac_cv_data_model="i16" ; n="unusual int16 model" ;;
|
||||
124/444) ac_cv_data_model="ILP32" ; n="standard 32bit unixish" ;;
|
||||
124/488) ac_cv_data_model="LP64" ; n="standard 64bit unixish" ;;
|
||||
124/448) ac_cv_data_model="LLP64" ; n="unusual 64bit unixish" ;;
|
||||
124/*) ac_cv_data_model="i32" ; n="unusual int32 model" ;;
|
||||
128/888) ac_cv_data_model="ILP64" ; n="unusual 64bit numeric" ;;
|
||||
128/*) ac_cv_data_model="i64" ; n="unusual int64 model" ;;
|
||||
222/*2) ac_cv_data_model="DSP16" ; n="strict 16bit dsptype" ;;
|
||||
333/*3) ac_cv_data_model="DSP24" ; n="strict 24bit dsptype" ;;
|
||||
444/*4) ac_cv_data_model="DSP32" ; n="strict 32bit dsptype" ;;
|
||||
666/*6) ac_cv_data_model="DSP48" ; n="strict 48bit dsptype" ;;
|
||||
888/*8) ac_cv_data_model="DSP64" ; n="strict 64bit dsptype" ;;
|
||||
222/*|333/*|444/*|666/*|888/*) :
|
||||
ac_cv_data_model="iDSP" ; n="unusual dsptype" ;;
|
||||
*) ac_cv_data_model="none" ; n="very unusual model" ;;
|
||||
esac
|
||||
AC_MSG_RESULT([$ac_cv_data_model ($ac_cv_long_data_model, $n)])
|
||||
])
|
||||
|
||||
dnl AX_CHECK_HEADER_STDINT_X([HEADERLIST][,ACTION-IF])
|
||||
AC_DEFUN([AX_CHECK_HEADER_STDINT_X],[
|
||||
AC_CACHE_CHECK([for stdint uintptr_t], [ac_cv_header_stdint_x],[
|
||||
ac_cv_header_stdint_x="" # the 1997 typedefs (inttypes.h)
|
||||
AC_MSG_RESULT([(..)])
|
||||
for i in m4_ifval([$1],[$1],[stdint.h inttypes.h sys/inttypes.h sys/types.h])
|
||||
do
|
||||
unset ac_cv_type_uintptr_t
|
||||
unset ac_cv_type_uint64_t
|
||||
AC_CHECK_TYPE(uintptr_t,[ac_cv_header_stdint_x=$i],continue,[#include <$i>])
|
||||
AC_CHECK_TYPE(uint64_t,[and64="/uint64_t"],[and64=""],[#include<$i>])
|
||||
m4_ifvaln([$1],[$1]) break
|
||||
done
|
||||
AC_MSG_CHECKING([for stdint uintptr_t])
|
||||
])
|
||||
])
|
||||
|
||||
AC_DEFUN([AX_CHECK_HEADER_STDINT_O],[
|
||||
AC_CACHE_CHECK([for stdint uint32_t], [ac_cv_header_stdint_o],[
|
||||
ac_cv_header_stdint_o="" # the 1995 typedefs (sys/inttypes.h)
|
||||
AC_MSG_RESULT([(..)])
|
||||
for i in m4_ifval([$1],[$1],[inttypes.h sys/inttypes.h sys/types.h stdint.h])
|
||||
do
|
||||
unset ac_cv_type_uint32_t
|
||||
unset ac_cv_type_uint64_t
|
||||
AC_CHECK_TYPE(uint32_t,[ac_cv_header_stdint_o=$i],continue,[#include <$i>])
|
||||
AC_CHECK_TYPE(uint64_t,[and64="/uint64_t"],[and64=""],[#include<$i>])
|
||||
m4_ifvaln([$1],[$1]) break
|
||||
break;
|
||||
done
|
||||
AC_MSG_CHECKING([for stdint uint32_t])
|
||||
])
|
||||
])
|
||||
|
||||
AC_DEFUN([AX_CHECK_HEADER_STDINT_U],[
|
||||
AC_CACHE_CHECK([for stdint u_int32_t], [ac_cv_header_stdint_u],[
|
||||
ac_cv_header_stdint_u="" # the BSD typedefs (sys/types.h)
|
||||
AC_MSG_RESULT([(..)])
|
||||
for i in m4_ifval([$1],[$1],[sys/types.h inttypes.h sys/inttypes.h]) ; do
|
||||
unset ac_cv_type_u_int32_t
|
||||
unset ac_cv_type_u_int64_t
|
||||
AC_CHECK_TYPE(u_int32_t,[ac_cv_header_stdint_u=$i],continue,[#include <$i>])
|
||||
AC_CHECK_TYPE(u_int64_t,[and64="/u_int64_t"],[and64=""],[#include<$i>])
|
||||
m4_ifvaln([$1],[$1]) break
|
||||
break;
|
||||
done
|
||||
AC_MSG_CHECKING([for stdint u_int32_t])
|
||||
])
|
||||
])
|
||||
|
||||
AC_DEFUN([AX_CREATE_STDINT_H],
|
||||
[# ------ AX CREATE STDINT H -------------------------------------
|
||||
AC_MSG_CHECKING([for stdint types])
|
||||
ac_stdint_h=`echo ifelse($1, , _stdint.h, $1)`
|
||||
# try to shortcircuit - if the default include path of the compiler
|
||||
# can find a "stdint.h" header then we assume that all compilers can.
|
||||
AC_CACHE_VAL([ac_cv_header_stdint_t],[
|
||||
old_CXXFLAGS="$CXXFLAGS" ; CXXFLAGS=""
|
||||
old_CPPFLAGS="$CPPFLAGS" ; CPPFLAGS=""
|
||||
old_CFLAGS="$CFLAGS" ; CFLAGS=""
|
||||
AC_TRY_COMPILE([#include <stdint.h>],[int_least32_t v = 0;],
|
||||
[ac_cv_stdint_result="(assuming C99 compatible system)"
|
||||
ac_cv_header_stdint_t="stdint.h"; ],
|
||||
[ac_cv_header_stdint_t=""])
|
||||
if test "$GCC" = "yes" && test ".$ac_cv_header_stdint_t" = "."; then
|
||||
CFLAGS="-std=c99"
|
||||
AC_TRY_COMPILE([#include <stdint.h>],[int_least32_t v = 0;],
|
||||
[AC_MSG_WARN(your GCC compiler has a defunct stdint.h for its default-mode)])
|
||||
fi
|
||||
CXXFLAGS="$old_CXXFLAGS"
|
||||
CPPFLAGS="$old_CPPFLAGS"
|
||||
CFLAGS="$old_CFLAGS" ])
|
||||
|
||||
v="... $ac_cv_header_stdint_h"
|
||||
if test "$ac_stdint_h" = "stdint.h" ; then
|
||||
AC_MSG_RESULT([(are you sure you want them in ./stdint.h?)])
|
||||
elif test "$ac_stdint_h" = "inttypes.h" ; then
|
||||
AC_MSG_RESULT([(are you sure you want them in ./inttypes.h?)])
|
||||
elif test "_$ac_cv_header_stdint_t" = "_" ; then
|
||||
AC_MSG_RESULT([(putting them into $ac_stdint_h)$v])
|
||||
else
|
||||
ac_cv_header_stdint="$ac_cv_header_stdint_t"
|
||||
AC_MSG_RESULT([$ac_cv_header_stdint (shortcircuit)])
|
||||
fi
|
||||
|
||||
if test "_$ac_cv_header_stdint_t" = "_" ; then # can not shortcircuit..
|
||||
|
||||
dnl .....intro message done, now do a few system checks.....
|
||||
dnl btw, all old CHECK_TYPE macros do automatically "DEFINE" a type,
|
||||
dnl therefore we use the autoconf implementation detail CHECK_TYPE_NEW
|
||||
dnl instead that is triggered with 3 or more arguments (see types.m4)
|
||||
|
||||
inttype_headers=`echo $2 | sed -e 's/,/ /g'`
|
||||
|
||||
ac_cv_stdint_result="(no helpful system typedefs seen)"
|
||||
AX_CHECK_HEADER_STDINT_X(dnl
|
||||
stdint.h inttypes.h sys/inttypes.h $inttype_headers,
|
||||
ac_cv_stdint_result="(seen uintptr_t$and64 in $i)")
|
||||
|
||||
if test "_$ac_cv_header_stdint_x" = "_" ; then
|
||||
AX_CHECK_HEADER_STDINT_O(dnl,
|
||||
inttypes.h sys/inttypes.h stdint.h $inttype_headers,
|
||||
ac_cv_stdint_result="(seen uint32_t$and64 in $i)")
|
||||
fi
|
||||
|
||||
if test "_$ac_cv_header_stdint_x" = "_" ; then
|
||||
if test "_$ac_cv_header_stdint_o" = "_" ; then
|
||||
AX_CHECK_HEADER_STDINT_U(dnl,
|
||||
sys/types.h inttypes.h sys/inttypes.h $inttype_headers,
|
||||
ac_cv_stdint_result="(seen u_int32_t$and64 in $i)")
|
||||
fi fi
|
||||
|
||||
dnl if there was no good C99 header file, do some typedef checks...
|
||||
if test "_$ac_cv_header_stdint_x" = "_" ; then
|
||||
AC_MSG_CHECKING([for stdint datatype model])
|
||||
AC_MSG_RESULT([(..)])
|
||||
AX_CHECK_DATA_MODEL
|
||||
fi
|
||||
|
||||
if test "_$ac_cv_header_stdint_x" != "_" ; then
|
||||
ac_cv_header_stdint="$ac_cv_header_stdint_x"
|
||||
elif test "_$ac_cv_header_stdint_o" != "_" ; then
|
||||
ac_cv_header_stdint="$ac_cv_header_stdint_o"
|
||||
elif test "_$ac_cv_header_stdint_u" != "_" ; then
|
||||
ac_cv_header_stdint="$ac_cv_header_stdint_u"
|
||||
else
|
||||
ac_cv_header_stdint="stddef.h"
|
||||
fi
|
||||
|
||||
AC_MSG_CHECKING([for extra inttypes in chosen header])
|
||||
AC_MSG_RESULT([($ac_cv_header_stdint)])
|
||||
dnl see if int_least and int_fast types are present in _this_ header.
|
||||
unset ac_cv_type_int_least32_t
|
||||
unset ac_cv_type_int_fast32_t
|
||||
AC_CHECK_TYPE(int_least32_t,,,[#include <$ac_cv_header_stdint>])
|
||||
AC_CHECK_TYPE(int_fast32_t,,,[#include<$ac_cv_header_stdint>])
|
||||
AC_CHECK_TYPE(intmax_t,,,[#include <$ac_cv_header_stdint>])
|
||||
|
||||
fi # shortcircut to system "stdint.h"
|
||||
# ------------------ PREPARE VARIABLES ------------------------------
|
||||
if test "$GCC" = "yes" ; then
|
||||
ac_cv_stdint_message="using gnu compiler "`$CC --version | head -1`
|
||||
else
|
||||
ac_cv_stdint_message="using $CC"
|
||||
fi
|
||||
|
||||
AC_MSG_RESULT([make use of $ac_cv_header_stdint in $ac_stdint_h dnl
|
||||
$ac_cv_stdint_result])
|
||||
|
||||
dnl -----------------------------------------------------------------
|
||||
# ----------------- DONE inttypes.h checks START header -------------
|
||||
AC_CONFIG_COMMANDS([$ac_stdint_h],[
|
||||
AC_MSG_NOTICE(creating $ac_stdint_h : $_ac_stdint_h)
|
||||
ac_stdint=$tmp/_stdint.h
|
||||
|
||||
echo "#ifndef" $_ac_stdint_h >$ac_stdint
|
||||
echo "#define" $_ac_stdint_h "1" >>$ac_stdint
|
||||
echo "#ifndef" _GENERATED_STDINT_H >>$ac_stdint
|
||||
echo "#define" _GENERATED_STDINT_H '"'$PACKAGE $VERSION'"' >>$ac_stdint
|
||||
echo "/* generated $ac_cv_stdint_message */" >>$ac_stdint
|
||||
if test "_$ac_cv_header_stdint_t" != "_" ; then
|
||||
echo "#define _STDINT_HAVE_STDINT_H" "1" >>$ac_stdint
|
||||
echo "#include <stdint.h>" >>$ac_stdint
|
||||
echo "#endif" >>$ac_stdint
|
||||
echo "#endif" >>$ac_stdint
|
||||
else
|
||||
|
||||
cat >>$ac_stdint <<STDINT_EOF
|
||||
|
||||
/* ................... shortcircuit part ........................... */
|
||||
|
||||
#if defined HAVE_STDINT_H || defined _STDINT_HAVE_STDINT_H
|
||||
#include <stdint.h>
|
||||
#else
|
||||
#include <stddef.h>
|
||||
|
||||
/* .................... configured part ............................ */
|
||||
|
||||
STDINT_EOF
|
||||
|
||||
echo "/* whether we have a C99 compatible stdint header file */" >>$ac_stdint
|
||||
if test "_$ac_cv_header_stdint_x" != "_" ; then
|
||||
ac_header="$ac_cv_header_stdint_x"
|
||||
echo "#define _STDINT_HEADER_INTPTR" '"'"$ac_header"'"' >>$ac_stdint
|
||||
else
|
||||
echo "/* #undef _STDINT_HEADER_INTPTR */" >>$ac_stdint
|
||||
fi
|
||||
|
||||
echo "/* whether we have a C96 compatible inttypes header file */" >>$ac_stdint
|
||||
if test "_$ac_cv_header_stdint_o" != "_" ; then
|
||||
ac_header="$ac_cv_header_stdint_o"
|
||||
echo "#define _STDINT_HEADER_UINT32" '"'"$ac_header"'"' >>$ac_stdint
|
||||
else
|
||||
echo "/* #undef _STDINT_HEADER_UINT32 */" >>$ac_stdint
|
||||
fi
|
||||
|
||||
echo "/* whether we have a BSD compatible inet types header */" >>$ac_stdint
|
||||
if test "_$ac_cv_header_stdint_u" != "_" ; then
|
||||
ac_header="$ac_cv_header_stdint_u"
|
||||
echo "#define _STDINT_HEADER_U_INT32" '"'"$ac_header"'"' >>$ac_stdint
|
||||
else
|
||||
echo "/* #undef _STDINT_HEADER_U_INT32 */" >>$ac_stdint
|
||||
fi
|
||||
|
||||
echo "" >>$ac_stdint
|
||||
|
||||
if test "_$ac_header" != "_" ; then if test "$ac_header" != "stddef.h" ; then
|
||||
echo "#include <$ac_header>" >>$ac_stdint
|
||||
echo "" >>$ac_stdint
|
||||
fi fi
|
||||
|
||||
echo "/* which 64bit typedef has been found */" >>$ac_stdint
|
||||
if test "$ac_cv_type_uint64_t" = "yes" ; then
|
||||
echo "#define _STDINT_HAVE_UINT64_T" "1" >>$ac_stdint
|
||||
else
|
||||
echo "/* #undef _STDINT_HAVE_UINT64_T */" >>$ac_stdint
|
||||
fi
|
||||
if test "$ac_cv_type_u_int64_t" = "yes" ; then
|
||||
echo "#define _STDINT_HAVE_U_INT64_T" "1" >>$ac_stdint
|
||||
else
|
||||
echo "/* #undef _STDINT_HAVE_U_INT64_T */" >>$ac_stdint
|
||||
fi
|
||||
echo "" >>$ac_stdint
|
||||
|
||||
echo "/* which type model has been detected */" >>$ac_stdint
|
||||
if test "_$ac_cv_char_data_model" != "_" ; then
|
||||
echo "#define _STDINT_CHAR_MODEL" "$ac_cv_char_data_model" >>$ac_stdint
|
||||
echo "#define _STDINT_LONG_MODEL" "$ac_cv_long_data_model" >>$ac_stdint
|
||||
else
|
||||
echo "/* #undef _STDINT_CHAR_MODEL // skipped */" >>$ac_stdint
|
||||
echo "/* #undef _STDINT_LONG_MODEL // skipped */" >>$ac_stdint
|
||||
fi
|
||||
echo "" >>$ac_stdint
|
||||
|
||||
echo "/* whether int_least types were detected */" >>$ac_stdint
|
||||
if test "$ac_cv_type_int_least32_t" = "yes"; then
|
||||
echo "#define _STDINT_HAVE_INT_LEAST32_T" "1" >>$ac_stdint
|
||||
else
|
||||
echo "/* #undef _STDINT_HAVE_INT_LEAST32_T */" >>$ac_stdint
|
||||
fi
|
||||
echo "/* whether int_fast types were detected */" >>$ac_stdint
|
||||
if test "$ac_cv_type_int_fast32_t" = "yes"; then
|
||||
echo "#define _STDINT_HAVE_INT_FAST32_T" "1" >>$ac_stdint
|
||||
else
|
||||
echo "/* #undef _STDINT_HAVE_INT_FAST32_T */" >>$ac_stdint
|
||||
fi
|
||||
echo "/* whether intmax_t type was detected */" >>$ac_stdint
|
||||
if test "$ac_cv_type_intmax_t" = "yes"; then
|
||||
echo "#define _STDINT_HAVE_INTMAX_T" "1" >>$ac_stdint
|
||||
else
|
||||
echo "/* #undef _STDINT_HAVE_INTMAX_T */" >>$ac_stdint
|
||||
fi
|
||||
echo "" >>$ac_stdint
|
||||
|
||||
cat >>$ac_stdint <<STDINT_EOF
|
||||
/* .................... detections part ............................ */
|
||||
|
||||
/* whether we need to define bitspecific types from compiler base types */
|
||||
#ifndef _STDINT_HEADER_INTPTR
|
||||
#ifndef _STDINT_HEADER_UINT32
|
||||
#ifndef _STDINT_HEADER_U_INT32
|
||||
#define _STDINT_NEED_INT_MODEL_T
|
||||
#else
|
||||
#define _STDINT_HAVE_U_INT_TYPES
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef _STDINT_HAVE_U_INT_TYPES
|
||||
#undef _STDINT_NEED_INT_MODEL_T
|
||||
#endif
|
||||
|
||||
#ifdef _STDINT_CHAR_MODEL
|
||||
#if _STDINT_CHAR_MODEL+0 == 122 || _STDINT_CHAR_MODEL+0 == 124
|
||||
#ifndef _STDINT_BYTE_MODEL
|
||||
#define _STDINT_BYTE_MODEL 12
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef _STDINT_HAVE_INT_LEAST32_T
|
||||
#define _STDINT_NEED_INT_LEAST_T
|
||||
#endif
|
||||
|
||||
#ifndef _STDINT_HAVE_INT_FAST32_T
|
||||
#define _STDINT_NEED_INT_FAST_T
|
||||
#endif
|
||||
|
||||
#ifndef _STDINT_HEADER_INTPTR
|
||||
#define _STDINT_NEED_INTPTR_T
|
||||
#ifndef _STDINT_HAVE_INTMAX_T
|
||||
#define _STDINT_NEED_INTMAX_T
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
||||
/* .................... definition part ............................ */
|
||||
|
||||
/* some system headers have good uint64_t */
|
||||
#ifndef _HAVE_UINT64_T
|
||||
#if defined _STDINT_HAVE_UINT64_T || defined HAVE_UINT64_T
|
||||
#define _HAVE_UINT64_T
|
||||
#elif defined _STDINT_HAVE_U_INT64_T || defined HAVE_U_INT64_T
|
||||
#define _HAVE_UINT64_T
|
||||
typedef u_int64_t uint64_t;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef _HAVE_UINT64_T
|
||||
/* .. here are some common heuristics using compiler runtime specifics */
|
||||
#if defined __STDC_VERSION__ && defined __STDC_VERSION__ >= 199901L
|
||||
#define _HAVE_UINT64_T
|
||||
#define _HAVE_LONGLONG_UINT64_T
|
||||
typedef long long int64_t;
|
||||
typedef unsigned long long uint64_t;
|
||||
|
||||
#elif !defined __STRICT_ANSI__
|
||||
#if defined _MSC_VER || defined __WATCOMC__ || defined __BORLANDC__
|
||||
#define _HAVE_UINT64_T
|
||||
typedef __int64 int64_t;
|
||||
typedef unsigned __int64 uint64_t;
|
||||
|
||||
#elif defined __GNUC__ || defined __MWERKS__ || defined __ELF__
|
||||
/* note: all ELF-systems seem to have loff-support which needs 64-bit */
|
||||
#if !defined _NO_LONGLONG
|
||||
#define _HAVE_UINT64_T
|
||||
#define _HAVE_LONGLONG_UINT64_T
|
||||
typedef long long int64_t;
|
||||
typedef unsigned long long uint64_t;
|
||||
#endif
|
||||
|
||||
#elif defined __alpha || (defined __mips && defined _ABIN32)
|
||||
#if !defined _NO_LONGLONG
|
||||
typedef long int64_t;
|
||||
typedef unsigned long uint64_t;
|
||||
#endif
|
||||
/* compiler/cpu type to define int64_t */
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if defined _STDINT_HAVE_U_INT_TYPES
|
||||
/* int8_t int16_t int32_t defined by inet code, redeclare the u_intXX types */
|
||||
typedef u_int8_t uint8_t;
|
||||
typedef u_int16_t uint16_t;
|
||||
typedef u_int32_t uint32_t;
|
||||
|
||||
/* glibc compatibility */
|
||||
#ifndef __int8_t_defined
|
||||
#define __int8_t_defined
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef _STDINT_NEED_INT_MODEL_T
|
||||
/* we must guess all the basic types. Apart from byte-adressable system, */
|
||||
/* there a few 32-bit-only dsp-systems that we guard with BYTE_MODEL 8-} */
|
||||
/* (btw, those nibble-addressable systems are way off, or so we assume) */
|
||||
|
||||
dnl /* have a look at "64bit and data size neutrality" at */
|
||||
dnl /* http://unix.org/version2/whatsnew/login_64bit.html */
|
||||
dnl /* (the shorthand "ILP" types always have a "P" part) */
|
||||
|
||||
#if defined _STDINT_BYTE_MODEL
|
||||
#if _STDINT_LONG_MODEL+0 == 242
|
||||
/* 2:4:2 = IP16 = a normal 16-bit system */
|
||||
typedef unsigned char uint8_t;
|
||||
typedef unsigned short uint16_t;
|
||||
typedef unsigned long uint32_t;
|
||||
#ifndef __int8_t_defined
|
||||
#define __int8_t_defined
|
||||
typedef char int8_t;
|
||||
typedef short int16_t;
|
||||
typedef long int32_t;
|
||||
#endif
|
||||
#elif _STDINT_LONG_MODEL+0 == 244 || _STDINT_LONG_MODEL == 444
|
||||
/* 2:4:4 = LP32 = a 32-bit system derived from a 16-bit */
|
||||
/* 4:4:4 = ILP32 = a normal 32-bit system */
|
||||
typedef unsigned char uint8_t;
|
||||
typedef unsigned short uint16_t;
|
||||
typedef unsigned int uint32_t;
|
||||
#ifndef __int8_t_defined
|
||||
#define __int8_t_defined
|
||||
typedef char int8_t;
|
||||
typedef short int16_t;
|
||||
typedef int int32_t;
|
||||
#endif
|
||||
#elif _STDINT_LONG_MODEL+0 == 484 || _STDINT_LONG_MODEL+0 == 488
|
||||
/* 4:8:4 = IP32 = a 32-bit system prepared for 64-bit */
|
||||
/* 4:8:8 = LP64 = a normal 64-bit system */
|
||||
typedef unsigned char uint8_t;
|
||||
typedef unsigned short uint16_t;
|
||||
typedef unsigned int uint32_t;
|
||||
#ifndef __int8_t_defined
|
||||
#define __int8_t_defined
|
||||
typedef char int8_t;
|
||||
typedef short int16_t;
|
||||
typedef int int32_t;
|
||||
#endif
|
||||
/* this system has a "long" of 64bit */
|
||||
#ifndef _HAVE_UINT64_T
|
||||
#define _HAVE_UINT64_T
|
||||
typedef unsigned long uint64_t;
|
||||
typedef long int64_t;
|
||||
#endif
|
||||
#elif _STDINT_LONG_MODEL+0 == 448
|
||||
/* LLP64 a 64-bit system derived from a 32-bit system */
|
||||
typedef unsigned char uint8_t;
|
||||
typedef unsigned short uint16_t;
|
||||
typedef unsigned int uint32_t;
|
||||
#ifndef __int8_t_defined
|
||||
#define __int8_t_defined
|
||||
typedef char int8_t;
|
||||
typedef short int16_t;
|
||||
typedef int int32_t;
|
||||
#endif
|
||||
/* assuming the system has a "long long" */
|
||||
#ifndef _HAVE_UINT64_T
|
||||
#define _HAVE_UINT64_T
|
||||
#define _HAVE_LONGLONG_UINT64_T
|
||||
typedef unsigned long long uint64_t;
|
||||
typedef long long int64_t;
|
||||
#endif
|
||||
#else
|
||||
#define _STDINT_NO_INT32_T
|
||||
#endif
|
||||
#else
|
||||
#define _STDINT_NO_INT8_T
|
||||
#define _STDINT_NO_INT32_T
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/*
|
||||
* quote from SunOS-5.8 sys/inttypes.h:
|
||||
* Use at your own risk. As of February 1996, the committee is squarely
|
||||
* behind the fixed sized types; the "least" and "fast" types are still being
|
||||
* discussed. The probability that the "fast" types may be removed before
|
||||
* the standard is finalized is high enough that they are not currently
|
||||
* implemented.
|
||||
*/
|
||||
|
||||
#if defined _STDINT_NEED_INT_LEAST_T
|
||||
typedef int8_t int_least8_t;
|
||||
typedef int16_t int_least16_t;
|
||||
typedef int32_t int_least32_t;
|
||||
#ifdef _HAVE_UINT64_T
|
||||
typedef int64_t int_least64_t;
|
||||
#endif
|
||||
|
||||
typedef uint8_t uint_least8_t;
|
||||
typedef uint16_t uint_least16_t;
|
||||
typedef uint32_t uint_least32_t;
|
||||
#ifdef _HAVE_UINT64_T
|
||||
typedef uint64_t uint_least64_t;
|
||||
#endif
|
||||
/* least types */
|
||||
#endif
|
||||
|
||||
#if defined _STDINT_NEED_INT_FAST_T
|
||||
typedef int8_t int_fast8_t;
|
||||
typedef int int_fast16_t;
|
||||
typedef int32_t int_fast32_t;
|
||||
#ifdef _HAVE_UINT64_T
|
||||
typedef int64_t int_fast64_t;
|
||||
#endif
|
||||
|
||||
typedef uint8_t uint_fast8_t;
|
||||
typedef unsigned uint_fast16_t;
|
||||
typedef uint32_t uint_fast32_t;
|
||||
#ifdef _HAVE_UINT64_T
|
||||
typedef uint64_t uint_fast64_t;
|
||||
#endif
|
||||
/* fast types */
|
||||
#endif
|
||||
|
||||
#ifdef _STDINT_NEED_INTMAX_T
|
||||
#ifdef _HAVE_UINT64_T
|
||||
typedef int64_t intmax_t;
|
||||
typedef uint64_t uintmax_t;
|
||||
#else
|
||||
typedef long intmax_t;
|
||||
typedef unsigned long uintmax_t;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef _STDINT_NEED_INTPTR_T
|
||||
#ifndef __intptr_t_defined
|
||||
#define __intptr_t_defined
|
||||
/* we encourage using "long" to store pointer values, never use "int" ! */
|
||||
#if _STDINT_LONG_MODEL+0 == 242 || _STDINT_LONG_MODEL+0 == 484
|
||||
typedef unsigned int uintptr_t;
|
||||
typedef int intptr_t;
|
||||
#elif _STDINT_LONG_MODEL+0 == 244 || _STDINT_LONG_MODEL+0 == 444
|
||||
typedef unsigned long uintptr_t;
|
||||
typedef long intptr_t;
|
||||
#elif _STDINT_LONG_MODEL+0 == 448 && defined _HAVE_UINT64_T
|
||||
typedef uint64_t uintptr_t;
|
||||
typedef int64_t intptr_t;
|
||||
#else /* matches typical system types ILP32 and LP64 - but not IP16 or LLP64 */
|
||||
typedef unsigned long uintptr_t;
|
||||
typedef long intptr_t;
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/* The ISO C99 standard specifies that in C++ implementations these
|
||||
should only be defined if explicitly requested. */
|
||||
#if !defined __cplusplus || defined __STDC_CONSTANT_MACROS
|
||||
#ifndef UINT32_C
|
||||
|
||||
/* Signed. */
|
||||
# define INT8_C(c) c
|
||||
# define INT16_C(c) c
|
||||
# define INT32_C(c) c
|
||||
# ifdef _HAVE_LONGLONG_UINT64_T
|
||||
# define INT64_C(c) c ## L
|
||||
# else
|
||||
# define INT64_C(c) c ## LL
|
||||
# endif
|
||||
|
||||
/* Unsigned. */
|
||||
# define UINT8_C(c) c ## U
|
||||
# define UINT16_C(c) c ## U
|
||||
# define UINT32_C(c) c ## U
|
||||
# ifdef _HAVE_LONGLONG_UINT64_T
|
||||
# define UINT64_C(c) c ## UL
|
||||
# else
|
||||
# define UINT64_C(c) c ## ULL
|
||||
# endif
|
||||
|
||||
/* Maximal type. */
|
||||
# ifdef _HAVE_LONGLONG_UINT64_T
|
||||
# define INTMAX_C(c) c ## L
|
||||
# define UINTMAX_C(c) c ## UL
|
||||
# else
|
||||
# define INTMAX_C(c) c ## LL
|
||||
# define UINTMAX_C(c) c ## ULL
|
||||
# endif
|
||||
|
||||
/* literalnumbers */
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/* These limits are merily those of a two complement byte-oriented system */
|
||||
|
||||
/* Minimum of signed integral types. */
|
||||
# define INT8_MIN (-128)
|
||||
# define INT16_MIN (-32767-1)
|
||||
# define INT32_MIN (-2147483647-1)
|
||||
# define INT64_MIN (-__INT64_C(9223372036854775807)-1)
|
||||
/* Maximum of signed integral types. */
|
||||
# define INT8_MAX (127)
|
||||
# define INT16_MAX (32767)
|
||||
# define INT32_MAX (2147483647)
|
||||
# define INT64_MAX (__INT64_C(9223372036854775807))
|
||||
|
||||
/* Maximum of unsigned integral types. */
|
||||
# define UINT8_MAX (255)
|
||||
# define UINT16_MAX (65535)
|
||||
# define UINT32_MAX (4294967295U)
|
||||
# define UINT64_MAX (__UINT64_C(18446744073709551615))
|
||||
|
||||
/* Minimum of signed integral types having a minimum size. */
|
||||
# define INT_LEAST8_MIN INT8_MIN
|
||||
# define INT_LEAST16_MIN INT16_MIN
|
||||
# define INT_LEAST32_MIN INT32_MIN
|
||||
# define INT_LEAST64_MIN INT64_MIN
|
||||
/* Maximum of signed integral types having a minimum size. */
|
||||
# define INT_LEAST8_MAX INT8_MAX
|
||||
# define INT_LEAST16_MAX INT16_MAX
|
||||
# define INT_LEAST32_MAX INT32_MAX
|
||||
# define INT_LEAST64_MAX INT64_MAX
|
||||
|
||||
/* Maximum of unsigned integral types having a minimum size. */
|
||||
# define UINT_LEAST8_MAX UINT8_MAX
|
||||
# define UINT_LEAST16_MAX UINT16_MAX
|
||||
# define UINT_LEAST32_MAX UINT32_MAX
|
||||
# define UINT_LEAST64_MAX UINT64_MAX
|
||||
|
||||
/* shortcircuit*/
|
||||
#endif
|
||||
/* once */
|
||||
#endif
|
||||
#endif
|
||||
STDINT_EOF
|
||||
fi
|
||||
if cmp -s $ac_stdint_h $ac_stdint 2>/dev/null; then
|
||||
AC_MSG_NOTICE([$ac_stdint_h is unchanged])
|
||||
else
|
||||
ac_dir=`AS_DIRNAME(["$ac_stdint_h"])`
|
||||
AS_MKDIR_P(["$ac_dir"])
|
||||
rm -f $ac_stdint_h
|
||||
mv $ac_stdint $ac_stdint_h
|
||||
fi
|
||||
],[# variables for create stdint.h replacement
|
||||
PACKAGE="$PACKAGE"
|
||||
VERSION="$VERSION"
|
||||
ac_stdint_h="$ac_stdint_h"
|
||||
_ac_stdint_h=AS_TR_CPP(_$PACKAGE-$ac_stdint_h)
|
||||
ac_cv_stdint_message="$ac_cv_stdint_message"
|
||||
ac_cv_header_stdint_t="$ac_cv_header_stdint_t"
|
||||
ac_cv_header_stdint_x="$ac_cv_header_stdint_x"
|
||||
ac_cv_header_stdint_o="$ac_cv_header_stdint_o"
|
||||
ac_cv_header_stdint_u="$ac_cv_header_stdint_u"
|
||||
ac_cv_type_uint64_t="$ac_cv_type_uint64_t"
|
||||
ac_cv_type_u_int64_t="$ac_cv_type_u_int64_t"
|
||||
ac_cv_char_data_model="$ac_cv_char_data_model"
|
||||
ac_cv_long_data_model="$ac_cv_long_data_model"
|
||||
ac_cv_type_int_least32_t="$ac_cv_type_int_least32_t"
|
||||
ac_cv_type_int_fast32_t="$ac_cv_type_int_fast32_t"
|
||||
ac_cv_type_intmax_t="$ac_cv_type_intmax_t"
|
||||
])
|
||||
])
|
157
m4/pkg.m4
Normal file
157
m4/pkg.m4
Normal file
|
@ -0,0 +1,157 @@
|
|||
# pkg.m4 - Macros to locate and utilise pkg-config. -*- Autoconf -*-
|
||||
#
|
||||
# Copyright © 2004 Scott James Remnant <scott@netsplit.com>.
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program 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
|
||||
# General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
#
|
||||
# As a special exception to the GNU General Public License, if you
|
||||
# distribute this file as part of a program that contains a
|
||||
# configuration script generated by Autoconf, you may include it under
|
||||
# the same distribution terms that you use for the rest of that program.
|
||||
|
||||
# PKG_PROG_PKG_CONFIG([MIN-VERSION])
|
||||
# ----------------------------------
|
||||
AC_DEFUN([PKG_PROG_PKG_CONFIG],
|
||||
[m4_pattern_forbid([^_?PKG_[A-Z_]+$])
|
||||
m4_pattern_allow([^PKG_CONFIG(_PATH)?$])
|
||||
AC_ARG_VAR([PKG_CONFIG], [path to pkg-config utility])dnl
|
||||
if test "x$ac_cv_env_PKG_CONFIG_set" != "xset"; then
|
||||
AC_PATH_TOOL([PKG_CONFIG], [pkg-config])
|
||||
fi
|
||||
if test -n "$PKG_CONFIG"; then
|
||||
_pkg_min_version=m4_default([$1], [0.9.0])
|
||||
AC_MSG_CHECKING([pkg-config is at least version $_pkg_min_version])
|
||||
if $PKG_CONFIG --atleast-pkgconfig-version $_pkg_min_version; then
|
||||
AC_MSG_RESULT([yes])
|
||||
else
|
||||
AC_MSG_RESULT([no])
|
||||
PKG_CONFIG=""
|
||||
fi
|
||||
|
||||
fi[]dnl
|
||||
])# PKG_PROG_PKG_CONFIG
|
||||
|
||||
# PKG_CHECK_EXISTS(MODULES, [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND])
|
||||
#
|
||||
# Check to see whether a particular set of modules exists. Similar
|
||||
# to PKG_CHECK_MODULES(), but does not set variables or print errors.
|
||||
#
|
||||
#
|
||||
# Similar to PKG_CHECK_MODULES, make sure that the first instance of
|
||||
# this or PKG_CHECK_MODULES is called, or make sure to call
|
||||
# PKG_CHECK_EXISTS manually
|
||||
# --------------------------------------------------------------
|
||||
AC_DEFUN([PKG_CHECK_EXISTS],
|
||||
[AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl
|
||||
if test -n "$PKG_CONFIG" && \
|
||||
AC_RUN_LOG([$PKG_CONFIG --exists --print-errors "$1"]); then
|
||||
m4_ifval([$2], [$2], [:])
|
||||
m4_ifvaln([$3], [else
|
||||
$3])dnl
|
||||
fi])
|
||||
|
||||
|
||||
# _PKG_CONFIG([VARIABLE], [COMMAND], [MODULES])
|
||||
# ---------------------------------------------
|
||||
m4_define([_PKG_CONFIG],
|
||||
[if test -n "$PKG_CONFIG"; then
|
||||
if test -n "$$1"; then
|
||||
pkg_cv_[]$1="$$1"
|
||||
else
|
||||
PKG_CHECK_EXISTS([$3],
|
||||
[pkg_cv_[]$1=`$PKG_CONFIG --[]$2 "$3" 2>/dev/null`],
|
||||
[pkg_failed=yes])
|
||||
fi
|
||||
else
|
||||
pkg_failed=untried
|
||||
fi[]dnl
|
||||
])# _PKG_CONFIG
|
||||
|
||||
# _PKG_SHORT_ERRORS_SUPPORTED
|
||||
# -----------------------------
|
||||
AC_DEFUN([_PKG_SHORT_ERRORS_SUPPORTED],
|
||||
[AC_REQUIRE([PKG_PROG_PKG_CONFIG])
|
||||
if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then
|
||||
_pkg_short_errors_supported=yes
|
||||
else
|
||||
_pkg_short_errors_supported=no
|
||||
fi[]dnl
|
||||
])# _PKG_SHORT_ERRORS_SUPPORTED
|
||||
|
||||
|
||||
# PKG_CHECK_MODULES(VARIABLE-PREFIX, MODULES, [ACTION-IF-FOUND],
|
||||
# [ACTION-IF-NOT-FOUND])
|
||||
#
|
||||
#
|
||||
# Note that if there is a possibility the first call to
|
||||
# PKG_CHECK_MODULES might not happen, you should be sure to include an
|
||||
# explicit call to PKG_PROG_PKG_CONFIG in your configure.ac
|
||||
#
|
||||
#
|
||||
# --------------------------------------------------------------
|
||||
AC_DEFUN([PKG_CHECK_MODULES],
|
||||
[AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl
|
||||
AC_ARG_VAR([$1][_CFLAGS], [C compiler flags for $1, overriding pkg-config])dnl
|
||||
AC_ARG_VAR([$1][_LIBS], [linker flags for $1, overriding pkg-config])dnl
|
||||
|
||||
pkg_failed=no
|
||||
AC_MSG_CHECKING([for $1])
|
||||
|
||||
_PKG_CONFIG([$1][_CFLAGS], [cflags], [$2])
|
||||
_PKG_CONFIG([$1][_LIBS], [libs], [$2])
|
||||
|
||||
m4_define([_PKG_TEXT], [Alternatively, you may set the environment variables $1[]_CFLAGS
|
||||
and $1[]_LIBS to avoid the need to call pkg-config.
|
||||
See the pkg-config man page for more details.])
|
||||
|
||||
if test $pkg_failed = yes; then
|
||||
_PKG_SHORT_ERRORS_SUPPORTED
|
||||
if test $_pkg_short_errors_supported = yes; then
|
||||
$1[]_PKG_ERRORS=`$PKG_CONFIG --short-errors --errors-to-stdout --print-errors "$2"`
|
||||
else
|
||||
$1[]_PKG_ERRORS=`$PKG_CONFIG --errors-to-stdout --print-errors "$2"`
|
||||
fi
|
||||
# Put the nasty error message in config.log where it belongs
|
||||
echo "$$1[]_PKG_ERRORS" >&AS_MESSAGE_LOG_FD
|
||||
|
||||
ifelse([$4], , [AC_MSG_ERROR(dnl
|
||||
[Package requirements ($2) were not met:
|
||||
|
||||
$$1_PKG_ERRORS
|
||||
|
||||
Consider adjusting the PKG_CONFIG_PATH environment variable if you
|
||||
installed software in a non-standard prefix.
|
||||
|
||||
_PKG_TEXT
|
||||
])],
|
||||
[AC_MSG_RESULT([no])
|
||||
$4])
|
||||
elif test $pkg_failed = untried; then
|
||||
ifelse([$4], , [AC_MSG_FAILURE(dnl
|
||||
[The pkg-config script could not be found or is too old. Make sure it
|
||||
is in your PATH or set the PKG_CONFIG environment variable to the full
|
||||
path to pkg-config.
|
||||
|
||||
_PKG_TEXT
|
||||
|
||||
To get pkg-config, see <http://pkg-config.freedesktop.org/>.])],
|
||||
[$4])
|
||||
else
|
||||
$1[]_CFLAGS=$pkg_cv_[]$1[]_CFLAGS
|
||||
$1[]_LIBS=$pkg_cv_[]$1[]_LIBS
|
||||
AC_MSG_RESULT([yes])
|
||||
ifelse([$3], , :, [$3])
|
||||
fi[]dnl
|
||||
])# PKG_CHECK_MODULES
|
212
src/cubeb_coreaudio.c
Normal file
212
src/cubeb_coreaudio.c
Normal file
|
@ -0,0 +1,212 @@
|
|||
/*
|
||||
* Copyright © 2011 Mozilla Foundation
|
||||
*
|
||||
* This program is made available under an ISC-style license. See the
|
||||
* accompanying file LICENSE for details.
|
||||
*/
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
#include <AudioToolbox/AudioToolbox.h>
|
||||
#include "cubeb/cubeb.h"
|
||||
|
||||
#define NBUFS 4
|
||||
|
||||
struct cubeb_stream {
|
||||
AudioQueueRef queue;
|
||||
AudioQueueBufferRef buffers[NBUFS];
|
||||
cubeb_data_callback data_callback;
|
||||
cubeb_state_callback state_callback;
|
||||
void * user_ptr;
|
||||
AudioStreamBasicDescription sample_spec;
|
||||
int shutdown;
|
||||
};
|
||||
|
||||
static void
|
||||
audio_queue_listener_callback(void * userptr, AudioQueueRef queue, AudioQueuePropertyID id)
|
||||
{
|
||||
cubeb_stream * stm;
|
||||
OSStatus rv;
|
||||
UInt32 playing, playing_size;
|
||||
|
||||
stm = userptr;
|
||||
|
||||
assert(id == kAudioQueueProperty_IsRunning);
|
||||
|
||||
playing_size = sizeof(playing);
|
||||
rv = AudioQueueGetProperty(queue, kAudioQueueProperty_IsRunning, &playing, &playing_size);
|
||||
assert(rv == 0);
|
||||
|
||||
if (!playing) {
|
||||
stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_DRAINED);
|
||||
|
||||
rv = AudioQueueRemovePropertyListener(queue, kAudioQueueProperty_IsRunning,
|
||||
audio_queue_listener_callback, stm);
|
||||
assert(rv == 0);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
audio_queue_output_callback(void * userptr, AudioQueueRef queue, AudioQueueBufferRef buffer)
|
||||
{
|
||||
cubeb_stream * stm;
|
||||
long got;
|
||||
OSStatus rv;
|
||||
|
||||
stm = userptr;
|
||||
|
||||
if (stm->shutdown)
|
||||
return;
|
||||
|
||||
got = stm->data_callback(stm, stm->user_ptr, buffer->mAudioData,
|
||||
buffer->mAudioDataBytesCapacity / stm->sample_spec.mBytesPerFrame);
|
||||
if (got < 0) {
|
||||
// XXX handle this case.
|
||||
assert(false);
|
||||
return;
|
||||
}
|
||||
|
||||
buffer->mAudioDataByteSize = got * stm->sample_spec.mBytesPerFrame;
|
||||
if (got > 0) {
|
||||
rv = AudioQueueEnqueueBuffer(queue, buffer, 0, NULL);
|
||||
assert(rv == 0);
|
||||
}
|
||||
|
||||
if (got < buffer->mAudioDataBytesCapacity / stm->sample_spec.mBytesPerFrame) {
|
||||
stm->shutdown = 1;
|
||||
rv = AudioQueueAddPropertyListener(queue, kAudioQueueProperty_IsRunning,
|
||||
audio_queue_listener_callback, stm);
|
||||
assert(rv == 0);
|
||||
rv = AudioQueueStop(queue, false);
|
||||
assert(rv == 0);
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
cubeb_init(cubeb ** context, char const * context_name)
|
||||
{
|
||||
*context = (void *) 0xdeadbeef;
|
||||
return CUBEB_OK;
|
||||
}
|
||||
|
||||
void
|
||||
cubeb_destroy(cubeb * ctx)
|
||||
{
|
||||
}
|
||||
|
||||
int
|
||||
cubeb_stream_init(cubeb * context, cubeb_stream ** stream, char const * stream_name,
|
||||
cubeb_stream_params stream_params, unsigned int latency,
|
||||
cubeb_data_callback data_callback, cubeb_state_callback state_callback,
|
||||
void * user_ptr)
|
||||
{
|
||||
AudioStreamBasicDescription ss;
|
||||
unsigned int buffer_size;
|
||||
int i;
|
||||
|
||||
ss.mFormatFlags = kAudioFormatFlagsAreAllClear;
|
||||
|
||||
switch (stream_params.format) {
|
||||
case CUBEB_SAMPLE_U8:
|
||||
ss.mBitsPerChannel = 8;
|
||||
break;
|
||||
case CUBEB_SAMPLE_S16LE:
|
||||
ss.mBitsPerChannel = 16;
|
||||
ss.mFormatFlags |= kAudioFormatFlagIsSignedInteger;
|
||||
break;
|
||||
case CUBEB_SAMPLE_FLOAT32LE:
|
||||
ss.mBitsPerChannel = 32;
|
||||
ss.mFormatFlags |= kAudioFormatFlagIsFloat;
|
||||
break;
|
||||
default:
|
||||
return CUBEB_ERROR_INVALID_FORMAT;
|
||||
}
|
||||
|
||||
ss.mFormatID = kAudioFormatLinearPCM;
|
||||
ss.mFormatFlags |= kLinearPCMFormatFlagIsPacked;
|
||||
ss.mSampleRate = stream_params.rate;
|
||||
ss.mChannelsPerFrame = stream_params.channels;
|
||||
|
||||
ss.mBytesPerFrame = (ss.mBitsPerChannel / 8) * ss.mChannelsPerFrame;
|
||||
ss.mFramesPerPacket = 1;
|
||||
ss.mBytesPerPacket = ss.mBytesPerFrame * ss.mFramesPerPacket;
|
||||
|
||||
cubeb_stream * stm = calloc(1, sizeof(*stm));
|
||||
assert(stm);
|
||||
|
||||
stm->data_callback = data_callback;
|
||||
stm->state_callback = state_callback;
|
||||
stm->user_ptr = user_ptr;
|
||||
|
||||
stm->sample_spec = ss;
|
||||
|
||||
OSStatus r = AudioQueueNewOutput(&stm->sample_spec, audio_queue_output_callback,
|
||||
stm, NULL, NULL, 0, &stm->queue);
|
||||
assert(r == 0);
|
||||
|
||||
buffer_size = latency * ss.mBytesPerFrame / NBUFS;
|
||||
|
||||
for (i = 0; i < NBUFS; ++i) {
|
||||
r = AudioQueueAllocateBuffer(stm->queue, buffer_size, &stm->buffers[i]);
|
||||
assert(r == 0);
|
||||
|
||||
audio_queue_output_callback(stm, stm->queue, stm->buffers[i]);
|
||||
}
|
||||
|
||||
*stream = stm;
|
||||
|
||||
return CUBEB_OK;
|
||||
}
|
||||
|
||||
void
|
||||
cubeb_stream_destroy(cubeb_stream * stm)
|
||||
{
|
||||
OSStatus r = AudioQueueDispose(stm->queue, true);
|
||||
assert(r == 0);
|
||||
free(stm);
|
||||
}
|
||||
|
||||
int
|
||||
cubeb_stream_start(cubeb_stream * stm)
|
||||
{
|
||||
OSStatus r = AudioQueueStart(stm->queue, NULL);
|
||||
assert(r == 0);
|
||||
stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_STARTED);
|
||||
return CUBEB_OK;
|
||||
}
|
||||
|
||||
int
|
||||
cubeb_stream_stop(cubeb_stream * stm)
|
||||
{
|
||||
OSStatus r = AudioQueuePause(stm->queue);
|
||||
assert(r == 0);
|
||||
stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_STOPPED);
|
||||
return CUBEB_OK;
|
||||
}
|
||||
|
||||
int
|
||||
cubeb_stream_get_position(cubeb_stream * stm, uint64_t * position)
|
||||
{
|
||||
AudioTimeStamp tstamp;
|
||||
OSStatus r = AudioQueueGetCurrentTime(stm->queue, NULL, &tstamp, NULL);
|
||||
if (r == kAudioQueueErr_InvalidRunState) {
|
||||
*position = 0;
|
||||
return CUBEB_OK;
|
||||
} else if (r != 0) {
|
||||
return CUBEB_ERROR;
|
||||
}
|
||||
assert(tstamp.mFlags & kAudioTimeStampSampleTimeValid);
|
||||
*position = tstamp.mSampleTime;
|
||||
/* XXX need to investigate why GetCurrentTime returns a "valid" negative time */
|
||||
if (tstamp.mSampleTime < 0) {
|
||||
*position = 0;
|
||||
}
|
||||
return CUBEB_OK;
|
||||
}
|
||||
|
||||
int
|
||||
cubeb_stream_set_volume(cubeb_stream * stm, float volume)
|
||||
{
|
||||
OSStatus r = AudioQueueSetParameter(stm->queue, kAudioQueueParam_Volume, volume);
|
||||
assert(r == 0);
|
||||
return CUBEB_OK;
|
||||
}
|
390
src/cubeb_pulse.c
Normal file
390
src/cubeb_pulse.c
Normal file
|
@ -0,0 +1,390 @@
|
|||
/*
|
||||
* Copyright © 2011 Mozilla Foundation
|
||||
*
|
||||
* This program is made available under an ISC-style license. See the
|
||||
* accompanying file LICENSE for details.
|
||||
*/
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
#include <pulse/pulseaudio.h>
|
||||
#include "cubeb/cubeb.h"
|
||||
|
||||
struct cubeb {
|
||||
pa_threaded_mainloop * mainloop;
|
||||
pa_context * context;
|
||||
};
|
||||
|
||||
struct cubeb_stream {
|
||||
struct cubeb * context;
|
||||
pa_stream * stream;
|
||||
cubeb_data_callback data_callback;
|
||||
cubeb_state_callback state_callback;
|
||||
void * user_ptr;
|
||||
pa_sample_spec sample_spec;
|
||||
pa_operation * draining;
|
||||
int shutdown;
|
||||
};
|
||||
|
||||
enum cork_state {
|
||||
UNCORK = 0,
|
||||
CORK = 1 << 0,
|
||||
NOTIFY = 1 << 1
|
||||
};
|
||||
|
||||
static void
|
||||
context_state_callback(pa_context * c, void * m)
|
||||
{
|
||||
if (pa_context_get_state(c) != PA_CONTEXT_TERMINATED) {
|
||||
assert(PA_CONTEXT_IS_GOOD(pa_context_get_state(c)));
|
||||
}
|
||||
pa_threaded_mainloop_signal(m, 0);
|
||||
}
|
||||
|
||||
static void
|
||||
context_success_callback(pa_context * c, int success, void * m)
|
||||
{
|
||||
pa_threaded_mainloop_signal(m, 0);
|
||||
}
|
||||
|
||||
static void
|
||||
context_notify_callback(pa_context * c, void * m)
|
||||
{
|
||||
pa_threaded_mainloop_signal(m, 0);
|
||||
}
|
||||
|
||||
static void
|
||||
stream_success_callback(pa_stream * s, int success, void * m)
|
||||
{
|
||||
pa_threaded_mainloop_signal(m, 0);
|
||||
}
|
||||
|
||||
static void
|
||||
stream_drain_success_callback(pa_stream * s, int success, void * u)
|
||||
{
|
||||
cubeb_stream * stm = u;
|
||||
|
||||
stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_DRAINED);
|
||||
|
||||
pa_threaded_mainloop_signal(stm->context->mainloop, 0);
|
||||
}
|
||||
|
||||
static void
|
||||
stream_state_callback(pa_stream * s, void * m)
|
||||
{
|
||||
/* XXX handle PA_STREAM_FAILED during creation */
|
||||
if (pa_stream_get_state(s) == PA_STREAM_TERMINATED) {
|
||||
} else {
|
||||
assert(PA_STREAM_IS_GOOD(pa_stream_get_state(s)));
|
||||
}
|
||||
pa_threaded_mainloop_signal(m, 0);
|
||||
}
|
||||
|
||||
static void
|
||||
stream_request_callback(pa_stream * s, size_t nbytes, void * u)
|
||||
{
|
||||
cubeb_stream * stm;
|
||||
void * buffer;
|
||||
size_t size;
|
||||
int r;
|
||||
long got;
|
||||
size_t towrite;
|
||||
size_t frame_size;
|
||||
|
||||
stm = u;
|
||||
|
||||
if (stm->shutdown)
|
||||
return;
|
||||
|
||||
frame_size = pa_frame_size(&stm->sample_spec);
|
||||
|
||||
assert(nbytes % frame_size == 0);
|
||||
|
||||
towrite = nbytes;
|
||||
|
||||
while (towrite) {
|
||||
size = towrite;
|
||||
r = pa_stream_begin_write(s, &buffer, &size);
|
||||
assert(r == 0);
|
||||
assert(size > 0);
|
||||
assert(size % frame_size == 0);
|
||||
|
||||
got = stm->data_callback(stm, stm->user_ptr, buffer, size / frame_size);
|
||||
if (got < 0) {
|
||||
pa_stream_cancel_write(s);
|
||||
stm->shutdown = 1;
|
||||
return;
|
||||
}
|
||||
|
||||
r = pa_stream_write(s, buffer, got * frame_size, NULL, 0, PA_SEEK_RELATIVE);
|
||||
assert(r == 0);
|
||||
|
||||
if ((size_t) got < size / frame_size) {
|
||||
stm->draining = pa_stream_drain(s, stream_drain_success_callback, stm);
|
||||
stm->shutdown = 1;
|
||||
return;
|
||||
}
|
||||
|
||||
towrite -= size;
|
||||
}
|
||||
|
||||
assert(towrite == 0);
|
||||
}
|
||||
|
||||
static void
|
||||
state_wait(cubeb * ctx, pa_context_state_t target_state)
|
||||
{
|
||||
pa_threaded_mainloop_lock(ctx->mainloop);
|
||||
for (;;) {
|
||||
pa_context_state_t state = pa_context_get_state(ctx->context);
|
||||
assert(PA_CONTEXT_IS_GOOD(state));
|
||||
if (state == target_state)
|
||||
break;
|
||||
pa_threaded_mainloop_wait(ctx->mainloop);
|
||||
}
|
||||
pa_threaded_mainloop_unlock(ctx->mainloop);
|
||||
}
|
||||
|
||||
static void
|
||||
stream_state_wait(cubeb_stream * stm, pa_stream_state_t target_state)
|
||||
{
|
||||
pa_threaded_mainloop_lock(stm->context->mainloop);
|
||||
for (;;) {
|
||||
pa_stream_state_t state = pa_stream_get_state(stm->stream);
|
||||
assert(PA_CONTEXT_IS_GOOD(state));
|
||||
if (state == target_state)
|
||||
break;
|
||||
pa_threaded_mainloop_wait(stm->context->mainloop);
|
||||
}
|
||||
pa_threaded_mainloop_unlock(stm->context->mainloop);
|
||||
}
|
||||
|
||||
static void
|
||||
operation_wait(cubeb * ctx, pa_operation * o)
|
||||
{
|
||||
for (;;) {
|
||||
if (pa_operation_get_state(o) != PA_OPERATION_RUNNING)
|
||||
break;
|
||||
pa_threaded_mainloop_wait(ctx->mainloop);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
stream_cork(cubeb_stream * stm, enum cork_state state)
|
||||
{
|
||||
pa_operation * o;
|
||||
|
||||
pa_threaded_mainloop_lock(stm->context->mainloop);
|
||||
o = pa_stream_cork(stm->stream, state & CORK, stream_success_callback, stm->context->mainloop);
|
||||
operation_wait(stm->context, o);
|
||||
pa_operation_unref(o);
|
||||
pa_threaded_mainloop_unlock(stm->context->mainloop);
|
||||
|
||||
if (state & NOTIFY) {
|
||||
stm->state_callback(stm, stm->user_ptr,
|
||||
state & CORK ? CUBEB_STATE_STOPPED : CUBEB_STATE_STARTED);
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
cubeb_init(cubeb ** context, char const * context_name)
|
||||
{
|
||||
cubeb * ctx;
|
||||
|
||||
ctx = calloc(1, sizeof(*ctx));
|
||||
|
||||
ctx->mainloop = pa_threaded_mainloop_new();
|
||||
ctx->context = pa_context_new(pa_threaded_mainloop_get_api(ctx->mainloop), context_name);
|
||||
|
||||
pa_context_set_state_callback(ctx->context, context_state_callback, ctx->mainloop);
|
||||
pa_threaded_mainloop_start(ctx->mainloop);
|
||||
|
||||
pa_threaded_mainloop_lock(ctx->mainloop);
|
||||
pa_context_connect(ctx->context, NULL, 0, NULL);
|
||||
pa_threaded_mainloop_unlock(ctx->mainloop);
|
||||
|
||||
state_wait(ctx, PA_CONTEXT_READY);
|
||||
|
||||
*context = ctx;
|
||||
|
||||
return CUBEB_OK;
|
||||
}
|
||||
|
||||
void
|
||||
cubeb_destroy(cubeb * ctx)
|
||||
{
|
||||
pa_operation * o;
|
||||
|
||||
if (ctx->context) {
|
||||
pa_threaded_mainloop_lock(ctx->mainloop);
|
||||
o = pa_context_drain(ctx->context, context_notify_callback, ctx->mainloop);
|
||||
if (o) {
|
||||
operation_wait(ctx, o);
|
||||
pa_operation_unref(o);
|
||||
}
|
||||
pa_context_disconnect(ctx->context);
|
||||
pa_context_unref(ctx->context);
|
||||
pa_threaded_mainloop_unlock(ctx->mainloop);
|
||||
}
|
||||
|
||||
if (ctx->mainloop) {
|
||||
pa_threaded_mainloop_stop(ctx->mainloop);
|
||||
pa_threaded_mainloop_free(ctx->mainloop);
|
||||
}
|
||||
|
||||
free(ctx);
|
||||
}
|
||||
|
||||
int
|
||||
cubeb_stream_init(cubeb * context, cubeb_stream ** stream, char const * stream_name,
|
||||
cubeb_stream_params stream_params, unsigned int latency,
|
||||
cubeb_data_callback data_callback, cubeb_state_callback state_callback,
|
||||
void * user_ptr)
|
||||
{
|
||||
pa_sample_spec ss;
|
||||
cubeb_stream * stm;
|
||||
pa_operation * o;
|
||||
pa_buffer_attr battr;
|
||||
pa_channel_map map;
|
||||
|
||||
switch (stream_params.format) {
|
||||
case CUBEB_SAMPLE_U8:
|
||||
ss.format = PA_SAMPLE_U8;
|
||||
break;
|
||||
case CUBEB_SAMPLE_S16LE:
|
||||
ss.format = PA_SAMPLE_S16LE;
|
||||
break;
|
||||
case CUBEB_SAMPLE_FLOAT32LE:
|
||||
ss.format = PA_SAMPLE_FLOAT32LE;
|
||||
break;
|
||||
default:
|
||||
return CUBEB_ERROR_INVALID_FORMAT;
|
||||
}
|
||||
|
||||
ss.rate = stream_params.rate;
|
||||
ss.channels = stream_params.channels;
|
||||
|
||||
/* XXX check that this does the right thing for Vorbis and WaveEx */
|
||||
pa_channel_map_init_auto(&map, ss.channels, PA_CHANNEL_MAP_DEFAULT);
|
||||
|
||||
stm = calloc(1, sizeof(*stm));
|
||||
assert(stm);
|
||||
|
||||
stm->context = context;
|
||||
assert(stm->context);
|
||||
|
||||
stm->data_callback = data_callback;
|
||||
stm->state_callback = state_callback;
|
||||
stm->user_ptr = user_ptr;
|
||||
|
||||
stm->sample_spec = ss;
|
||||
|
||||
battr.maxlength = -1;
|
||||
battr.tlength = latency * pa_frame_size(&stm->sample_spec);
|
||||
battr.prebuf = -1;
|
||||
battr.minreq = battr.tlength / 2;
|
||||
battr.fragsize = -1;
|
||||
|
||||
pa_threaded_mainloop_lock(stm->context->mainloop);
|
||||
stm->stream = pa_stream_new(stm->context->context, stream_name, &ss, &map);
|
||||
pa_stream_set_state_callback(stm->stream, stream_state_callback, stm->context->mainloop);
|
||||
pa_stream_set_write_callback(stm->stream, stream_request_callback, stm);
|
||||
pa_stream_connect_playback(stm->stream, NULL, &battr,
|
||||
PA_STREAM_AUTO_TIMING_UPDATE | PA_STREAM_INTERPOLATE_TIMING |
|
||||
PA_STREAM_START_CORKED,
|
||||
NULL, NULL);
|
||||
pa_threaded_mainloop_unlock(stm->context->mainloop);
|
||||
|
||||
stream_state_wait(stm, PA_STREAM_READY);
|
||||
|
||||
/* force a timing update now, otherwise timing info does not become valid
|
||||
until some point after initialization has completed. */
|
||||
pa_threaded_mainloop_lock(stm->context->mainloop);
|
||||
o = pa_stream_update_timing_info(stm->stream, stream_success_callback, stm->context->mainloop);
|
||||
operation_wait(stm->context, o);
|
||||
pa_operation_unref(o);
|
||||
pa_threaded_mainloop_unlock(stm->context->mainloop);
|
||||
|
||||
*stream = stm;
|
||||
|
||||
return CUBEB_OK;
|
||||
}
|
||||
|
||||
void
|
||||
cubeb_stream_destroy(cubeb_stream * stm)
|
||||
{
|
||||
if (stm->stream) {
|
||||
stream_cork(stm, CORK);
|
||||
|
||||
pa_threaded_mainloop_lock(stm->context->mainloop);
|
||||
|
||||
if (stm->draining) {
|
||||
pa_operation_cancel(stm->draining);
|
||||
pa_operation_unref(stm->draining);
|
||||
}
|
||||
|
||||
pa_stream_disconnect(stm->stream);
|
||||
pa_stream_unref(stm->stream);
|
||||
pa_threaded_mainloop_unlock(stm->context->mainloop);
|
||||
}
|
||||
|
||||
free(stm);
|
||||
}
|
||||
|
||||
int
|
||||
cubeb_stream_start(cubeb_stream * stm)
|
||||
{
|
||||
stream_cork(stm, UNCORK | NOTIFY);
|
||||
return CUBEB_OK;
|
||||
}
|
||||
|
||||
int
|
||||
cubeb_stream_stop(cubeb_stream * stm)
|
||||
{
|
||||
stream_cork(stm, CORK | NOTIFY);
|
||||
return CUBEB_OK;
|
||||
}
|
||||
|
||||
int
|
||||
cubeb_stream_get_position(cubeb_stream * stm, uint64_t * position)
|
||||
{
|
||||
int r;
|
||||
pa_usec_t r_usec;
|
||||
uint64_t bytes;
|
||||
|
||||
pa_threaded_mainloop_lock(stm->context->mainloop);
|
||||
r = pa_stream_get_time(stm->stream, &r_usec);
|
||||
pa_threaded_mainloop_unlock(stm->context->mainloop);
|
||||
|
||||
if (r != 0) {
|
||||
return CUBEB_ERROR;
|
||||
}
|
||||
|
||||
/* XXX might be more accurate to compute directly from get_timing_info */
|
||||
bytes = pa_usec_to_bytes(r_usec, &stm->sample_spec);
|
||||
*position = bytes / pa_frame_size(&stm->sample_spec);
|
||||
|
||||
return CUBEB_OK;
|
||||
}
|
||||
|
||||
int
|
||||
cubeb_stream_set_volume(cubeb_stream * stm, float volume)
|
||||
{
|
||||
pa_volume_t v;
|
||||
pa_cvolume cv;
|
||||
pa_operation * o;
|
||||
|
||||
assert(volume >= 0.0 && volume <= 1.0);
|
||||
v = volume * PA_VOLUME_NORM;
|
||||
|
||||
pa_cvolume_init(&cv);
|
||||
pa_cvolume_set(&cv, stm->sample_spec.channels, v);
|
||||
|
||||
pa_threaded_mainloop_lock(stm->context->mainloop);
|
||||
o = pa_context_set_sink_input_volume(stm->context->context, pa_stream_get_index(stm->stream),
|
||||
&cv, context_success_callback, stm->context->mainloop);
|
||||
operation_wait(stm->context, o);
|
||||
pa_operation_unref(o);
|
||||
pa_threaded_mainloop_unlock(stm->context->mainloop);
|
||||
|
||||
return CUBEB_OK;
|
||||
}
|
373
test/test_sanity.c
Normal file
373
test/test_sanity.c
Normal file
|
@ -0,0 +1,373 @@
|
|||
/*
|
||||
* Copyright © 2011 Mozilla Foundation
|
||||
*
|
||||
* This program is made available under an ISC-style license. See the
|
||||
* accompanying file LICENSE for details.
|
||||
*/
|
||||
#include "cubeb/cubeb.h"
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
static int dummy;
|
||||
static uint64_t total_frames_written;
|
||||
|
||||
static long
|
||||
test_data_callback(cubeb_stream * stm, void * user_ptr, void * p, long nframes)
|
||||
{
|
||||
assert(stm && user_ptr == &dummy && p && nframes > 0);
|
||||
memset(p, 0, nframes);
|
||||
total_frames_written += nframes;
|
||||
return nframes;
|
||||
}
|
||||
|
||||
int
|
||||
test_state_callback(cubeb_stream * stm, void * user_ptr, cubeb_state state)
|
||||
{
|
||||
return CUBEB_OK;
|
||||
}
|
||||
|
||||
static void
|
||||
test_init_destroy_context(void)
|
||||
{
|
||||
int r;
|
||||
cubeb * ctx;
|
||||
|
||||
r = cubeb_init(&ctx, "test_sanity");
|
||||
assert(r == 0 && ctx);
|
||||
|
||||
cubeb_destroy(ctx);
|
||||
}
|
||||
|
||||
static void
|
||||
test_init_destroy_multiple_contexts(void)
|
||||
{
|
||||
int i;
|
||||
int r;
|
||||
cubeb * ctx[4];
|
||||
|
||||
for (i = 0; i < 4; ++i) {
|
||||
r = cubeb_init(&ctx[i], NULL);
|
||||
assert(r == 0 && ctx[i]);
|
||||
}
|
||||
|
||||
/* destroy in a different order */
|
||||
cubeb_destroy(ctx[2]);
|
||||
cubeb_destroy(ctx[0]);
|
||||
cubeb_destroy(ctx[3]);
|
||||
cubeb_destroy(ctx[1]);
|
||||
}
|
||||
|
||||
static void
|
||||
test_init_destroy_stream(void)
|
||||
{
|
||||
int r;
|
||||
cubeb * ctx;
|
||||
cubeb_stream * stream;
|
||||
cubeb_stream_params params;
|
||||
|
||||
r = cubeb_init(&ctx, "test_sanity");
|
||||
assert(r == 0 && ctx);
|
||||
|
||||
params.format = CUBEB_SAMPLE_U8;
|
||||
params.rate = 44100;
|
||||
params.channels = 1;
|
||||
|
||||
r = cubeb_stream_init(ctx, &stream, "test", params, params.rate,
|
||||
test_data_callback, test_state_callback, &dummy);
|
||||
assert(r == 0 && stream);
|
||||
|
||||
cubeb_stream_destroy(stream);
|
||||
cubeb_destroy(ctx);
|
||||
}
|
||||
|
||||
static void
|
||||
test_init_destroy_multiple_streams(void)
|
||||
{
|
||||
int i;
|
||||
int r;
|
||||
cubeb * ctx;
|
||||
cubeb_stream * stream[16];
|
||||
cubeb_stream_params params;
|
||||
|
||||
r = cubeb_init(&ctx, "test_sanity");
|
||||
assert(r == 0 && ctx);
|
||||
|
||||
params.format = CUBEB_SAMPLE_U8;
|
||||
params.rate = 44100;
|
||||
params.channels = 1;
|
||||
|
||||
for (i = 0; i < 16; ++i) {
|
||||
r = cubeb_stream_init(ctx, &stream[i], "test", params, params.rate,
|
||||
test_data_callback, test_state_callback, &dummy);
|
||||
assert(r == 0 && stream[i]);
|
||||
}
|
||||
|
||||
for (i = 0; i < 16; ++i) {
|
||||
cubeb_stream_destroy(stream[i]);
|
||||
}
|
||||
|
||||
cubeb_destroy(ctx);
|
||||
}
|
||||
|
||||
static void
|
||||
test_init_destroy_multiple_contexts_and_streams(void)
|
||||
{
|
||||
int i, j;
|
||||
int r;
|
||||
cubeb * ctx[4];
|
||||
cubeb_stream * stream[16];
|
||||
cubeb_stream_params params;
|
||||
|
||||
params.format = CUBEB_SAMPLE_U8;
|
||||
params.rate = 44100;
|
||||
params.channels = 1;
|
||||
|
||||
for (i = 0; i < 4; ++i) {
|
||||
r = cubeb_init(&ctx[i], "test_sanity");
|
||||
assert(r == 0 && ctx[i]);
|
||||
|
||||
for (j = 0; j < 4; ++j) {
|
||||
r = cubeb_stream_init(ctx[i], &stream[i * 4 + j], "test", params, params.rate,
|
||||
test_data_callback, test_state_callback, &dummy);
|
||||
assert(r == 0 && stream[i * 4 + j]);
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < 4; ++i) {
|
||||
for (j = 0; j < 4; ++j) {
|
||||
cubeb_stream_destroy(stream[i * 4 + j]);
|
||||
}
|
||||
cubeb_destroy(ctx[i]);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
test_basic_stream_operations(void)
|
||||
{
|
||||
int r;
|
||||
cubeb * ctx;
|
||||
cubeb_stream * stream;
|
||||
cubeb_stream_params params;
|
||||
uint64_t position;
|
||||
|
||||
r = cubeb_init(&ctx, "test_sanity");
|
||||
assert(r == 0 && ctx);
|
||||
|
||||
params.format = CUBEB_SAMPLE_U8;
|
||||
params.rate = 44100;
|
||||
params.channels = 1;
|
||||
|
||||
r = cubeb_stream_init(ctx, &stream, "test", params, params.rate,
|
||||
test_data_callback, test_state_callback, &dummy);
|
||||
assert(r == 0 && stream);
|
||||
|
||||
/* position and volume before stream has started */
|
||||
r = cubeb_stream_get_position(stream, &position);
|
||||
assert(r == 0 && position == 0);
|
||||
|
||||
r = cubeb_stream_set_volume(stream, 0.3);
|
||||
assert(r == 0);
|
||||
|
||||
r = cubeb_stream_start(stream);
|
||||
assert(r == 0);
|
||||
|
||||
/* position and volume after while stream running */
|
||||
r = cubeb_stream_get_position(stream, &position);
|
||||
assert(r == 0);
|
||||
|
||||
r = cubeb_stream_set_volume(stream, 0.4);
|
||||
assert(r == 0);
|
||||
|
||||
r = cubeb_stream_stop(stream);
|
||||
assert(r == 0);
|
||||
|
||||
/* position and volume after stream has stopped */
|
||||
r = cubeb_stream_get_position(stream, &position);
|
||||
assert(r == 0);
|
||||
|
||||
r = cubeb_stream_set_volume(stream, 0.5);
|
||||
assert(r == 0);
|
||||
|
||||
cubeb_stream_destroy(stream);
|
||||
cubeb_destroy(ctx);
|
||||
}
|
||||
|
||||
static void
|
||||
test_stream_position(void)
|
||||
{
|
||||
int i;
|
||||
int r;
|
||||
cubeb * ctx;
|
||||
cubeb_stream * stream;
|
||||
cubeb_stream_params params;
|
||||
uint64_t position, last_position;
|
||||
|
||||
total_frames_written = 0;
|
||||
|
||||
r = cubeb_init(&ctx, "test_sanity");
|
||||
assert(r == 0 && ctx);
|
||||
|
||||
params.format = CUBEB_SAMPLE_U8;
|
||||
params.rate = 44100;
|
||||
params.channels = 1;
|
||||
|
||||
r = cubeb_stream_init(ctx, &stream, "test", params, params.rate,
|
||||
test_data_callback, test_state_callback, &dummy);
|
||||
assert(r == 0 && stream);
|
||||
|
||||
/* stream position should not advance before starting playback */
|
||||
r = cubeb_stream_get_position(stream, &position);
|
||||
assert(r == 0 && position == 0);
|
||||
|
||||
sleep(1);
|
||||
|
||||
r = cubeb_stream_get_position(stream, &position);
|
||||
assert(r == 0 && position == 0);
|
||||
|
||||
/* stream position should advance during playback */
|
||||
r = cubeb_stream_start(stream);
|
||||
assert(r == 0);
|
||||
|
||||
/* stream should have prefilled */
|
||||
assert(total_frames_written > 0);
|
||||
|
||||
r = cubeb_stream_get_position(stream, &position);
|
||||
assert(r == 0);
|
||||
last_position = position;
|
||||
|
||||
sleep(1);
|
||||
|
||||
r = cubeb_stream_get_position(stream, &position);
|
||||
assert(r == 0);
|
||||
assert(position >= last_position);
|
||||
last_position = position;
|
||||
|
||||
/* stream position should not exceed total frames written */
|
||||
for (i = 0; i < 5; ++i) {
|
||||
r = cubeb_stream_get_position(stream, &position);
|
||||
assert(r == 0);
|
||||
assert(position >= last_position);
|
||||
assert(position <= total_frames_written);
|
||||
last_position = position;
|
||||
sleep(1);
|
||||
}
|
||||
|
||||
/* stream position should not advance after stopping playback */
|
||||
r = cubeb_stream_stop(stream);
|
||||
assert(r == 0);
|
||||
|
||||
/* XXX allow stream to settle */
|
||||
sleep(1);
|
||||
|
||||
r = cubeb_stream_get_position(stream, &position);
|
||||
assert(r == 0);
|
||||
last_position = position;
|
||||
|
||||
sleep(1);
|
||||
|
||||
r = cubeb_stream_get_position(stream, &position);
|
||||
assert(r == 0);
|
||||
assert(position == last_position);
|
||||
|
||||
cubeb_stream_destroy(stream);
|
||||
cubeb_destroy(ctx);
|
||||
}
|
||||
|
||||
static int do_drain;
|
||||
static int got_drain;
|
||||
|
||||
static long
|
||||
test_drain_data_callback(cubeb_stream * stm, void * user_ptr, void * p, long nframes)
|
||||
{
|
||||
assert(stm && user_ptr == &dummy && p && nframes > 0);
|
||||
if (do_drain == 1) {
|
||||
do_drain = 2;
|
||||
return 0;
|
||||
}
|
||||
/* once drain has started, callback must never be called again */
|
||||
assert(do_drain != 2);
|
||||
memset(p, 0, nframes);
|
||||
total_frames_written += nframes;
|
||||
return nframes;
|
||||
}
|
||||
|
||||
int
|
||||
test_drain_state_callback(cubeb_stream * stm, void * user_ptr, cubeb_state state)
|
||||
{
|
||||
if (state == CUBEB_STATE_DRAINED) {
|
||||
assert(!got_drain);
|
||||
got_drain = 1;
|
||||
}
|
||||
return CUBEB_OK;
|
||||
}
|
||||
|
||||
static void
|
||||
test_drain(void)
|
||||
{
|
||||
int r;
|
||||
cubeb * ctx;
|
||||
cubeb_stream * stream;
|
||||
cubeb_stream_params params;
|
||||
uint64_t position;
|
||||
|
||||
total_frames_written = 0;
|
||||
|
||||
r = cubeb_init(&ctx, "test_sanity");
|
||||
assert(r == 0 && ctx);
|
||||
|
||||
params.format = CUBEB_SAMPLE_U8;
|
||||
params.rate = 44100;
|
||||
params.channels = 1;
|
||||
|
||||
r = cubeb_stream_init(ctx, &stream, "test", params, params.rate,
|
||||
test_drain_data_callback, test_drain_state_callback, &dummy);
|
||||
assert(r == 0 && stream);
|
||||
|
||||
r = cubeb_stream_start(stream);
|
||||
assert(r == 0);
|
||||
|
||||
sleep(1);
|
||||
|
||||
do_drain = 1;
|
||||
|
||||
for (;;) {
|
||||
r = cubeb_stream_get_position(stream, &position);
|
||||
assert(r == 0);
|
||||
assert(position <= total_frames_written);
|
||||
if (got_drain) {
|
||||
break;
|
||||
}
|
||||
sleep(1);
|
||||
}
|
||||
|
||||
r = cubeb_stream_get_position(stream, &position);
|
||||
assert(r == 0);
|
||||
assert(got_drain);
|
||||
assert(position == total_frames_written);
|
||||
|
||||
cubeb_stream_destroy(stream);
|
||||
cubeb_destroy(ctx);
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char * argv[])
|
||||
{
|
||||
test_init_destroy_context();
|
||||
test_init_destroy_multiple_contexts();
|
||||
test_init_destroy_stream();
|
||||
test_init_destroy_multiple_streams();
|
||||
test_init_destroy_multiple_contexts_and_streams();
|
||||
test_basic_stream_operations();
|
||||
test_stream_position();
|
||||
/*
|
||||
to fix:
|
||||
test_drain();
|
||||
*/
|
||||
/*
|
||||
to implement:
|
||||
test_eos_during_prefill();
|
||||
test_stream_destroy_pending_drain();
|
||||
*/
|
||||
return 0;
|
||||
}
|
Loading…
Reference in a new issue