initial import (from 892ca582fe32577392a9843f14b8c5370df6ed78).

This commit is contained in:
Matthew Gregan 2011-07-08 16:23:47 +12:00
commit 5045555f25
19 changed files with 4141 additions and 0 deletions

41
.gitignore vendored Normal file
View 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
View file

@ -0,0 +1 @@
Matthew Gregan <kinetik@flim.org>

8
INSTALL Normal file
View 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
View 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
View 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
View file

@ -0,0 +1,3 @@
See INSTALL for build instructions.
Licensed under an ISC-style license. See LICENSE for details.

219
TODO Normal file
View 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
View 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
View 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
View 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

File diff suppressed because it is too large Load diff

38
docs/Makefile.am Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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;
}