summaryrefslogtreecommitdiff
path: root/pjmedia
diff options
context:
space:
mode:
authorBenny Prijono <bennylp@teluu.com>2005-11-13 19:40:44 +0000
committerBenny Prijono <bennylp@teluu.com>2005-11-13 19:40:44 +0000
commita08b589d09d5197f9a76d549a189e4686bd2ca8c (patch)
tree549904e7680dfab96b3ce579b1843c5d58107100 /pjmedia
parent8df70c6d5fef443506618bf31b686d53fef3f259 (diff)
Applying license to pjproject
git-svn-id: http://svn.pjsip.org/repos/pjproject/trunk@49 74dad513-b988-da41-8d7b-12977e46ad98
Diffstat (limited to 'pjmedia')
-rw-r--r--pjmedia/src/pjmedia.h55
-rw-r--r--pjmedia/src/pjmedia/codec.c203
-rw-r--r--pjmedia/src/pjmedia/codec.h699
-rw-r--r--pjmedia/src/pjmedia/config.h47
-rw-r--r--pjmedia/src/pjmedia/dsound.c1747
-rw-r--r--pjmedia/src/pjmedia/g711.c1225
-rw-r--r--pjmedia/src/pjmedia/jbuf.c833
-rw-r--r--pjmedia/src/pjmedia/jbuf.h277
-rw-r--r--pjmedia/src/pjmedia/mediamgr.c215
-rw-r--r--pjmedia/src/pjmedia/mediamgr.h193
-rw-r--r--pjmedia/src/pjmedia/nullsound.c245
-rw-r--r--pjmedia/src/pjmedia/pasound.c799
-rw-r--r--pjmedia/src/pjmedia/portaudio/dsound_wrapper.c1253
-rw-r--r--pjmedia/src/pjmedia/portaudio/dsound_wrapper.h281
-rw-r--r--pjmedia/src/pjmedia/portaudio/pa_allocation.c489
-rw-r--r--pjmedia/src/pjmedia/portaudio/pa_allocation.h211
-rw-r--r--pjmedia/src/pjmedia/portaudio/pa_converters.c3873
-rw-r--r--pjmedia/src/pjmedia/portaudio/pa_converters.h529
-rw-r--r--pjmedia/src/pjmedia/portaudio/pa_cpuload.c213
-rw-r--r--pjmedia/src/pjmedia/portaudio/pa_cpuload.h147
-rw-r--r--pjmedia/src/pjmedia/portaudio/pa_dither.c429
-rw-r--r--pjmedia/src/pjmedia/portaudio/pa_dither.h203
-rw-r--r--pjmedia/src/pjmedia/portaudio/pa_endianness.h243
-rw-r--r--pjmedia/src/pjmedia/portaudio/pa_front.c3973
-rw-r--r--pjmedia/src/pjmedia/portaudio/pa_hostapi.h509
-rw-r--r--pjmedia/src/pjmedia/portaudio/pa_linux_alsa.c6565
-rw-r--r--pjmedia/src/pjmedia/portaudio/pa_linux_alsa.h149
-rw-r--r--pjmedia/src/pjmedia/portaudio/pa_process.c3533
-rw-r--r--pjmedia/src/pjmedia/portaudio/pa_process.h1503
-rw-r--r--pjmedia/src/pjmedia/portaudio/pa_skeleton.c1635
-rw-r--r--pjmedia/src/pjmedia/portaudio/pa_stream.c303
-rw-r--r--pjmedia/src/pjmedia/portaudio/pa_stream.h413
-rw-r--r--pjmedia/src/pjmedia/portaudio/pa_trace.c197
-rw-r--r--pjmedia/src/pjmedia/portaudio/pa_trace.h161
-rw-r--r--pjmedia/src/pjmedia/portaudio/pa_types.h151
-rw-r--r--pjmedia/src/pjmedia/portaudio/pa_unix_hostapis.c149
-rw-r--r--pjmedia/src/pjmedia/portaudio/pa_unix_oss.c3857
-rw-r--r--pjmedia/src/pjmedia/portaudio/pa_unix_util.c371
-rw-r--r--pjmedia/src/pjmedia/portaudio/pa_unix_util.h167
-rw-r--r--pjmedia/src/pjmedia/portaudio/pa_util.h355
-rw-r--r--pjmedia/src/pjmedia/portaudio/pa_win_ds.c3677
-rw-r--r--pjmedia/src/pjmedia/portaudio/pa_win_hostapis.c193
-rw-r--r--pjmedia/src/pjmedia/portaudio/pa_win_util.c289
-rw-r--r--pjmedia/src/pjmedia/portaudio/pa_win_wmme.c7267
-rw-r--r--pjmedia/src/pjmedia/portaudio/pa_win_wmme.h341
-rw-r--r--pjmedia/src/pjmedia/portaudio/pa_x86_plain_converters.c2355
-rw-r--r--pjmedia/src/pjmedia/portaudio/pa_x86_plain_converters.h59
-rw-r--r--pjmedia/src/pjmedia/portaudio/portaudio.h2267
-rw-r--r--pjmedia/src/pjmedia/rtcp.c425
-rw-r--r--pjmedia/src/pjmedia/rtcp.h377
-rw-r--r--pjmedia/src/pjmedia/rtp.c515
-rw-r--r--pjmedia/src/pjmedia/rtp.h505
-rw-r--r--pjmedia/src/pjmedia/sdp.c1893
-rw-r--r--pjmedia/src/pjmedia/sdp.h657
-rw-r--r--pjmedia/src/pjmedia/session.c1657
-rw-r--r--pjmedia/src/pjmedia/session.h297
-rw-r--r--pjmedia/src/pjmedia/sound.h395
-rw-r--r--pjmedia/src/pjmedia/stream.c1281
-rw-r--r--pjmedia/src/pjmedia/stream.h191
-rw-r--r--pjmedia/src/test/audio_tool.c809
-rw-r--r--pjmedia/src/test/jbuf_test.c299
-rw-r--r--pjmedia/src/test/main.c75
-rw-r--r--pjmedia/src/test/rtp_test.c65
-rw-r--r--pjmedia/src/test/sdptest.c233
-rw-r--r--pjmedia/src/test/session_test.c251
65 files changed, 33069 insertions, 31704 deletions
diff --git a/pjmedia/src/pjmedia.h b/pjmedia/src/pjmedia.h
index 627d15cf..534e0f24 100644
--- a/pjmedia/src/pjmedia.h
+++ b/pjmedia/src/pjmedia.h
@@ -1,17 +1,38 @@
-/* $Id$
- *
- */
-#ifndef __PJMEDIA_H__
-#define __PJMEDIA_H__
-
-#include <pjmedia/codec.h>
-#include <pjmedia/jbuf.h>
-#include <pjmedia/mediamgr.h>
-#include <pjmedia/rtcp.h>
-#include <pjmedia/rtp.h>
-#include <pjmedia/session.h>
-#include <pjmedia/sound.h>
-#include <pjmedia/sdp.h>
-
-#endif /* __PJMEDIA_H__ */
-
+/* $Id$
+ *
+ */
+/*
+ * PJMEDIA - Multimedia over IP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef __PJMEDIA_H__
+#define __PJMEDIA_H__
+
+#include <pjmedia/codec.h>
+#include <pjmedia/jbuf.h>
+#include <pjmedia/mediamgr.h>
+#include <pjmedia/rtcp.h>
+#include <pjmedia/rtp.h>
+#include <pjmedia/session.h>
+#include <pjmedia/sound.h>
+#include <pjmedia/sdp.h>
+
+#endif /* __PJMEDIA_H__ */
+
diff --git a/pjmedia/src/pjmedia/codec.c b/pjmedia/src/pjmedia/codec.c
index d64ac059..c1e12f91 100644
--- a/pjmedia/src/pjmedia/codec.c
+++ b/pjmedia/src/pjmedia/codec.c
@@ -1,91 +1,112 @@
-/* $Id$
- *
- */
-#include <pjmedia/codec.h>
-#include <pj/pool.h>
-#include <pj/string.h>
-#include <pj/log.h>
-
-#define THIS_FILE "codec.c"
-
-static void enum_all_codecs (pj_codec_mgr *cm)
-{
- pj_codec_factory *cf;
-
- cf = cm->factory_list.next;
- cm->codec_cnt = 0;
- while (cf != &cm->factory_list) {
- pj_codec_id temp[PJ_CODEC_MGR_MAX_CODECS];
- int i, cnt;
-
- cnt = cf->op->enum_codecs (cf, PJ_CODEC_MGR_MAX_CODECS, temp);
- if (cnt > PJ_CODEC_MGR_MAX_CODECS) {
- pj_assert(0);
- PJ_LOG(4, (THIS_FILE, "Too many codecs reported by factory"));
- cnt = PJ_CODEC_MGR_MAX_CODECS;
- }
-
- for (i=0; i<cnt && cm->codec_cnt < PJ_CODEC_MGR_MAX_CODECS; ++i) {
- cm->codecs[cm->codec_cnt++] = temp[i];
- }
-
- cf = cf->next;
- }
-}
-
-PJ_DEF(pj_status_t) pj_codec_mgr_init (pj_codec_mgr *mgr)
-{
- pj_list_init (&mgr->factory_list);
- mgr->codec_cnt = 0;
- return 0;
-}
-
-PJ_DEF(pj_status_t) pj_codec_mgr_register_factory (pj_codec_mgr *mgr,
- pj_codec_factory *factory)
-{
- pj_list_insert_before (&mgr->factory_list, factory);
- enum_all_codecs (mgr);
- return 0;
-}
-
-PJ_DEF(void) pj_codec_mgr_unregister_factory (pj_codec_mgr *mgr, pj_codec_factory *factory)
-{
- PJ_UNUSED_ARG(mgr)
- pj_list_erase(factory);
- enum_all_codecs (mgr);
-}
-
-PJ_DEF(unsigned)
-pj_codec_mgr_enum_codecs (pj_codec_mgr *mgr, unsigned count, const pj_codec_id *codecs[])
-{
- unsigned i;
-
- if (count > mgr->codec_cnt)
- count = mgr->codec_cnt;
-
- for (i=0; i<count; ++i)
- codecs[i] = &mgr->codecs[i];
-
- return mgr->codec_cnt;
-}
-
-PJ_DEF(pj_codec*) pj_codec_mgr_alloc_codec (pj_codec_mgr *mgr, const struct pj_codec_id *id)
-{
- pj_codec_factory *factory = mgr->factory_list.next;
- while (factory != &mgr->factory_list) {
- if ( (*factory->op->match_id)(factory, id) == 0 ) {
- pj_codec *codec = (*factory->op->alloc_codec)(factory, id);
- if (codec != NULL)
- return codec;
- }
- factory = factory->next;
- }
- return NULL;
-}
-
-PJ_DEF(void) pj_codec_mgr_dealloc_codec (pj_codec_mgr *mgr, pj_codec *codec)
-{
- PJ_UNUSED_ARG(mgr)
- (*codec->factory->op->dealloc_codec)(codec->factory, codec);
-}
-
+/* $Id$
+ *
+ */
+/*
+ * PJMEDIA - Multimedia over IP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include <pjmedia/codec.h>
+#include <pj/pool.h>
+#include <pj/string.h>
+#include <pj/log.h>
+
+#define THIS_FILE "codec.c"
+
+static void enum_all_codecs (pj_codec_mgr *cm)
+{
+ pj_codec_factory *cf;
+
+ cf = cm->factory_list.next;
+ cm->codec_cnt = 0;
+ while (cf != &cm->factory_list) {
+ pj_codec_id temp[PJ_CODEC_MGR_MAX_CODECS];
+ int i, cnt;
+
+ cnt = cf->op->enum_codecs (cf, PJ_CODEC_MGR_MAX_CODECS, temp);
+ if (cnt > PJ_CODEC_MGR_MAX_CODECS) {
+ pj_assert(0);
+ PJ_LOG(4, (THIS_FILE, "Too many codecs reported by factory"));
+ cnt = PJ_CODEC_MGR_MAX_CODECS;
+ }
+
+ for (i=0; i<cnt && cm->codec_cnt < PJ_CODEC_MGR_MAX_CODECS; ++i) {
+ cm->codecs[cm->codec_cnt++] = temp[i];
+ }
+
+ cf = cf->next;
+ }
+}
+
+PJ_DEF(pj_status_t) pj_codec_mgr_init (pj_codec_mgr *mgr)
+{
+ pj_list_init (&mgr->factory_list);
+ mgr->codec_cnt = 0;
+ return 0;
+}
+
+PJ_DEF(pj_status_t) pj_codec_mgr_register_factory (pj_codec_mgr *mgr,
+ pj_codec_factory *factory)
+{
+ pj_list_insert_before (&mgr->factory_list, factory);
+ enum_all_codecs (mgr);
+ return 0;
+}
+
+PJ_DEF(void) pj_codec_mgr_unregister_factory (pj_codec_mgr *mgr, pj_codec_factory *factory)
+{
+ PJ_UNUSED_ARG(mgr)
+ pj_list_erase(factory);
+ enum_all_codecs (mgr);
+}
+
+PJ_DEF(unsigned)
+pj_codec_mgr_enum_codecs (pj_codec_mgr *mgr, unsigned count, const pj_codec_id *codecs[])
+{
+ unsigned i;
+
+ if (count > mgr->codec_cnt)
+ count = mgr->codec_cnt;
+
+ for (i=0; i<count; ++i)
+ codecs[i] = &mgr->codecs[i];
+
+ return mgr->codec_cnt;
+}
+
+PJ_DEF(pj_codec*) pj_codec_mgr_alloc_codec (pj_codec_mgr *mgr, const struct pj_codec_id *id)
+{
+ pj_codec_factory *factory = mgr->factory_list.next;
+ while (factory != &mgr->factory_list) {
+ if ( (*factory->op->match_id)(factory, id) == 0 ) {
+ pj_codec *codec = (*factory->op->alloc_codec)(factory, id);
+ if (codec != NULL)
+ return codec;
+ }
+ factory = factory->next;
+ }
+ return NULL;
+}
+
+PJ_DEF(void) pj_codec_mgr_dealloc_codec (pj_codec_mgr *mgr, pj_codec *codec)
+{
+ PJ_UNUSED_ARG(mgr)
+ (*codec->factory->op->dealloc_codec)(codec->factory, codec);
+}
+
diff --git a/pjmedia/src/pjmedia/codec.h b/pjmedia/src/pjmedia/codec.h
index fcf2384e..bf20d199 100644
--- a/pjmedia/src/pjmedia/codec.h
+++ b/pjmedia/src/pjmedia/codec.h
@@ -1,339 +1,360 @@
-/* $Id$
- *
- */
-
-#ifndef __PJMEDIA_CODEC_H__
-#define __PJMEDIA_CODEC_H__
-
-
-/**
- * @file codec.h
- * @brief Codec framework.
- */
-
-#include <pj/list.h>
-
-PJ_BEGIN_DECL
-
-
-/**
- * @defgroup PJMED_CODEC Codec framework.
- * @ingroup PJMEDIA
- * @{
- */
-
-/** Top most media type. */
-typedef enum pj_media_type
-{
- /** No type. */
- PJ_MEDIA_TYPE_NONE = 0,
-
- /** The media is audio */
- PJ_MEDIA_TYPE_AUDIO = 1,
-
- /** The media is video. */
- PJ_MEDIA_TYPE_VIDEO = 2,
-
- /** Unknown media type, in this case the name will be specified in
- * encoding_name.
- */
- PJ_MEDIA_TYPE_UNKNOWN = 3,
-
-} pj_media_type;
-
-
-/** Media direction. */
-typedef enum pj_media_dir_t
-{
- /** None */
- PJ_MEDIA_DIR_NONE = 0,
-
- /** Encoding (outgoing to network) stream */
- PJ_MEDIA_DIR_ENCODING = 1,
-
- /** Decoding (incoming from network) stream. */
- PJ_MEDIA_DIR_DECODING = 2,
-
- /** Incoming and outgoing stream. */
- PJ_MEDIA_DIR_ENCODING_DECODING = 3,
-
-} pj_media_dir_t;
-
-
-/** Standard RTP paylist types. */
-typedef enum pj_rtp_pt
-{
- PJ_RTP_PT_PCMU = 0, /* audio PCMU */
- PJ_RTP_PT_GSM = 3, /* audio GSM */
- PJ_RTP_PT_G723 = 4, /* audio G723 */
- PJ_RTP_PT_DVI4_8K = 5, /* audio DVI4 8KHz */
- PJ_RTP_PT_DVI4_16K = 6, /* audio DVI4 16Khz */
- PJ_RTP_PT_LPC = 7, /* audio LPC */
- PJ_RTP_PT_PCMA = 8, /* audio PCMA */
- PJ_RTP_PT_G722 = 9, /* audio G722 */
- PJ_RTP_PT_L16_2 = 10, /* audio 16bit linear 44.1KHz stereo */
- PJ_RTP_PT_L16_1 = 11, /* audio 16bit linear 44.1KHz mono */
- PJ_RTP_PT_QCELP = 12, /* audio QCELP */
- PJ_RTP_PT_CN = 13, /* audio Comfort Noise */
- PJ_RTP_PT_MPA = 14, /* audio MPEG1 or MPEG2 as elementary streams */
- PJ_RTP_PT_G728 = 15, /* audio G728 */
- PJ_RTP_PT_DVI4_11K = 16, /* audio DVI4 11.025KHz mono */
- PJ_RTP_PT_DVI4_22K = 17, /* audio DVI4 22.050KHz mono */
- PJ_RTP_PT_G729 = 18, /* audio G729 */
- PJ_RTP_PT_CELB = 25, /* video/comb Cell-B by Sun Microsystems (RFC 2029) */
- PJ_RTP_PT_JPEG = 26, /* video JPEG */
- PJ_RTP_PT_NV = 28, /* video NV implemented by nv program by Xerox */
- PJ_RTP_PT_H261 = 31, /* video H261 */
- PJ_RTP_PT_MPV = 32, /* video MPEG1 or MPEG2 elementary streams */
- PJ_RTP_PT_MP2T = 33, /* video MPEG2 transport */
- PJ_RTP_PT_H263 = 34, /* video H263 */
-
- PJ_RTP_PT_DYNAMIC = 96, /* start of dynamic RTP payload */
-} pj_rtp_pt;
-
-
-/** Identification used to search for codec factory that supports specific
- * codec specification.
- */
-typedef struct pj_codec_id
-{
- /** Media type. */
- pj_media_type type;
-
- /** Payload type (can be dynamic). */
- unsigned pt;
-
- /** Encoding name, must be present if the payload type is dynamic. */
- pj_str_t encoding_name;
-
- /** Sampling rate. */
- unsigned sample_rate;
-} pj_codec_id;
-
-
-/** Detailed codec attributes used both to configure a codec and to query
- * the capability of codec factories.
- */
-typedef struct pj_codec_attr
-{
- pj_uint32_t sample_rate; /* Sampling rate in Hz */
- pj_uint32_t avg_bps; /* Average bandwidth in bits per second */
-
- pj_uint8_t pcm_bits_per_sample;/* Bits per sample in the PCM side */
- pj_uint16_t ptime; /* Packet time in miliseconds */
-
- unsigned pt:8; /* Payload type. */
- unsigned vad_enabled:1; /* Voice Activity Detector. */
- unsigned cng_enabled:1; /* Comfort Noise Generator. */
- unsigned lpf_enabled:1; /* Low pass filter */
- unsigned hpf_enabled:1; /* High pass filter */
- unsigned penh_enabled:1; /* Perceptual Enhancement */
- unsigned concl_enabled:1; /* Packet loss concealment */
- unsigned reserved_bit:1;
-
-} pj_codec_attr;
-
-/** Types of audio frame. */
-typedef enum pj_audio_frame_type
-{
- /** The frame is a silence audio frame. */
- PJ_AUDIO_FRAME_SILENCE,
-
- /** The frame is a non-silence audio frame. */
- PJ_AUDIO_FRAME_AUDIO,
-
-} pj_audio_frame_type;
-
-typedef struct pj_codec pj_codec;
-typedef struct pj_codec_factory pj_codec_factory;
-
-
-/** This structure describes an audio frame. */
-struct pj_audio_frame
-{
- /** Type: silence or non-silence. */
- pj_audio_frame_type type;
-
- /** Pointer to buffer. */
- void *buf;
-
- /** Frame size in bytes. */
- unsigned size;
-};
-
-/**
- * Operations that must be supported by the codec.
- */
-typedef struct pj_codec_op
-{
- /** Get default attributes. */
- pj_status_t (*default_attr) (pj_codec *codec, pj_codec_attr *attr);
-
- /** Open and initialize codec using the specified attribute.
- * @return zero on success.
- */
- pj_status_t (*init)( pj_codec *codec, pj_pool_t *pool );
-
- /** Close and shutdown codec.
- */
- pj_status_t (*open)( pj_codec *codec, pj_codec_attr *attr );
-
- /** Close and shutdown codec.
- */
- pj_status_t (*close)( pj_codec *codec );
-
- /** Encode frame.
- */
- pj_status_t (*encode)( pj_codec *codec, const struct pj_audio_frame *input,
- unsigned output_buf_len, struct pj_audio_frame *output);
-
- /** Decode frame.
- */
- pj_status_t (*decode)( pj_codec *codec, const struct pj_audio_frame *input,
- unsigned output_buf_len, struct pj_audio_frame *output);
-
-} pj_codec_op;
-
-/**
- * A codec describes an instance to encode or decode media frames.
- */
-struct pj_codec
-{
- /** Entries to put this codec instance in codec factory's list. */
- PJ_DECL_LIST_MEMBER(struct pj_codec)
-
- /** Codec's private data. */
- void *codec_data;
-
- /** Codec factory where this codec was allocated. */
- pj_codec_factory *factory;
-
- /** Operations to codec. */
- pj_codec_op *op;
-};
-
-/**
- * This structure describes operations that must be supported by codec factories.
- */
-typedef struct pj_codec_factory_op
-{
- /** Check whether the factory can create codec with the specified ID.
- * @param factory The codec factory.
- * @param id The codec ID.
- * @return zero it matches.
- */
- pj_status_t (*match_id)( pj_codec_factory *factory, const pj_codec_id *id );
-
- /** Create default attributes for the specified codec ID. This function can
- * be called by application to get the capability of the codec.
- * @param factory The codec factory.
- * @param id The codec ID.
- * @param attr The attribute to be initialized.
- * @return zero if success.
- */
- pj_status_t (*default_attr)( pj_codec_factory *factory, const pj_codec_id *id,
- pj_codec_attr *attr );
-
- /** Enumerate supported codecs.
- * @param factory The codec factory.
- * @param count Number of entries in the array.
- * @param codecs The codec array.
- * @return the total number of supported codecs, which can be less or
- * greater than requested.
- */
- unsigned (*enum_codecs) (pj_codec_factory *factory, unsigned count, pj_codec_id codecs[]);
-
- /** This function is called by codec manager to instantiate one codec
- * instance.
- * @param factory The codec factory.
- * @param id The codec ID.
- * @return the instance of the codec, or NULL if codec can not be created.
- */
- pj_codec* (*alloc_codec)( pj_codec_factory *factory, const pj_codec_id *id);
-
- /** This function is called by codec manager to return a particular instance
- * of codec back to the codec factory.
- * @param factory The codec factory.
- * @param codec The codec instance to be returned.
- */
- void (*dealloc_codec)( pj_codec_factory *factory, pj_codec *codec );
-
-} pj_codec_factory_op;
-
-/**
- * Codec factory describes a module that is able to create codec with specific
- * capabilities. These capabilities can be queried by codec manager to create
- * instances of codec.
- */
-struct pj_codec_factory
-{
- /** Entries to put this structure in the codec manager list. */
- PJ_DECL_LIST_MEMBER(struct pj_codec_factory)
-
- /** The factory's private data. */
- void *factory_data;
-
- /** Operations to the factory. */
- pj_codec_factory_op *op;
-
-};
-
-/**
- * Declare maximum codecs
- */
-#define PJ_CODEC_MGR_MAX_CODECS 32
-
-/**
- * Codec manager maintains codec factory etc.
- */
-typedef struct pj_codec_mgr
-{
- pj_codec_factory factory_list;
- unsigned codec_cnt;
- pj_codec_id codecs[PJ_CODEC_MGR_MAX_CODECS];
-} pj_codec_mgr;
-
-/**
- * Init codec manager.
- */
-PJ_DECL(pj_status_t)
-pj_codec_mgr_init (pj_codec_mgr *mgr);
-
-/**
- * Register codec to codec manager.
- */
-PJ_DECL(pj_status_t)
-pj_codec_mgr_register_factory (pj_codec_mgr *mgr, pj_codec_factory *factory);
-
-/**
- * Unregister codec.
- */
-PJ_DECL(void)
-pj_codec_mgr_unregister_factory (pj_codec_mgr *mgr, pj_codec_factory *factory);
-
-/**
- * Enumerate codecs.
- */
-PJ_DECL(unsigned)
-pj_codec_mgr_enum_codecs (pj_codec_mgr *mgr, unsigned count, const pj_codec_id *codecs[]);
-
-/**
- * Open codec.
- */
-PJ_DECL(pj_codec*)
-pj_codec_mgr_alloc_codec (pj_codec_mgr *mgr, const struct pj_codec_id *id);
-
-/**
- * Close codec.
- */
-PJ_DECL(void)
-pj_codec_mgr_dealloc_codec (pj_codec_mgr *mgr, pj_codec *codec);
-
-/**
- * @}
- */
-
-PJ_END_DECL
-
-
-#endif /* __PJMEDIA_CODEC_H__ */
+/* $Id$
+ *
+ */
+/*
+ * PJMEDIA - Multimedia over IP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef __PJMEDIA_CODEC_H__
+#define __PJMEDIA_CODEC_H__
+
+
+/**
+ * @file codec.h
+ * @brief Codec framework.
+ */
+
+#include <pj/list.h>
+
+PJ_BEGIN_DECL
+
+
+/**
+ * @defgroup PJMED_CODEC Codec framework.
+ * @ingroup PJMEDIA
+ * @{
+ */
+
+/** Top most media type. */
+typedef enum pj_media_type
+{
+ /** No type. */
+ PJ_MEDIA_TYPE_NONE = 0,
+
+ /** The media is audio */
+ PJ_MEDIA_TYPE_AUDIO = 1,
+
+ /** The media is video. */
+ PJ_MEDIA_TYPE_VIDEO = 2,
+
+ /** Unknown media type, in this case the name will be specified in
+ * encoding_name.
+ */
+ PJ_MEDIA_TYPE_UNKNOWN = 3,
+
+} pj_media_type;
+
+
+/** Media direction. */
+typedef enum pj_media_dir_t
+{
+ /** None */
+ PJ_MEDIA_DIR_NONE = 0,
+
+ /** Encoding (outgoing to network) stream */
+ PJ_MEDIA_DIR_ENCODING = 1,
+
+ /** Decoding (incoming from network) stream. */
+ PJ_MEDIA_DIR_DECODING = 2,
+
+ /** Incoming and outgoing stream. */
+ PJ_MEDIA_DIR_ENCODING_DECODING = 3,
+
+} pj_media_dir_t;
+
+
+/** Standard RTP paylist types. */
+typedef enum pj_rtp_pt
+{
+ PJ_RTP_PT_PCMU = 0, /* audio PCMU */
+ PJ_RTP_PT_GSM = 3, /* audio GSM */
+ PJ_RTP_PT_G723 = 4, /* audio G723 */
+ PJ_RTP_PT_DVI4_8K = 5, /* audio DVI4 8KHz */
+ PJ_RTP_PT_DVI4_16K = 6, /* audio DVI4 16Khz */
+ PJ_RTP_PT_LPC = 7, /* audio LPC */
+ PJ_RTP_PT_PCMA = 8, /* audio PCMA */
+ PJ_RTP_PT_G722 = 9, /* audio G722 */
+ PJ_RTP_PT_L16_2 = 10, /* audio 16bit linear 44.1KHz stereo */
+ PJ_RTP_PT_L16_1 = 11, /* audio 16bit linear 44.1KHz mono */
+ PJ_RTP_PT_QCELP = 12, /* audio QCELP */
+ PJ_RTP_PT_CN = 13, /* audio Comfort Noise */
+ PJ_RTP_PT_MPA = 14, /* audio MPEG1 or MPEG2 as elementary streams */
+ PJ_RTP_PT_G728 = 15, /* audio G728 */
+ PJ_RTP_PT_DVI4_11K = 16, /* audio DVI4 11.025KHz mono */
+ PJ_RTP_PT_DVI4_22K = 17, /* audio DVI4 22.050KHz mono */
+ PJ_RTP_PT_G729 = 18, /* audio G729 */
+ PJ_RTP_PT_CELB = 25, /* video/comb Cell-B by Sun Microsystems (RFC 2029) */
+ PJ_RTP_PT_JPEG = 26, /* video JPEG */
+ PJ_RTP_PT_NV = 28, /* video NV implemented by nv program by Xerox */
+ PJ_RTP_PT_H261 = 31, /* video H261 */
+ PJ_RTP_PT_MPV = 32, /* video MPEG1 or MPEG2 elementary streams */
+ PJ_RTP_PT_MP2T = 33, /* video MPEG2 transport */
+ PJ_RTP_PT_H263 = 34, /* video H263 */
+
+ PJ_RTP_PT_DYNAMIC = 96, /* start of dynamic RTP payload */
+} pj_rtp_pt;
+
+
+/** Identification used to search for codec factory that supports specific
+ * codec specification.
+ */
+typedef struct pj_codec_id
+{
+ /** Media type. */
+ pj_media_type type;
+
+ /** Payload type (can be dynamic). */
+ unsigned pt;
+
+ /** Encoding name, must be present if the payload type is dynamic. */
+ pj_str_t encoding_name;
+
+ /** Sampling rate. */
+ unsigned sample_rate;
+} pj_codec_id;
+
+
+/** Detailed codec attributes used both to configure a codec and to query
+ * the capability of codec factories.
+ */
+typedef struct pj_codec_attr
+{
+ pj_uint32_t sample_rate; /* Sampling rate in Hz */
+ pj_uint32_t avg_bps; /* Average bandwidth in bits per second */
+
+ pj_uint8_t pcm_bits_per_sample;/* Bits per sample in the PCM side */
+ pj_uint16_t ptime; /* Packet time in miliseconds */
+
+ unsigned pt:8; /* Payload type. */
+ unsigned vad_enabled:1; /* Voice Activity Detector. */
+ unsigned cng_enabled:1; /* Comfort Noise Generator. */
+ unsigned lpf_enabled:1; /* Low pass filter */
+ unsigned hpf_enabled:1; /* High pass filter */
+ unsigned penh_enabled:1; /* Perceptual Enhancement */
+ unsigned concl_enabled:1; /* Packet loss concealment */
+ unsigned reserved_bit:1;
+
+} pj_codec_attr;
+
+/** Types of audio frame. */
+typedef enum pj_audio_frame_type
+{
+ /** The frame is a silence audio frame. */
+ PJ_AUDIO_FRAME_SILENCE,
+
+ /** The frame is a non-silence audio frame. */
+ PJ_AUDIO_FRAME_AUDIO,
+
+} pj_audio_frame_type;
+
+typedef struct pj_codec pj_codec;
+typedef struct pj_codec_factory pj_codec_factory;
+
+
+/** This structure describes an audio frame. */
+struct pj_audio_frame
+{
+ /** Type: silence or non-silence. */
+ pj_audio_frame_type type;
+
+ /** Pointer to buffer. */
+ void *buf;
+
+ /** Frame size in bytes. */
+ unsigned size;
+};
+
+/**
+ * Operations that must be supported by the codec.
+ */
+typedef struct pj_codec_op
+{
+ /** Get default attributes. */
+ pj_status_t (*default_attr) (pj_codec *codec, pj_codec_attr *attr);
+
+ /** Open and initialize codec using the specified attribute.
+ * @return zero on success.
+ */
+ pj_status_t (*init)( pj_codec *codec, pj_pool_t *pool );
+
+ /** Close and shutdown codec.
+ */
+ pj_status_t (*open)( pj_codec *codec, pj_codec_attr *attr );
+
+ /** Close and shutdown codec.
+ */
+ pj_status_t (*close)( pj_codec *codec );
+
+ /** Encode frame.
+ */
+ pj_status_t (*encode)( pj_codec *codec, const struct pj_audio_frame *input,
+ unsigned output_buf_len, struct pj_audio_frame *output);
+
+ /** Decode frame.
+ */
+ pj_status_t (*decode)( pj_codec *codec, const struct pj_audio_frame *input,
+ unsigned output_buf_len, struct pj_audio_frame *output);
+
+} pj_codec_op;
+
+/**
+ * A codec describes an instance to encode or decode media frames.
+ */
+struct pj_codec
+{
+ /** Entries to put this codec instance in codec factory's list. */
+ PJ_DECL_LIST_MEMBER(struct pj_codec)
+
+ /** Codec's private data. */
+ void *codec_data;
+
+ /** Codec factory where this codec was allocated. */
+ pj_codec_factory *factory;
+
+ /** Operations to codec. */
+ pj_codec_op *op;
+};
+
+/**
+ * This structure describes operations that must be supported by codec factories.
+ */
+typedef struct pj_codec_factory_op
+{
+ /** Check whether the factory can create codec with the specified ID.
+ * @param factory The codec factory.
+ * @param id The codec ID.
+ * @return zero it matches.
+ */
+ pj_status_t (*match_id)( pj_codec_factory *factory, const pj_codec_id *id );
+
+ /** Create default attributes for the specified codec ID. This function can
+ * be called by application to get the capability of the codec.
+ * @param factory The codec factory.
+ * @param id The codec ID.
+ * @param attr The attribute to be initialized.
+ * @return zero if success.
+ */
+ pj_status_t (*default_attr)( pj_codec_factory *factory, const pj_codec_id *id,
+ pj_codec_attr *attr );
+
+ /** Enumerate supported codecs.
+ * @param factory The codec factory.
+ * @param count Number of entries in the array.
+ * @param codecs The codec array.
+ * @return the total number of supported codecs, which can be less or
+ * greater than requested.
+ */
+ unsigned (*enum_codecs) (pj_codec_factory *factory, unsigned count, pj_codec_id codecs[]);
+
+ /** This function is called by codec manager to instantiate one codec
+ * instance.
+ * @param factory The codec factory.
+ * @param id The codec ID.
+ * @return the instance of the codec, or NULL if codec can not be created.
+ */
+ pj_codec* (*alloc_codec)( pj_codec_factory *factory, const pj_codec_id *id);
+
+ /** This function is called by codec manager to return a particular instance
+ * of codec back to the codec factory.
+ * @param factory The codec factory.
+ * @param codec The codec instance to be returned.
+ */
+ void (*dealloc_codec)( pj_codec_factory *factory, pj_codec *codec );
+
+} pj_codec_factory_op;
+
+/**
+ * Codec factory describes a module that is able to create codec with specific
+ * capabilities. These capabilities can be queried by codec manager to create
+ * instances of codec.
+ */
+struct pj_codec_factory
+{
+ /** Entries to put this structure in the codec manager list. */
+ PJ_DECL_LIST_MEMBER(struct pj_codec_factory)
+
+ /** The factory's private data. */
+ void *factory_data;
+
+ /** Operations to the factory. */
+ pj_codec_factory_op *op;
+
+};
+
+/**
+ * Declare maximum codecs
+ */
+#define PJ_CODEC_MGR_MAX_CODECS 32
+
+/**
+ * Codec manager maintains codec factory etc.
+ */
+typedef struct pj_codec_mgr
+{
+ pj_codec_factory factory_list;
+ unsigned codec_cnt;
+ pj_codec_id codecs[PJ_CODEC_MGR_MAX_CODECS];
+} pj_codec_mgr;
+
+/**
+ * Init codec manager.
+ */
+PJ_DECL(pj_status_t)
+pj_codec_mgr_init (pj_codec_mgr *mgr);
+
+/**
+ * Register codec to codec manager.
+ */
+PJ_DECL(pj_status_t)
+pj_codec_mgr_register_factory (pj_codec_mgr *mgr, pj_codec_factory *factory);
+
+/**
+ * Unregister codec.
+ */
+PJ_DECL(void)
+pj_codec_mgr_unregister_factory (pj_codec_mgr *mgr, pj_codec_factory *factory);
+
+/**
+ * Enumerate codecs.
+ */
+PJ_DECL(unsigned)
+pj_codec_mgr_enum_codecs (pj_codec_mgr *mgr, unsigned count, const pj_codec_id *codecs[]);
+
+/**
+ * Open codec.
+ */
+PJ_DECL(pj_codec*)
+pj_codec_mgr_alloc_codec (pj_codec_mgr *mgr, const struct pj_codec_id *id);
+
+/**
+ * Close codec.
+ */
+PJ_DECL(void)
+pj_codec_mgr_dealloc_codec (pj_codec_mgr *mgr, pj_codec *codec);
+
+/**
+ * @}
+ */
+
+PJ_END_DECL
+
+
+#endif /* __PJMEDIA_CODEC_H__ */
diff --git a/pjmedia/src/pjmedia/config.h b/pjmedia/src/pjmedia/config.h
index cc388922..757a8dc8 100644
--- a/pjmedia/src/pjmedia/config.h
+++ b/pjmedia/src/pjmedia/config.h
@@ -1,13 +1,34 @@
-/* $Id$
- *
- */
-
-#ifndef __PJMED_CONFIG_H__
-#define __PJMED_CONFIG_H__
-
-/**
- * @defgroup PJMEDIA Media Stack
- */
-
-
-#endif /* __PJMED_CONFIG_H__ */
+/* $Id$
+ *
+ */
+/*
+ * PJMEDIA - Multimedia over IP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef __PJMED_CONFIG_H__
+#define __PJMED_CONFIG_H__
+
+/**
+ * @defgroup PJMEDIA Media Stack
+ */
+
+
+#endif /* __PJMED_CONFIG_H__ */
diff --git a/pjmedia/src/pjmedia/dsound.c b/pjmedia/src/pjmedia/dsound.c
index 6f3b67d8..22b0a5dd 100644
--- a/pjmedia/src/pjmedia/dsound.c
+++ b/pjmedia/src/pjmedia/dsound.c
@@ -1,863 +1,884 @@
-/* $Id$
- *
- */
-
-#ifdef _MSC_VER
-//# pragma warning(disable: 4201) // non-standard extension: nameless struct/union
-# pragma warning(push, 3)
-#endif
-#include <pj/config.h>
-#include <pj/os.h>
-#include <pj/log.h>
-
-#include <dsound.h>
-#include <stdio.h>
-#include <assert.h>
-#include <pjmedia/sound.h>
-
-#define THIS_FILE "dsound.c"
-
-/*
- * Constants
- */
-#define PACKET_BUFFER_COUNT 4
-
-typedef struct PJ_Direct_Sound_Device PJ_Direct_Sound_Device;
-
-
-/*
- * DirectSound Factory Operations
- */
-static pj_status_t dsound_init(void);
-static const char *dsound_get_name(void);
-static pj_status_t dsound_destroy(void);
-static pj_status_t dsound_enum_devices(int *count, char *dev_names[]);
-static pj_status_t dsound_create_dev(const char *dev_name, pj_snd_dev *dev);
-static pj_status_t dsound_destroy_dev(pj_snd_dev *dev);
-
-
-/*
- * DirectSound Device Operations
- */
-static pj_status_t dsound_dev_open( pj_snd_dev *dev, pj_snd_role_t role );
-static pj_status_t dsound_dev_close( pj_snd_dev *dev );
-static pj_status_t dsound_dev_play( pj_snd_dev *dev );
-static pj_status_t dsound_dev_record( pj_snd_dev *dev );
-
-/*
- * Utils.
- */
-static pj_status_t dsound_destroy_dsound_dev( PJ_Direct_Sound_Device *dsDev );
-
-
-static pj_snd_dev_factory dsound_factory =
-{
- &dsound_init,
- &dsound_get_name,
- &dsound_destroy,
- &dsound_enum_devices,
- &dsound_create_dev,
- &dsound_destroy_dev
-};
-
-static struct pj_snd_dev_op dsound_dev_op =
-{
- &dsound_dev_open,
- &dsound_dev_close,
- &dsound_dev_play,
- &dsound_dev_record
-};
-
-#define DSOUND_TYPE_PLAYER 1
-#define DSOUND_TYPE_RECORDER 2
-
-typedef struct Direct_Sound_Descriptor
-{
- int type;
-
- LPDIRECTSOUND lpDsPlay;
- LPDIRECTSOUNDBUFFER lpDsPlayBuffer;
-
- LPDIRECTSOUNDCAPTURE lpDsCapture;
- LPDIRECTSOUNDCAPTUREBUFFER lpDsCaptureBuffer;
-
- LPDIRECTSOUNDNOTIFY lpDsNotify;
- HANDLE hEvent;
- HANDLE hThread;
- HANDLE hStartEvent;
- DWORD dwThreadQuitFlag;
- pj_thread_desc thread_desc;
- pj_thread_t *thread;
-} Direct_Sound_Descriptor;
-
-struct PJ_Direct_Sound_Device
-{
- Direct_Sound_Descriptor playDesc;
- Direct_Sound_Descriptor recDesc;
-};
-
-struct Thread_Param
-{
- pj_snd_dev *dev;
- Direct_Sound_Descriptor *desc;
-};
-
-PJ_DEF(pj_snd_dev_factory*) pj_dsound_get_factory()
-{
- return &dsound_factory;
-}
-
-/*
- * Init DirectSound.
- */
-static pj_status_t dsound_init(void)
-{
- /* Nothing to do. */
- return 0;
-}
-
-/*
- * Get the name of the factory.
- */
-static const char *dsound_get_name(void)
-{
- return "DirectSound";
-}
-
-/*
- * Destroy DirectSound.
- */
-static pj_status_t dsound_destroy(void)
-{
- /* TODO: clean up devices in case application haven't done it. */
- return 0;
-}
-
-/*
- * Enum devices in the system.
- */
-static pj_status_t dsound_enum_devices(int *count, char *dev_names[])
-{
- dev_names[0] = "DirectSound Default Device";
- *count = 1;
- return 0;
-}
-
-/*
- * Create DirectSound device.
- */
-static pj_status_t dsound_create_dev(const char *dev_name, pj_snd_dev *dev)
-{
- PJ_Direct_Sound_Device *dsDev;
-
- /* TODO: create based on the name. */
- PJ_TODO(DSOUND_CREATE_DEVICE_BY_NAME);
-
- /* Create DirectSound structure. */
- dsDev = malloc(sizeof(*dsDev));
- if (!dsDev) {
- PJ_LOG(1,(THIS_FILE, "No memory to allocate device!"));
- return -1;
- }
- memset(dsDev, 0, sizeof(*dsDev));
-
- /* Associate DirectSound device with device. */
- dev->device = dsDev;
- dev->op = &dsound_dev_op;
-
- return 0;
-}
-
-/*
- * Destroy DirectSound device.
- */
-static pj_status_t dsound_destroy_dev( pj_snd_dev *dev )
-{
- if (dev->device) {
- free(dev->device);
- dev->device = NULL;
- }
- return 0;
-}
-
-static void dsound_release_descriptor(Direct_Sound_Descriptor *desc)
-{
- if (desc->lpDsNotify)
- IDirectSoundNotify_Release( desc->lpDsNotify );
-
- if (desc->hEvent)
- CloseHandle(desc->hEvent);
-
- if (desc->lpDsPlayBuffer)
- IDirectSoundBuffer_Release( desc->lpDsPlayBuffer );
-
- if (desc->lpDsPlay)
- IDirectSound_Release( desc->lpDsPlay );
-
- if (desc->lpDsCaptureBuffer)
- IDirectSoundCaptureBuffer_Release(desc->lpDsCaptureBuffer);
-
- if (desc->lpDsCapture)
- IDirectSoundCapture_Release(desc->lpDsCapture);
-}
-
-/*
- * Destroy DirectSound resources.
- */
-static pj_status_t dsound_destroy_dsound_dev( PJ_Direct_Sound_Device *dsDev )
-{
- dsound_release_descriptor( &dsDev->playDesc );
- dsound_release_descriptor( &dsDev->recDesc );
- memset(dsDev, 0, sizeof(*dsDev));
- return 0;
-}
-
-static void init_waveformatex (PCMWAVEFORMAT *pcmwf, pj_snd_dev *dev)
-{
- memset(pcmwf, 0, sizeof(PCMWAVEFORMAT));
- pcmwf->wf.wFormatTag = WAVE_FORMAT_PCM;
- pcmwf->wf.nChannels = 1;
- pcmwf->wf.nSamplesPerSec = dev->param.samples_per_sec;
- pcmwf->wf.nBlockAlign = dev->param.bytes_per_frame;
- pcmwf->wf.nAvgBytesPerSec =
- dev->param.samples_per_sec * dev->param.bytes_per_frame;
- pcmwf->wBitsPerSample = dev->param.bits_per_sample;
-}
-
-/*
- * Initialize DirectSound player device.
- */
-static pj_status_t dsound_init_player (pj_snd_dev *dev)
-{
- HRESULT hr;
- HWND hwnd;
- PCMWAVEFORMAT pcmwf;
- DSBUFFERDESC dsbdesc;
- DSBPOSITIONNOTIFY dsPosNotify[PACKET_BUFFER_COUNT];
- unsigned i;
- PJ_Direct_Sound_Device *dsDev = dev->device;
-
- /*
- * Check parameters.
- */
- if (dev->play_cb == NULL) {
- assert(0);
- return -1;
- }
- if (dev->device == NULL) {
- assert(0);
- return -1;
- }
-
- PJ_LOG(4,(THIS_FILE, "Creating DirectSound player device"));
-
- /*
- * Create DirectSound device.
- */
- hr = DirectSoundCreate(NULL, &dsDev->playDesc.lpDsPlay, NULL);
- if (FAILED(hr))
- goto on_error;
-
- hwnd = GetForegroundWindow();
- if (hwnd == NULL) {
- hwnd = GetDesktopWindow();
- }
- hr = IDirectSound_SetCooperativeLevel( dsDev->playDesc.lpDsPlay, hwnd,
- DSSCL_PRIORITY);
- if FAILED(hr)
- goto on_error;
-
- /*
- * Create DirectSound play buffer.
- */
- // Set up wave format structure.
- init_waveformatex (&pcmwf, dev);
-
- // Set up DSBUFFERDESC structure.
- memset(&dsbdesc, 0, sizeof(DSBUFFERDESC));
- dsbdesc.dwSize = sizeof(DSBUFFERDESC);
- dsbdesc.dwFlags = DSBCAPS_CTRLVOLUME | DSBCAPS_CTRLPOSITIONNOTIFY |
- DSBCAPS_GETCURRENTPOSITION2;
-
- dsbdesc.dwBufferBytes =
- (PACKET_BUFFER_COUNT * dev->param.bytes_per_frame *
- dev->param.frames_per_packet);
- dsbdesc.lpwfxFormat = (LPWAVEFORMATEX)&pcmwf;
-
- // Create buffer.
- hr = IDirectSound_CreateSoundBuffer(dsDev->playDesc.lpDsPlay, &dsbdesc,
- &dsDev->playDesc.lpDsPlayBuffer, NULL);
- if (FAILED(hr) )
- goto on_error;
-
- /*
- * Create event for play notification.
- */
- dsDev->playDesc.hEvent = CreateEvent( NULL, FALSE, FALSE, NULL);
- if (dsDev->playDesc.hEvent == NULL)
- goto on_error;
-
- /*
- * Setup notification for play.
- */
- hr = IDirectSoundBuffer_QueryInterface( dsDev->playDesc.lpDsPlayBuffer,
- &IID_IDirectSoundNotify,
- (LPVOID *)&dsDev->playDesc.lpDsNotify);
- if (FAILED(hr))
- goto on_error;
-
-
- for (i=0; i<PACKET_BUFFER_COUNT; ++i) {
- dsPosNotify[i].dwOffset = i * dev->param.bytes_per_frame *
- dev->param.frames_per_packet;
- dsPosNotify[i].hEventNotify = dsDev->playDesc.hEvent;
- }
-
- hr = IDirectSoundNotify_SetNotificationPositions( dsDev->playDesc.lpDsNotify,
- PACKET_BUFFER_COUNT,
- dsPosNotify);
- if (FAILED(hr))
- goto on_error;
-
- /* Done setting up play device. */
- PJ_LOG(4,(THIS_FILE, "DirectSound player device created"));
-
- return 0;
-
-on_error:
- PJ_LOG(2,(THIS_FILE, "Error creating player device, hresult=0x%x", hr));
- dsound_destroy_dsound_dev(dsDev);
- return -1;
-}
-
-/*
- * Initialize DirectSound recorder device
- */
-static pj_status_t dsound_init_recorder (pj_snd_dev *dev)
-{
- HRESULT hr;
- PCMWAVEFORMAT pcmwf;
- DSCBUFFERDESC dscbdesc;
- DSBPOSITIONNOTIFY dsPosNotify[PACKET_BUFFER_COUNT];
- unsigned i;
- PJ_Direct_Sound_Device *dsDev = dev->device;
-
- /*
- * Check parameters.
- */
- if (dev->rec_cb == NULL) {
- assert(0);
- return -1;
- }
- if (dev->device == NULL) {
- assert(0);
- return -1;
- }
-
- PJ_LOG(4,(THIS_FILE, "Creating DirectSound recorder device"));
-
- /*
- * Creating recorder device.
- */
- hr = DirectSoundCaptureCreate(NULL, &dsDev->recDesc.lpDsCapture, NULL);
- if (FAILED(hr))
- goto on_error;
-
- /* Init wave format */
- init_waveformatex (&pcmwf, dev);
-
- /*
- * Setup capture buffer using sound buffer structure that was passed
- * to play buffer creation earlier.
- */
- memset(&dscbdesc, 0, sizeof(DSCBUFFERDESC));
- dscbdesc.dwSize = sizeof(DSCBUFFERDESC);
- dscbdesc.dwFlags = DSCBCAPS_WAVEMAPPED ;
- dscbdesc.dwBufferBytes =
- (PACKET_BUFFER_COUNT * dev->param.bytes_per_frame *
- dev->param.frames_per_packet);
- dscbdesc.lpwfxFormat = (LPWAVEFORMATEX)&pcmwf;
-
- hr = IDirectSoundCapture_CreateCaptureBuffer( dsDev->recDesc.lpDsCapture,
- &dscbdesc,
- &dsDev->recDesc.lpDsCaptureBuffer,
- NULL);
- if (FAILED(hr))
- goto on_error;
-
- /*
- * Create event for play notification.
- */
- dsDev->recDesc.hEvent = CreateEvent( NULL, FALSE, FALSE, NULL);
- if (dsDev->recDesc.hEvent == NULL)
- goto on_error;
-
- /*
- * Setup notifications for recording.
- */
- hr = IDirectSoundCaptureBuffer_QueryInterface( dsDev->recDesc.lpDsCaptureBuffer,
- &IID_IDirectSoundNotify,
- (LPVOID *)&dsDev->recDesc.lpDsNotify);
- if (FAILED(hr))
- goto on_error;
-
-
- for (i=0; i<PACKET_BUFFER_COUNT; ++i) {
- dsPosNotify[i].dwOffset = i * dev->param.bytes_per_frame *
- dev->param.frames_per_packet;
- dsPosNotify[i].hEventNotify = dsDev->recDesc.hEvent;
- }
-
- hr = IDirectSoundNotify_SetNotificationPositions( dsDev->recDesc.lpDsNotify,
- PACKET_BUFFER_COUNT,
- dsPosNotify);
- if (FAILED(hr))
- goto on_error;
-
- /* Done setting up recorder device. */
- PJ_LOG(4,(THIS_FILE, "DirectSound recorder device created"));
-
- return 0;
-
-on_error:
- PJ_LOG(4,(THIS_FILE, "Error creating device, hresult=%d", hr));
- dsound_destroy_dsound_dev(dsDev);
- return -1;
-}
-
-/*
- * Initialize DirectSound device.
- */
-static pj_status_t dsound_dev_open( pj_snd_dev *dev, pj_snd_role_t role )
-{
- PJ_Direct_Sound_Device *dsDev = dev->device;
- pj_status_t status;
-
- dsDev->playDesc.type = DSOUND_TYPE_PLAYER;
- dsDev->recDesc.type = DSOUND_TYPE_RECORDER;
-
- if (role & PJ_SOUND_PLAYER) {
- status = dsound_init_player (dev);
- if (status != 0)
- return status;
- }
-
- if (role & PJ_SOUND_RECORDER) {
- status = dsound_init_recorder (dev);
- if (status != 0)
- return status;
- }
-
- return 0;
-}
-
-/*
- * Close DirectSound device.
- */
-static pj_status_t dsound_dev_close( pj_snd_dev *dev )
-{
- PJ_LOG(4,(THIS_FILE, "Closing DirectSound device"));
-
- if (dev->device) {
- PJ_Direct_Sound_Device *dsDev = dev->device;
-
- if (dsDev->playDesc.hThread) {
- PJ_LOG(4,(THIS_FILE, "Stopping DirectSound player"));
- dsDev->playDesc.dwThreadQuitFlag = 1;
- SetEvent(dsDev->playDesc.hEvent);
- if (WaitForSingleObject(dsDev->playDesc.hThread, 1000) != WAIT_OBJECT_0) {
- PJ_LOG(4,(THIS_FILE, "Timed out waiting player thread to quit"));
- TerminateThread(dsDev->playDesc.hThread, -1);
- IDirectSoundBuffer_Stop( dsDev->playDesc.lpDsPlayBuffer );
- }
-
- pj_thread_destroy (dsDev->playDesc.thread);
- }
-
- if (dsDev->recDesc.hThread) {
- PJ_LOG(4,(THIS_FILE, "Stopping DirectSound recorder"));
- dsDev->recDesc.dwThreadQuitFlag = 1;
- SetEvent(dsDev->recDesc.hEvent);
- if (WaitForSingleObject(dsDev->recDesc.hThread, 1000) != WAIT_OBJECT_0) {
- PJ_LOG(4,(THIS_FILE, "Timed out waiting recorder thread to quit"));
- TerminateThread(dsDev->recDesc.hThread, -1);
- IDirectSoundCaptureBuffer_Stop( dsDev->recDesc.lpDsCaptureBuffer );
- }
-
- pj_thread_destroy (dsDev->recDesc.thread);
- }
-
- dsound_destroy_dsound_dev( dev->device );
- dev->op = NULL;
- }
-
- PJ_LOG(4,(THIS_FILE, "DirectSound device closed"));
- return 0;
-}
-
-static BOOL AppReadDataFromBuffer(LPDIRECTSOUNDCAPTUREBUFFER lpDsb, // The buffer.
- DWORD dwOffset, // Our own write cursor.
- LPBYTE lpbSoundData, // Start of our data.
- DWORD dwSoundBytes) // Size of block to copy.
-{
- LPVOID lpvPtr1;
- DWORD dwBytes1;
- LPVOID lpvPtr2;
- DWORD dwBytes2;
- HRESULT hr;
-
- // Obtain memory address of write block. This will be in two parts
- // if the block wraps around.
-
- hr = IDirectSoundCaptureBuffer_Lock( lpDsb, dwOffset, dwSoundBytes, &lpvPtr1,
- &dwBytes1, &lpvPtr2, &dwBytes2, 0);
-
- if SUCCEEDED(hr) {
- // Read from pointers.
- CopyMemory(lpbSoundData, lpvPtr1, dwBytes1);
- if (lpvPtr2 != NULL)
- CopyMemory(lpbSoundData+dwBytes1, lpvPtr2, dwBytes2);
-
- // Release the data back to DirectSound.
- hr = IDirectSoundCaptureBuffer_Unlock(lpDsb, lpvPtr1, dwBytes1, lpvPtr2, dwBytes2);
- if SUCCEEDED(hr)
- return TRUE;
- }
-
- // Lock, Unlock, or Restore failed.
- return FALSE;
-}
-
-
-static BOOL AppWriteDataToBuffer(LPDIRECTSOUNDBUFFER lpDsb, // The buffer.
- DWORD dwOffset, // Our own write cursor.
- LPBYTE lpbSoundData, // Start of our data.
- DWORD dwSoundBytes) // Size of block to copy.
-{
- LPVOID lpvPtr1;
- DWORD dwBytes1;
- LPVOID lpvPtr2;
- DWORD dwBytes2;
- HRESULT hr;
-
- // Obtain memory address of write block. This will be in two parts
- // if the block wraps around.
-
- hr = IDirectSoundBuffer_Lock( lpDsb, dwOffset, dwSoundBytes, &lpvPtr1,
- &dwBytes1, &lpvPtr2, &dwBytes2, 0);
-
- // If the buffer was lost, restore and retry lock.
- if (DSERR_BUFFERLOST == hr) {
- IDirectSoundBuffer_Restore(lpDsb);
- hr = IDirectSoundBuffer_Lock( lpDsb, dwOffset, dwSoundBytes,
- &lpvPtr1, &dwBytes1, &lpvPtr2, &dwBytes2, 0);
- }
- if SUCCEEDED(hr) {
- CopyMemory(lpvPtr1, lpbSoundData, dwBytes1);
- if (NULL != lpvPtr2)
- CopyMemory(lpvPtr2, lpbSoundData+dwBytes1, dwBytes2);
-
- hr = IDirectSoundBuffer_Unlock(lpDsb, lpvPtr1, dwBytes1, lpvPtr2, dwBytes2);
- if SUCCEEDED(hr)
- return TRUE;
- }
-
- return FALSE;
-}
-
-
-/*
- * Player thread.
- */
-static DWORD WINAPI dsound_dev_thread(void *arg)
-{
- struct Thread_Param *param = arg;
- pj_snd_dev *dev = param->dev;
- Direct_Sound_Descriptor *desc = param->desc;
- unsigned bytes_per_pkt = dev->param.bytes_per_frame *
- dev->param.frames_per_packet;
- unsigned samples_per_pkt = dev->param.samples_per_frame *
- dev->param.frames_per_packet;
- void *buffer = NULL;
- DWORD size;
- pj_status_t status;
- DWORD sample_pos;
- DWORD byte_pos;
- DWORD buffer_size =
- (PACKET_BUFFER_COUNT * dev->param.bytes_per_frame *
- dev->param.frames_per_packet);
- HRESULT hr;
-
- PJ_LOG(4,(THIS_FILE, "DirectSound thread starting"));
-
- /* Allocate buffer for sound data */
- buffer = malloc(bytes_per_pkt);
- if (!buffer) {
- PJ_LOG(1,(THIS_FILE, "Unable to allocate packet buffer!"));
- return (DWORD)-1;
- }
-
- desc->thread = pj_thread_register ("dsound", desc->thread_desc);
- if (desc->thread == NULL)
- return (DWORD)-1;
-
- /*
- * Start playing or recording!
- */
- if (desc->type == DSOUND_TYPE_PLAYER) {
- hr = IDirectSoundBuffer_SetCurrentPosition( desc->lpDsPlayBuffer, 0);
- if (FAILED(hr)) {
- PJ_LOG(1,(THIS_FILE, "DirectSound play: SetCurrentPosition() error %d", hr));
- goto on_error;
- }
-
- hr = IDirectSoundBuffer_Play(desc->lpDsPlayBuffer, 0, 0, DSBPLAY_LOOPING);
- if (FAILED(hr)) {
- PJ_LOG(1,(THIS_FILE, "DirectSound: Play() error %d", hr));
- goto on_error;
- }
- } else {
- hr = IDirectSoundCaptureBuffer_Start(desc->lpDsCaptureBuffer, DSCBSTART_LOOPING );
- if (FAILED(hr)) {
- PJ_LOG(1,(THIS_FILE, "DirectSound: Record() error %d", hr));
- goto on_error;
- }
- }
-
- /*
- * Reset initial positions.
- */
- byte_pos = 0xFFFFFFFF;
- sample_pos = 0;
-
- /*
- * Wait to get the first notification.
- */
- if (WaitForSingleObject(desc->hEvent, 100) != WAIT_OBJECT_0) {
- PJ_LOG(1,(THIS_FILE, "DirectSound: error getting notification"));
- goto on_error;
- }
-
-
- /* Get initial byte position. */
- if (desc->type == DSOUND_TYPE_PLAYER) {
- hr = IDirectSoundBuffer_GetCurrentPosition( desc->lpDsPlayBuffer,
- NULL, &byte_pos );
- if (FAILED(hr)) {
- PJ_LOG(1,(THIS_FILE, "DirectSound: unable to get "
- "position, err %d", hr));
- goto on_error;
- }
- } else {
- hr = IDirectSoundCaptureBuffer_GetCurrentPosition( desc->lpDsCaptureBuffer,
- NULL, &byte_pos );
- if (FAILED(hr)) {
- PJ_LOG(1,(THIS_FILE, "DirectSound: unable to get "
- "position, err %d", hr));
- goto on_error;
- }
- }
-
- /* Signal main thread that we're running. */
- assert( desc->hStartEvent );
- SetEvent( desc->hStartEvent );
-
- /*
- * Loop while not signalled to quit, wait for event object to be signalled
- * by DirectSound play buffer, then request for sound data from the
- * application and write it to DirectSound buffer.
- */
- do {
-
- /* Call callback to get sound data */
- if (desc->type == DSOUND_TYPE_PLAYER) {
- size = bytes_per_pkt;
- status = (*dev->play_cb)(dev, sample_pos, buffer, &size);
-
- /* Quit thread on error. */
- if (status != 0)
- break;
-
- /* Write zeroes when we've got nothing from application. */
- if (size == 0) {
- memset(buffer, 0, bytes_per_pkt);
- size = bytes_per_pkt;
- }
-
- /* Write to DirectSound buffer. */
- AppWriteDataToBuffer( desc->lpDsPlayBuffer, byte_pos,
- (LPBYTE)buffer, size);
-
- } else {
- /* Capture from DirectSound buffer. */
- size = bytes_per_pkt;
- if (AppReadDataFromBuffer( desc->lpDsCaptureBuffer, byte_pos,
- (LPBYTE)buffer, size)) {
-
- } else {
- memset(buffer, 0, size);
- }
-
- /* Call callback */
- status = (*dev->rec_cb)(dev, sample_pos, buffer, size);
-
- /* Quit thread on error. */
- if (status != 0)
- break;
- }
-
- /* Increment position. */
- byte_pos += size;
- if (byte_pos >= buffer_size)
- byte_pos -= buffer_size;
- sample_pos += samples_per_pkt;
-
- while (WaitForSingleObject(desc->hEvent, 500) != WAIT_OBJECT_0 &&
- (!desc->dwThreadQuitFlag))
- {
- Sleep(1);
- }
- } while (!desc->dwThreadQuitFlag);
-
-
- PJ_LOG(4,(THIS_FILE, "DirectSound: stopping.."));
-
- free(buffer);
- if (desc->type == DSOUND_TYPE_PLAYER) {
- IDirectSoundBuffer_Stop( desc->lpDsPlayBuffer );
- } else {
- IDirectSoundCaptureBuffer_Stop( desc->lpDsCaptureBuffer );
- }
- return 0;
-
-on_error:
- PJ_LOG(4,(THIS_FILE, "DirectSound play stopping"));
-
- if (buffer)
- free(buffer);
- if (desc->type == DSOUND_TYPE_PLAYER) {
- IDirectSoundBuffer_Stop( desc->lpDsPlayBuffer );
- } else {
- IDirectSoundCaptureBuffer_Stop( desc->lpDsCaptureBuffer );
- }
- desc->dwThreadQuitFlag = 1;
-
- /* Signal main thread that we failed to initialize */
- assert( desc->hStartEvent );
- SetEvent( desc->hStartEvent );
- return -1;
-}
-
-
-/*
- * Generic starter for play/record.
- */
-static pj_status_t dsound_dev_play_record( pj_snd_dev *dev,
- Direct_Sound_Descriptor *desc )
-{
- DWORD threadId;
- int op_type = desc->type;
- const char *op_name = (op_type == DSOUND_TYPE_PLAYER) ? "play" : "record";
- struct Thread_Param param;
-
- PJ_LOG(4,(THIS_FILE, "DirectSound %s()", op_name));
-
- /*
- * Create event for the thread to signal us that it is starting or
- * quitting during startup.
- */
- desc->hStartEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
- if (desc->hStartEvent == NULL) {
- PJ_LOG(1,(THIS_FILE, "DirectSound %s: unable to create event", op_name));
- return -1;
- }
-
- param.dev = dev;
- param.desc = desc;
-
- /*
- * Create thread to handle feeding up data to player/recorder.
- */
- desc->hThread = NULL;
- desc->dwThreadQuitFlag = 0;
- desc->hThread = CreateThread( NULL, 0, &dsound_dev_thread, &param,
- CREATE_SUSPENDED, &threadId);
- if (!desc->hThread) {
- PJ_LOG(1,(THIS_FILE, "DirectSound %s(): unable to create thread", op_name));
- return -1;
- }
-
- SetThreadPriority( desc->hThread, THREAD_PRIORITY_HIGHEST);
-
- /*
- * Resume thread.
- */
- if (ResumeThread(desc->hThread) == (DWORD)-1) {
- PJ_LOG(1,(THIS_FILE, "DirectSound %s(): unable to resume thread", op_name));
- goto on_error;
- }
-
- /*
- * Wait until we've got signal from the thread that it has successfully
- * started, or when it is quitting.
- */
- WaitForSingleObject( desc->hStartEvent, INFINITE);
-
- /* We can destroy the event now. */
- CloseHandle( desc->hStartEvent );
- desc->hStartEvent = NULL;
-
- /* Examine thread status. */
- if (desc->dwThreadQuitFlag != 0) {
- /* Thread failed to initialize */
- WaitForSingleObject(desc->hThread, INFINITE);
- CloseHandle(desc->hThread);
- desc->hThread = NULL;
- return -1;
- }
-
- return 0;
-
-on_error:
- TerminateThread(desc->hThread, -1);
- CloseHandle(desc->hThread);
- desc->hThread = NULL;
- return -1;
-}
-
-/*
- * Start playing.
- */
-static pj_status_t dsound_dev_play( pj_snd_dev *dev )
-{
- PJ_Direct_Sound_Device *dsDev = dev->device;
-
- assert(dsDev);
- if (!dsDev) {
- assert(0);
- return -1;
- }
-
- return dsound_dev_play_record( dev, &dsDev->playDesc );
-}
-
-/*
- * Start recording.
- */
-static pj_status_t dsound_dev_record( pj_snd_dev *dev )
-{
- PJ_Direct_Sound_Device *dsDev = dev->device;
-
- assert(dsDev);
- if (!dsDev) {
- assert(0);
- return -1;
- }
-
- return dsound_dev_play_record( dev, &dsDev->recDesc );
-}
-
-#ifdef _MSC_VER
-# pragma warning(pop)
-# pragma warning(disable: 4514) // unreferenced inline function has been removed
-#endif
+/* $Id$
+ *
+ */
+/*
+ * PJMEDIA - Multimedia over IP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifdef _MSC_VER
+//# pragma warning(disable: 4201) // non-standard extension: nameless struct/union
+# pragma warning(push, 3)
+#endif
+#include <pj/config.h>
+#include <pj/os.h>
+#include <pj/log.h>
+
+#include <dsound.h>
+#include <stdio.h>
+#include <assert.h>
+#include <pjmedia/sound.h>
+
+#define THIS_FILE "dsound.c"
+
+/*
+ * Constants
+ */
+#define PACKET_BUFFER_COUNT 4
+
+typedef struct PJ_Direct_Sound_Device PJ_Direct_Sound_Device;
+
+
+/*
+ * DirectSound Factory Operations
+ */
+static pj_status_t dsound_init(void);
+static const char *dsound_get_name(void);
+static pj_status_t dsound_destroy(void);
+static pj_status_t dsound_enum_devices(int *count, char *dev_names[]);
+static pj_status_t dsound_create_dev(const char *dev_name, pj_snd_dev *dev);
+static pj_status_t dsound_destroy_dev(pj_snd_dev *dev);
+
+
+/*
+ * DirectSound Device Operations
+ */
+static pj_status_t dsound_dev_open( pj_snd_dev *dev, pj_snd_role_t role );
+static pj_status_t dsound_dev_close( pj_snd_dev *dev );
+static pj_status_t dsound_dev_play( pj_snd_dev *dev );
+static pj_status_t dsound_dev_record( pj_snd_dev *dev );
+
+/*
+ * Utils.
+ */
+static pj_status_t dsound_destroy_dsound_dev( PJ_Direct_Sound_Device *dsDev );
+
+
+static pj_snd_dev_factory dsound_factory =
+{
+ &dsound_init,
+ &dsound_get_name,
+ &dsound_destroy,
+ &dsound_enum_devices,
+ &dsound_create_dev,
+ &dsound_destroy_dev
+};
+
+static struct pj_snd_dev_op dsound_dev_op =
+{
+ &dsound_dev_open,
+ &dsound_dev_close,
+ &dsound_dev_play,
+ &dsound_dev_record
+};
+
+#define DSOUND_TYPE_PLAYER 1
+#define DSOUND_TYPE_RECORDER 2
+
+typedef struct Direct_Sound_Descriptor
+{
+ int type;
+
+ LPDIRECTSOUND lpDsPlay;
+ LPDIRECTSOUNDBUFFER lpDsPlayBuffer;
+
+ LPDIRECTSOUNDCAPTURE lpDsCapture;
+ LPDIRECTSOUNDCAPTUREBUFFER lpDsCaptureBuffer;
+
+ LPDIRECTSOUNDNOTIFY lpDsNotify;
+ HANDLE hEvent;
+ HANDLE hThread;
+ HANDLE hStartEvent;
+ DWORD dwThreadQuitFlag;
+ pj_thread_desc thread_desc;
+ pj_thread_t *thread;
+} Direct_Sound_Descriptor;
+
+struct PJ_Direct_Sound_Device
+{
+ Direct_Sound_Descriptor playDesc;
+ Direct_Sound_Descriptor recDesc;
+};
+
+struct Thread_Param
+{
+ pj_snd_dev *dev;
+ Direct_Sound_Descriptor *desc;
+};
+
+PJ_DEF(pj_snd_dev_factory*) pj_dsound_get_factory()
+{
+ return &dsound_factory;
+}
+
+/*
+ * Init DirectSound.
+ */
+static pj_status_t dsound_init(void)
+{
+ /* Nothing to do. */
+ return 0;
+}
+
+/*
+ * Get the name of the factory.
+ */
+static const char *dsound_get_name(void)
+{
+ return "DirectSound";
+}
+
+/*
+ * Destroy DirectSound.
+ */
+static pj_status_t dsound_destroy(void)
+{
+ /* TODO: clean up devices in case application haven't done it. */
+ return 0;
+}
+
+/*
+ * Enum devices in the system.
+ */
+static pj_status_t dsound_enum_devices(int *count, char *dev_names[])
+{
+ dev_names[0] = "DirectSound Default Device";
+ *count = 1;
+ return 0;
+}
+
+/*
+ * Create DirectSound device.
+ */
+static pj_status_t dsound_create_dev(const char *dev_name, pj_snd_dev *dev)
+{
+ PJ_Direct_Sound_Device *dsDev;
+
+ /* TODO: create based on the name. */
+ PJ_TODO(DSOUND_CREATE_DEVICE_BY_NAME);
+
+ /* Create DirectSound structure. */
+ dsDev = malloc(sizeof(*dsDev));
+ if (!dsDev) {
+ PJ_LOG(1,(THIS_FILE, "No memory to allocate device!"));
+ return -1;
+ }
+ memset(dsDev, 0, sizeof(*dsDev));
+
+ /* Associate DirectSound device with device. */
+ dev->device = dsDev;
+ dev->op = &dsound_dev_op;
+
+ return 0;
+}
+
+/*
+ * Destroy DirectSound device.
+ */
+static pj_status_t dsound_destroy_dev( pj_snd_dev *dev )
+{
+ if (dev->device) {
+ free(dev->device);
+ dev->device = NULL;
+ }
+ return 0;
+}
+
+static void dsound_release_descriptor(Direct_Sound_Descriptor *desc)
+{
+ if (desc->lpDsNotify)
+ IDirectSoundNotify_Release( desc->lpDsNotify );
+
+ if (desc->hEvent)
+ CloseHandle(desc->hEvent);
+
+ if (desc->lpDsPlayBuffer)
+ IDirectSoundBuffer_Release( desc->lpDsPlayBuffer );
+
+ if (desc->lpDsPlay)
+ IDirectSound_Release( desc->lpDsPlay );
+
+ if (desc->lpDsCaptureBuffer)
+ IDirectSoundCaptureBuffer_Release(desc->lpDsCaptureBuffer);
+
+ if (desc->lpDsCapture)
+ IDirectSoundCapture_Release(desc->lpDsCapture);
+}
+
+/*
+ * Destroy DirectSound resources.
+ */
+static pj_status_t dsound_destroy_dsound_dev( PJ_Direct_Sound_Device *dsDev )
+{
+ dsound_release_descriptor( &dsDev->playDesc );
+ dsound_release_descriptor( &dsDev->recDesc );
+ memset(dsDev, 0, sizeof(*dsDev));
+ return 0;
+}
+
+static void init_waveformatex (PCMWAVEFORMAT *pcmwf, pj_snd_dev *dev)
+{
+ memset(pcmwf, 0, sizeof(PCMWAVEFORMAT));
+ pcmwf->wf.wFormatTag = WAVE_FORMAT_PCM;
+ pcmwf->wf.nChannels = 1;
+ pcmwf->wf.nSamplesPerSec = dev->param.samples_per_sec;
+ pcmwf->wf.nBlockAlign = dev->param.bytes_per_frame;
+ pcmwf->wf.nAvgBytesPerSec =
+ dev->param.samples_per_sec * dev->param.bytes_per_frame;
+ pcmwf->wBitsPerSample = dev->param.bits_per_sample;
+}
+
+/*
+ * Initialize DirectSound player device.
+ */
+static pj_status_t dsound_init_player (pj_snd_dev *dev)
+{
+ HRESULT hr;
+ HWND hwnd;
+ PCMWAVEFORMAT pcmwf;
+ DSBUFFERDESC dsbdesc;
+ DSBPOSITIONNOTIFY dsPosNotify[PACKET_BUFFER_COUNT];
+ unsigned i;
+ PJ_Direct_Sound_Device *dsDev = dev->device;
+
+ /*
+ * Check parameters.
+ */
+ if (dev->play_cb == NULL) {
+ assert(0);
+ return -1;
+ }
+ if (dev->device == NULL) {
+ assert(0);
+ return -1;
+ }
+
+ PJ_LOG(4,(THIS_FILE, "Creating DirectSound player device"));
+
+ /*
+ * Create DirectSound device.
+ */
+ hr = DirectSoundCreate(NULL, &dsDev->playDesc.lpDsPlay, NULL);
+ if (FAILED(hr))
+ goto on_error;
+
+ hwnd = GetForegroundWindow();
+ if (hwnd == NULL) {
+ hwnd = GetDesktopWindow();
+ }
+ hr = IDirectSound_SetCooperativeLevel( dsDev->playDesc.lpDsPlay, hwnd,
+ DSSCL_PRIORITY);
+ if FAILED(hr)
+ goto on_error;
+
+ /*
+ * Create DirectSound play buffer.
+ */
+ // Set up wave format structure.
+ init_waveformatex (&pcmwf, dev);
+
+ // Set up DSBUFFERDESC structure.
+ memset(&dsbdesc, 0, sizeof(DSBUFFERDESC));
+ dsbdesc.dwSize = sizeof(DSBUFFERDESC);
+ dsbdesc.dwFlags = DSBCAPS_CTRLVOLUME | DSBCAPS_CTRLPOSITIONNOTIFY |
+ DSBCAPS_GETCURRENTPOSITION2;
+
+ dsbdesc.dwBufferBytes =
+ (PACKET_BUFFER_COUNT * dev->param.bytes_per_frame *
+ dev->param.frames_per_packet);
+ dsbdesc.lpwfxFormat = (LPWAVEFORMATEX)&pcmwf;
+
+ // Create buffer.
+ hr = IDirectSound_CreateSoundBuffer(dsDev->playDesc.lpDsPlay, &dsbdesc,
+ &dsDev->playDesc.lpDsPlayBuffer, NULL);
+ if (FAILED(hr) )
+ goto on_error;
+
+ /*
+ * Create event for play notification.
+ */
+ dsDev->playDesc.hEvent = CreateEvent( NULL, FALSE, FALSE, NULL);
+ if (dsDev->playDesc.hEvent == NULL)
+ goto on_error;
+
+ /*
+ * Setup notification for play.
+ */
+ hr = IDirectSoundBuffer_QueryInterface( dsDev->playDesc.lpDsPlayBuffer,
+ &IID_IDirectSoundNotify,
+ (LPVOID *)&dsDev->playDesc.lpDsNotify);
+ if (FAILED(hr))
+ goto on_error;
+
+
+ for (i=0; i<PACKET_BUFFER_COUNT; ++i) {
+ dsPosNotify[i].dwOffset = i * dev->param.bytes_per_frame *
+ dev->param.frames_per_packet;
+ dsPosNotify[i].hEventNotify = dsDev->playDesc.hEvent;
+ }
+
+ hr = IDirectSoundNotify_SetNotificationPositions( dsDev->playDesc.lpDsNotify,
+ PACKET_BUFFER_COUNT,
+ dsPosNotify);
+ if (FAILED(hr))
+ goto on_error;
+
+ /* Done setting up play device. */
+ PJ_LOG(4,(THIS_FILE, "DirectSound player device created"));
+
+ return 0;
+
+on_error:
+ PJ_LOG(2,(THIS_FILE, "Error creating player device, hresult=0x%x", hr));
+ dsound_destroy_dsound_dev(dsDev);
+ return -1;
+}
+
+/*
+ * Initialize DirectSound recorder device
+ */
+static pj_status_t dsound_init_recorder (pj_snd_dev *dev)
+{
+ HRESULT hr;
+ PCMWAVEFORMAT pcmwf;
+ DSCBUFFERDESC dscbdesc;
+ DSBPOSITIONNOTIFY dsPosNotify[PACKET_BUFFER_COUNT];
+ unsigned i;
+ PJ_Direct_Sound_Device *dsDev = dev->device;
+
+ /*
+ * Check parameters.
+ */
+ if (dev->rec_cb == NULL) {
+ assert(0);
+ return -1;
+ }
+ if (dev->device == NULL) {
+ assert(0);
+ return -1;
+ }
+
+ PJ_LOG(4,(THIS_FILE, "Creating DirectSound recorder device"));
+
+ /*
+ * Creating recorder device.
+ */
+ hr = DirectSoundCaptureCreate(NULL, &dsDev->recDesc.lpDsCapture, NULL);
+ if (FAILED(hr))
+ goto on_error;
+
+ /* Init wave format */
+ init_waveformatex (&pcmwf, dev);
+
+ /*
+ * Setup capture buffer using sound buffer structure that was passed
+ * to play buffer creation earlier.
+ */
+ memset(&dscbdesc, 0, sizeof(DSCBUFFERDESC));
+ dscbdesc.dwSize = sizeof(DSCBUFFERDESC);
+ dscbdesc.dwFlags = DSCBCAPS_WAVEMAPPED ;
+ dscbdesc.dwBufferBytes =
+ (PACKET_BUFFER_COUNT * dev->param.bytes_per_frame *
+ dev->param.frames_per_packet);
+ dscbdesc.lpwfxFormat = (LPWAVEFORMATEX)&pcmwf;
+
+ hr = IDirectSoundCapture_CreateCaptureBuffer( dsDev->recDesc.lpDsCapture,
+ &dscbdesc,
+ &dsDev->recDesc.lpDsCaptureBuffer,
+ NULL);
+ if (FAILED(hr))
+ goto on_error;
+
+ /*
+ * Create event for play notification.
+ */
+ dsDev->recDesc.hEvent = CreateEvent( NULL, FALSE, FALSE, NULL);
+ if (dsDev->recDesc.hEvent == NULL)
+ goto on_error;
+
+ /*
+ * Setup notifications for recording.
+ */
+ hr = IDirectSoundCaptureBuffer_QueryInterface( dsDev->recDesc.lpDsCaptureBuffer,
+ &IID_IDirectSoundNotify,
+ (LPVOID *)&dsDev->recDesc.lpDsNotify);
+ if (FAILED(hr))
+ goto on_error;
+
+
+ for (i=0; i<PACKET_BUFFER_COUNT; ++i) {
+ dsPosNotify[i].dwOffset = i * dev->param.bytes_per_frame *
+ dev->param.frames_per_packet;
+ dsPosNotify[i].hEventNotify = dsDev->recDesc.hEvent;
+ }
+
+ hr = IDirectSoundNotify_SetNotificationPositions( dsDev->recDesc.lpDsNotify,
+ PACKET_BUFFER_COUNT,
+ dsPosNotify);
+ if (FAILED(hr))
+ goto on_error;
+
+ /* Done setting up recorder device. */
+ PJ_LOG(4,(THIS_FILE, "DirectSound recorder device created"));
+
+ return 0;
+
+on_error:
+ PJ_LOG(4,(THIS_FILE, "Error creating device, hresult=%d", hr));
+ dsound_destroy_dsound_dev(dsDev);
+ return -1;
+}
+
+/*
+ * Initialize DirectSound device.
+ */
+static pj_status_t dsound_dev_open( pj_snd_dev *dev, pj_snd_role_t role )
+{
+ PJ_Direct_Sound_Device *dsDev = dev->device;
+ pj_status_t status;
+
+ dsDev->playDesc.type = DSOUND_TYPE_PLAYER;
+ dsDev->recDesc.type = DSOUND_TYPE_RECORDER;
+
+ if (role & PJ_SOUND_PLAYER) {
+ status = dsound_init_player (dev);
+ if (status != 0)
+ return status;
+ }
+
+ if (role & PJ_SOUND_RECORDER) {
+ status = dsound_init_recorder (dev);
+ if (status != 0)
+ return status;
+ }
+
+ return 0;
+}
+
+/*
+ * Close DirectSound device.
+ */
+static pj_status_t dsound_dev_close( pj_snd_dev *dev )
+{
+ PJ_LOG(4,(THIS_FILE, "Closing DirectSound device"));
+
+ if (dev->device) {
+ PJ_Direct_Sound_Device *dsDev = dev->device;
+
+ if (dsDev->playDesc.hThread) {
+ PJ_LOG(4,(THIS_FILE, "Stopping DirectSound player"));
+ dsDev->playDesc.dwThreadQuitFlag = 1;
+ SetEvent(dsDev->playDesc.hEvent);
+ if (WaitForSingleObject(dsDev->playDesc.hThread, 1000) != WAIT_OBJECT_0) {
+ PJ_LOG(4,(THIS_FILE, "Timed out waiting player thread to quit"));
+ TerminateThread(dsDev->playDesc.hThread, -1);
+ IDirectSoundBuffer_Stop( dsDev->playDesc.lpDsPlayBuffer );
+ }
+
+ pj_thread_destroy (dsDev->playDesc.thread);
+ }
+
+ if (dsDev->recDesc.hThread) {
+ PJ_LOG(4,(THIS_FILE, "Stopping DirectSound recorder"));
+ dsDev->recDesc.dwThreadQuitFlag = 1;
+ SetEvent(dsDev->recDesc.hEvent);
+ if (WaitForSingleObject(dsDev->recDesc.hThread, 1000) != WAIT_OBJECT_0) {
+ PJ_LOG(4,(THIS_FILE, "Timed out waiting recorder thread to quit"));
+ TerminateThread(dsDev->recDesc.hThread, -1);
+ IDirectSoundCaptureBuffer_Stop( dsDev->recDesc.lpDsCaptureBuffer );
+ }
+
+ pj_thread_destroy (dsDev->recDesc.thread);
+ }
+
+ dsound_destroy_dsound_dev( dev->device );
+ dev->op = NULL;
+ }
+
+ PJ_LOG(4,(THIS_FILE, "DirectSound device closed"));
+ return 0;
+}
+
+static BOOL AppReadDataFromBuffer(LPDIRECTSOUNDCAPTUREBUFFER lpDsb, // The buffer.
+ DWORD dwOffset, // Our own write cursor.
+ LPBYTE lpbSoundData, // Start of our data.
+ DWORD dwSoundBytes) // Size of block to copy.
+{
+ LPVOID lpvPtr1;
+ DWORD dwBytes1;
+ LPVOID lpvPtr2;
+ DWORD dwBytes2;
+ HRESULT hr;
+
+ // Obtain memory address of write block. This will be in two parts
+ // if the block wraps around.
+
+ hr = IDirectSoundCaptureBuffer_Lock( lpDsb, dwOffset, dwSoundBytes, &lpvPtr1,
+ &dwBytes1, &lpvPtr2, &dwBytes2, 0);
+
+ if SUCCEEDED(hr) {
+ // Read from pointers.
+ CopyMemory(lpbSoundData, lpvPtr1, dwBytes1);
+ if (lpvPtr2 != NULL)
+ CopyMemory(lpbSoundData+dwBytes1, lpvPtr2, dwBytes2);
+
+ // Release the data back to DirectSound.
+ hr = IDirectSoundCaptureBuffer_Unlock(lpDsb, lpvPtr1, dwBytes1, lpvPtr2, dwBytes2);
+ if SUCCEEDED(hr)
+ return TRUE;
+ }
+
+ // Lock, Unlock, or Restore failed.
+ return FALSE;
+}
+
+
+static BOOL AppWriteDataToBuffer(LPDIRECTSOUNDBUFFER lpDsb, // The buffer.
+ DWORD dwOffset, // Our own write cursor.
+ LPBYTE lpbSoundData, // Start of our data.
+ DWORD dwSoundBytes) // Size of block to copy.
+{
+ LPVOID lpvPtr1;
+ DWORD dwBytes1;
+ LPVOID lpvPtr2;
+ DWORD dwBytes2;
+ HRESULT hr;
+
+ // Obtain memory address of write block. This will be in two parts
+ // if the block wraps around.
+
+ hr = IDirectSoundBuffer_Lock( lpDsb, dwOffset, dwSoundBytes, &lpvPtr1,
+ &dwBytes1, &lpvPtr2, &dwBytes2, 0);
+
+ // If the buffer was lost, restore and retry lock.
+ if (DSERR_BUFFERLOST == hr) {
+ IDirectSoundBuffer_Restore(lpDsb);
+ hr = IDirectSoundBuffer_Lock( lpDsb, dwOffset, dwSoundBytes,
+ &lpvPtr1, &dwBytes1, &lpvPtr2, &dwBytes2, 0);
+ }
+ if SUCCEEDED(hr) {
+ CopyMemory(lpvPtr1, lpbSoundData, dwBytes1);
+ if (NULL != lpvPtr2)
+ CopyMemory(lpvPtr2, lpbSoundData+dwBytes1, dwBytes2);
+
+ hr = IDirectSoundBuffer_Unlock(lpDsb, lpvPtr1, dwBytes1, lpvPtr2, dwBytes2);
+ if SUCCEEDED(hr)
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+
+/*
+ * Player thread.
+ */
+static DWORD WINAPI dsound_dev_thread(void *arg)
+{
+ struct Thread_Param *param = arg;
+ pj_snd_dev *dev = param->dev;
+ Direct_Sound_Descriptor *desc = param->desc;
+ unsigned bytes_per_pkt = dev->param.bytes_per_frame *
+ dev->param.frames_per_packet;
+ unsigned samples_per_pkt = dev->param.samples_per_frame *
+ dev->param.frames_per_packet;
+ void *buffer = NULL;
+ DWORD size;
+ pj_status_t status;
+ DWORD sample_pos;
+ DWORD byte_pos;
+ DWORD buffer_size =
+ (PACKET_BUFFER_COUNT * dev->param.bytes_per_frame *
+ dev->param.frames_per_packet);
+ HRESULT hr;
+
+ PJ_LOG(4,(THIS_FILE, "DirectSound thread starting"));
+
+ /* Allocate buffer for sound data */
+ buffer = malloc(bytes_per_pkt);
+ if (!buffer) {
+ PJ_LOG(1,(THIS_FILE, "Unable to allocate packet buffer!"));
+ return (DWORD)-1;
+ }
+
+ desc->thread = pj_thread_register ("dsound", desc->thread_desc);
+ if (desc->thread == NULL)
+ return (DWORD)-1;
+
+ /*
+ * Start playing or recording!
+ */
+ if (desc->type == DSOUND_TYPE_PLAYER) {
+ hr = IDirectSoundBuffer_SetCurrentPosition( desc->lpDsPlayBuffer, 0);
+ if (FAILED(hr)) {
+ PJ_LOG(1,(THIS_FILE, "DirectSound play: SetCurrentPosition() error %d", hr));
+ goto on_error;
+ }
+
+ hr = IDirectSoundBuffer_Play(desc->lpDsPlayBuffer, 0, 0, DSBPLAY_LOOPING);
+ if (FAILED(hr)) {
+ PJ_LOG(1,(THIS_FILE, "DirectSound: Play() error %d", hr));
+ goto on_error;
+ }
+ } else {
+ hr = IDirectSoundCaptureBuffer_Start(desc->lpDsCaptureBuffer, DSCBSTART_LOOPING );
+ if (FAILED(hr)) {
+ PJ_LOG(1,(THIS_FILE, "DirectSound: Record() error %d", hr));
+ goto on_error;
+ }
+ }
+
+ /*
+ * Reset initial positions.
+ */
+ byte_pos = 0xFFFFFFFF;
+ sample_pos = 0;
+
+ /*
+ * Wait to get the first notification.
+ */
+ if (WaitForSingleObject(desc->hEvent, 100) != WAIT_OBJECT_0) {
+ PJ_LOG(1,(THIS_FILE, "DirectSound: error getting notification"));
+ goto on_error;
+ }
+
+
+ /* Get initial byte position. */
+ if (desc->type == DSOUND_TYPE_PLAYER) {
+ hr = IDirectSoundBuffer_GetCurrentPosition( desc->lpDsPlayBuffer,
+ NULL, &byte_pos );
+ if (FAILED(hr)) {
+ PJ_LOG(1,(THIS_FILE, "DirectSound: unable to get "
+ "position, err %d", hr));
+ goto on_error;
+ }
+ } else {
+ hr = IDirectSoundCaptureBuffer_GetCurrentPosition( desc->lpDsCaptureBuffer,
+ NULL, &byte_pos );
+ if (FAILED(hr)) {
+ PJ_LOG(1,(THIS_FILE, "DirectSound: unable to get "
+ "position, err %d", hr));
+ goto on_error;
+ }
+ }
+
+ /* Signal main thread that we're running. */
+ assert( desc->hStartEvent );
+ SetEvent( desc->hStartEvent );
+
+ /*
+ * Loop while not signalled to quit, wait for event object to be signalled
+ * by DirectSound play buffer, then request for sound data from the
+ * application and write it to DirectSound buffer.
+ */
+ do {
+
+ /* Call callback to get sound data */
+ if (desc->type == DSOUND_TYPE_PLAYER) {
+ size = bytes_per_pkt;
+ status = (*dev->play_cb)(dev, sample_pos, buffer, &size);
+
+ /* Quit thread on error. */
+ if (status != 0)
+ break;
+
+ /* Write zeroes when we've got nothing from application. */
+ if (size == 0) {
+ memset(buffer, 0, bytes_per_pkt);
+ size = bytes_per_pkt;
+ }
+
+ /* Write to DirectSound buffer. */
+ AppWriteDataToBuffer( desc->lpDsPlayBuffer, byte_pos,
+ (LPBYTE)buffer, size);
+
+ } else {
+ /* Capture from DirectSound buffer. */
+ size = bytes_per_pkt;
+ if (AppReadDataFromBuffer( desc->lpDsCaptureBuffer, byte_pos,
+ (LPBYTE)buffer, size)) {
+
+ } else {
+ memset(buffer, 0, size);
+ }
+
+ /* Call callback */
+ status = (*dev->rec_cb)(dev, sample_pos, buffer, size);
+
+ /* Quit thread on error. */
+ if (status != 0)
+ break;
+ }
+
+ /* Increment position. */
+ byte_pos += size;
+ if (byte_pos >= buffer_size)
+ byte_pos -= buffer_size;
+ sample_pos += samples_per_pkt;
+
+ while (WaitForSingleObject(desc->hEvent, 500) != WAIT_OBJECT_0 &&
+ (!desc->dwThreadQuitFlag))
+ {
+ Sleep(1);
+ }
+ } while (!desc->dwThreadQuitFlag);
+
+
+ PJ_LOG(4,(THIS_FILE, "DirectSound: stopping.."));
+
+ free(buffer);
+ if (desc->type == DSOUND_TYPE_PLAYER) {
+ IDirectSoundBuffer_Stop( desc->lpDsPlayBuffer );
+ } else {
+ IDirectSoundCaptureBuffer_Stop( desc->lpDsCaptureBuffer );
+ }
+ return 0;
+
+on_error:
+ PJ_LOG(4,(THIS_FILE, "DirectSound play stopping"));
+
+ if (buffer)
+ free(buffer);
+ if (desc->type == DSOUND_TYPE_PLAYER) {
+ IDirectSoundBuffer_Stop( desc->lpDsPlayBuffer );
+ } else {
+ IDirectSoundCaptureBuffer_Stop( desc->lpDsCaptureBuffer );
+ }
+ desc->dwThreadQuitFlag = 1;
+
+ /* Signal main thread that we failed to initialize */
+ assert( desc->hStartEvent );
+ SetEvent( desc->hStartEvent );
+ return -1;
+}
+
+
+/*
+ * Generic starter for play/record.
+ */
+static pj_status_t dsound_dev_play_record( pj_snd_dev *dev,
+ Direct_Sound_Descriptor *desc )
+{
+ DWORD threadId;
+ int op_type = desc->type;
+ const char *op_name = (op_type == DSOUND_TYPE_PLAYER) ? "play" : "record";
+ struct Thread_Param param;
+
+ PJ_LOG(4,(THIS_FILE, "DirectSound %s()", op_name));
+
+ /*
+ * Create event for the thread to signal us that it is starting or
+ * quitting during startup.
+ */
+ desc->hStartEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
+ if (desc->hStartEvent == NULL) {
+ PJ_LOG(1,(THIS_FILE, "DirectSound %s: unable to create event", op_name));
+ return -1;
+ }
+
+ param.dev = dev;
+ param.desc = desc;
+
+ /*
+ * Create thread to handle feeding up data to player/recorder.
+ */
+ desc->hThread = NULL;
+ desc->dwThreadQuitFlag = 0;
+ desc->hThread = CreateThread( NULL, 0, &dsound_dev_thread, &param,
+ CREATE_SUSPENDED, &threadId);
+ if (!desc->hThread) {
+ PJ_LOG(1,(THIS_FILE, "DirectSound %s(): unable to create thread", op_name));
+ return -1;
+ }
+
+ SetThreadPriority( desc->hThread, THREAD_PRIORITY_HIGHEST);
+
+ /*
+ * Resume thread.
+ */
+ if (ResumeThread(desc->hThread) == (DWORD)-1) {
+ PJ_LOG(1,(THIS_FILE, "DirectSound %s(): unable to resume thread", op_name));
+ goto on_error;
+ }
+
+ /*
+ * Wait until we've got signal from the thread that it has successfully
+ * started, or when it is quitting.
+ */
+ WaitForSingleObject( desc->hStartEvent, INFINITE);
+
+ /* We can destroy the event now. */
+ CloseHandle( desc->hStartEvent );
+ desc->hStartEvent = NULL;
+
+ /* Examine thread status. */
+ if (desc->dwThreadQuitFlag != 0) {
+ /* Thread failed to initialize */
+ WaitForSingleObject(desc->hThread, INFINITE);
+ CloseHandle(desc->hThread);
+ desc->hThread = NULL;
+ return -1;
+ }
+
+ return 0;
+
+on_error:
+ TerminateThread(desc->hThread, -1);
+ CloseHandle(desc->hThread);
+ desc->hThread = NULL;
+ return -1;
+}
+
+/*
+ * Start playing.
+ */
+static pj_status_t dsound_dev_play( pj_snd_dev *dev )
+{
+ PJ_Direct_Sound_Device *dsDev = dev->device;
+
+ assert(dsDev);
+ if (!dsDev) {
+ assert(0);
+ return -1;
+ }
+
+ return dsound_dev_play_record( dev, &dsDev->playDesc );
+}
+
+/*
+ * Start recording.
+ */
+static pj_status_t dsound_dev_record( pj_snd_dev *dev )
+{
+ PJ_Direct_Sound_Device *dsDev = dev->device;
+
+ assert(dsDev);
+ if (!dsDev) {
+ assert(0);
+ return -1;
+ }
+
+ return dsound_dev_play_record( dev, &dsDev->recDesc );
+}
+
+#ifdef _MSC_VER
+# pragma warning(pop)
+# pragma warning(disable: 4514) // unreferenced inline function has been removed
+#endif
diff --git a/pjmedia/src/pjmedia/g711.c b/pjmedia/src/pjmedia/g711.c
index e8ff9684..e953170e 100644
--- a/pjmedia/src/pjmedia/g711.c
+++ b/pjmedia/src/pjmedia/g711.c
@@ -1,602 +1,623 @@
-/* $Id$
- *
- */
-/* This file contains file from Sun Microsystems, Inc, with the complete
- * copyright notice in the second half of this file.
- */
-#include <pjmedia/codec.h>
-#include <pj/pool.h>
-#include <pj/string.h>
-#include <string.h> /* memset */
-
-#define G711_BPS 64000
-#define G711_CODEC_CNT 0 /* number of codec to preallocate in memory */
-
-/* These are the only public functions exported to applications */
-PJ_DECL(pj_status_t) g711_init_factory (pj_codec_factory *factory, pj_pool_t *pool);
-PJ_DECL(pj_status_t) g711_deinit_factory (pj_codec_factory *factory);
-
-/* Algorithm prototypes. */
-static unsigned char linear2alaw(int pcm_val); /* 2's complement (16-bit range) */
-static int alaw2linear(unsigned char a_val);
-static unsigned char linear2ulaw(int pcm_val);
-static int ulaw2linear(unsigned char u_val);
-
-/* Prototypes for G711 factory */
-static pj_status_t g711_match_id( pj_codec_factory *factory, const pj_codec_id *id );
-static pj_status_t g711_default_attr( pj_codec_factory *factory, const pj_codec_id *id, pj_codec_attr *attr );
-static unsigned g711_enum_codecs (pj_codec_factory *factory, unsigned count, pj_codec_id codecs[]);
-static pj_codec* g711_alloc_codec( pj_codec_factory *factory, const pj_codec_id *id);
-static void g711_dealloc_codec( pj_codec_factory *factory, pj_codec *codec );
-
-/* Prototypes for G711 implementation. */
-static pj_status_t g711_codec_default_attr (pj_codec *codec, pj_codec_attr *attr);
-static pj_status_t g711_init( pj_codec *codec, pj_pool_t *pool );
-static pj_status_t g711_open( pj_codec *codec, pj_codec_attr *attr );
-static pj_status_t g711_close( pj_codec *codec );
-static pj_status_t g711_encode( pj_codec *codec, const struct pj_audio_frame *input,
- unsigned output_buf_len, struct pj_audio_frame *output);
-static pj_status_t g711_decode( pj_codec *codec, const struct pj_audio_frame *input,
- unsigned output_buf_len, struct pj_audio_frame *output);
-
-/* Definition for G711 codec operations. */
-static pj_codec_op g711_op =
-{
- &g711_codec_default_attr ,
- &g711_init,
- &g711_open,
- &g711_close,
- &g711_encode,
- &g711_decode
-};
-
-/* Definition for G711 codec factory operations. */
-static pj_codec_factory_op g711_factory_op =
-{
- &g711_match_id,
- &g711_default_attr,
- &g711_enum_codecs,
- &g711_alloc_codec,
- &g711_dealloc_codec
-};
-
-/* G711 factory private data */
-struct g711_factory_private
-{
- pj_pool_t *pool;
- pj_codec codec_list;
-};
-
-/* G711 codec private data. */
-struct g711_private
-{
- unsigned pt;
-};
-
-
-PJ_DEF(pj_status_t) g711_init_factory (pj_codec_factory *factory, pj_pool_t *pool)
-{
- struct g711_factory_private *priv;
- //enum { CODEC_MEM_SIZE = sizeof(pj_codec) + sizeof(struct g711_private) + 4 };
-
- /* Create pool. */
- /*
- pool = pj_pool_pool_create_pool(pp, "g711ftry",
- G711_CODEC_CNT*CODEC_MEM_SIZE +
- sizeof(struct g711_factory_private),
- CODEC_MEM_SIZE, NULL);
- if (!pool)
- return -1;
- */
-
- priv = pj_pool_alloc(pool, sizeof(struct g711_factory_private));
- if (!priv)
- return -1;
-
- factory->factory_data = priv;
- factory->op = &g711_factory_op;
-
- priv->pool = pool;
- pj_list_init(&priv->codec_list);
- return 0;
-}
-
-PJ_DEF(pj_status_t) g711_deinit_factory (pj_codec_factory *factory)
-{
- struct g711_factory_private *priv = factory->factory_data;
-
- /* Invalidate member to help detect errors */
- priv->pool = NULL;
- priv->codec_list.next = priv->codec_list.prev = NULL;
- return 0;
-}
-
-static pj_status_t g711_match_id( pj_codec_factory *factory, const pj_codec_id *id )
-{
- PJ_UNUSED_ARG(factory)
-
- /* It's sufficient to check payload type only. */
- return (id->pt==PJ_RTP_PT_PCMU || id->pt==PJ_RTP_PT_PCMA) ? 0 : -1;
-}
-
-static pj_status_t g711_default_attr (pj_codec_factory *factory,
- const pj_codec_id *id,
- pj_codec_attr *attr )
-{
- PJ_UNUSED_ARG(factory)
-
- memset(attr, 0, sizeof(pj_codec_attr));
- attr->sample_rate = 8000;
- attr->avg_bps = G711_BPS;
- attr->pcm_bits_per_sample = 16;
- attr->ptime = 20;
- attr->pt = id->pt;
-
- /* Default all flag bits disabled. */
-
- return PJ_SUCCESS;
-}
-
-static unsigned g711_enum_codecs (pj_codec_factory *factory,
- unsigned count, pj_codec_id codecs[])
-{
- PJ_UNUSED_ARG(factory)
-
- if (count > 0) {
- codecs[0].type = PJ_MEDIA_TYPE_AUDIO;
- codecs[0].pt = PJ_RTP_PT_PCMU;
- codecs[0].encoding_name = pj_str("PCMU");
- codecs[0].sample_rate = 8000;
- }
- if (count > 1) {
- codecs[1].type = PJ_MEDIA_TYPE_AUDIO;
- codecs[1].pt = PJ_RTP_PT_PCMA;
- codecs[1].encoding_name = pj_str("PCMA");
- codecs[1].sample_rate = 8000;
- }
-
- return 2;
-}
-
-static pj_codec *g711_alloc_codec( pj_codec_factory *factory, const pj_codec_id *id)
-{
- struct g711_factory_private *priv = factory->factory_data;
- pj_codec *codec = NULL;
-
- /* Allocate new codec if no more is available */
- if (pj_list_empty(&priv->codec_list)) {
- struct g711_private *codec_priv;
-
- codec = pj_pool_alloc(priv->pool, sizeof(pj_codec));
- codec_priv = pj_pool_alloc(priv->pool, sizeof(struct g711_private));
- if (!codec || !codec_priv)
- return NULL;
-
- codec_priv->pt = id->pt;
-
- codec->factory = factory;
- codec->op = &g711_op;
- codec->codec_data = codec_priv;
- } else {
- codec = priv->codec_list.next;
- pj_list_erase(codec);
- }
-
- /* Zero the list, for error detection in g711_dealloc_codec */
- codec->next = codec->prev = NULL;
-
- return codec;
-}
-
-static void g711_dealloc_codec( pj_codec_factory *factory, pj_codec *codec )
-{
- struct g711_factory_private *priv = factory->factory_data;
-
- /* Check that this node has not been deallocated before */
- pj_assert (codec->next==NULL && codec->prev==NULL);
- if (codec->next!=NULL || codec->prev!=NULL) {
- return;
- }
-
- /* Insert at the back of the list */
- pj_list_insert_before(&priv->codec_list, codec);
-}
-
-static pj_status_t g711_codec_default_attr (pj_codec *codec, pj_codec_attr *attr)
-{
- struct g711_private *priv = codec->codec_data;
- pj_codec_id id;
-
- id.pt = priv->pt;
- return g711_default_attr (NULL, &id, attr);
-}
-
-static pj_status_t g711_init( pj_codec *codec, pj_pool_t *pool )
-{
- /* There's nothing to do here really */
- PJ_UNUSED_ARG(codec)
- PJ_UNUSED_ARG(pool)
-
- return PJ_SUCCESS;
-}
-
-static pj_status_t g711_open( pj_codec *codec, pj_codec_attr *attr )
-{
- struct g711_private *priv = codec->codec_data;
- priv->pt = attr->pt;
- return PJ_SUCCESS;
-}
-
-static pj_status_t g711_close( pj_codec *codec )
-{
- PJ_UNUSED_ARG(codec);
- /* Nothing to do */
- return PJ_SUCCESS;
-}
-
-static pj_status_t g711_encode( pj_codec *codec, const struct pj_audio_frame *input,
- unsigned output_buf_len, struct pj_audio_frame *output)
-{
- pj_int16_t *samples = (pj_int16_t*) input->buf;
- struct g711_private *priv = codec->codec_data;
-
- /* Check output buffer length */
- if (output_buf_len < input->size / 2)
- return -1;
-
- /* Encode */
- if (priv->pt == PJ_RTP_PT_PCMA) {
- unsigned i;
- pj_uint8_t *dst = output->buf;
-
- for (i=0; i!=input->size/2; ++i, ++dst) {
- *dst = linear2alaw(samples[i]);
- }
- } else if (priv->pt == PJ_RTP_PT_PCMU) {
- unsigned i;
- pj_uint8_t *dst = output->buf;
-
- for (i=0; i!=input->size/2; ++i, ++dst) {
- *dst = linear2ulaw(samples[i]);
- }
-
- } else {
- return -1;
- }
-
- output->type = PJ_AUDIO_FRAME_AUDIO;
- output->size = input->size / 2;
-
- return 0;
-}
-
-static pj_status_t g711_decode( pj_codec *codec, const struct pj_audio_frame *input,
- unsigned output_buf_len, struct pj_audio_frame *output)
-{
- struct g711_private *priv = codec->codec_data;
-
- /* Check output buffer length */
- if (output_buf_len < input->size * 2)
- return -1;
-
- /* Decode */
- if (priv->pt == PJ_RTP_PT_PCMA) {
- unsigned i;
- pj_uint8_t *src = input->buf;
- pj_uint16_t *dst = output->buf;
-
- for (i=0; i!=input->size; ++i) {
- *dst++ = (pj_uint16_t) alaw2linear(*src++);
- }
- } else if (priv->pt == PJ_RTP_PT_PCMU) {
- unsigned i;
- pj_uint8_t *src = input->buf;
- pj_uint16_t *dst = output->buf;
-
- for (i=0; i!=input->size; ++i) {
- *dst++ = (pj_uint16_t) ulaw2linear(*src++);
- }
-
- } else {
- return -1;
- }
-
- output->type = PJ_AUDIO_FRAME_AUDIO;
- output->size = input->size * 2;
-
- return 0;
-}
-
-
-/*
- * This source code is a product of Sun Microsystems, Inc. and is provided
- * for unrestricted use. Users may copy or modify this source code without
- * charge.
- *
- * SUN SOURCE CODE IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING
- * THE WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
- *
- * Sun source code is provided with no support and without any obligation on
- * the part of Sun Microsystems, Inc. to assist in its use, correction,
- * modification or enhancement.
- *
- * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
- * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY THIS SOFTWARE
- * OR ANY PART THEREOF.
- *
- * In no event will Sun Microsystems, Inc. be liable for any lost revenue
- * or profits or other special, indirect and consequential damages, even if
- * Sun has been advised of the possibility of such damages.
- *
- * Sun Microsystems, Inc.
- * 2550 Garcia Avenue
- * Mountain View, California 94043
- */
-
-
-
-#ifdef _MSC_VER
-# pragma warning ( disable: 4244 ) /* Conversion from int to char etc */
-#endif
-
-/*
- * g711.c
- *
- * u-law, A-law and linear PCM conversions.
- */
-#define SIGN_BIT (0x80) /* Sign bit for a A-law byte. */
-#define QUANT_MASK (0xf) /* Quantization field mask. */
-#define NSEGS (8) /* Number of A-law segments. */
-#define SEG_SHIFT (4) /* Left shift for segment number. */
-#define SEG_MASK (0x70) /* Segment field mask. */
-
-static short seg_end[8] = {0xFF, 0x1FF, 0x3FF, 0x7FF,
- 0xFFF, 0x1FFF, 0x3FFF, 0x7FFF};
-
-/* copy from CCITT G.711 specifications */
-static unsigned char _u2a[128] = { /* u- to A-law conversions */
- 1, 1, 2, 2, 3, 3, 4, 4,
- 5, 5, 6, 6, 7, 7, 8, 8,
- 9, 10, 11, 12, 13, 14, 15, 16,
- 17, 18, 19, 20, 21, 22, 23, 24,
- 25, 27, 29, 31, 33, 34, 35, 36,
- 37, 38, 39, 40, 41, 42, 43, 44,
- 46, 48, 49, 50, 51, 52, 53, 54,
- 55, 56, 57, 58, 59, 60, 61, 62,
- 64, 65, 66, 67, 68, 69, 70, 71,
- 72, 73, 74, 75, 76, 77, 78, 79,
- 81, 82, 83, 84, 85, 86, 87, 88,
- 89, 90, 91, 92, 93, 94, 95, 96,
- 97, 98, 99, 100, 101, 102, 103, 104,
- 105, 106, 107, 108, 109, 110, 111, 112,
- 113, 114, 115, 116, 117, 118, 119, 120,
- 121, 122, 123, 124, 125, 126, 127, 128};
-
-static unsigned char _a2u[128] = { /* A- to u-law conversions */
- 1, 3, 5, 7, 9, 11, 13, 15,
- 16, 17, 18, 19, 20, 21, 22, 23,
- 24, 25, 26, 27, 28, 29, 30, 31,
- 32, 32, 33, 33, 34, 34, 35, 35,
- 36, 37, 38, 39, 40, 41, 42, 43,
- 44, 45, 46, 47, 48, 48, 49, 49,
- 50, 51, 52, 53, 54, 55, 56, 57,
- 58, 59, 60, 61, 62, 63, 64, 64,
- 65, 66, 67, 68, 69, 70, 71, 72,
- 73, 74, 75, 76, 77, 78, 79, 79,
- 80, 81, 82, 83, 84, 85, 86, 87,
- 88, 89, 90, 91, 92, 93, 94, 95,
- 96, 97, 98, 99, 100, 101, 102, 103,
- 104, 105, 106, 107, 108, 109, 110, 111,
- 112, 113, 114, 115, 116, 117, 118, 119,
- 120, 121, 122, 123, 124, 125, 126, 127};
-
-static int
-search(
- int val,
- short *table,
- int size)
-{
- int i;
-
- for (i = 0; i < size; i++) {
- if (val <= *table++)
- return (i);
- }
- return (size);
-}
-
-/*
- * linear2alaw() - Convert a 16-bit linear PCM value to 8-bit A-law
- *
- * linear2alaw() accepts an 16-bit integer and encodes it as A-law data.
- *
- * Linear Input Code Compressed Code
- * ------------------------ ---------------
- * 0000000wxyza 000wxyz
- * 0000001wxyza 001wxyz
- * 000001wxyzab 010wxyz
- * 00001wxyzabc 011wxyz
- * 0001wxyzabcd 100wxyz
- * 001wxyzabcde 101wxyz
- * 01wxyzabcdef 110wxyz
- * 1wxyzabcdefg 111wxyz
- *
- * For further information see John C. Bellamy's Digital Telephony, 1982,
- * John Wiley & Sons, pps 98-111 and 472-476.
- */
-static unsigned char
-linear2alaw(
- int pcm_val) /* 2's complement (16-bit range) */
-{
- int mask;
- int seg;
- unsigned char aval;
-
- if (pcm_val >= 0) {
- mask = 0xD5; /* sign (7th) bit = 1 */
- } else {
- mask = 0x55; /* sign bit = 0 */
- pcm_val = -pcm_val - 8;
- }
-
- /* Convert the scaled magnitude to segment number. */
- seg = search(pcm_val, seg_end, 8);
-
- /* Combine the sign, segment, and quantization bits. */
-
- if (seg >= 8) /* out of range, return maximum value. */
- return (0x7F ^ mask);
- else {
- aval = seg << SEG_SHIFT;
- if (seg < 2)
- aval |= (pcm_val >> 4) & QUANT_MASK;
- else
- aval |= (pcm_val >> (seg + 3)) & QUANT_MASK;
- return (aval ^ mask);
- }
-}
-
-/*
- * alaw2linear() - Convert an A-law value to 16-bit linear PCM
- *
- */
-static int
-alaw2linear(
- unsigned char a_val)
-{
- int t;
- int seg;
-
- a_val ^= 0x55;
-
- t = (a_val & QUANT_MASK) << 4;
- seg = ((unsigned)a_val & SEG_MASK) >> SEG_SHIFT;
- switch (seg) {
- case 0:
- t += 8;
- break;
- case 1:
- t += 0x108;
- break;
- default:
- t += 0x108;
- t <<= seg - 1;
- }
- return ((a_val & SIGN_BIT) ? t : -t);
-}
-
-#define BIAS (0x84) /* Bias for linear code. */
-
-/*
- * linear2ulaw() - Convert a linear PCM value to u-law
- *
- * In order to simplify the encoding process, the original linear magnitude
- * is biased by adding 33 which shifts the encoding range from (0 - 8158) to
- * (33 - 8191). The result can be seen in the following encoding table:
- *
- * Biased Linear Input Code Compressed Code
- * ------------------------ ---------------
- * 00000001wxyza 000wxyz
- * 0000001wxyzab 001wxyz
- * 000001wxyzabc 010wxyz
- * 00001wxyzabcd 011wxyz
- * 0001wxyzabcde 100wxyz
- * 001wxyzabcdef 101wxyz
- * 01wxyzabcdefg 110wxyz
- * 1wxyzabcdefgh 111wxyz
- *
- * Each biased linear code has a leading 1 which identifies the segment
- * number. The value of the segment number is equal to 7 minus the number
- * of leading 0's. The quantization interval is directly available as the
- * four bits wxyz. * The trailing bits (a - h) are ignored.
- *
- * Ordinarily the complement of the resulting code word is used for
- * transmission, and so the code word is complemented before it is returned.
- *
- * For further information see John C. Bellamy's Digital Telephony, 1982,
- * John Wiley & Sons, pps 98-111 and 472-476.
- */
-static unsigned char
-linear2ulaw(
- int pcm_val) /* 2's complement (16-bit range) */
-{
- int mask;
- int seg;
- unsigned char uval;
-
- /* Get the sign and the magnitude of the value. */
- if (pcm_val < 0) {
- pcm_val = BIAS - pcm_val;
- mask = 0x7F;
- } else {
- pcm_val += BIAS;
- mask = 0xFF;
- }
-
- /* Convert the scaled magnitude to segment number. */
- seg = search(pcm_val, seg_end, 8);
-
- /*
- * Combine the sign, segment, quantization bits;
- * and complement the code word.
- */
- if (seg >= 8) /* out of range, return maximum value. */
- return (0x7F ^ mask);
- else {
- uval = (seg << 4) | ((pcm_val >> (seg + 3)) & 0xF);
- return (uval ^ mask);
- }
-
-}
-
-/*
- * ulaw2linear() - Convert a u-law value to 16-bit linear PCM
- *
- * First, a biased linear code is derived from the code word. An unbiased
- * output can then be obtained by subtracting 33 from the biased code.
- *
- * Note that this function expects to be passed the complement of the
- * original code word. This is in keeping with ISDN conventions.
- */
-static int
-ulaw2linear(
- unsigned char u_val)
-{
- int t;
-
- /* Complement to obtain normal u-law value. */
- u_val = ~u_val;
-
- /*
- * Extract and bias the quantization bits. Then
- * shift up by the segment number and subtract out the bias.
- */
- t = ((u_val & QUANT_MASK) << 3) + BIAS;
- t <<= ((unsigned)u_val & SEG_MASK) >> SEG_SHIFT;
-
- return ((u_val & SIGN_BIT) ? (BIAS - t) : (t - BIAS));
-}
-
-/* A-law to u-law conversion */
-unsigned char
-alaw2ulaw(
- unsigned char aval)
-{
- aval &= 0xff;
- return ((aval & 0x80) ? (0xFF ^ _a2u[aval ^ 0xD5]) :
- (0x7F ^ _a2u[aval ^ 0x55]));
-}
-
-/* u-law to A-law conversion */
-unsigned char
-ulaw2alaw(
- unsigned char uval)
-{
- uval &= 0xff;
- return ((uval & 0x80) ? (0xD5 ^ (_u2a[0xFF ^ uval] - 1)) :
- (0x55 ^ (_u2a[0x7F ^ uval] - 1)));
-}
-
-
-
+/* $Id$
+ *
+ */
+/*
+ * PJMEDIA - Multimedia over IP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+/* This file contains file from Sun Microsystems, Inc, with the complete
+ * copyright notice in the second half of this file.
+ */
+#include <pjmedia/codec.h>
+#include <pj/pool.h>
+#include <pj/string.h>
+#include <string.h> /* memset */
+
+#define G711_BPS 64000
+#define G711_CODEC_CNT 0 /* number of codec to preallocate in memory */
+
+/* These are the only public functions exported to applications */
+PJ_DECL(pj_status_t) g711_init_factory (pj_codec_factory *factory, pj_pool_t *pool);
+PJ_DECL(pj_status_t) g711_deinit_factory (pj_codec_factory *factory);
+
+/* Algorithm prototypes. */
+static unsigned char linear2alaw(int pcm_val); /* 2's complement (16-bit range) */
+static int alaw2linear(unsigned char a_val);
+static unsigned char linear2ulaw(int pcm_val);
+static int ulaw2linear(unsigned char u_val);
+
+/* Prototypes for G711 factory */
+static pj_status_t g711_match_id( pj_codec_factory *factory, const pj_codec_id *id );
+static pj_status_t g711_default_attr( pj_codec_factory *factory, const pj_codec_id *id, pj_codec_attr *attr );
+static unsigned g711_enum_codecs (pj_codec_factory *factory, unsigned count, pj_codec_id codecs[]);
+static pj_codec* g711_alloc_codec( pj_codec_factory *factory, const pj_codec_id *id);
+static void g711_dealloc_codec( pj_codec_factory *factory, pj_codec *codec );
+
+/* Prototypes for G711 implementation. */
+static pj_status_t g711_codec_default_attr (pj_codec *codec, pj_codec_attr *attr);
+static pj_status_t g711_init( pj_codec *codec, pj_pool_t *pool );
+static pj_status_t g711_open( pj_codec *codec, pj_codec_attr *attr );
+static pj_status_t g711_close( pj_codec *codec );
+static pj_status_t g711_encode( pj_codec *codec, const struct pj_audio_frame *input,
+ unsigned output_buf_len, struct pj_audio_frame *output);
+static pj_status_t g711_decode( pj_codec *codec, const struct pj_audio_frame *input,
+ unsigned output_buf_len, struct pj_audio_frame *output);
+
+/* Definition for G711 codec operations. */
+static pj_codec_op g711_op =
+{
+ &g711_codec_default_attr ,
+ &g711_init,
+ &g711_open,
+ &g711_close,
+ &g711_encode,
+ &g711_decode
+};
+
+/* Definition for G711 codec factory operations. */
+static pj_codec_factory_op g711_factory_op =
+{
+ &g711_match_id,
+ &g711_default_attr,
+ &g711_enum_codecs,
+ &g711_alloc_codec,
+ &g711_dealloc_codec
+};
+
+/* G711 factory private data */
+struct g711_factory_private
+{
+ pj_pool_t *pool;
+ pj_codec codec_list;
+};
+
+/* G711 codec private data. */
+struct g711_private
+{
+ unsigned pt;
+};
+
+
+PJ_DEF(pj_status_t) g711_init_factory (pj_codec_factory *factory, pj_pool_t *pool)
+{
+ struct g711_factory_private *priv;
+ //enum { CODEC_MEM_SIZE = sizeof(pj_codec) + sizeof(struct g711_private) + 4 };
+
+ /* Create pool. */
+ /*
+ pool = pj_pool_pool_create_pool(pp, "g711ftry",
+ G711_CODEC_CNT*CODEC_MEM_SIZE +
+ sizeof(struct g711_factory_private),
+ CODEC_MEM_SIZE, NULL);
+ if (!pool)
+ return -1;
+ */
+
+ priv = pj_pool_alloc(pool, sizeof(struct g711_factory_private));
+ if (!priv)
+ return -1;
+
+ factory->factory_data = priv;
+ factory->op = &g711_factory_op;
+
+ priv->pool = pool;
+ pj_list_init(&priv->codec_list);
+ return 0;
+}
+
+PJ_DEF(pj_status_t) g711_deinit_factory (pj_codec_factory *factory)
+{
+ struct g711_factory_private *priv = factory->factory_data;
+
+ /* Invalidate member to help detect errors */
+ priv->pool = NULL;
+ priv->codec_list.next = priv->codec_list.prev = NULL;
+ return 0;
+}
+
+static pj_status_t g711_match_id( pj_codec_factory *factory, const pj_codec_id *id )
+{
+ PJ_UNUSED_ARG(factory)
+
+ /* It's sufficient to check payload type only. */
+ return (id->pt==PJ_RTP_PT_PCMU || id->pt==PJ_RTP_PT_PCMA) ? 0 : -1;
+}
+
+static pj_status_t g711_default_attr (pj_codec_factory *factory,
+ const pj_codec_id *id,
+ pj_codec_attr *attr )
+{
+ PJ_UNUSED_ARG(factory)
+
+ memset(attr, 0, sizeof(pj_codec_attr));
+ attr->sample_rate = 8000;
+ attr->avg_bps = G711_BPS;
+ attr->pcm_bits_per_sample = 16;
+ attr->ptime = 20;
+ attr->pt = id->pt;
+
+ /* Default all flag bits disabled. */
+
+ return PJ_SUCCESS;
+}
+
+static unsigned g711_enum_codecs (pj_codec_factory *factory,
+ unsigned count, pj_codec_id codecs[])
+{
+ PJ_UNUSED_ARG(factory)
+
+ if (count > 0) {
+ codecs[0].type = PJ_MEDIA_TYPE_AUDIO;
+ codecs[0].pt = PJ_RTP_PT_PCMU;
+ codecs[0].encoding_name = pj_str("PCMU");
+ codecs[0].sample_rate = 8000;
+ }
+ if (count > 1) {
+ codecs[1].type = PJ_MEDIA_TYPE_AUDIO;
+ codecs[1].pt = PJ_RTP_PT_PCMA;
+ codecs[1].encoding_name = pj_str("PCMA");
+ codecs[1].sample_rate = 8000;
+ }
+
+ return 2;
+}
+
+static pj_codec *g711_alloc_codec( pj_codec_factory *factory, const pj_codec_id *id)
+{
+ struct g711_factory_private *priv = factory->factory_data;
+ pj_codec *codec = NULL;
+
+ /* Allocate new codec if no more is available */
+ if (pj_list_empty(&priv->codec_list)) {
+ struct g711_private *codec_priv;
+
+ codec = pj_pool_alloc(priv->pool, sizeof(pj_codec));
+ codec_priv = pj_pool_alloc(priv->pool, sizeof(struct g711_private));
+ if (!codec || !codec_priv)
+ return NULL;
+
+ codec_priv->pt = id->pt;
+
+ codec->factory = factory;
+ codec->op = &g711_op;
+ codec->codec_data = codec_priv;
+ } else {
+ codec = priv->codec_list.next;
+ pj_list_erase(codec);
+ }
+
+ /* Zero the list, for error detection in g711_dealloc_codec */
+ codec->next = codec->prev = NULL;
+
+ return codec;
+}
+
+static void g711_dealloc_codec( pj_codec_factory *factory, pj_codec *codec )
+{
+ struct g711_factory_private *priv = factory->factory_data;
+
+ /* Check that this node has not been deallocated before */
+ pj_assert (codec->next==NULL && codec->prev==NULL);
+ if (codec->next!=NULL || codec->prev!=NULL) {
+ return;
+ }
+
+ /* Insert at the back of the list */
+ pj_list_insert_before(&priv->codec_list, codec);
+}
+
+static pj_status_t g711_codec_default_attr (pj_codec *codec, pj_codec_attr *attr)
+{
+ struct g711_private *priv = codec->codec_data;
+ pj_codec_id id;
+
+ id.pt = priv->pt;
+ return g711_default_attr (NULL, &id, attr);
+}
+
+static pj_status_t g711_init( pj_codec *codec, pj_pool_t *pool )
+{
+ /* There's nothing to do here really */
+ PJ_UNUSED_ARG(codec)
+ PJ_UNUSED_ARG(pool)
+
+ return PJ_SUCCESS;
+}
+
+static pj_status_t g711_open( pj_codec *codec, pj_codec_attr *attr )
+{
+ struct g711_private *priv = codec->codec_data;
+ priv->pt = attr->pt;
+ return PJ_SUCCESS;
+}
+
+static pj_status_t g711_close( pj_codec *codec )
+{
+ PJ_UNUSED_ARG(codec);
+ /* Nothing to do */
+ return PJ_SUCCESS;
+}
+
+static pj_status_t g711_encode( pj_codec *codec, const struct pj_audio_frame *input,
+ unsigned output_buf_len, struct pj_audio_frame *output)
+{
+ pj_int16_t *samples = (pj_int16_t*) input->buf;
+ struct g711_private *priv = codec->codec_data;
+
+ /* Check output buffer length */
+ if (output_buf_len < input->size / 2)
+ return -1;
+
+ /* Encode */
+ if (priv->pt == PJ_RTP_PT_PCMA) {
+ unsigned i;
+ pj_uint8_t *dst = output->buf;
+
+ for (i=0; i!=input->size/2; ++i, ++dst) {
+ *dst = linear2alaw(samples[i]);
+ }
+ } else if (priv->pt == PJ_RTP_PT_PCMU) {
+ unsigned i;
+ pj_uint8_t *dst = output->buf;
+
+ for (i=0; i!=input->size/2; ++i, ++dst) {
+ *dst = linear2ulaw(samples[i]);
+ }
+
+ } else {
+ return -1;
+ }
+
+ output->type = PJ_AUDIO_FRAME_AUDIO;
+ output->size = input->size / 2;
+
+ return 0;
+}
+
+static pj_status_t g711_decode( pj_codec *codec, const struct pj_audio_frame *input,
+ unsigned output_buf_len, struct pj_audio_frame *output)
+{
+ struct g711_private *priv = codec->codec_data;
+
+ /* Check output buffer length */
+ if (output_buf_len < input->size * 2)
+ return -1;
+
+ /* Decode */
+ if (priv->pt == PJ_RTP_PT_PCMA) {
+ unsigned i;
+ pj_uint8_t *src = input->buf;
+ pj_uint16_t *dst = output->buf;
+
+ for (i=0; i!=input->size; ++i) {
+ *dst++ = (pj_uint16_t) alaw2linear(*src++);
+ }
+ } else if (priv->pt == PJ_RTP_PT_PCMU) {
+ unsigned i;
+ pj_uint8_t *src = input->buf;
+ pj_uint16_t *dst = output->buf;
+
+ for (i=0; i!=input->size; ++i) {
+ *dst++ = (pj_uint16_t) ulaw2linear(*src++);
+ }
+
+ } else {
+ return -1;
+ }
+
+ output->type = PJ_AUDIO_FRAME_AUDIO;
+ output->size = input->size * 2;
+
+ return 0;
+}
+
+
+/*
+ * This source code is a product of Sun Microsystems, Inc. and is provided
+ * for unrestricted use. Users may copy or modify this source code without
+ * charge.
+ *
+ * SUN SOURCE CODE IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING
+ * THE WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
+ *
+ * Sun source code is provided with no support and without any obligation on
+ * the part of Sun Microsystems, Inc. to assist in its use, correction,
+ * modification or enhancement.
+ *
+ * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
+ * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY THIS SOFTWARE
+ * OR ANY PART THEREOF.
+ *
+ * In no event will Sun Microsystems, Inc. be liable for any lost revenue
+ * or profits or other special, indirect and consequential damages, even if
+ * Sun has been advised of the possibility of such damages.
+ *
+ * Sun Microsystems, Inc.
+ * 2550 Garcia Avenue
+ * Mountain View, California 94043
+ */
+
+
+
+#ifdef _MSC_VER
+# pragma warning ( disable: 4244 ) /* Conversion from int to char etc */
+#endif
+
+/*
+ * g711.c
+ *
+ * u-law, A-law and linear PCM conversions.
+ */
+#define SIGN_BIT (0x80) /* Sign bit for a A-law byte. */
+#define QUANT_MASK (0xf) /* Quantization field mask. */
+#define NSEGS (8) /* Number of A-law segments. */
+#define SEG_SHIFT (4) /* Left shift for segment number. */
+#define SEG_MASK (0x70) /* Segment field mask. */
+
+static short seg_end[8] = {0xFF, 0x1FF, 0x3FF, 0x7FF,
+ 0xFFF, 0x1FFF, 0x3FFF, 0x7FFF};
+
+/* copy from CCITT G.711 specifications */
+static unsigned char _u2a[128] = { /* u- to A-law conversions */
+ 1, 1, 2, 2, 3, 3, 4, 4,
+ 5, 5, 6, 6, 7, 7, 8, 8,
+ 9, 10, 11, 12, 13, 14, 15, 16,
+ 17, 18, 19, 20, 21, 22, 23, 24,
+ 25, 27, 29, 31, 33, 34, 35, 36,
+ 37, 38, 39, 40, 41, 42, 43, 44,
+ 46, 48, 49, 50, 51, 52, 53, 54,
+ 55, 56, 57, 58, 59, 60, 61, 62,
+ 64, 65, 66, 67, 68, 69, 70, 71,
+ 72, 73, 74, 75, 76, 77, 78, 79,
+ 81, 82, 83, 84, 85, 86, 87, 88,
+ 89, 90, 91, 92, 93, 94, 95, 96,
+ 97, 98, 99, 100, 101, 102, 103, 104,
+ 105, 106, 107, 108, 109, 110, 111, 112,
+ 113, 114, 115, 116, 117, 118, 119, 120,
+ 121, 122, 123, 124, 125, 126, 127, 128};
+
+static unsigned char _a2u[128] = { /* A- to u-law conversions */
+ 1, 3, 5, 7, 9, 11, 13, 15,
+ 16, 17, 18, 19, 20, 21, 22, 23,
+ 24, 25, 26, 27, 28, 29, 30, 31,
+ 32, 32, 33, 33, 34, 34, 35, 35,
+ 36, 37, 38, 39, 40, 41, 42, 43,
+ 44, 45, 46, 47, 48, 48, 49, 49,
+ 50, 51, 52, 53, 54, 55, 56, 57,
+ 58, 59, 60, 61, 62, 63, 64, 64,
+ 65, 66, 67, 68, 69, 70, 71, 72,
+ 73, 74, 75, 76, 77, 78, 79, 79,
+ 80, 81, 82, 83, 84, 85, 86, 87,
+ 88, 89, 90, 91, 92, 93, 94, 95,
+ 96, 97, 98, 99, 100, 101, 102, 103,
+ 104, 105, 106, 107, 108, 109, 110, 111,
+ 112, 113, 114, 115, 116, 117, 118, 119,
+ 120, 121, 122, 123, 124, 125, 126, 127};
+
+static int
+search(
+ int val,
+ short *table,
+ int size)
+{
+ int i;
+
+ for (i = 0; i < size; i++) {
+ if (val <= *table++)
+ return (i);
+ }
+ return (size);
+}
+
+/*
+ * linear2alaw() - Convert a 16-bit linear PCM value to 8-bit A-law
+ *
+ * linear2alaw() accepts an 16-bit integer and encodes it as A-law data.
+ *
+ * Linear Input Code Compressed Code
+ * ------------------------ ---------------
+ * 0000000wxyza 000wxyz
+ * 0000001wxyza 001wxyz
+ * 000001wxyzab 010wxyz
+ * 00001wxyzabc 011wxyz
+ * 0001wxyzabcd 100wxyz
+ * 001wxyzabcde 101wxyz
+ * 01wxyzabcdef 110wxyz
+ * 1wxyzabcdefg 111wxyz
+ *
+ * For further information see John C. Bellamy's Digital Telephony, 1982,
+ * John Wiley & Sons, pps 98-111 and 472-476.
+ */
+static unsigned char
+linear2alaw(
+ int pcm_val) /* 2's complement (16-bit range) */
+{
+ int mask;
+ int seg;
+ unsigned char aval;
+
+ if (pcm_val >= 0) {
+ mask = 0xD5; /* sign (7th) bit = 1 */
+ } else {
+ mask = 0x55; /* sign bit = 0 */
+ pcm_val = -pcm_val - 8;
+ }
+
+ /* Convert the scaled magnitude to segment number. */
+ seg = search(pcm_val, seg_end, 8);
+
+ /* Combine the sign, segment, and quantization bits. */
+
+ if (seg >= 8) /* out of range, return maximum value. */
+ return (0x7F ^ mask);
+ else {
+ aval = seg << SEG_SHIFT;
+ if (seg < 2)
+ aval |= (pcm_val >> 4) & QUANT_MASK;
+ else
+ aval |= (pcm_val >> (seg + 3)) & QUANT_MASK;
+ return (aval ^ mask);
+ }
+}
+
+/*
+ * alaw2linear() - Convert an A-law value to 16-bit linear PCM
+ *
+ */
+static int
+alaw2linear(
+ unsigned char a_val)
+{
+ int t;
+ int seg;
+
+ a_val ^= 0x55;
+
+ t = (a_val & QUANT_MASK) << 4;
+ seg = ((unsigned)a_val & SEG_MASK) >> SEG_SHIFT;
+ switch (seg) {
+ case 0:
+ t += 8;
+ break;
+ case 1:
+ t += 0x108;
+ break;
+ default:
+ t += 0x108;
+ t <<= seg - 1;
+ }
+ return ((a_val & SIGN_BIT) ? t : -t);
+}
+
+#define BIAS (0x84) /* Bias for linear code. */
+
+/*
+ * linear2ulaw() - Convert a linear PCM value to u-law
+ *
+ * In order to simplify the encoding process, the original linear magnitude
+ * is biased by adding 33 which shifts the encoding range from (0 - 8158) to
+ * (33 - 8191). The result can be seen in the following encoding table:
+ *
+ * Biased Linear Input Code Compressed Code
+ * ------------------------ ---------------
+ * 00000001wxyza 000wxyz
+ * 0000001wxyzab 001wxyz
+ * 000001wxyzabc 010wxyz
+ * 00001wxyzabcd 011wxyz
+ * 0001wxyzabcde 100wxyz
+ * 001wxyzabcdef 101wxyz
+ * 01wxyzabcdefg 110wxyz
+ * 1wxyzabcdefgh 111wxyz
+ *
+ * Each biased linear code has a leading 1 which identifies the segment
+ * number. The value of the segment number is equal to 7 minus the number
+ * of leading 0's. The quantization interval is directly available as the
+ * four bits wxyz. * The trailing bits (a - h) are ignored.
+ *
+ * Ordinarily the complement of the resulting code word is used for
+ * transmission, and so the code word is complemented before it is returned.
+ *
+ * For further information see John C. Bellamy's Digital Telephony, 1982,
+ * John Wiley & Sons, pps 98-111 and 472-476.
+ */
+static unsigned char
+linear2ulaw(
+ int pcm_val) /* 2's complement (16-bit range) */
+{
+ int mask;
+ int seg;
+ unsigned char uval;
+
+ /* Get the sign and the magnitude of the value. */
+ if (pcm_val < 0) {
+ pcm_val = BIAS - pcm_val;
+ mask = 0x7F;
+ } else {
+ pcm_val += BIAS;
+ mask = 0xFF;
+ }
+
+ /* Convert the scaled magnitude to segment number. */
+ seg = search(pcm_val, seg_end, 8);
+
+ /*
+ * Combine the sign, segment, quantization bits;
+ * and complement the code word.
+ */
+ if (seg >= 8) /* out of range, return maximum value. */
+ return (0x7F ^ mask);
+ else {
+ uval = (seg << 4) | ((pcm_val >> (seg + 3)) & 0xF);
+ return (uval ^ mask);
+ }
+
+}
+
+/*
+ * ulaw2linear() - Convert a u-law value to 16-bit linear PCM
+ *
+ * First, a biased linear code is derived from the code word. An unbiased
+ * output can then be obtained by subtracting 33 from the biased code.
+ *
+ * Note that this function expects to be passed the complement of the
+ * original code word. This is in keeping with ISDN conventions.
+ */
+static int
+ulaw2linear(
+ unsigned char u_val)
+{
+ int t;
+
+ /* Complement to obtain normal u-law value. */
+ u_val = ~u_val;
+
+ /*
+ * Extract and bias the quantization bits. Then
+ * shift up by the segment number and subtract out the bias.
+ */
+ t = ((u_val & QUANT_MASK) << 3) + BIAS;
+ t <<= ((unsigned)u_val & SEG_MASK) >> SEG_SHIFT;
+
+ return ((u_val & SIGN_BIT) ? (BIAS - t) : (t - BIAS));
+}
+
+/* A-law to u-law conversion */
+unsigned char
+alaw2ulaw(
+ unsigned char aval)
+{
+ aval &= 0xff;
+ return ((aval & 0x80) ? (0xFF ^ _a2u[aval ^ 0xD5]) :
+ (0x7F ^ _a2u[aval ^ 0x55]));
+}
+
+/* u-law to A-law conversion */
+unsigned char
+ulaw2alaw(
+ unsigned char uval)
+{
+ uval &= 0xff;
+ return ((uval & 0x80) ? (0xD5 ^ (_u2a[0xFF ^ uval] - 1)) :
+ (0x55 ^ (_u2a[0x7F ^ uval] - 1)));
+}
+
+
+
diff --git a/pjmedia/src/pjmedia/jbuf.c b/pjmedia/src/pjmedia/jbuf.c
index 0ed22007..d35fad35 100644
--- a/pjmedia/src/pjmedia/jbuf.c
+++ b/pjmedia/src/pjmedia/jbuf.c
@@ -1,406 +1,427 @@
-/* $Id$
- *
- */
-
-#include <pjmedia/jbuf.h>
-#include <pj/log.h>
-#include <pj/pool.h>
-#include <string.h> /* memset() */
-
-/*
- * At the current state, this is basicly an ugly jitter buffer.
- * It worked before by observing level, bit it doesn't work.
- * Then I used the size, which makes the level obsolete.
- * That's why it's ugly!
- */
-
-#define MAX_SEQ_RANGE 1000 /* Range in which sequence is considered still within session */
-#define UPDATE_DURATION 20 /* Number of frames retrieved before jitter is updated */
-
-#define THIS_FILE "jbuf"
-
-/* Individual frame in the frame list. */
-struct pj_jbframe
-{
- pj_uint32_t extseq;
- void *buf;
-};
-
-
-/* Jitter buffer state. */
-typedef enum jb_state_t
-{
- JB_PREFETCH,
- JB_NORMAL,
-} jb_state_t;
-
-
-/* Jitter buffer last operation. */
-typedef enum jb_op_t
-{
- JB_PUT,
- JB_GET,
-} jb_op_t;
-
-
-/* Short name for convenience. */
-typedef struct pj_jitter_buffer JB;
-
-
-/* Initialize framelist. */
-static pj_status_t
-pj_framelist_init( pj_jbframelist *lst, pj_pool_t *pool, unsigned maxcount )
-{
- PJ_LOG(5, (THIS_FILE, "..pj_frame_list_init [lst=%p], maxcount=%d", lst, maxcount));
-
- memset(lst, 0, sizeof(*lst));
- lst->maxcount = maxcount;
- lst->frames = pj_pool_calloc( pool, maxcount, sizeof(*lst->frames) );
- if (lst->frames == NULL) {
- PJ_LOG(1,(THIS_FILE, "Unable to allocate frame list!"));
- return -1;
- }
- return 0;
-}
-
-/* Reset framelist. */
-static void
-pj_framelist_reset( pj_jbframelist *lst )
-{
- PJ_LOG(6, (THIS_FILE, "..pj_frame_list_reset [lst=%p]", lst));
-
- lst->count = 0;
- lst->head = 0;
- lst->frames[0].extseq = 0;
-}
-
-/* Put a buffer with the specified sequence into the ordered list. */
-static int
-pj_framelist_put( pj_jbframelist *lst, pj_uint32_t extseq, void *buf )
-{
- unsigned pos = (unsigned)-1;
- pj_uint32_t startseq = lst->frames[lst->head].extseq;
-
- if (lst->count == 0) {
- /* Empty list. Initialize frame list. */
- PJ_LOG(6, (THIS_FILE, " ..pj_frame_list_put [lst=%p], empty, seq=%u@pos=%d",
- lst, extseq, lst->head));
-
- lst->head = 0;
- lst->count = 1;
- lst->frames[0].buf = buf;
- lst->frames[0].extseq = extseq;
- return 0;
-
- } else if (extseq < startseq) {
- /* The sequence number is lower than our oldest packet. This can mean
- two things:
- - old packet has been receieved, or
- - the sequence number has wrapped around.
- */
- if (startseq + lst->maxcount <= extseq) {
- /* The sequence number has wrapped around, but it is beyond
- the capacity of the list (i.e. too soon).
- */
- PJ_LOG(5, (THIS_FILE, " ..pj_frame_list_put TOO_SOON! [lst=%p] seq=%u, startseq=%d",
- lst, extseq, startseq));
- return PJ_JB_STATUS_TOO_SOON;
-
- } else if (startseq-extseq > lst->maxcount && startseq+lst->maxcount > extseq) {
- /* The sequence number has wrapped around, and it is still inside
- the 'window' of the framelist.
- */
- pos = extseq - startseq;
- } else {
- /* The new frame is too old. */
- PJ_LOG(5, (THIS_FILE, " ..pj_frame_list_put TOO_OLD! [lst=%p] seq=%u, startseq=%d",
- lst, extseq, startseq));
- return PJ_JB_STATUS_TOO_OLD;
- }
-
- } else if (extseq > startseq + lst->maxcount) {
- /* Two possibilities here. Either:
- - packet is really too soon, or
- - sequence number of startseq has just wrapped around, and old packet
- which hasn't wrapped is received.
- */
- if (extseq < MAX_SEQ_RANGE /*approx 20 seconds with 50 fps*/) {
- PJ_LOG(5, (THIS_FILE, " ..pj_frame_list_put TOO_SOON! [lst=%p] seq=%u, startseq=%d",
- lst, extseq, startseq));
- return PJ_JB_STATUS_TOO_SOON;
- } else {
- PJ_LOG(5, (THIS_FILE, " ..pj_frame_list_put TOO_OLD! [lst=%p] seq=%u, startseq=%d",
- lst, extseq, startseq));
- return PJ_JB_STATUS_TOO_OLD;
- }
- }
-
- /* The new frame is within the framelist capacity.
- Calculate position where to put it in the list.
- */
- if (pos == (unsigned)-1)
- pos = ((extseq - startseq) + lst->head) % lst->maxcount;
-
- pj_assert(pos < lst->maxcount);
-
- /* Update count only if we're not overwriting existing frame. */
- if (lst->frames[pos].buf == NULL)
- ++lst->count;
-
- lst->frames[pos].buf = buf;
- lst->frames[pos].extseq = extseq;
-
- PJ_LOG(6, (THIS_FILE, " ..pj_frame_list_put [lst=%p] seq=%u, startseq=%d, head=%d, pos=%d",
- lst, extseq, startseq, lst->head, pos));
- return 0;
-}
-
-/* Get the first element of the list. */
-static int
-pj_framelist_get( pj_jbframelist *lst, pj_uint32_t *extseq, void **buf )
-{
- if (lst->count == 0) {
- /* Empty. */
- *buf = NULL;
- *extseq = 0;
- PJ_LOG(6, (THIS_FILE, " ..pj_frame_list_get [lst=%p], empty!", lst));
- return -1;
-
- } else {
- *buf = lst->frames[lst->head].buf;
- *extseq = lst->frames[lst->head].extseq;
- lst->frames[lst->head].buf = NULL;
-
- PJ_LOG(6, (THIS_FILE, " ..pj_frame_list_get [lst=%p] seq=%u, head=%d",
- lst, *extseq, lst->head));
-
- lst->head = (lst->head + 1) % lst->maxcount;
- --lst->count;
- return 0;
- }
-}
-
-
-/*****************************************************************************
- * Reset jitter buffer.
- ****************************************************************************
-*/
-PJ_DEF(void) pj_jb_reset(JB *jb)
-{
- PJ_LOG(6, (THIS_FILE, "pj_jb_reset [jb=%p]", jb));
-
- jb->level = jb->max_level = 1;
- jb->prefetch = jb->min;
- jb->get_cnt = 0;
- jb->lastseq = 0;
- jb->state = JB_PREFETCH;
- jb->upd_count = 0;
- jb->last_op = -1;
- pj_framelist_reset( &jb->lst );
-}
-
-
-/*****************************************************************************
- * Create jitter buffer.
- *****************************************************************************
- */
-PJ_DEF(pj_status_t) pj_jb_init( pj_jitter_buffer *jb, pj_pool_t *pool,
- unsigned min, unsigned max, unsigned maxcount)
-{
- pj_status_t status;
-
- if (maxcount <= max) {
- maxcount = max * 5 / 4;
- PJ_LOG(3,(THIS_FILE, "Jitter buffer maximum count was adjusted."));
- }
-
- jb->min = min;
- jb->max = max;
-
- status = pj_framelist_init( &jb->lst, pool, maxcount );
- if (status != PJ_SUCCESS)
- return status;
-
- pj_jb_reset(jb);
-
- PJ_LOG(4, (THIS_FILE, "pj_jb_init success [jb=%p], min=%d, max=%d, maxcount=%d",
- jb, min, max, maxcount));
- return PJ_SUCCESS;
-}
-
-
-/*****************************************************************************
- * Put a packet to the jitter buffer.
- *****************************************************************************
- */
-PJ_DEF(pj_status_t) pj_jb_put( JB *jb, pj_uint32_t extseq, void *buf )
-{
- unsigned distance;
- int status;
-
- PJ_LOG(6, (THIS_FILE, "==> pj_jb_put [jb=%p], seq=%u, buf=%p", jb, extseq, buf));
-
- if (jb->lastseq == 0)
- jb->lastseq = extseq - 1;
-
- /* Calculate distance between this packet and last received packet
- to detect long jump (indicating probably remote has just been
- restarted.
- */
- distance = (extseq > jb->lastseq) ? extseq - jb->lastseq : jb->lastseq - extseq;
- if (distance > MAX_SEQ_RANGE) {
- /* Distance is out of range, reset jitter while maintaining current jitter
- level.
- */
- int old_level = jb->level;
- int old_prefetch = jb->prefetch;
-
- PJ_LOG(4, (THIS_FILE, " ..[jb=%p] distance out of range, resetting", jb));
-
- pj_jb_reset(jb);
- jb->level = old_level;
- jb->prefetch = old_prefetch;
- distance = 1;
- jb->lastseq = extseq - 1;
- }
-
- jb->lastseq = extseq;
-
- status = pj_framelist_put( &jb->lst, extseq, buf );
- if (status == PJ_JB_STATUS_TOO_OLD)
- return -1;
-
- if (status == PJ_JB_STATUS_TOO_SOON) {
- /* TODO: discard old packets.. */
- /* No, don't do it without putting a way to inform application so that
- it can free the memory */
- }
-
-
- if (jb->last_op != JB_PUT) {
- if (jb->state != JB_PREFETCH)
- jb->level--;
- } else {
- jb->level++;
- }
-
- if (jb->lst.count > jb->max_level)
- jb->max_level++;
-
- jb->last_op = JB_PUT;
- return 0;
-}
-
-
-/*
- * Update jitter buffer algorithm.
- */
-static void jb_update(JB *jb, int apply, int log_info)
-{
- unsigned abs_level = jb->max_level > 0 ? jb->max_level : -jb->max_level;
- unsigned new_prefetch;
-
- /* Update prefetch count */
- if (abs_level > jb->prefetch)
- new_prefetch = (jb->prefetch + abs_level*9 + 1) / 10;
- else {
- new_prefetch = (jb->prefetch*4 + abs_level) / 5;
- pj_assert(new_prefetch <= jb->prefetch);
- }
-
- if (log_info) {
- PJ_LOG(5, (THIS_FILE, " ..jb_update [jb=%p], level=%d, max_level=%d, old_prefetch=%d, new_prefetch=%d",
- jb, jb->level, jb->max_level, jb->prefetch, new_prefetch));
- } else {
- PJ_LOG(6, (THIS_FILE, " ..jb_update [jb=%p], level=%d, max_level=%d, old_prefetch=%d, new_prefetch=%d",
- jb, jb->level, jb->max_level, jb->prefetch, new_prefetch));
- }
-
- if (new_prefetch < jb->min) new_prefetch = jb->min;
- if (new_prefetch > jb->max) new_prefetch = jb->max;
-
- /* If jitter buffer is empty, set state to JB_PREFETCH, taking care of the
- new prefetch setting.
- */
- if (jb->lst.count == 0) {
- jb->state = JB_PREFETCH;
- jb->get_cnt = 0;
- } else {
- /* Check if delay is too long, which in this case probably better to
- discard some frames..
- */
- /* No, don't do it without putting a way to inform application so that
- it can free the memory */
- }
-
-
- if (apply) {
- jb->prefetch = new_prefetch;
- if (jb->max_level > 0)
- jb->max_level--;
- } else {
- jb->level = new_prefetch;
- }
-}
-
-
-/*****************************************************************************
- * Get the oldest frame from jitter buffer.
- *****************************************************************************
- */
-PJ_DEF(pj_status_t) pj_jb_get( JB *jb, pj_uint32_t *extseq, void **buf )
-{
- pj_status_t status;
-
- PJ_LOG(6, (THIS_FILE, "<== pj_jb_get [jb=%p]", jb));
-
- /*
- * Check whether we're ready to give frame. When we're in JB_PREFETCH state,
- * only give frames only when:
- * - the buffer has enough frames in it (jb->list.count > jb->prefetch), OR
- * - after 'prefetch' attempts, there's still no frame, which in this
- * case PJ_JB_STATUS_FRAME_NULL will be returned by the next check.
- */
- if (jb->state == JB_PREFETCH && jb->lst.count <= jb->prefetch && jb->get_cnt < jb->prefetch) {
- jb->get_cnt++;
- jb->last_op = JB_GET;
- PJ_LOG(5, (THIS_FILE, " ..[jb=%p] bufferring...", jb));
- return PJ_JB_STATUS_FRAME_NULL;
- }
-
- /* Attempt to get one frame from the list. */
- status = pj_framelist_get( &jb->lst, extseq, buf );
- if (status != 0) {
- PJ_LOG(6, (THIS_FILE, " ..[jb=%p] no packet!", jb));
- status = jb->lst.count ? PJ_JB_STATUS_FRAME_MISSING : PJ_JB_STATUS_FRAME_NULL;
- jb_update(jb, 1, 0);
- return status;
- }
-
- /* Force state to NORMAL */
- jb->state = JB_NORMAL;
-
- /* Increase level only when last operation is GET.
- * This is to prevent level from increasing during silence period, which
- * no packets is receieved.
- */
- if (jb->last_op != JB_GET) {
- int apply;
-
- //jb->level++;
- jb->last_op = JB_GET;
-
- apply = (++jb->upd_count > UPDATE_DURATION);
- if (apply)
- jb->upd_count = 0;
-
- jb_update(jb, apply, apply);
- }
-
- PJ_LOG(6, (THIS_FILE, " ..[jb=%p] seq=%u, level=%d, prefetch=%d, size=%u, delay=%d",
- jb, *extseq, jb->level, jb->prefetch, jb->lst.count,
- jb->lastseq - *extseq));
- return 0;
-}
-
-
+/* $Id$
+ *
+ */
+/*
+ * PJMEDIA - Multimedia over IP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <pjmedia/jbuf.h>
+#include <pj/log.h>
+#include <pj/pool.h>
+#include <string.h> /* memset() */
+
+/*
+ * At the current state, this is basicly an ugly jitter buffer.
+ * It worked before by observing level, bit it doesn't work.
+ * Then I used the size, which makes the level obsolete.
+ * That's why it's ugly!
+ */
+
+#define MAX_SEQ_RANGE 1000 /* Range in which sequence is considered still within session */
+#define UPDATE_DURATION 20 /* Number of frames retrieved before jitter is updated */
+
+#define THIS_FILE "jbuf"
+
+/* Individual frame in the frame list. */
+struct pj_jbframe
+{
+ pj_uint32_t extseq;
+ void *buf;
+};
+
+
+/* Jitter buffer state. */
+typedef enum jb_state_t
+{
+ JB_PREFETCH,
+ JB_NORMAL,
+} jb_state_t;
+
+
+/* Jitter buffer last operation. */
+typedef enum jb_op_t
+{
+ JB_PUT,
+ JB_GET,
+} jb_op_t;
+
+
+/* Short name for convenience. */
+typedef struct pj_jitter_buffer JB;
+
+
+/* Initialize framelist. */
+static pj_status_t
+pj_framelist_init( pj_jbframelist *lst, pj_pool_t *pool, unsigned maxcount )
+{
+ PJ_LOG(5, (THIS_FILE, "..pj_frame_list_init [lst=%p], maxcount=%d", lst, maxcount));
+
+ memset(lst, 0, sizeof(*lst));
+ lst->maxcount = maxcount;
+ lst->frames = pj_pool_calloc( pool, maxcount, sizeof(*lst->frames) );
+ if (lst->frames == NULL) {
+ PJ_LOG(1,(THIS_FILE, "Unable to allocate frame list!"));
+ return -1;
+ }
+ return 0;
+}
+
+/* Reset framelist. */
+static void
+pj_framelist_reset( pj_jbframelist *lst )
+{
+ PJ_LOG(6, (THIS_FILE, "..pj_frame_list_reset [lst=%p]", lst));
+
+ lst->count = 0;
+ lst->head = 0;
+ lst->frames[0].extseq = 0;
+}
+
+/* Put a buffer with the specified sequence into the ordered list. */
+static int
+pj_framelist_put( pj_jbframelist *lst, pj_uint32_t extseq, void *buf )
+{
+ unsigned pos = (unsigned)-1;
+ pj_uint32_t startseq = lst->frames[lst->head].extseq;
+
+ if (lst->count == 0) {
+ /* Empty list. Initialize frame list. */
+ PJ_LOG(6, (THIS_FILE, " ..pj_frame_list_put [lst=%p], empty, seq=%u@pos=%d",
+ lst, extseq, lst->head));
+
+ lst->head = 0;
+ lst->count = 1;
+ lst->frames[0].buf = buf;
+ lst->frames[0].extseq = extseq;
+ return 0;
+
+ } else if (extseq < startseq) {
+ /* The sequence number is lower than our oldest packet. This can mean
+ two things:
+ - old packet has been receieved, or
+ - the sequence number has wrapped around.
+ */
+ if (startseq + lst->maxcount <= extseq) {
+ /* The sequence number has wrapped around, but it is beyond
+ the capacity of the list (i.e. too soon).
+ */
+ PJ_LOG(5, (THIS_FILE, " ..pj_frame_list_put TOO_SOON! [lst=%p] seq=%u, startseq=%d",
+ lst, extseq, startseq));
+ return PJ_JB_STATUS_TOO_SOON;
+
+ } else if (startseq-extseq > lst->maxcount && startseq+lst->maxcount > extseq) {
+ /* The sequence number has wrapped around, and it is still inside
+ the 'window' of the framelist.
+ */
+ pos = extseq - startseq;
+ } else {
+ /* The new frame is too old. */
+ PJ_LOG(5, (THIS_FILE, " ..pj_frame_list_put TOO_OLD! [lst=%p] seq=%u, startseq=%d",
+ lst, extseq, startseq));
+ return PJ_JB_STATUS_TOO_OLD;
+ }
+
+ } else if (extseq > startseq + lst->maxcount) {
+ /* Two possibilities here. Either:
+ - packet is really too soon, or
+ - sequence number of startseq has just wrapped around, and old packet
+ which hasn't wrapped is received.
+ */
+ if (extseq < MAX_SEQ_RANGE /*approx 20 seconds with 50 fps*/) {
+ PJ_LOG(5, (THIS_FILE, " ..pj_frame_list_put TOO_SOON! [lst=%p] seq=%u, startseq=%d",
+ lst, extseq, startseq));
+ return PJ_JB_STATUS_TOO_SOON;
+ } else {
+ PJ_LOG(5, (THIS_FILE, " ..pj_frame_list_put TOO_OLD! [lst=%p] seq=%u, startseq=%d",
+ lst, extseq, startseq));
+ return PJ_JB_STATUS_TOO_OLD;
+ }
+ }
+
+ /* The new frame is within the framelist capacity.
+ Calculate position where to put it in the list.
+ */
+ if (pos == (unsigned)-1)
+ pos = ((extseq - startseq) + lst->head) % lst->maxcount;
+
+ pj_assert(pos < lst->maxcount);
+
+ /* Update count only if we're not overwriting existing frame. */
+ if (lst->frames[pos].buf == NULL)
+ ++lst->count;
+
+ lst->frames[pos].buf = buf;
+ lst->frames[pos].extseq = extseq;
+
+ PJ_LOG(6, (THIS_FILE, " ..pj_frame_list_put [lst=%p] seq=%u, startseq=%d, head=%d, pos=%d",
+ lst, extseq, startseq, lst->head, pos));
+ return 0;
+}
+
+/* Get the first element of the list. */
+static int
+pj_framelist_get( pj_jbframelist *lst, pj_uint32_t *extseq, void **buf )
+{
+ if (lst->count == 0) {
+ /* Empty. */
+ *buf = NULL;
+ *extseq = 0;
+ PJ_LOG(6, (THIS_FILE, " ..pj_frame_list_get [lst=%p], empty!", lst));
+ return -1;
+
+ } else {
+ *buf = lst->frames[lst->head].buf;
+ *extseq = lst->frames[lst->head].extseq;
+ lst->frames[lst->head].buf = NULL;
+
+ PJ_LOG(6, (THIS_FILE, " ..pj_frame_list_get [lst=%p] seq=%u, head=%d",
+ lst, *extseq, lst->head));
+
+ lst->head = (lst->head + 1) % lst->maxcount;
+ --lst->count;
+ return 0;
+ }
+}
+
+
+/*****************************************************************************
+ * Reset jitter buffer.
+ ****************************************************************************
+*/
+PJ_DEF(void) pj_jb_reset(JB *jb)
+{
+ PJ_LOG(6, (THIS_FILE, "pj_jb_reset [jb=%p]", jb));
+
+ jb->level = jb->max_level = 1;
+ jb->prefetch = jb->min;
+ jb->get_cnt = 0;
+ jb->lastseq = 0;
+ jb->state = JB_PREFETCH;
+ jb->upd_count = 0;
+ jb->last_op = -1;
+ pj_framelist_reset( &jb->lst );
+}
+
+
+/*****************************************************************************
+ * Create jitter buffer.
+ *****************************************************************************
+ */
+PJ_DEF(pj_status_t) pj_jb_init( pj_jitter_buffer *jb, pj_pool_t *pool,
+ unsigned min, unsigned max, unsigned maxcount)
+{
+ pj_status_t status;
+
+ if (maxcount <= max) {
+ maxcount = max * 5 / 4;
+ PJ_LOG(3,(THIS_FILE, "Jitter buffer maximum count was adjusted."));
+ }
+
+ jb->min = min;
+ jb->max = max;
+
+ status = pj_framelist_init( &jb->lst, pool, maxcount );
+ if (status != PJ_SUCCESS)
+ return status;
+
+ pj_jb_reset(jb);
+
+ PJ_LOG(4, (THIS_FILE, "pj_jb_init success [jb=%p], min=%d, max=%d, maxcount=%d",
+ jb, min, max, maxcount));
+ return PJ_SUCCESS;
+}
+
+
+/*****************************************************************************
+ * Put a packet to the jitter buffer.
+ *****************************************************************************
+ */
+PJ_DEF(pj_status_t) pj_jb_put( JB *jb, pj_uint32_t extseq, void *buf )
+{
+ unsigned distance;
+ int status;
+
+ PJ_LOG(6, (THIS_FILE, "==> pj_jb_put [jb=%p], seq=%u, buf=%p", jb, extseq, buf));
+
+ if (jb->lastseq == 0)
+ jb->lastseq = extseq - 1;
+
+ /* Calculate distance between this packet and last received packet
+ to detect long jump (indicating probably remote has just been
+ restarted.
+ */
+ distance = (extseq > jb->lastseq) ? extseq - jb->lastseq : jb->lastseq - extseq;
+ if (distance > MAX_SEQ_RANGE) {
+ /* Distance is out of range, reset jitter while maintaining current jitter
+ level.
+ */
+ int old_level = jb->level;
+ int old_prefetch = jb->prefetch;
+
+ PJ_LOG(4, (THIS_FILE, " ..[jb=%p] distance out of range, resetting", jb));
+
+ pj_jb_reset(jb);
+ jb->level = old_level;
+ jb->prefetch = old_prefetch;
+ distance = 1;
+ jb->lastseq = extseq - 1;
+ }
+
+ jb->lastseq = extseq;
+
+ status = pj_framelist_put( &jb->lst, extseq, buf );
+ if (status == PJ_JB_STATUS_TOO_OLD)
+ return -1;
+
+ if (status == PJ_JB_STATUS_TOO_SOON) {
+ /* TODO: discard old packets.. */
+ /* No, don't do it without putting a way to inform application so that
+ it can free the memory */
+ }
+
+
+ if (jb->last_op != JB_PUT) {
+ if (jb->state != JB_PREFETCH)
+ jb->level--;
+ } else {
+ jb->level++;
+ }
+
+ if (jb->lst.count > jb->max_level)
+ jb->max_level++;
+
+ jb->last_op = JB_PUT;
+ return 0;
+}
+
+
+/*
+ * Update jitter buffer algorithm.
+ */
+static void jb_update(JB *jb, int apply, int log_info)
+{
+ unsigned abs_level = jb->max_level > 0 ? jb->max_level : -jb->max_level;
+ unsigned new_prefetch;
+
+ /* Update prefetch count */
+ if (abs_level > jb->prefetch)
+ new_prefetch = (jb->prefetch + abs_level*9 + 1) / 10;
+ else {
+ new_prefetch = (jb->prefetch*4 + abs_level) / 5;
+ pj_assert(new_prefetch <= jb->prefetch);
+ }
+
+ if (log_info) {
+ PJ_LOG(5, (THIS_FILE, " ..jb_update [jb=%p], level=%d, max_level=%d, old_prefetch=%d, new_prefetch=%d",
+ jb, jb->level, jb->max_level, jb->prefetch, new_prefetch));
+ } else {
+ PJ_LOG(6, (THIS_FILE, " ..jb_update [jb=%p], level=%d, max_level=%d, old_prefetch=%d, new_prefetch=%d",
+ jb, jb->level, jb->max_level, jb->prefetch, new_prefetch));
+ }
+
+ if (new_prefetch < jb->min) new_prefetch = jb->min;
+ if (new_prefetch > jb->max) new_prefetch = jb->max;
+
+ /* If jitter buffer is empty, set state to JB_PREFETCH, taking care of the
+ new prefetch setting.
+ */
+ if (jb->lst.count == 0) {
+ jb->state = JB_PREFETCH;
+ jb->get_cnt = 0;
+ } else {
+ /* Check if delay is too long, which in this case probably better to
+ discard some frames..
+ */
+ /* No, don't do it without putting a way to inform application so that
+ it can free the memory */
+ }
+
+
+ if (apply) {
+ jb->prefetch = new_prefetch;
+ if (jb->max_level > 0)
+ jb->max_level--;
+ } else {
+ jb->level = new_prefetch;
+ }
+}
+
+
+/*****************************************************************************
+ * Get the oldest frame from jitter buffer.
+ *****************************************************************************
+ */
+PJ_DEF(pj_status_t) pj_jb_get( JB *jb, pj_uint32_t *extseq, void **buf )
+{
+ pj_status_t status;
+
+ PJ_LOG(6, (THIS_FILE, "<== pj_jb_get [jb=%p]", jb));
+
+ /*
+ * Check whether we're ready to give frame. When we're in JB_PREFETCH state,
+ * only give frames only when:
+ * - the buffer has enough frames in it (jb->list.count > jb->prefetch), OR
+ * - after 'prefetch' attempts, there's still no frame, which in this
+ * case PJ_JB_STATUS_FRAME_NULL will be returned by the next check.
+ */
+ if (jb->state == JB_PREFETCH && jb->lst.count <= jb->prefetch && jb->get_cnt < jb->prefetch) {
+ jb->get_cnt++;
+ jb->last_op = JB_GET;
+ PJ_LOG(5, (THIS_FILE, " ..[jb=%p] bufferring...", jb));
+ return PJ_JB_STATUS_FRAME_NULL;
+ }
+
+ /* Attempt to get one frame from the list. */
+ status = pj_framelist_get( &jb->lst, extseq, buf );
+ if (status != 0) {
+ PJ_LOG(6, (THIS_FILE, " ..[jb=%p] no packet!", jb));
+ status = jb->lst.count ? PJ_JB_STATUS_FRAME_MISSING : PJ_JB_STATUS_FRAME_NULL;
+ jb_update(jb, 1, 0);
+ return status;
+ }
+
+ /* Force state to NORMAL */
+ jb->state = JB_NORMAL;
+
+ /* Increase level only when last operation is GET.
+ * This is to prevent level from increasing during silence period, which
+ * no packets is receieved.
+ */
+ if (jb->last_op != JB_GET) {
+ int apply;
+
+ //jb->level++;
+ jb->last_op = JB_GET;
+
+ apply = (++jb->upd_count > UPDATE_DURATION);
+ if (apply)
+ jb->upd_count = 0;
+
+ jb_update(jb, apply, apply);
+ }
+
+ PJ_LOG(6, (THIS_FILE, " ..[jb=%p] seq=%u, level=%d, prefetch=%d, size=%u, delay=%d",
+ jb, *extseq, jb->level, jb->prefetch, jb->lst.count,
+ jb->lastseq - *extseq));
+ return 0;
+}
+
+
diff --git a/pjmedia/src/pjmedia/jbuf.h b/pjmedia/src/pjmedia/jbuf.h
index fc4325b4..9229b412 100644
--- a/pjmedia/src/pjmedia/jbuf.h
+++ b/pjmedia/src/pjmedia/jbuf.h
@@ -1,128 +1,149 @@
-/* $Id$
- *
- */
-
-#ifndef __PJMEDIA_JBUF_H__
-#define __PJMEDIA_JBUF_H__
-
-
-/**
- * @file jbuf.h
- * @brief Adaptive jitter buffer implementation.
- */
-/**
- * @defgroup PJMED_JBUF Adaptive jitter buffer
- * @ingroup PJMEDIA
- * @{
- */
-
-#include <pj/types.h>
-
-
-PJ_BEGIN_DECL
-
-
-/**
- * Opaque declaration of internal frame type used by jitter buffer.
- */
-struct pj_jbframe;
-
-
-/**
- * Miscelaneous operation result/status.
- */
-typedef enum jb_op_status
-{
- PJ_JB_STATUS_TOO_OLD = -2, /** The packet is too old. */
- PJ_JB_STATUS_TOO_SOON = -3, /** The packet is too soon. */
- PJ_JB_STATUS_FRAME_NULL = -4, /** No packet can be retrieved */
- PJ_JB_STATUS_FRAME_MISSING = -5, /** The specified packet is missing/lost */
-} jb_op_status;
-
-
-/*
- * Frame list, container abstraction for ordered list with fixed maximum
- * size. It is used internally by the jitter buffer.
- */
-typedef struct pj_jbframelist
-{
- unsigned head, count, maxcount;
- struct pj_jbframe *frames;
-} pj_jbframelist;
-
-
-/**
- * Jitter buffer implementation.
- */
-typedef struct pj_jitter_buffer
-{
- pj_jbframelist lst; /** The frame list. */
- int level; /** Current, real-time jitter level. */
- int max_level; /** Maximum level for the period. */
- unsigned prefetch; /** Prefetch count currently used. */
- unsigned get_cnt; /** Number of get operation during prefetch state. */
- unsigned min; /** Minimum jitter size, in packets. */
- unsigned max; /** Maximum jitter size, in packets. */
- pj_uint32_t lastseq; /** Last sequence number put to jitter buffer. */
- unsigned upd_count; /** Internal counter to manage update interval. */
- int state; /** Jitter buffer state (1==operational) */
- int last_op; /** Last jitter buffer operation. */
-} pj_jitter_buffer;
-
-
-/**
- * Initialize jitter buffer with the specified parameters.
- * This function will allocate internal frame buffer from the specified pool.
- * @param jb The jitter buffer to be initialized.
- * @param pool Pool where memory will be allocated for the frame buffer.
- * @param min The minimum value of jitter buffer, in packets.
- * @param max The maximum value of jitter buffer, in packets.
- * @param maxcount The maximum number of delay, in packets, which must be
- * greater than max.
- * @return PJ_SUCCESS on success.
- */
-PJ_DECL(pj_status_t) pj_jb_init(pj_jitter_buffer *jb, pj_pool_t *pool,
- unsigned min, unsigned max, unsigned maxcount);
-
-/**
- * Reset jitter buffer according to the parameters specified when the jitter
- * buffer was initialized. Any packets currently in the buffer will be
- * discarded.
- */
-PJ_DECL(void) pj_jb_reset(pj_jitter_buffer *jb);
-
-/**
- * Put a pointer to the buffer with the specified sequence number. The pointer
- * normally points to a buffer held by application, and this pointer will be
- * returned later on when pj_jb_get() is called. The jitter buffer will not try
- * to interpret the content of this pointer.
- * @return:
- * - PJ_SUCCESS on success.
- * - PJ_JB_STATUS_TOO_OLD when the packet is too old.
- * - PJ_JB_STATUS_TOO_SOON when the packet is too soon.
- */
-PJ_DECL(pj_status_t) pj_jb_put( pj_jitter_buffer *jb, pj_uint32_t extseq, void *buf );
-
-/**
- * Get the earliest data from the jitter buffer. ONLY when the operation succeeds,
- * the function returns both sequence number and a pointer in the parameters.
- * This returned data corresponds to sequence number and pointer that were
- * given to jitter buffer in the previous pj_jb_put operation.
- * @return
- * - PJ_SUCCESS on success
- * - PJ_JB_STATUS_FRAME_NULL when there is no frames to be returned.
- * - PJ_JB_STATUS_FRAME_MISSING if the jitter buffer detects that the packet
- * is lost. Application may run packet concealment algorithm when it
- * receives this status.
- */
-PJ_DECL(pj_status_t) pj_jb_get( pj_jitter_buffer *jb, pj_uint32_t *extseq, void **buf );
-
-
-
-PJ_END_DECL
-
-/**
- * @}
- */
-
-#endif /* __PJMEDIA_JBUF_H__ */
+/* $Id$
+ *
+ */
+/*
+ * PJMEDIA - Multimedia over IP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef __PJMEDIA_JBUF_H__
+#define __PJMEDIA_JBUF_H__
+
+
+/**
+ * @file jbuf.h
+ * @brief Adaptive jitter buffer implementation.
+ */
+/**
+ * @defgroup PJMED_JBUF Adaptive jitter buffer
+ * @ingroup PJMEDIA
+ * @{
+ */
+
+#include <pj/types.h>
+
+
+PJ_BEGIN_DECL
+
+
+/**
+ * Opaque declaration of internal frame type used by jitter buffer.
+ */
+struct pj_jbframe;
+
+
+/**
+ * Miscelaneous operation result/status.
+ */
+typedef enum jb_op_status
+{
+ PJ_JB_STATUS_TOO_OLD = -2, /** The packet is too old. */
+ PJ_JB_STATUS_TOO_SOON = -3, /** The packet is too soon. */
+ PJ_JB_STATUS_FRAME_NULL = -4, /** No packet can be retrieved */
+ PJ_JB_STATUS_FRAME_MISSING = -5, /** The specified packet is missing/lost */
+} jb_op_status;
+
+
+/*
+ * Frame list, container abstraction for ordered list with fixed maximum
+ * size. It is used internally by the jitter buffer.
+ */
+typedef struct pj_jbframelist
+{
+ unsigned head, count, maxcount;
+ struct pj_jbframe *frames;
+} pj_jbframelist;
+
+
+/**
+ * Jitter buffer implementation.
+ */
+typedef struct pj_jitter_buffer
+{
+ pj_jbframelist lst; /** The frame list. */
+ int level; /** Current, real-time jitter level. */
+ int max_level; /** Maximum level for the period. */
+ unsigned prefetch; /** Prefetch count currently used. */
+ unsigned get_cnt; /** Number of get operation during prefetch state. */
+ unsigned min; /** Minimum jitter size, in packets. */
+ unsigned max; /** Maximum jitter size, in packets. */
+ pj_uint32_t lastseq; /** Last sequence number put to jitter buffer. */
+ unsigned upd_count; /** Internal counter to manage update interval. */
+ int state; /** Jitter buffer state (1==operational) */
+ int last_op; /** Last jitter buffer operation. */
+} pj_jitter_buffer;
+
+
+/**
+ * Initialize jitter buffer with the specified parameters.
+ * This function will allocate internal frame buffer from the specified pool.
+ * @param jb The jitter buffer to be initialized.
+ * @param pool Pool where memory will be allocated for the frame buffer.
+ * @param min The minimum value of jitter buffer, in packets.
+ * @param max The maximum value of jitter buffer, in packets.
+ * @param maxcount The maximum number of delay, in packets, which must be
+ * greater than max.
+ * @return PJ_SUCCESS on success.
+ */
+PJ_DECL(pj_status_t) pj_jb_init(pj_jitter_buffer *jb, pj_pool_t *pool,
+ unsigned min, unsigned max, unsigned maxcount);
+
+/**
+ * Reset jitter buffer according to the parameters specified when the jitter
+ * buffer was initialized. Any packets currently in the buffer will be
+ * discarded.
+ */
+PJ_DECL(void) pj_jb_reset(pj_jitter_buffer *jb);
+
+/**
+ * Put a pointer to the buffer with the specified sequence number. The pointer
+ * normally points to a buffer held by application, and this pointer will be
+ * returned later on when pj_jb_get() is called. The jitter buffer will not try
+ * to interpret the content of this pointer.
+ * @return:
+ * - PJ_SUCCESS on success.
+ * - PJ_JB_STATUS_TOO_OLD when the packet is too old.
+ * - PJ_JB_STATUS_TOO_SOON when the packet is too soon.
+ */
+PJ_DECL(pj_status_t) pj_jb_put( pj_jitter_buffer *jb, pj_uint32_t extseq, void *buf );
+
+/**
+ * Get the earliest data from the jitter buffer. ONLY when the operation succeeds,
+ * the function returns both sequence number and a pointer in the parameters.
+ * This returned data corresponds to sequence number and pointer that were
+ * given to jitter buffer in the previous pj_jb_put operation.
+ * @return
+ * - PJ_SUCCESS on success
+ * - PJ_JB_STATUS_FRAME_NULL when there is no frames to be returned.
+ * - PJ_JB_STATUS_FRAME_MISSING if the jitter buffer detects that the packet
+ * is lost. Application may run packet concealment algorithm when it
+ * receives this status.
+ */
+PJ_DECL(pj_status_t) pj_jb_get( pj_jitter_buffer *jb, pj_uint32_t *extseq, void **buf );
+
+
+
+PJ_END_DECL
+
+/**
+ * @}
+ */
+
+#endif /* __PJMEDIA_JBUF_H__ */
diff --git a/pjmedia/src/pjmedia/mediamgr.c b/pjmedia/src/pjmedia/mediamgr.c
index 79fd1087..b87d6508 100644
--- a/pjmedia/src/pjmedia/mediamgr.c
+++ b/pjmedia/src/pjmedia/mediamgr.c
@@ -1,97 +1,118 @@
-/* $Id$
- *
- */
-#include <pjmedia/mediamgr.h>
-#include <pj/sock.h>
-#include <pj/pool.h>
-#include <pj/string.h>
-
-PJ_DECL(pj_status_t) g711_init_factory (pj_codec_factory *factory, pj_pool_t *pool);
-PJ_DECL(pj_status_t) g711_deinit_factory (pj_codec_factory *factory);
-
-/** Concrete declaration of media manager. */
-struct pj_med_mgr_t
-{
- /** Pool. */
- pj_pool_t *pool;
-
- /** Pool factory. */
- pj_pool_factory *pf;
-
- /** Codec manager. */
- pj_codec_mgr codec_mgr;
-};
-
-/**
- * Initialize and get the instance of media manager.
- */
-PJ_DEF(pj_med_mgr_t*) pj_med_mgr_create ( pj_pool_factory *pf)
-{
- pj_pool_t *pool;
- pj_med_mgr_t *mm;
- pj_codec_factory *cf;
- pj_status_t status;
-
- pool = pj_pool_create(pf, "mediamgr", 512, 512, NULL);
- if (!pool)
- return NULL;
-
- mm = pj_pool_calloc(pool, 1, sizeof(struct pj_med_mgr_t));
- mm->pool = pool;
- mm->pf = pf;
-
- /* Sound */
- pj_snd_init(pf);
-
- /* Init codec manager. */
- status = pj_codec_mgr_init(&mm->codec_mgr);
- if (status != 0) {
- pj_snd_deinit();
- goto on_error;
- }
-
- /* Init and register G.711 codec. */
- cf = pj_pool_alloc (mm->pool, sizeof(pj_codec_factory));
-
- status = g711_init_factory (cf, mm->pool);
- if (status != 0) {
- pj_snd_deinit();
- return NULL;
- }
-
- status = pj_codec_mgr_register_factory (&mm->codec_mgr, cf);
- if (status != 0)
- return NULL;
-
- return mm;
-
-on_error:
- pj_pool_release(pool);
- return NULL;
-}
-
-/**
- * Get the codec manager instance.
- */
-PJ_DEF(pj_codec_mgr*) pj_med_mgr_get_codec_mgr (pj_med_mgr_t *mgr)
-{
- return &mgr->codec_mgr;
-}
-
-/**
- * Deinitialize media manager.
- */
-PJ_DEF(pj_status_t) pj_med_mgr_destroy (pj_med_mgr_t *mgr)
-{
- pj_snd_deinit();
- pj_pool_release (mgr->pool);
- return 0;
-}
-
-/**
- * Get pool factory.
- */
-PJ_DEF(pj_pool_factory*) pj_med_mgr_get_pool_factory (pj_med_mgr_t *mgr)
-{
- return mgr->pf;
-}
+/* $Id$
+ *
+ */
+/*
+ * PJMEDIA - Multimedia over IP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include <pjmedia/mediamgr.h>
+#include <pj/sock.h>
+#include <pj/pool.h>
+#include <pj/string.h>
+
+PJ_DECL(pj_status_t) g711_init_factory (pj_codec_factory *factory, pj_pool_t *pool);
+PJ_DECL(pj_status_t) g711_deinit_factory (pj_codec_factory *factory);
+
+/** Concrete declaration of media manager. */
+struct pj_med_mgr_t
+{
+ /** Pool. */
+ pj_pool_t *pool;
+
+ /** Pool factory. */
+ pj_pool_factory *pf;
+
+ /** Codec manager. */
+ pj_codec_mgr codec_mgr;
+};
+
+/**
+ * Initialize and get the instance of media manager.
+ */
+PJ_DEF(pj_med_mgr_t*) pj_med_mgr_create ( pj_pool_factory *pf)
+{
+ pj_pool_t *pool;
+ pj_med_mgr_t *mm;
+ pj_codec_factory *cf;
+ pj_status_t status;
+
+ pool = pj_pool_create(pf, "mediamgr", 512, 512, NULL);
+ if (!pool)
+ return NULL;
+
+ mm = pj_pool_calloc(pool, 1, sizeof(struct pj_med_mgr_t));
+ mm->pool = pool;
+ mm->pf = pf;
+
+ /* Sound */
+ pj_snd_init(pf);
+
+ /* Init codec manager. */
+ status = pj_codec_mgr_init(&mm->codec_mgr);
+ if (status != 0) {
+ pj_snd_deinit();
+ goto on_error;
+ }
+
+ /* Init and register G.711 codec. */
+ cf = pj_pool_alloc (mm->pool, sizeof(pj_codec_factory));
+
+ status = g711_init_factory (cf, mm->pool);
+ if (status != 0) {
+ pj_snd_deinit();
+ return NULL;
+ }
+
+ status = pj_codec_mgr_register_factory (&mm->codec_mgr, cf);
+ if (status != 0)
+ return NULL;
+
+ return mm;
+
+on_error:
+ pj_pool_release(pool);
+ return NULL;
+}
+
+/**
+ * Get the codec manager instance.
+ */
+PJ_DEF(pj_codec_mgr*) pj_med_mgr_get_codec_mgr (pj_med_mgr_t *mgr)
+{
+ return &mgr->codec_mgr;
+}
+
+/**
+ * Deinitialize media manager.
+ */
+PJ_DEF(pj_status_t) pj_med_mgr_destroy (pj_med_mgr_t *mgr)
+{
+ pj_snd_deinit();
+ pj_pool_release (mgr->pool);
+ return 0;
+}
+
+/**
+ * Get pool factory.
+ */
+PJ_DEF(pj_pool_factory*) pj_med_mgr_get_pool_factory (pj_med_mgr_t *mgr)
+{
+ return mgr->pf;
+}
diff --git a/pjmedia/src/pjmedia/mediamgr.h b/pjmedia/src/pjmedia/mediamgr.h
index df4ee633..834cf366 100644
--- a/pjmedia/src/pjmedia/mediamgr.h
+++ b/pjmedia/src/pjmedia/mediamgr.h
@@ -1,86 +1,107 @@
-/* $Id$
- *
- */
-
-#ifndef __PJMEDIA_MEDIAMGR_H__
-#define __PJMEDIA_MEDIAMGR_H__
-
-
-/**
- * @file mediamgr.h
- * @brief Media Manager.
- */
-/**
- * @defgroup PJMED_MGR Media Manager
- * @ingroup PJMEDIA
- * @{
- *
- * The media manager acts as placeholder for endpoint capabilities. Each
- * media manager will have a codec manager to manage list of codecs installed
- * in the endpoint and a sound device factory.
- *
- * A reference to media manager instance is required when application wants
- * to create a media session (#pj_media_session_create or
- * #pj_media_session_create_from_sdp).
- */
-
-#include <pjmedia/sound.h>
-#include <pjmedia/codec.h>
-
-
-PJ_BEGIN_DECL
-
-
-/** Opague declaration of media manager. */
-typedef struct pj_med_mgr_t pj_med_mgr_t;
-
-/**
- * Create an instance of media manager.
- *
- * @param pf Pool factory.
- * @param conn_addr Connection address to be used by this media manager.
- *
- * @return A new instance of media manager, or NULL if failed.
- */
-PJ_DECL(pj_med_mgr_t*) pj_med_mgr_create (pj_pool_factory *pf);
-
-/**
- * Destroy media manager instance.
- *
- * @param mgr Media manager instance.
- *
- * @return zero on success.
- */
-PJ_DECL(pj_status_t) pj_med_mgr_destroy (pj_med_mgr_t *mgr);
-
-/**
- * Get pool factory of the media manager as specified when the media
- * manager was created.
- *
- * @param mgr The media manager instance.
- *
- * @return Pool factory instance of the media manager.
- */
-PJ_DECL(pj_pool_factory*) pj_med_mgr_get_pool_factory (pj_med_mgr_t *mgr);
-
-/**
- * Get the codec manager instance.
- *
- * @param mgr The media manager instance.
- *
- * @return The instance of codec manager.
- */
-PJ_DECL(pj_codec_mgr*) pj_med_mgr_get_codec_mgr (pj_med_mgr_t *mgr);
-
-
-
-PJ_END_DECL
-
-
-/**
- * @}
- */
-
-
-
-#endif /* __PJMEDIA_MEDIAMGR_H__ */
+/* $Id$
+ *
+ */
+/*
+ * PJMEDIA - Multimedia over IP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef __PJMEDIA_MEDIAMGR_H__
+#define __PJMEDIA_MEDIAMGR_H__
+
+
+/**
+ * @file mediamgr.h
+ * @brief Media Manager.
+ */
+/**
+ * @defgroup PJMED_MGR Media Manager
+ * @ingroup PJMEDIA
+ * @{
+ *
+ * The media manager acts as placeholder for endpoint capabilities. Each
+ * media manager will have a codec manager to manage list of codecs installed
+ * in the endpoint and a sound device factory.
+ *
+ * A reference to media manager instance is required when application wants
+ * to create a media session (#pj_media_session_create or
+ * #pj_media_session_create_from_sdp).
+ */
+
+#include <pjmedia/sound.h>
+#include <pjmedia/codec.h>
+
+
+PJ_BEGIN_DECL
+
+
+/** Opague declaration of media manager. */
+typedef struct pj_med_mgr_t pj_med_mgr_t;
+
+/**
+ * Create an instance of media manager.
+ *
+ * @param pf Pool factory.
+ * @param conn_addr Connection address to be used by this media manager.
+ *
+ * @return A new instance of media manager, or NULL if failed.
+ */
+PJ_DECL(pj_med_mgr_t*) pj_med_mgr_create (pj_pool_factory *pf);
+
+/**
+ * Destroy media manager instance.
+ *
+ * @param mgr Media manager instance.
+ *
+ * @return zero on success.
+ */
+PJ_DECL(pj_status_t) pj_med_mgr_destroy (pj_med_mgr_t *mgr);
+
+/**
+ * Get pool factory of the media manager as specified when the media
+ * manager was created.
+ *
+ * @param mgr The media manager instance.
+ *
+ * @return Pool factory instance of the media manager.
+ */
+PJ_DECL(pj_pool_factory*) pj_med_mgr_get_pool_factory (pj_med_mgr_t *mgr);
+
+/**
+ * Get the codec manager instance.
+ *
+ * @param mgr The media manager instance.
+ *
+ * @return The instance of codec manager.
+ */
+PJ_DECL(pj_codec_mgr*) pj_med_mgr_get_codec_mgr (pj_med_mgr_t *mgr);
+
+
+
+PJ_END_DECL
+
+
+/**
+ * @}
+ */
+
+
+
+#endif /* __PJMEDIA_MEDIAMGR_H__ */
diff --git a/pjmedia/src/pjmedia/nullsound.c b/pjmedia/src/pjmedia/nullsound.c
index 959b800d..dc0b373e 100644
--- a/pjmedia/src/pjmedia/nullsound.c
+++ b/pjmedia/src/pjmedia/nullsound.c
@@ -1,112 +1,133 @@
-/* $Id$
- *
- */
-#include <pjmedia/sound.h>
-
-/*
- * Null Factory Operations
- */
-static pj_status_t null_sound_init(void);
-static const char *null_sound_get_name(void);
-static pj_status_t null_sound_destroy(void);
-static pj_status_t null_sound_enum_devices(int *count, char *dev_names[]);
-static pj_status_t null_sound_create_dev(const char *dev_name, pj_snd_dev *dev);
-static pj_status_t null_sound_destroy_dev(pj_snd_dev *dev);
-
-
-/*
- * Null Device Operations
- */
-static pj_status_t null_sound_dev_open( pj_snd_dev *dev, pj_snd_role_t role );
-static pj_status_t null_sound_dev_close( pj_snd_dev *dev );
-static pj_status_t null_sound_dev_play( pj_snd_dev *dev );
-static pj_status_t null_sound_dev_record( pj_snd_dev *dev );
-
-
-static pj_snd_dev_factory null_sound_factory =
-{
- &null_sound_init,
- &null_sound_get_name,
- &null_sound_destroy,
- &null_sound_enum_devices,
- &null_sound_create_dev,
- &null_sound_destroy_dev
-};
-
-static struct pj_snd_dev_op null_sound_dev_op =
-{
- &null_sound_dev_open,
- &null_sound_dev_close,
- &null_sound_dev_play,
- &null_sound_dev_record
-};
-
-PJ_DEF(pj_snd_dev_factory*) pj_nullsound_get_factory()
-{
- return &null_sound_factory;
-}
-
-static pj_status_t null_sound_init(void)
-{
- return 0;
-}
-
-static const char *null_sound_get_name(void)
-{
- return "nullsound";
-}
-
-static pj_status_t null_sound_destroy(void)
-{
- return 0;
-}
-
-static pj_status_t null_sound_enum_devices(int *count, char *dev_names[])
-{
- *count = 1;
- dev_names[0] = "nullsound";
- return 0;
-}
-
-static pj_status_t null_sound_create_dev(const char *dev_name, pj_snd_dev *dev)
-{
- PJ_UNUSED_ARG(dev_name);
- dev->op = &null_sound_dev_op;
- return 0;
-}
-
-static pj_status_t null_sound_destroy_dev(pj_snd_dev *dev)
-{
- PJ_UNUSED_ARG(dev);
- return 0;
-}
-
-
-/*
- * Null Device Operations
- */
-static pj_status_t null_sound_dev_open( pj_snd_dev *dev, pj_snd_role_t role )
-{
- PJ_UNUSED_ARG(dev);
- PJ_UNUSED_ARG(role);
- return 0;
-}
-
-static pj_status_t null_sound_dev_close( pj_snd_dev *dev )
-{
- PJ_UNUSED_ARG(dev);
- return 0;
-}
-
-static pj_status_t null_sound_dev_play( pj_snd_dev *dev )
-{
- PJ_UNUSED_ARG(dev);
- return 0;
-}
-
-static pj_status_t null_sound_dev_record( pj_snd_dev *dev )
-{
- PJ_UNUSED_ARG(dev);
- return 0;
-}
-
+/* $Id$
+ *
+ */
+/*
+ * PJMEDIA - Multimedia over IP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include <pjmedia/sound.h>
+
+/*
+ * Null Factory Operations
+ */
+static pj_status_t null_sound_init(void);
+static const char *null_sound_get_name(void);
+static pj_status_t null_sound_destroy(void);
+static pj_status_t null_sound_enum_devices(int *count, char *dev_names[]);
+static pj_status_t null_sound_create_dev(const char *dev_name, pj_snd_dev *dev);
+static pj_status_t null_sound_destroy_dev(pj_snd_dev *dev);
+
+
+/*
+ * Null Device Operations
+ */
+static pj_status_t null_sound_dev_open( pj_snd_dev *dev, pj_snd_role_t role );
+static pj_status_t null_sound_dev_close( pj_snd_dev *dev );
+static pj_status_t null_sound_dev_play( pj_snd_dev *dev );
+static pj_status_t null_sound_dev_record( pj_snd_dev *dev );
+
+
+static pj_snd_dev_factory null_sound_factory =
+{
+ &null_sound_init,
+ &null_sound_get_name,
+ &null_sound_destroy,
+ &null_sound_enum_devices,
+ &null_sound_create_dev,
+ &null_sound_destroy_dev
+};
+
+static struct pj_snd_dev_op null_sound_dev_op =
+{
+ &null_sound_dev_open,
+ &null_sound_dev_close,
+ &null_sound_dev_play,
+ &null_sound_dev_record
+};
+
+PJ_DEF(pj_snd_dev_factory*) pj_nullsound_get_factory()
+{
+ return &null_sound_factory;
+}
+
+static pj_status_t null_sound_init(void)
+{
+ return 0;
+}
+
+static const char *null_sound_get_name(void)
+{
+ return "nullsound";
+}
+
+static pj_status_t null_sound_destroy(void)
+{
+ return 0;
+}
+
+static pj_status_t null_sound_enum_devices(int *count, char *dev_names[])
+{
+ *count = 1;
+ dev_names[0] = "nullsound";
+ return 0;
+}
+
+static pj_status_t null_sound_create_dev(const char *dev_name, pj_snd_dev *dev)
+{
+ PJ_UNUSED_ARG(dev_name);
+ dev->op = &null_sound_dev_op;
+ return 0;
+}
+
+static pj_status_t null_sound_destroy_dev(pj_snd_dev *dev)
+{
+ PJ_UNUSED_ARG(dev);
+ return 0;
+}
+
+
+/*
+ * Null Device Operations
+ */
+static pj_status_t null_sound_dev_open( pj_snd_dev *dev, pj_snd_role_t role )
+{
+ PJ_UNUSED_ARG(dev);
+ PJ_UNUSED_ARG(role);
+ return 0;
+}
+
+static pj_status_t null_sound_dev_close( pj_snd_dev *dev )
+{
+ PJ_UNUSED_ARG(dev);
+ return 0;
+}
+
+static pj_status_t null_sound_dev_play( pj_snd_dev *dev )
+{
+ PJ_UNUSED_ARG(dev);
+ return 0;
+}
+
+static pj_status_t null_sound_dev_record( pj_snd_dev *dev )
+{
+ PJ_UNUSED_ARG(dev);
+ return 0;
+}
+
diff --git a/pjmedia/src/pjmedia/pasound.c b/pjmedia/src/pjmedia/pasound.c
index 60065b31..9eb2f1a6 100644
--- a/pjmedia/src/pjmedia/pasound.c
+++ b/pjmedia/src/pjmedia/pasound.c
@@ -1,389 +1,410 @@
-/* $Id$
- *
- */
-#include <pjmedia/sound.h>
-#include <pj/string.h>
-#include <pj/os.h>
-#include <pj/log.h>
-#include <portaudio.h>
-
-#define THIS_FILE "pasound.c"
-
-static struct snd_mgr
-{
- pj_pool_factory *factory;
-} snd_mgr;
-
-struct pj_snd_stream
-{
- pj_pool_t *pool;
- PaStream *stream;
- int dev_index;
- int bytes_per_sample;
- pj_uint32_t samples_per_sec;
- pj_uint32_t timestamp;
- pj_uint32_t underflow;
- pj_uint32_t overflow;
- void *user_data;
- pj_snd_rec_cb rec_cb;
- pj_snd_play_cb play_cb;
- pj_bool_t quit_flag;
- pj_bool_t thread_has_exited;
- pj_bool_t thread_initialized;
- pj_thread_desc thread_desc;
- pj_thread_t *thread;
-};
-
-
-static int PaRecorderCallback(const void *input,
- void *output,
- unsigned long frameCount,
- const PaStreamCallbackTimeInfo* timeInfo,
- PaStreamCallbackFlags statusFlags,
- void *userData )
-{
- pj_snd_stream *stream = userData;
- pj_status_t status;
-
- PJ_UNUSED_ARG(output)
- PJ_UNUSED_ARG(timeInfo)
-
- if (stream->quit_flag)
- goto on_break;
-
- if (stream->thread_initialized == 0) {
- stream->thread = pj_thread_register("pa_rec", stream->thread_desc);
- stream->thread_initialized = 1;
- }
-
- if (statusFlags & paInputUnderflow)
- ++stream->underflow;
- if (statusFlags & paInputOverflow)
- ++stream->overflow;
-
- stream->timestamp += frameCount;
-
- status = (*stream->rec_cb)(stream->user_data, stream->timestamp,
- input, frameCount * stream->bytes_per_sample);
-
- if (status==0)
- return paContinue;
-
-on_break:
- stream->thread_has_exited = 1;
- return paAbort;
-}
-
-static int PaPlayerCallback( const void *input,
- void *output,
- unsigned long frameCount,
- const PaStreamCallbackTimeInfo* timeInfo,
- PaStreamCallbackFlags statusFlags,
- void *userData )
-{
- pj_snd_stream *stream = userData;
- pj_status_t status;
- unsigned size = frameCount * stream->bytes_per_sample;
-
- PJ_UNUSED_ARG(input)
- PJ_UNUSED_ARG(timeInfo)
-
- if (stream->quit_flag)
- goto on_break;
-
- if (stream->thread_initialized == 0) {
- stream->thread = pj_thread_register("pa_rec", stream->thread_desc);
- stream->thread_initialized = 1;
- }
-
- if (statusFlags & paInputUnderflow)
- ++stream->underflow;
- if (statusFlags & paInputOverflow)
- ++stream->overflow;
-
- stream->timestamp += frameCount;
-
- status = (*stream->play_cb)(stream->user_data, stream->timestamp,
- output, size);
-
- if (status==0)
- return paContinue;
-
-on_break:
- stream->thread_has_exited = 1;
- return paAbort;
-}
-
-
-/*
- * Init sound library.
- */
-PJ_DEF(pj_status_t) pj_snd_init(pj_pool_factory *factory)
-{
- int err;
-
- snd_mgr.factory = factory;
- err = Pa_Initialize();
-
- PJ_LOG(4,(THIS_FILE, "PortAudio sound library initialized, status=%d", err));
-
- return err;
-}
-
-
-/*
- * Get device count.
- */
-PJ_DEF(int) pj_snd_get_dev_count(void)
-{
- return Pa_GetDeviceCount();
-}
-
-
-/*
- * Get device info.
- */
-PJ_DEF(const pj_snd_dev_info*) pj_snd_get_dev_info(unsigned index)
-{
- static pj_snd_dev_info info;
- const PaDeviceInfo *pa_info;
-
- pa_info = Pa_GetDeviceInfo(index);
- if (!pa_info)
- return NULL;
-
- pj_memset(&info, 0, sizeof(info));
- strncpy(info.name, pa_info->name, sizeof(info.name));
- info.name[sizeof(info.name)-1] = '\0';
- info.input_count = pa_info->maxInputChannels;
- info.output_count = pa_info->maxOutputChannels;
- info.default_samples_per_sec = (unsigned)pa_info->defaultSampleRate;
-
- return &info;
-}
-
-
-/*
- * Open stream.
- */
-PJ_DEF(pj_snd_stream*) pj_snd_open_recorder( int index,
- const pj_snd_stream_info *param,
- pj_snd_rec_cb rec_cb,
- void *user_data)
-{
- pj_pool_t *pool;
- pj_snd_stream *stream;
- PaStreamParameters inputParam;
- int sampleFormat;
- const PaDeviceInfo *paDevInfo = NULL;
- PaError err;
-
- if (index == -1) {
- int count = Pa_GetDeviceCount();
- for (index=0; index<count; ++index) {
- paDevInfo = Pa_GetDeviceInfo(index);
- if (paDevInfo->maxInputChannels > 0)
- break;
- }
- if (index == count) {
- PJ_LOG(2,(THIS_FILE, "Error: unable to find recorder device"));
- return NULL;
- }
- } else {
- paDevInfo = Pa_GetDeviceInfo(index);
- if (!paDevInfo)
- return NULL;
- }
-
- if (param->bits_per_sample == 8)
- sampleFormat = paUInt8;
- else if (param->bits_per_sample == 16)
- sampleFormat = paInt16;
- else if (param->bits_per_sample == 32)
- sampleFormat = paInt32;
- else
- return NULL;
-
- pool = pj_pool_create( snd_mgr.factory, "sndstream", 1024, 1024, NULL);
- if (!pool)
- return NULL;
-
- stream = pj_pool_calloc(pool, 1, sizeof(*stream));
- stream->pool = pool;
- stream->user_data = user_data;
- stream->dev_index = index;
- stream->samples_per_sec = param->samples_per_frame;
- stream->bytes_per_sample = param->bits_per_sample / 8;
- stream->rec_cb = rec_cb;
-
- pj_memset(&inputParam, 0, sizeof(inputParam));
- inputParam.device = index;
- inputParam.channelCount = 1;
- inputParam.hostApiSpecificStreamInfo = NULL;
- inputParam.sampleFormat = sampleFormat;
- inputParam.suggestedLatency = paDevInfo->defaultLowInputLatency;
-
- err = Pa_OpenStream( &stream->stream, &inputParam, NULL,
- param->samples_per_sec,
- param->samples_per_frame * param->frames_per_packet,
- 0,
- &PaRecorderCallback, stream );
- if (err != paNoError) {
- pj_pool_release(pool);
- PJ_LOG(2,(THIS_FILE, "Error opening player: %s", Pa_GetErrorText(err)));
- return NULL;
- }
-
- PJ_LOG(4,(THIS_FILE, "%s opening device %s for recording, sample rate=%d, "
- "%d bits per sample, %d samples per buffer",
- (err==0 ? "Success" : "Error"),
- paDevInfo->name, param->samples_per_sec,
- param->bits_per_sample,
- param->samples_per_frame * param->frames_per_packet));
-
- return stream;
-}
-
-
-PJ_DEF(pj_snd_stream*) pj_snd_open_player(int index,
- const pj_snd_stream_info *param,
- pj_snd_play_cb play_cb,
- void *user_data)
-{
- pj_pool_t *pool;
- pj_snd_stream *stream;
- PaStreamParameters outputParam;
- int sampleFormat;
- const PaDeviceInfo *paDevInfo = NULL;
- PaError err;
-
- if (index == -1) {
- int count = Pa_GetDeviceCount();
- for (index=0; index<count; ++index) {
- paDevInfo = Pa_GetDeviceInfo(index);
- if (paDevInfo->maxOutputChannels > 0)
- break;
- }
- if (index == count) {
- PJ_LOG(2,(THIS_FILE, "Error: unable to find player device"));
- return NULL;
- }
- } else {
- paDevInfo = Pa_GetDeviceInfo(index);
- if (!paDevInfo)
- return NULL;
- }
-
- if (param->bits_per_sample == 8)
- sampleFormat = paUInt8;
- else if (param->bits_per_sample == 16)
- sampleFormat = paInt16;
- else if (param->bits_per_sample == 32)
- sampleFormat = paInt32;
- else
- return NULL;
-
- pool = pj_pool_create( snd_mgr.factory, "sndstream", 1024, 1024, NULL);
- if (!pool)
- return NULL;
-
- stream = pj_pool_calloc(pool, 1, sizeof(*stream));
- stream->pool = pool;
- stream->user_data = user_data;
- stream->samples_per_sec = param->samples_per_frame;
- stream->bytes_per_sample = param->bits_per_sample / 8;
- stream->dev_index = index;
- stream->play_cb = play_cb;
-
- pj_memset(&outputParam, 0, sizeof(outputParam));
- outputParam.device = index;
- outputParam.channelCount = 1;
- outputParam.hostApiSpecificStreamInfo = NULL;
- outputParam.sampleFormat = sampleFormat;
- outputParam.suggestedLatency = paDevInfo->defaultLowInputLatency;
-
- err = Pa_OpenStream( &stream->stream, NULL, &outputParam,
- param->samples_per_sec,
- param->samples_per_frame * param->frames_per_packet,
- 0,
- &PaPlayerCallback, stream );
- if (err != paNoError) {
- pj_pool_release(pool);
- PJ_LOG(2,(THIS_FILE, "Error opening player: %s", Pa_GetErrorText(err)));
- return NULL;
- }
-
- PJ_LOG(4,(THIS_FILE, "%s opening device %s for playing, sample rate=%d, "
- "%d bits per sample, %d samples per buffer",
- (err==0 ? "Success" : "Error"),
- paDevInfo->name, param->samples_per_sec,
- param->bits_per_sample,
- param->samples_per_frame * param->frames_per_packet));
-
- return stream;
-}
-
-
-/*
- * Start stream.
- */
-PJ_DEF(pj_status_t) pj_snd_stream_start(pj_snd_stream *stream)
-{
- PJ_LOG(4,(THIS_FILE, "Starting stream.."));
- return Pa_StartStream(stream->stream);
-}
-
-/*
- * Stop stream.
- */
-PJ_DEF(pj_status_t) pj_snd_stream_stop(pj_snd_stream *stream)
-{
- int i, err;
-
- stream->quit_flag = 1;
- for (i=0; !stream->thread_has_exited && i<100; ++i)
- pj_thread_sleep(10);
-
- pj_thread_sleep(1);
-
- PJ_LOG(4,(THIS_FILE, "Stopping stream.."));
-
- err = Pa_StopStream(stream->stream);
- return err;
-}
-
-/*
- * Destroy stream.
- */
-PJ_DEF(pj_status_t) pj_snd_stream_close(pj_snd_stream *stream)
-{
- int i, err;
- const PaDeviceInfo *paDevInfo;
-
- stream->quit_flag = 1;
- for (i=0; !stream->thread_has_exited && i<100; ++i)
- pj_thread_sleep(10);
-
- pj_thread_sleep(1);
-
- paDevInfo = Pa_GetDeviceInfo(stream->dev_index);
-
- PJ_LOG(4,(THIS_FILE, "Closing %s: %lu underflow, %lu overflow",
- paDevInfo->name,
- stream->underflow, stream->overflow));
-
- err = Pa_CloseStream(stream->stream);
- pj_pool_release(stream->pool);
- return err;
-}
-
-/*
- * Deinitialize sound library.
- */
-PJ_DEF(pj_status_t) pj_snd_deinit(void)
-{
- PJ_LOG(4,(THIS_FILE, "PortAudio sound library shutting down.."));
-
- return Pa_Terminate();
-}
-
+/* $Id$
+ *
+ */
+/*
+ * PJMEDIA - Multimedia over IP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include <pjmedia/sound.h>
+#include <pj/string.h>
+#include <pj/os.h>
+#include <pj/log.h>
+#include <portaudio.h>
+
+#define THIS_FILE "pasound.c"
+
+static struct snd_mgr
+{
+ pj_pool_factory *factory;
+} snd_mgr;
+
+struct pj_snd_stream
+{
+ pj_pool_t *pool;
+ PaStream *stream;
+ int dev_index;
+ int bytes_per_sample;
+ pj_uint32_t samples_per_sec;
+ pj_uint32_t timestamp;
+ pj_uint32_t underflow;
+ pj_uint32_t overflow;
+ void *user_data;
+ pj_snd_rec_cb rec_cb;
+ pj_snd_play_cb play_cb;
+ pj_bool_t quit_flag;
+ pj_bool_t thread_has_exited;
+ pj_bool_t thread_initialized;
+ pj_thread_desc thread_desc;
+ pj_thread_t *thread;
+};
+
+
+static int PaRecorderCallback(const void *input,
+ void *output,
+ unsigned long frameCount,
+ const PaStreamCallbackTimeInfo* timeInfo,
+ PaStreamCallbackFlags statusFlags,
+ void *userData )
+{
+ pj_snd_stream *stream = userData;
+ pj_status_t status;
+
+ PJ_UNUSED_ARG(output)
+ PJ_UNUSED_ARG(timeInfo)
+
+ if (stream->quit_flag)
+ goto on_break;
+
+ if (stream->thread_initialized == 0) {
+ stream->thread = pj_thread_register("pa_rec", stream->thread_desc);
+ stream->thread_initialized = 1;
+ }
+
+ if (statusFlags & paInputUnderflow)
+ ++stream->underflow;
+ if (statusFlags & paInputOverflow)
+ ++stream->overflow;
+
+ stream->timestamp += frameCount;
+
+ status = (*stream->rec_cb)(stream->user_data, stream->timestamp,
+ input, frameCount * stream->bytes_per_sample);
+
+ if (status==0)
+ return paContinue;
+
+on_break:
+ stream->thread_has_exited = 1;
+ return paAbort;
+}
+
+static int PaPlayerCallback( const void *input,
+ void *output,
+ unsigned long frameCount,
+ const PaStreamCallbackTimeInfo* timeInfo,
+ PaStreamCallbackFlags statusFlags,
+ void *userData )
+{
+ pj_snd_stream *stream = userData;
+ pj_status_t status;
+ unsigned size = frameCount * stream->bytes_per_sample;
+
+ PJ_UNUSED_ARG(input)
+ PJ_UNUSED_ARG(timeInfo)
+
+ if (stream->quit_flag)
+ goto on_break;
+
+ if (stream->thread_initialized == 0) {
+ stream->thread = pj_thread_register("pa_rec", stream->thread_desc);
+ stream->thread_initialized = 1;
+ }
+
+ if (statusFlags & paInputUnderflow)
+ ++stream->underflow;
+ if (statusFlags & paInputOverflow)
+ ++stream->overflow;
+
+ stream->timestamp += frameCount;
+
+ status = (*stream->play_cb)(stream->user_data, stream->timestamp,
+ output, size);
+
+ if (status==0)
+ return paContinue;
+
+on_break:
+ stream->thread_has_exited = 1;
+ return paAbort;
+}
+
+
+/*
+ * Init sound library.
+ */
+PJ_DEF(pj_status_t) pj_snd_init(pj_pool_factory *factory)
+{
+ int err;
+
+ snd_mgr.factory = factory;
+ err = Pa_Initialize();
+
+ PJ_LOG(4,(THIS_FILE, "PortAudio sound library initialized, status=%d", err));
+
+ return err;
+}
+
+
+/*
+ * Get device count.
+ */
+PJ_DEF(int) pj_snd_get_dev_count(void)
+{
+ return Pa_GetDeviceCount();
+}
+
+
+/*
+ * Get device info.
+ */
+PJ_DEF(const pj_snd_dev_info*) pj_snd_get_dev_info(unsigned index)
+{
+ static pj_snd_dev_info info;
+ const PaDeviceInfo *pa_info;
+
+ pa_info = Pa_GetDeviceInfo(index);
+ if (!pa_info)
+ return NULL;
+
+ pj_memset(&info, 0, sizeof(info));
+ strncpy(info.name, pa_info->name, sizeof(info.name));
+ info.name[sizeof(info.name)-1] = '\0';
+ info.input_count = pa_info->maxInputChannels;
+ info.output_count = pa_info->maxOutputChannels;
+ info.default_samples_per_sec = (unsigned)pa_info->defaultSampleRate;
+
+ return &info;
+}
+
+
+/*
+ * Open stream.
+ */
+PJ_DEF(pj_snd_stream*) pj_snd_open_recorder( int index,
+ const pj_snd_stream_info *param,
+ pj_snd_rec_cb rec_cb,
+ void *user_data)
+{
+ pj_pool_t *pool;
+ pj_snd_stream *stream;
+ PaStreamParameters inputParam;
+ int sampleFormat;
+ const PaDeviceInfo *paDevInfo = NULL;
+ PaError err;
+
+ if (index == -1) {
+ int count = Pa_GetDeviceCount();
+ for (index=0; index<count; ++index) {
+ paDevInfo = Pa_GetDeviceInfo(index);
+ if (paDevInfo->maxInputChannels > 0)
+ break;
+ }
+ if (index == count) {
+ PJ_LOG(2,(THIS_FILE, "Error: unable to find recorder device"));
+ return NULL;
+ }
+ } else {
+ paDevInfo = Pa_GetDeviceInfo(index);
+ if (!paDevInfo)
+ return NULL;
+ }
+
+ if (param->bits_per_sample == 8)
+ sampleFormat = paUInt8;
+ else if (param->bits_per_sample == 16)
+ sampleFormat = paInt16;
+ else if (param->bits_per_sample == 32)
+ sampleFormat = paInt32;
+ else
+ return NULL;
+
+ pool = pj_pool_create( snd_mgr.factory, "sndstream", 1024, 1024, NULL);
+ if (!pool)
+ return NULL;
+
+ stream = pj_pool_calloc(pool, 1, sizeof(*stream));
+ stream->pool = pool;
+ stream->user_data = user_data;
+ stream->dev_index = index;
+ stream->samples_per_sec = param->samples_per_frame;
+ stream->bytes_per_sample = param->bits_per_sample / 8;
+ stream->rec_cb = rec_cb;
+
+ pj_memset(&inputParam, 0, sizeof(inputParam));
+ inputParam.device = index;
+ inputParam.channelCount = 1;
+ inputParam.hostApiSpecificStreamInfo = NULL;
+ inputParam.sampleFormat = sampleFormat;
+ inputParam.suggestedLatency = paDevInfo->defaultLowInputLatency;
+
+ err = Pa_OpenStream( &stream->stream, &inputParam, NULL,
+ param->samples_per_sec,
+ param->samples_per_frame * param->frames_per_packet,
+ 0,
+ &PaRecorderCallback, stream );
+ if (err != paNoError) {
+ pj_pool_release(pool);
+ PJ_LOG(2,(THIS_FILE, "Error opening player: %s", Pa_GetErrorText(err)));
+ return NULL;
+ }
+
+ PJ_LOG(4,(THIS_FILE, "%s opening device %s for recording, sample rate=%d, "
+ "%d bits per sample, %d samples per buffer",
+ (err==0 ? "Success" : "Error"),
+ paDevInfo->name, param->samples_per_sec,
+ param->bits_per_sample,
+ param->samples_per_frame * param->frames_per_packet));
+
+ return stream;
+}
+
+
+PJ_DEF(pj_snd_stream*) pj_snd_open_player(int index,
+ const pj_snd_stream_info *param,
+ pj_snd_play_cb play_cb,
+ void *user_data)
+{
+ pj_pool_t *pool;
+ pj_snd_stream *stream;
+ PaStreamParameters outputParam;
+ int sampleFormat;
+ const PaDeviceInfo *paDevInfo = NULL;
+ PaError err;
+
+ if (index == -1) {
+ int count = Pa_GetDeviceCount();
+ for (index=0; index<count; ++index) {
+ paDevInfo = Pa_GetDeviceInfo(index);
+ if (paDevInfo->maxOutputChannels > 0)
+ break;
+ }
+ if (index == count) {
+ PJ_LOG(2,(THIS_FILE, "Error: unable to find player device"));
+ return NULL;
+ }
+ } else {
+ paDevInfo = Pa_GetDeviceInfo(index);
+ if (!paDevInfo)
+ return NULL;
+ }
+
+ if (param->bits_per_sample == 8)
+ sampleFormat = paUInt8;
+ else if (param->bits_per_sample == 16)
+ sampleFormat = paInt16;
+ else if (param->bits_per_sample == 32)
+ sampleFormat = paInt32;
+ else
+ return NULL;
+
+ pool = pj_pool_create( snd_mgr.factory, "sndstream", 1024, 1024, NULL);
+ if (!pool)
+ return NULL;
+
+ stream = pj_pool_calloc(pool, 1, sizeof(*stream));
+ stream->pool = pool;
+ stream->user_data = user_data;
+ stream->samples_per_sec = param->samples_per_frame;
+ stream->bytes_per_sample = param->bits_per_sample / 8;
+ stream->dev_index = index;
+ stream->play_cb = play_cb;
+
+ pj_memset(&outputParam, 0, sizeof(outputParam));
+ outputParam.device = index;
+ outputParam.channelCount = 1;
+ outputParam.hostApiSpecificStreamInfo = NULL;
+ outputParam.sampleFormat = sampleFormat;
+ outputParam.suggestedLatency = paDevInfo->defaultLowInputLatency;
+
+ err = Pa_OpenStream( &stream->stream, NULL, &outputParam,
+ param->samples_per_sec,
+ param->samples_per_frame * param->frames_per_packet,
+ 0,
+ &PaPlayerCallback, stream );
+ if (err != paNoError) {
+ pj_pool_release(pool);
+ PJ_LOG(2,(THIS_FILE, "Error opening player: %s", Pa_GetErrorText(err)));
+ return NULL;
+ }
+
+ PJ_LOG(4,(THIS_FILE, "%s opening device %s for playing, sample rate=%d, "
+ "%d bits per sample, %d samples per buffer",
+ (err==0 ? "Success" : "Error"),
+ paDevInfo->name, param->samples_per_sec,
+ param->bits_per_sample,
+ param->samples_per_frame * param->frames_per_packet));
+
+ return stream;
+}
+
+
+/*
+ * Start stream.
+ */
+PJ_DEF(pj_status_t) pj_snd_stream_start(pj_snd_stream *stream)
+{
+ PJ_LOG(4,(THIS_FILE, "Starting stream.."));
+ return Pa_StartStream(stream->stream);
+}
+
+/*
+ * Stop stream.
+ */
+PJ_DEF(pj_status_t) pj_snd_stream_stop(pj_snd_stream *stream)
+{
+ int i, err;
+
+ stream->quit_flag = 1;
+ for (i=0; !stream->thread_has_exited && i<100; ++i)
+ pj_thread_sleep(10);
+
+ pj_thread_sleep(1);
+
+ PJ_LOG(4,(THIS_FILE, "Stopping stream.."));
+
+ err = Pa_StopStream(stream->stream);
+ return err;
+}
+
+/*
+ * Destroy stream.
+ */
+PJ_DEF(pj_status_t) pj_snd_stream_close(pj_snd_stream *stream)
+{
+ int i, err;
+ const PaDeviceInfo *paDevInfo;
+
+ stream->quit_flag = 1;
+ for (i=0; !stream->thread_has_exited && i<100; ++i)
+ pj_thread_sleep(10);
+
+ pj_thread_sleep(1);
+
+ paDevInfo = Pa_GetDeviceInfo(stream->dev_index);
+
+ PJ_LOG(4,(THIS_FILE, "Closing %s: %lu underflow, %lu overflow",
+ paDevInfo->name,
+ stream->underflow, stream->overflow));
+
+ err = Pa_CloseStream(stream->stream);
+ pj_pool_release(stream->pool);
+ return err;
+}
+
+/*
+ * Deinitialize sound library.
+ */
+PJ_DEF(pj_status_t) pj_snd_deinit(void)
+{
+ PJ_LOG(4,(THIS_FILE, "PortAudio sound library shutting down.."));
+
+ return Pa_Terminate();
+}
+
diff --git a/pjmedia/src/pjmedia/portaudio/dsound_wrapper.c b/pjmedia/src/pjmedia/portaudio/dsound_wrapper.c
index 207d2873..782e824f 100644
--- a/pjmedia/src/pjmedia/portaudio/dsound_wrapper.c
+++ b/pjmedia/src/pjmedia/portaudio/dsound_wrapper.c
@@ -1,616 +1,637 @@
-/*
- * $Id: dsound_wrapper.c,v 1.1.1.1.2.11 2003/09/07 13:04:53 rossbencina Exp $
- * Simplified DirectSound interface.
- *
- * Author: Phil Burk & Robert Marsanyi
- *
- * PortAudio Portable Real-Time Audio Library
- * For more information see: http://www.softsynth.com/portaudio/
- * DirectSound Implementation
- * Copyright (c) 1999-2000 Phil Burk & Robert Marsanyi
- *
- * Permission is hereby granted, free of charge, to any person obtaining
- * a copy of this software and associated documentation files
- * (the "Software"), to deal in the Software without restriction,
- * including without limitation the rights to use, copy, modify, merge,
- * publish, distribute, sublicense, and/or sell copies of the Software,
- * and to permit persons to whom the Software is furnished to do so,
- * subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be
- * included in all copies or substantial portions of the Software.
- *
- * Any person wishing to distribute modifications to the Software is
- * requested to send the modifications to the original developer so that
- * they can be incorporated into the canonical version.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
- * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
- * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
- * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
- * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
- *
- */
-#include <stdio.h>
-#include <stdlib.h>
-#include <math.h>
-
-#include "dsound_wrapper.h"
-#include "pa_trace.h"
-
-/*
- Rather than linking with dxguid.a or using "#define INITGUID" to force a
- header file to instantiate the required GUID(s), we define them directly
- below.
-*/
-#include <initguid.h> // needed for the DEFINE_GUID macro
-DEFINE_GUID(IID_IDirectSoundNotify, 0xb0210783, 0x89cd, 0x11d0, 0xaf, 0x8, 0x0, 0xa0, 0xc9, 0x25, 0xcd, 0x16);
-
-
-/************************************************************************************/
-DSoundEntryPoints dswDSoundEntryPoints = { 0, 0, 0, 0, 0, 0, 0 };
-/************************************************************************************/
-static HRESULT WINAPI DummyDirectSoundCreate(LPGUID lpcGuidDevice, LPDIRECTSOUND *ppDS, LPUNKNOWN pUnkOuter)
-{
- (void)lpcGuidDevice; /* unused parameter */
- (void)ppDS; /* unused parameter */
- (void)pUnkOuter; /* unused parameter */
- return E_NOTIMPL;
-}
-
-static HRESULT WINAPI DummyDirectSoundEnumerateW(LPDSENUMCALLBACKW lpDSEnumCallback, LPVOID lpContext)
-{
- (void)lpDSEnumCallback; /* unused parameter */
- (void)lpContext; /* unused parameter */
- return E_NOTIMPL;
-}
-
-static HRESULT WINAPI DummyDirectSoundEnumerateA(LPDSENUMCALLBACKA lpDSEnumCallback, LPVOID lpContext)
-{
- (void)lpDSEnumCallback; /* unused parameter */
- (void)lpContext; /* unused parameter */
- return E_NOTIMPL;
-}
-
-static HRESULT WINAPI DummyDirectSoundCaptureCreate(LPGUID lpcGUID, LPDIRECTSOUNDCAPTURE *lplpDSC, LPUNKNOWN pUnkOuter)
-{
- (void)lpcGUID; /* unused parameter */
- (void)lplpDSC; /* unused parameter */
- (void)pUnkOuter; /* unused parameter */
- return E_NOTIMPL;
-}
-
-static HRESULT WINAPI DummyDirectSoundCaptureEnumerateW(LPDSENUMCALLBACKW lpDSCEnumCallback, LPVOID lpContext)
-{
- (void)lpDSCEnumCallback; /* unused parameter */
- (void)lpContext; /* unused parameter */
- return E_NOTIMPL;
-}
-
-static HRESULT WINAPI DummyDirectSoundCaptureEnumerateA(LPDSENUMCALLBACKA lpDSCEnumCallback, LPVOID lpContext)
-{
- (void)lpDSCEnumCallback; /* unused parameter */
- (void)lpContext; /* unused parameter */
- return E_NOTIMPL;
-}
-/************************************************************************************/
-void DSW_InitializeDSoundEntryPoints(void)
-{
- dswDSoundEntryPoints.hInstance_ = LoadLibrary("dsound.dll");
- if( dswDSoundEntryPoints.hInstance_ != NULL )
- {
- dswDSoundEntryPoints.DirectSoundCreate =
- (HRESULT (WINAPI *)(LPGUID, LPDIRECTSOUND *, LPUNKNOWN))
- GetProcAddress( dswDSoundEntryPoints.hInstance_, "DirectSoundCreate" );
- if( dswDSoundEntryPoints.DirectSoundCreate == NULL )
- dswDSoundEntryPoints.DirectSoundCreate = DummyDirectSoundCreate;
-
- dswDSoundEntryPoints.DirectSoundEnumerateW =
- (HRESULT (WINAPI *)(LPDSENUMCALLBACKW, LPVOID))
- GetProcAddress( dswDSoundEntryPoints.hInstance_, "DirectSoundEnumerateW" );
- if( dswDSoundEntryPoints.DirectSoundEnumerateW == NULL )
- dswDSoundEntryPoints.DirectSoundEnumerateW = DummyDirectSoundEnumerateW;
-
- dswDSoundEntryPoints.DirectSoundEnumerateA =
- (HRESULT (WINAPI *)(LPDSENUMCALLBACKA, LPVOID))
- GetProcAddress( dswDSoundEntryPoints.hInstance_, "DirectSoundEnumerateA" );
- if( dswDSoundEntryPoints.DirectSoundEnumerateA == NULL )
- dswDSoundEntryPoints.DirectSoundEnumerateA = DummyDirectSoundEnumerateA;
-
- dswDSoundEntryPoints.DirectSoundCaptureCreate =
- (HRESULT (WINAPI *)(LPGUID, LPDIRECTSOUNDCAPTURE *, LPUNKNOWN))
- GetProcAddress( dswDSoundEntryPoints.hInstance_, "DirectSoundCaptureCreate" );
- if( dswDSoundEntryPoints.DirectSoundCaptureCreate == NULL )
- dswDSoundEntryPoints.DirectSoundCaptureCreate = DummyDirectSoundCaptureCreate;
-
- dswDSoundEntryPoints.DirectSoundCaptureEnumerateW =
- (HRESULT (WINAPI *)(LPDSENUMCALLBACKW, LPVOID))
- GetProcAddress( dswDSoundEntryPoints.hInstance_, "DirectSoundCaptureEnumerateW" );
- if( dswDSoundEntryPoints.DirectSoundCaptureEnumerateW == NULL )
- dswDSoundEntryPoints.DirectSoundCaptureEnumerateW = DummyDirectSoundCaptureEnumerateW;
-
- dswDSoundEntryPoints.DirectSoundCaptureEnumerateA =
- (HRESULT (WINAPI *)(LPDSENUMCALLBACKA, LPVOID))
- GetProcAddress( dswDSoundEntryPoints.hInstance_, "DirectSoundCaptureEnumerateA" );
- if( dswDSoundEntryPoints.DirectSoundCaptureEnumerateA == NULL )
- dswDSoundEntryPoints.DirectSoundCaptureEnumerateA = DummyDirectSoundCaptureEnumerateA;
- }
- else
- {
- /* initialize with dummy entry points to make live easy when ds isn't present */
- dswDSoundEntryPoints.DirectSoundCreate = DummyDirectSoundCreate;
- dswDSoundEntryPoints.DirectSoundEnumerateW = DummyDirectSoundEnumerateW;
- dswDSoundEntryPoints.DirectSoundEnumerateA = DummyDirectSoundEnumerateA;
- dswDSoundEntryPoints.DirectSoundCaptureCreate = DummyDirectSoundCaptureCreate;
- dswDSoundEntryPoints.DirectSoundCaptureEnumerateW = DummyDirectSoundCaptureEnumerateW;
- dswDSoundEntryPoints.DirectSoundCaptureEnumerateA = DummyDirectSoundCaptureEnumerateA;
- }
-}
-/************************************************************************************/
-void DSW_TerminateDSoundEntryPoints(void)
-{
- if( dswDSoundEntryPoints.hInstance_ != NULL )
- {
- FreeLibrary( dswDSoundEntryPoints.hInstance_ );
- dswDSoundEntryPoints.hInstance_ = NULL;
- /* ensure that we crash reliably if the entry points arent initialised */
- dswDSoundEntryPoints.DirectSoundCreate = 0;
- dswDSoundEntryPoints.DirectSoundEnumerateW = 0;
- dswDSoundEntryPoints.DirectSoundEnumerateA = 0;
- dswDSoundEntryPoints.DirectSoundCaptureCreate = 0;
- dswDSoundEntryPoints.DirectSoundCaptureEnumerateW = 0;
- dswDSoundEntryPoints.DirectSoundCaptureEnumerateA = 0;
- }
-}
-/************************************************************************************/
-void DSW_Term( DSoundWrapper *dsw )
-{
- // Cleanup the sound buffers
- if (dsw->dsw_OutputBuffer)
- {
- IDirectSoundBuffer_Stop( dsw->dsw_OutputBuffer );
- IDirectSoundBuffer_Release( dsw->dsw_OutputBuffer );
- dsw->dsw_OutputBuffer = NULL;
- }
-
- if (dsw->dsw_InputBuffer)
- {
- IDirectSoundCaptureBuffer_Stop( dsw->dsw_InputBuffer );
- IDirectSoundCaptureBuffer_Release( dsw->dsw_InputBuffer );
- dsw->dsw_InputBuffer = NULL;
- }
-
- if (dsw->dsw_pDirectSoundCapture)
- {
- IDirectSoundCapture_Release( dsw->dsw_pDirectSoundCapture );
- dsw->dsw_pDirectSoundCapture = NULL;
- }
-
- if (dsw->dsw_pDirectSound)
- {
- IDirectSound_Release( dsw->dsw_pDirectSound );
- dsw->dsw_pDirectSound = NULL;
- }
-}
-/************************************************************************************/
-HRESULT DSW_Init( DSoundWrapper *dsw )
-{
- memset( dsw, 0, sizeof(DSoundWrapper) );
- return 0;
-}
-/************************************************************************************/
-HRESULT DSW_InitOutputDevice( DSoundWrapper *dsw, LPGUID lpGUID )
-{
- // Create the DS object
- HRESULT hr = dswDSoundEntryPoints.DirectSoundCreate( lpGUID, &dsw->dsw_pDirectSound, NULL );
- if( hr != DS_OK ) return hr;
- return hr;
-}
-
-/************************************************************************************/
-HRESULT DSW_InitOutputBuffer( DSoundWrapper *dsw, unsigned long nFrameRate, WORD nChannels, int bytesPerBuffer )
-{
- DWORD dwDataLen;
- DWORD playCursor;
- HRESULT result;
- LPDIRECTSOUNDBUFFER pPrimaryBuffer;
- HWND hWnd;
- HRESULT hr;
- WAVEFORMATEX wfFormat;
- DSBUFFERDESC primaryDesc;
- DSBUFFERDESC secondaryDesc;
- unsigned char* pDSBuffData;
- LARGE_INTEGER counterFrequency;
-
- dsw->dsw_OutputSize = bytesPerBuffer;
- dsw->dsw_OutputRunning = FALSE;
- dsw->dsw_OutputUnderflows = 0;
- dsw->dsw_FramesWritten = 0;
- dsw->dsw_BytesPerOutputFrame = nChannels * sizeof(short);
-
- // We were using getForegroundWindow() but sometimes the ForegroundWindow may not be the
- // applications's window. Also if that window is closed before the Buffer is closed
- // then DirectSound can crash. (Thanks for Scott Patterson for reporting this.)
- // So we will use GetDesktopWindow() which was suggested by Miller Puckette.
- // hWnd = GetForegroundWindow();
- //
- // FIXME: The example code I have on the net creates a hidden window that
- // is managed by our code - I think we should do that - one hidden
- // window for the whole of Pa_DS
- //
- hWnd = GetDesktopWindow();
-
- // Set cooperative level to DSSCL_EXCLUSIVE so that we can get 16 bit output, 44.1 KHz.
- // Exclusize also prevents unexpected sounds from other apps during a performance.
- if ((hr = IDirectSound_SetCooperativeLevel( dsw->dsw_pDirectSound,
- hWnd, DSSCL_EXCLUSIVE)) != DS_OK)
- {
- return hr;
- }
-
- // -----------------------------------------------------------------------
- // Create primary buffer and set format just so we can specify our custom format.
- // Otherwise we would be stuck with the default which might be 8 bit or 22050 Hz.
- // Setup the primary buffer description
- ZeroMemory(&primaryDesc, sizeof(DSBUFFERDESC));
- primaryDesc.dwSize = sizeof(DSBUFFERDESC);
- primaryDesc.dwFlags = DSBCAPS_PRIMARYBUFFER; // all panning, mixing, etc done by synth
- primaryDesc.dwBufferBytes = 0;
- primaryDesc.lpwfxFormat = NULL;
- // Create the buffer
- if ((result = IDirectSound_CreateSoundBuffer( dsw->dsw_pDirectSound,
- &primaryDesc, &pPrimaryBuffer, NULL)) != DS_OK) return result;
- // Define the buffer format
- wfFormat.wFormatTag = WAVE_FORMAT_PCM;
- wfFormat.nChannels = nChannels;
- wfFormat.nSamplesPerSec = nFrameRate;
- wfFormat.wBitsPerSample = 8 * sizeof(short);
- wfFormat.nBlockAlign = (WORD)(wfFormat.nChannels * (wfFormat.wBitsPerSample / 8));
- wfFormat.nAvgBytesPerSec = wfFormat.nSamplesPerSec * wfFormat.nBlockAlign;
- wfFormat.cbSize = 0; /* No extended format info. */
- // Set the primary buffer's format
- if((result = IDirectSoundBuffer_SetFormat( pPrimaryBuffer, &wfFormat)) != DS_OK) return result;
-
- // ----------------------------------------------------------------------
- // Setup the secondary buffer description
- ZeroMemory(&secondaryDesc, sizeof(DSBUFFERDESC));
- secondaryDesc.dwSize = sizeof(DSBUFFERDESC);
- secondaryDesc.dwFlags = DSBCAPS_GLOBALFOCUS | DSBCAPS_GETCURRENTPOSITION2;
- secondaryDesc.dwBufferBytes = bytesPerBuffer;
- secondaryDesc.lpwfxFormat = &wfFormat;
- // Create the secondary buffer
- if ((result = IDirectSound_CreateSoundBuffer( dsw->dsw_pDirectSound,
- &secondaryDesc, &dsw->dsw_OutputBuffer, NULL)) != DS_OK) return result;
- // Lock the DS buffer
- if ((result = IDirectSoundBuffer_Lock( dsw->dsw_OutputBuffer, 0, dsw->dsw_OutputSize, (LPVOID*)&pDSBuffData,
- &dwDataLen, NULL, 0, 0)) != DS_OK) return result;
- // Zero the DS buffer
- ZeroMemory(pDSBuffData, dwDataLen);
- // Unlock the DS buffer
- if ((result = IDirectSoundBuffer_Unlock( dsw->dsw_OutputBuffer, pDSBuffData, dwDataLen, NULL, 0)) != DS_OK) return result;
- if( QueryPerformanceFrequency( &counterFrequency ) )
- {
- int framesInBuffer = bytesPerBuffer / (nChannels * sizeof(short));
- dsw->dsw_CounterTicksPerBuffer.QuadPart = (counterFrequency.QuadPart * framesInBuffer) / nFrameRate;
- }
- else
- {
- dsw->dsw_CounterTicksPerBuffer.QuadPart = 0;
- }
- // Let DSound set the starting write position because if we set it to zero, it looks like the
- // buffer is full to begin with. This causes a long pause before sound starts when using large buffers.
- hr = IDirectSoundBuffer_GetCurrentPosition( dsw->dsw_OutputBuffer, &playCursor, &dsw->dsw_WriteOffset );
- if( hr != DS_OK )
- {
- return hr;
- }
- dsw->dsw_FramesWritten = dsw->dsw_WriteOffset / dsw->dsw_BytesPerOutputFrame;
- /* printf("DSW_InitOutputBuffer: playCursor = %d, writeCursor = %d\n", playCursor, dsw->dsw_WriteOffset ); */
- return DS_OK;
-}
-
-/************************************************************************************/
-HRESULT DSW_StartOutput( DSoundWrapper *dsw )
-{
- HRESULT hr;
- QueryPerformanceCounter( &dsw->dsw_LastPlayTime );
- dsw->dsw_LastPlayCursor = 0;
- dsw->dsw_FramesPlayed = 0;
- hr = IDirectSoundBuffer_SetCurrentPosition( dsw->dsw_OutputBuffer, 0 );
- if( hr != DS_OK )
- {
- return hr;
- }
- // Start the buffer playback in a loop.
- if( dsw->dsw_OutputBuffer != NULL )
- {
- hr = IDirectSoundBuffer_Play( dsw->dsw_OutputBuffer, 0, 0, DSBPLAY_LOOPING );
- if( hr != DS_OK )
- {
- return hr;
- }
- dsw->dsw_OutputRunning = TRUE;
- }
-
- return 0;
-}
-/************************************************************************************/
-HRESULT DSW_StopOutput( DSoundWrapper *dsw )
-{
- // Stop the buffer playback
- if( dsw->dsw_OutputBuffer != NULL )
- {
- dsw->dsw_OutputRunning = FALSE;
- return IDirectSoundBuffer_Stop( dsw->dsw_OutputBuffer );
- }
- else return 0;
-}
-
-/************************************************************************************/
-HRESULT DSW_QueryOutputFilled( DSoundWrapper *dsw, long *bytesFilledPtr )
-{
- HRESULT hr;
- DWORD playCursor;
- DWORD writeCursor;
- long bytesFilled;
- // Query to see where play position is.
- // We don't need the writeCursor but sometimes DirectSound doesn't handle NULLS correctly
- // so let's pass a pointer just to be safe.
- hr = IDirectSoundBuffer_GetCurrentPosition( dsw->dsw_OutputBuffer, &playCursor, &writeCursor );
- if( hr != DS_OK )
- {
- return hr;
- }
- bytesFilled = dsw->dsw_WriteOffset - playCursor;
- if( bytesFilled < 0 ) bytesFilled += dsw->dsw_OutputSize; // unwrap offset
- *bytesFilledPtr = bytesFilled;
- return hr;
-}
-
-/************************************************************************************
- * Determine how much space can be safely written to in DS buffer.
- * Detect underflows and overflows.
- * Does not allow writing into safety gap maintained by DirectSound.
- */
-HRESULT DSW_QueryOutputSpace( DSoundWrapper *dsw, long *bytesEmpty )
-{
- HRESULT hr;
- DWORD playCursor;
- DWORD writeCursor;
- long numBytesEmpty;
- long playWriteGap;
- // Query to see how much room is in buffer.
- hr = IDirectSoundBuffer_GetCurrentPosition( dsw->dsw_OutputBuffer, &playCursor, &writeCursor );
- if( hr != DS_OK )
- {
- return hr;
- }
- // Determine size of gap between playIndex and WriteIndex that we cannot write into.
- playWriteGap = writeCursor - playCursor;
- if( playWriteGap < 0 ) playWriteGap += dsw->dsw_OutputSize; // unwrap
- /* DirectSound doesn't have a large enough playCursor so we cannot detect wrap-around. */
- /* Attempt to detect playCursor wrap-around and correct it. */
- if( dsw->dsw_OutputRunning && (dsw->dsw_CounterTicksPerBuffer.QuadPart != 0) )
- {
- /* How much time has elapsed since last check. */
- LARGE_INTEGER currentTime;
- LARGE_INTEGER elapsedTime;
- long bytesPlayed;
- long bytesExpected;
- long buffersWrapped;
- QueryPerformanceCounter( &currentTime );
- elapsedTime.QuadPart = currentTime.QuadPart - dsw->dsw_LastPlayTime.QuadPart;
- dsw->dsw_LastPlayTime = currentTime;
- /* How many bytes does DirectSound say have been played. */
- bytesPlayed = playCursor - dsw->dsw_LastPlayCursor;
- if( bytesPlayed < 0 ) bytesPlayed += dsw->dsw_OutputSize; // unwrap
- dsw->dsw_LastPlayCursor = playCursor;
- /* Calculate how many bytes we would have expected to been played by now. */
- bytesExpected = (long) ((elapsedTime.QuadPart * dsw->dsw_OutputSize) / dsw->dsw_CounterTicksPerBuffer.QuadPart);
- buffersWrapped = (bytesExpected - bytesPlayed) / dsw->dsw_OutputSize;
- if( buffersWrapped > 0 )
- {
- playCursor += (buffersWrapped * dsw->dsw_OutputSize);
- bytesPlayed += (buffersWrapped * dsw->dsw_OutputSize);
- }
- /* Maintain frame output cursor. */
- dsw->dsw_FramesPlayed += (bytesPlayed / dsw->dsw_BytesPerOutputFrame);
- }
- numBytesEmpty = playCursor - dsw->dsw_WriteOffset;
- if( numBytesEmpty < 0 ) numBytesEmpty += dsw->dsw_OutputSize; // unwrap offset
- /* Have we underflowed? */
- if( numBytesEmpty > (dsw->dsw_OutputSize - playWriteGap) )
- {
- if( dsw->dsw_OutputRunning )
- {
- dsw->dsw_OutputUnderflows += 1;
- }
- dsw->dsw_WriteOffset = writeCursor;
- numBytesEmpty = dsw->dsw_OutputSize - playWriteGap;
- }
- *bytesEmpty = numBytesEmpty;
- return hr;
-}
-
-/************************************************************************************/
-HRESULT DSW_ZeroEmptySpace( DSoundWrapper *dsw )
-{
- HRESULT hr;
- LPBYTE lpbuf1 = NULL;
- LPBYTE lpbuf2 = NULL;
- DWORD dwsize1 = 0;
- DWORD dwsize2 = 0;
- long bytesEmpty;
- hr = DSW_QueryOutputSpace( dsw, &bytesEmpty ); // updates dsw_FramesPlayed
- if (hr != DS_OK) return hr;
- if( bytesEmpty == 0 ) return DS_OK;
- // Lock free space in the DS
- hr = IDirectSoundBuffer_Lock( dsw->dsw_OutputBuffer, dsw->dsw_WriteOffset, bytesEmpty, (void **) &lpbuf1, &dwsize1,
- (void **) &lpbuf2, &dwsize2, 0);
- if (hr == DS_OK)
- {
- // Copy the buffer into the DS
- ZeroMemory(lpbuf1, dwsize1);
- if(lpbuf2 != NULL)
- {
- ZeroMemory(lpbuf2, dwsize2);
- }
- // Update our buffer offset and unlock sound buffer
- dsw->dsw_WriteOffset = (dsw->dsw_WriteOffset + dwsize1 + dwsize2) % dsw->dsw_OutputSize;
- IDirectSoundBuffer_Unlock( dsw->dsw_OutputBuffer, lpbuf1, dwsize1, lpbuf2, dwsize2);
- dsw->dsw_FramesWritten += bytesEmpty / dsw->dsw_BytesPerOutputFrame;
- }
- return hr;
-}
-
-/************************************************************************************/
-HRESULT DSW_WriteBlock( DSoundWrapper *dsw, char *buf, long numBytes )
-{
- HRESULT hr;
- LPBYTE lpbuf1 = NULL;
- LPBYTE lpbuf2 = NULL;
- DWORD dwsize1 = 0;
- DWORD dwsize2 = 0;
- // Lock free space in the DS
- hr = IDirectSoundBuffer_Lock( dsw->dsw_OutputBuffer, dsw->dsw_WriteOffset, numBytes, (void **) &lpbuf1, &dwsize1,
- (void **) &lpbuf2, &dwsize2, 0);
- if (hr == DS_OK)
- {
- // Copy the buffer into the DS
- CopyMemory(lpbuf1, buf, dwsize1);
- if(lpbuf2 != NULL)
- {
- CopyMemory(lpbuf2, buf+dwsize1, dwsize2);
- }
- // Update our buffer offset and unlock sound buffer
- dsw->dsw_WriteOffset = (dsw->dsw_WriteOffset + dwsize1 + dwsize2) % dsw->dsw_OutputSize;
- IDirectSoundBuffer_Unlock( dsw->dsw_OutputBuffer, lpbuf1, dwsize1, lpbuf2, dwsize2);
- dsw->dsw_FramesWritten += numBytes / dsw->dsw_BytesPerOutputFrame;
- }
- return hr;
-}
-
-/************************************************************************************/
-DWORD DSW_GetOutputStatus( DSoundWrapper *dsw )
-{
- DWORD status;
- if (IDirectSoundBuffer_GetStatus( dsw->dsw_OutputBuffer, &status ) != DS_OK)
- return( DSERR_INVALIDPARAM );
- else
- return( status );
-}
-
-/* These routines are used to support audio input.
- * Do NOT compile these calls when using NT4 because it does
- * not support the entry points.
- */
-/************************************************************************************/
-HRESULT DSW_InitInputDevice( DSoundWrapper *dsw, LPGUID lpGUID )
-{
- HRESULT hr = dswDSoundEntryPoints.DirectSoundCaptureCreate( lpGUID, &dsw->dsw_pDirectSoundCapture, NULL );
- if( hr != DS_OK ) return hr;
- return hr;
-}
-/************************************************************************************/
-HRESULT DSW_InitInputBuffer( DSoundWrapper *dsw, unsigned long nFrameRate, WORD nChannels, int bytesPerBuffer )
-{
- DSCBUFFERDESC captureDesc;
- WAVEFORMATEX wfFormat;
- HRESULT result;
-
- dsw->dsw_BytesPerInputFrame = nChannels * sizeof(short);
-
- // Define the buffer format
- wfFormat.wFormatTag = WAVE_FORMAT_PCM;
- wfFormat.nChannels = nChannels;
- wfFormat.nSamplesPerSec = nFrameRate;
- wfFormat.wBitsPerSample = 8 * sizeof(short);
- wfFormat.nBlockAlign = (WORD)(wfFormat.nChannels * (wfFormat.wBitsPerSample / 8));
- wfFormat.nAvgBytesPerSec = wfFormat.nSamplesPerSec * wfFormat.nBlockAlign;
- wfFormat.cbSize = 0; /* No extended format info. */
- dsw->dsw_InputSize = bytesPerBuffer;
- // ----------------------------------------------------------------------
- // Setup the secondary buffer description
- ZeroMemory(&captureDesc, sizeof(DSCBUFFERDESC));
- captureDesc.dwSize = sizeof(DSCBUFFERDESC);
- captureDesc.dwFlags = 0;
- captureDesc.dwBufferBytes = bytesPerBuffer;
- captureDesc.lpwfxFormat = &wfFormat;
- // Create the capture buffer
- if ((result = IDirectSoundCapture_CreateCaptureBuffer( dsw->dsw_pDirectSoundCapture,
- &captureDesc, &dsw->dsw_InputBuffer, NULL)) != DS_OK) return result;
- dsw->dsw_ReadOffset = 0; // reset last read position to start of buffer
- return DS_OK;
-}
-
-/************************************************************************************/
-HRESULT DSW_StartInput( DSoundWrapper *dsw )
-{
- // Start the buffer playback
- if( dsw->dsw_InputBuffer != NULL )
- {
- return IDirectSoundCaptureBuffer_Start( dsw->dsw_InputBuffer, DSCBSTART_LOOPING );
- }
- else return 0;
-}
-
-/************************************************************************************/
-HRESULT DSW_StopInput( DSoundWrapper *dsw )
-{
- // Stop the buffer playback
- if( dsw->dsw_InputBuffer != NULL )
- {
- return IDirectSoundCaptureBuffer_Stop( dsw->dsw_InputBuffer );
- }
- else return 0;
-}
-
-/************************************************************************************/
-HRESULT DSW_QueryInputFilled( DSoundWrapper *dsw, long *bytesFilled )
-{
- HRESULT hr;
- DWORD capturePos;
- DWORD readPos;
- long filled;
- // Query to see how much data is in buffer.
- // We don't need the capture position but sometimes DirectSound doesn't handle NULLS correctly
- // so let's pass a pointer just to be safe.
- hr = IDirectSoundCaptureBuffer_GetCurrentPosition( dsw->dsw_InputBuffer, &capturePos, &readPos );
- if( hr != DS_OK )
- {
- return hr;
- }
- filled = readPos - dsw->dsw_ReadOffset;
- if( filled < 0 ) filled += dsw->dsw_InputSize; // unwrap offset
- *bytesFilled = filled;
- return hr;
-}
-
-/************************************************************************************/
-HRESULT DSW_ReadBlock( DSoundWrapper *dsw, char *buf, long numBytes )
-{
- HRESULT hr;
- LPBYTE lpbuf1 = NULL;
- LPBYTE lpbuf2 = NULL;
- DWORD dwsize1 = 0;
- DWORD dwsize2 = 0;
- // Lock free space in the DS
- hr = IDirectSoundCaptureBuffer_Lock ( dsw->dsw_InputBuffer, dsw->dsw_ReadOffset, numBytes, (void **) &lpbuf1, &dwsize1,
- (void **) &lpbuf2, &dwsize2, 0);
- if (hr == DS_OK)
- {
- // Copy from DS to the buffer
- CopyMemory( buf, lpbuf1, dwsize1);
- if(lpbuf2 != NULL)
- {
- CopyMemory( buf+dwsize1, lpbuf2, dwsize2);
- }
- // Update our buffer offset and unlock sound buffer
- dsw->dsw_ReadOffset = (dsw->dsw_ReadOffset + dwsize1 + dwsize2) % dsw->dsw_InputSize;
- IDirectSoundCaptureBuffer_Unlock ( dsw->dsw_InputBuffer, lpbuf1, dwsize1, lpbuf2, dwsize2);
- }
- return hr;
-}
-
+/*
+ * $Id: dsound_wrapper.c,v 1.1.1.1.2.11 2003/09/07 13:04:53 rossbencina Exp $
+ * Simplified DirectSound interface.
+ *
+ * Author: Phil Burk & Robert Marsanyi
+ *
+ * PortAudio Portable Real-Time Audio Library
+ * For more information see: http://www.softsynth.com/portaudio/
+ * DirectSound Implementation
+ * Copyright (c) 1999-2000 Phil Burk & Robert Marsanyi
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * Any person wishing to distribute modifications to the Software is
+ * requested to send the modifications to the original developer so that
+ * they can be incorporated into the canonical version.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+ * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+/*
+ * PJMEDIA - Multimedia over IP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+
+#include "dsound_wrapper.h"
+#include "pa_trace.h"
+
+/*
+ Rather than linking with dxguid.a or using "#define INITGUID" to force a
+ header file to instantiate the required GUID(s), we define them directly
+ below.
+*/
+#include <initguid.h> // needed for the DEFINE_GUID macro
+DEFINE_GUID(IID_IDirectSoundNotify, 0xb0210783, 0x89cd, 0x11d0, 0xaf, 0x8, 0x0, 0xa0, 0xc9, 0x25, 0xcd, 0x16);
+
+
+/************************************************************************************/
+DSoundEntryPoints dswDSoundEntryPoints = { 0, 0, 0, 0, 0, 0, 0 };
+/************************************************************************************/
+static HRESULT WINAPI DummyDirectSoundCreate(LPGUID lpcGuidDevice, LPDIRECTSOUND *ppDS, LPUNKNOWN pUnkOuter)
+{
+ (void)lpcGuidDevice; /* unused parameter */
+ (void)ppDS; /* unused parameter */
+ (void)pUnkOuter; /* unused parameter */
+ return E_NOTIMPL;
+}
+
+static HRESULT WINAPI DummyDirectSoundEnumerateW(LPDSENUMCALLBACKW lpDSEnumCallback, LPVOID lpContext)
+{
+ (void)lpDSEnumCallback; /* unused parameter */
+ (void)lpContext; /* unused parameter */
+ return E_NOTIMPL;
+}
+
+static HRESULT WINAPI DummyDirectSoundEnumerateA(LPDSENUMCALLBACKA lpDSEnumCallback, LPVOID lpContext)
+{
+ (void)lpDSEnumCallback; /* unused parameter */
+ (void)lpContext; /* unused parameter */
+ return E_NOTIMPL;
+}
+
+static HRESULT WINAPI DummyDirectSoundCaptureCreate(LPGUID lpcGUID, LPDIRECTSOUNDCAPTURE *lplpDSC, LPUNKNOWN pUnkOuter)
+{
+ (void)lpcGUID; /* unused parameter */
+ (void)lplpDSC; /* unused parameter */
+ (void)pUnkOuter; /* unused parameter */
+ return E_NOTIMPL;
+}
+
+static HRESULT WINAPI DummyDirectSoundCaptureEnumerateW(LPDSENUMCALLBACKW lpDSCEnumCallback, LPVOID lpContext)
+{
+ (void)lpDSCEnumCallback; /* unused parameter */
+ (void)lpContext; /* unused parameter */
+ return E_NOTIMPL;
+}
+
+static HRESULT WINAPI DummyDirectSoundCaptureEnumerateA(LPDSENUMCALLBACKA lpDSCEnumCallback, LPVOID lpContext)
+{
+ (void)lpDSCEnumCallback; /* unused parameter */
+ (void)lpContext; /* unused parameter */
+ return E_NOTIMPL;
+}
+/************************************************************************************/
+void DSW_InitializeDSoundEntryPoints(void)
+{
+ dswDSoundEntryPoints.hInstance_ = LoadLibrary("dsound.dll");
+ if( dswDSoundEntryPoints.hInstance_ != NULL )
+ {
+ dswDSoundEntryPoints.DirectSoundCreate =
+ (HRESULT (WINAPI *)(LPGUID, LPDIRECTSOUND *, LPUNKNOWN))
+ GetProcAddress( dswDSoundEntryPoints.hInstance_, "DirectSoundCreate" );
+ if( dswDSoundEntryPoints.DirectSoundCreate == NULL )
+ dswDSoundEntryPoints.DirectSoundCreate = DummyDirectSoundCreate;
+
+ dswDSoundEntryPoints.DirectSoundEnumerateW =
+ (HRESULT (WINAPI *)(LPDSENUMCALLBACKW, LPVOID))
+ GetProcAddress( dswDSoundEntryPoints.hInstance_, "DirectSoundEnumerateW" );
+ if( dswDSoundEntryPoints.DirectSoundEnumerateW == NULL )
+ dswDSoundEntryPoints.DirectSoundEnumerateW = DummyDirectSoundEnumerateW;
+
+ dswDSoundEntryPoints.DirectSoundEnumerateA =
+ (HRESULT (WINAPI *)(LPDSENUMCALLBACKA, LPVOID))
+ GetProcAddress( dswDSoundEntryPoints.hInstance_, "DirectSoundEnumerateA" );
+ if( dswDSoundEntryPoints.DirectSoundEnumerateA == NULL )
+ dswDSoundEntryPoints.DirectSoundEnumerateA = DummyDirectSoundEnumerateA;
+
+ dswDSoundEntryPoints.DirectSoundCaptureCreate =
+ (HRESULT (WINAPI *)(LPGUID, LPDIRECTSOUNDCAPTURE *, LPUNKNOWN))
+ GetProcAddress( dswDSoundEntryPoints.hInstance_, "DirectSoundCaptureCreate" );
+ if( dswDSoundEntryPoints.DirectSoundCaptureCreate == NULL )
+ dswDSoundEntryPoints.DirectSoundCaptureCreate = DummyDirectSoundCaptureCreate;
+
+ dswDSoundEntryPoints.DirectSoundCaptureEnumerateW =
+ (HRESULT (WINAPI *)(LPDSENUMCALLBACKW, LPVOID))
+ GetProcAddress( dswDSoundEntryPoints.hInstance_, "DirectSoundCaptureEnumerateW" );
+ if( dswDSoundEntryPoints.DirectSoundCaptureEnumerateW == NULL )
+ dswDSoundEntryPoints.DirectSoundCaptureEnumerateW = DummyDirectSoundCaptureEnumerateW;
+
+ dswDSoundEntryPoints.DirectSoundCaptureEnumerateA =
+ (HRESULT (WINAPI *)(LPDSENUMCALLBACKA, LPVOID))
+ GetProcAddress( dswDSoundEntryPoints.hInstance_, "DirectSoundCaptureEnumerateA" );
+ if( dswDSoundEntryPoints.DirectSoundCaptureEnumerateA == NULL )
+ dswDSoundEntryPoints.DirectSoundCaptureEnumerateA = DummyDirectSoundCaptureEnumerateA;
+ }
+ else
+ {
+ /* initialize with dummy entry points to make live easy when ds isn't present */
+ dswDSoundEntryPoints.DirectSoundCreate = DummyDirectSoundCreate;
+ dswDSoundEntryPoints.DirectSoundEnumerateW = DummyDirectSoundEnumerateW;
+ dswDSoundEntryPoints.DirectSoundEnumerateA = DummyDirectSoundEnumerateA;
+ dswDSoundEntryPoints.DirectSoundCaptureCreate = DummyDirectSoundCaptureCreate;
+ dswDSoundEntryPoints.DirectSoundCaptureEnumerateW = DummyDirectSoundCaptureEnumerateW;
+ dswDSoundEntryPoints.DirectSoundCaptureEnumerateA = DummyDirectSoundCaptureEnumerateA;
+ }
+}
+/************************************************************************************/
+void DSW_TerminateDSoundEntryPoints(void)
+{
+ if( dswDSoundEntryPoints.hInstance_ != NULL )
+ {
+ FreeLibrary( dswDSoundEntryPoints.hInstance_ );
+ dswDSoundEntryPoints.hInstance_ = NULL;
+ /* ensure that we crash reliably if the entry points arent initialised */
+ dswDSoundEntryPoints.DirectSoundCreate = 0;
+ dswDSoundEntryPoints.DirectSoundEnumerateW = 0;
+ dswDSoundEntryPoints.DirectSoundEnumerateA = 0;
+ dswDSoundEntryPoints.DirectSoundCaptureCreate = 0;
+ dswDSoundEntryPoints.DirectSoundCaptureEnumerateW = 0;
+ dswDSoundEntryPoints.DirectSoundCaptureEnumerateA = 0;
+ }
+}
+/************************************************************************************/
+void DSW_Term( DSoundWrapper *dsw )
+{
+ // Cleanup the sound buffers
+ if (dsw->dsw_OutputBuffer)
+ {
+ IDirectSoundBuffer_Stop( dsw->dsw_OutputBuffer );
+ IDirectSoundBuffer_Release( dsw->dsw_OutputBuffer );
+ dsw->dsw_OutputBuffer = NULL;
+ }
+
+ if (dsw->dsw_InputBuffer)
+ {
+ IDirectSoundCaptureBuffer_Stop( dsw->dsw_InputBuffer );
+ IDirectSoundCaptureBuffer_Release( dsw->dsw_InputBuffer );
+ dsw->dsw_InputBuffer = NULL;
+ }
+
+ if (dsw->dsw_pDirectSoundCapture)
+ {
+ IDirectSoundCapture_Release( dsw->dsw_pDirectSoundCapture );
+ dsw->dsw_pDirectSoundCapture = NULL;
+ }
+
+ if (dsw->dsw_pDirectSound)
+ {
+ IDirectSound_Release( dsw->dsw_pDirectSound );
+ dsw->dsw_pDirectSound = NULL;
+ }
+}
+/************************************************************************************/
+HRESULT DSW_Init( DSoundWrapper *dsw )
+{
+ memset( dsw, 0, sizeof(DSoundWrapper) );
+ return 0;
+}
+/************************************************************************************/
+HRESULT DSW_InitOutputDevice( DSoundWrapper *dsw, LPGUID lpGUID )
+{
+ // Create the DS object
+ HRESULT hr = dswDSoundEntryPoints.DirectSoundCreate( lpGUID, &dsw->dsw_pDirectSound, NULL );
+ if( hr != DS_OK ) return hr;
+ return hr;
+}
+
+/************************************************************************************/
+HRESULT DSW_InitOutputBuffer( DSoundWrapper *dsw, unsigned long nFrameRate, WORD nChannels, int bytesPerBuffer )
+{
+ DWORD dwDataLen;
+ DWORD playCursor;
+ HRESULT result;
+ LPDIRECTSOUNDBUFFER pPrimaryBuffer;
+ HWND hWnd;
+ HRESULT hr;
+ WAVEFORMATEX wfFormat;
+ DSBUFFERDESC primaryDesc;
+ DSBUFFERDESC secondaryDesc;
+ unsigned char* pDSBuffData;
+ LARGE_INTEGER counterFrequency;
+
+ dsw->dsw_OutputSize = bytesPerBuffer;
+ dsw->dsw_OutputRunning = FALSE;
+ dsw->dsw_OutputUnderflows = 0;
+ dsw->dsw_FramesWritten = 0;
+ dsw->dsw_BytesPerOutputFrame = nChannels * sizeof(short);
+
+ // We were using getForegroundWindow() but sometimes the ForegroundWindow may not be the
+ // applications's window. Also if that window is closed before the Buffer is closed
+ // then DirectSound can crash. (Thanks for Scott Patterson for reporting this.)
+ // So we will use GetDesktopWindow() which was suggested by Miller Puckette.
+ // hWnd = GetForegroundWindow();
+ //
+ // FIXME: The example code I have on the net creates a hidden window that
+ // is managed by our code - I think we should do that - one hidden
+ // window for the whole of Pa_DS
+ //
+ hWnd = GetDesktopWindow();
+
+ // Set cooperative level to DSSCL_EXCLUSIVE so that we can get 16 bit output, 44.1 KHz.
+ // Exclusize also prevents unexpected sounds from other apps during a performance.
+ if ((hr = IDirectSound_SetCooperativeLevel( dsw->dsw_pDirectSound,
+ hWnd, DSSCL_EXCLUSIVE)) != DS_OK)
+ {
+ return hr;
+ }
+
+ // -----------------------------------------------------------------------
+ // Create primary buffer and set format just so we can specify our custom format.
+ // Otherwise we would be stuck with the default which might be 8 bit or 22050 Hz.
+ // Setup the primary buffer description
+ ZeroMemory(&primaryDesc, sizeof(DSBUFFERDESC));
+ primaryDesc.dwSize = sizeof(DSBUFFERDESC);
+ primaryDesc.dwFlags = DSBCAPS_PRIMARYBUFFER; // all panning, mixing, etc done by synth
+ primaryDesc.dwBufferBytes = 0;
+ primaryDesc.lpwfxFormat = NULL;
+ // Create the buffer
+ if ((result = IDirectSound_CreateSoundBuffer( dsw->dsw_pDirectSound,
+ &primaryDesc, &pPrimaryBuffer, NULL)) != DS_OK) return result;
+ // Define the buffer format
+ wfFormat.wFormatTag = WAVE_FORMAT_PCM;
+ wfFormat.nChannels = nChannels;
+ wfFormat.nSamplesPerSec = nFrameRate;
+ wfFormat.wBitsPerSample = 8 * sizeof(short);
+ wfFormat.nBlockAlign = (WORD)(wfFormat.nChannels * (wfFormat.wBitsPerSample / 8));
+ wfFormat.nAvgBytesPerSec = wfFormat.nSamplesPerSec * wfFormat.nBlockAlign;
+ wfFormat.cbSize = 0; /* No extended format info. */
+ // Set the primary buffer's format
+ if((result = IDirectSoundBuffer_SetFormat( pPrimaryBuffer, &wfFormat)) != DS_OK) return result;
+
+ // ----------------------------------------------------------------------
+ // Setup the secondary buffer description
+ ZeroMemory(&secondaryDesc, sizeof(DSBUFFERDESC));
+ secondaryDesc.dwSize = sizeof(DSBUFFERDESC);
+ secondaryDesc.dwFlags = DSBCAPS_GLOBALFOCUS | DSBCAPS_GETCURRENTPOSITION2;
+ secondaryDesc.dwBufferBytes = bytesPerBuffer;
+ secondaryDesc.lpwfxFormat = &wfFormat;
+ // Create the secondary buffer
+ if ((result = IDirectSound_CreateSoundBuffer( dsw->dsw_pDirectSound,
+ &secondaryDesc, &dsw->dsw_OutputBuffer, NULL)) != DS_OK) return result;
+ // Lock the DS buffer
+ if ((result = IDirectSoundBuffer_Lock( dsw->dsw_OutputBuffer, 0, dsw->dsw_OutputSize, (LPVOID*)&pDSBuffData,
+ &dwDataLen, NULL, 0, 0)) != DS_OK) return result;
+ // Zero the DS buffer
+ ZeroMemory(pDSBuffData, dwDataLen);
+ // Unlock the DS buffer
+ if ((result = IDirectSoundBuffer_Unlock( dsw->dsw_OutputBuffer, pDSBuffData, dwDataLen, NULL, 0)) != DS_OK) return result;
+ if( QueryPerformanceFrequency( &counterFrequency ) )
+ {
+ int framesInBuffer = bytesPerBuffer / (nChannels * sizeof(short));
+ dsw->dsw_CounterTicksPerBuffer.QuadPart = (counterFrequency.QuadPart * framesInBuffer) / nFrameRate;
+ }
+ else
+ {
+ dsw->dsw_CounterTicksPerBuffer.QuadPart = 0;
+ }
+ // Let DSound set the starting write position because if we set it to zero, it looks like the
+ // buffer is full to begin with. This causes a long pause before sound starts when using large buffers.
+ hr = IDirectSoundBuffer_GetCurrentPosition( dsw->dsw_OutputBuffer, &playCursor, &dsw->dsw_WriteOffset );
+ if( hr != DS_OK )
+ {
+ return hr;
+ }
+ dsw->dsw_FramesWritten = dsw->dsw_WriteOffset / dsw->dsw_BytesPerOutputFrame;
+ /* printf("DSW_InitOutputBuffer: playCursor = %d, writeCursor = %d\n", playCursor, dsw->dsw_WriteOffset ); */
+ return DS_OK;
+}
+
+/************************************************************************************/
+HRESULT DSW_StartOutput( DSoundWrapper *dsw )
+{
+ HRESULT hr;
+ QueryPerformanceCounter( &dsw->dsw_LastPlayTime );
+ dsw->dsw_LastPlayCursor = 0;
+ dsw->dsw_FramesPlayed = 0;
+ hr = IDirectSoundBuffer_SetCurrentPosition( dsw->dsw_OutputBuffer, 0 );
+ if( hr != DS_OK )
+ {
+ return hr;
+ }
+ // Start the buffer playback in a loop.
+ if( dsw->dsw_OutputBuffer != NULL )
+ {
+ hr = IDirectSoundBuffer_Play( dsw->dsw_OutputBuffer, 0, 0, DSBPLAY_LOOPING );
+ if( hr != DS_OK )
+ {
+ return hr;
+ }
+ dsw->dsw_OutputRunning = TRUE;
+ }
+
+ return 0;
+}
+/************************************************************************************/
+HRESULT DSW_StopOutput( DSoundWrapper *dsw )
+{
+ // Stop the buffer playback
+ if( dsw->dsw_OutputBuffer != NULL )
+ {
+ dsw->dsw_OutputRunning = FALSE;
+ return IDirectSoundBuffer_Stop( dsw->dsw_OutputBuffer );
+ }
+ else return 0;
+}
+
+/************************************************************************************/
+HRESULT DSW_QueryOutputFilled( DSoundWrapper *dsw, long *bytesFilledPtr )
+{
+ HRESULT hr;
+ DWORD playCursor;
+ DWORD writeCursor;
+ long bytesFilled;
+ // Query to see where play position is.
+ // We don't need the writeCursor but sometimes DirectSound doesn't handle NULLS correctly
+ // so let's pass a pointer just to be safe.
+ hr = IDirectSoundBuffer_GetCurrentPosition( dsw->dsw_OutputBuffer, &playCursor, &writeCursor );
+ if( hr != DS_OK )
+ {
+ return hr;
+ }
+ bytesFilled = dsw->dsw_WriteOffset - playCursor;
+ if( bytesFilled < 0 ) bytesFilled += dsw->dsw_OutputSize; // unwrap offset
+ *bytesFilledPtr = bytesFilled;
+ return hr;
+}
+
+/************************************************************************************
+ * Determine how much space can be safely written to in DS buffer.
+ * Detect underflows and overflows.
+ * Does not allow writing into safety gap maintained by DirectSound.
+ */
+HRESULT DSW_QueryOutputSpace( DSoundWrapper *dsw, long *bytesEmpty )
+{
+ HRESULT hr;
+ DWORD playCursor;
+ DWORD writeCursor;
+ long numBytesEmpty;
+ long playWriteGap;
+ // Query to see how much room is in buffer.
+ hr = IDirectSoundBuffer_GetCurrentPosition( dsw->dsw_OutputBuffer, &playCursor, &writeCursor );
+ if( hr != DS_OK )
+ {
+ return hr;
+ }
+ // Determine size of gap between playIndex and WriteIndex that we cannot write into.
+ playWriteGap = writeCursor - playCursor;
+ if( playWriteGap < 0 ) playWriteGap += dsw->dsw_OutputSize; // unwrap
+ /* DirectSound doesn't have a large enough playCursor so we cannot detect wrap-around. */
+ /* Attempt to detect playCursor wrap-around and correct it. */
+ if( dsw->dsw_OutputRunning && (dsw->dsw_CounterTicksPerBuffer.QuadPart != 0) )
+ {
+ /* How much time has elapsed since last check. */
+ LARGE_INTEGER currentTime;
+ LARGE_INTEGER elapsedTime;
+ long bytesPlayed;
+ long bytesExpected;
+ long buffersWrapped;
+ QueryPerformanceCounter( &currentTime );
+ elapsedTime.QuadPart = currentTime.QuadPart - dsw->dsw_LastPlayTime.QuadPart;
+ dsw->dsw_LastPlayTime = currentTime;
+ /* How many bytes does DirectSound say have been played. */
+ bytesPlayed = playCursor - dsw->dsw_LastPlayCursor;
+ if( bytesPlayed < 0 ) bytesPlayed += dsw->dsw_OutputSize; // unwrap
+ dsw->dsw_LastPlayCursor = playCursor;
+ /* Calculate how many bytes we would have expected to been played by now. */
+ bytesExpected = (long) ((elapsedTime.QuadPart * dsw->dsw_OutputSize) / dsw->dsw_CounterTicksPerBuffer.QuadPart);
+ buffersWrapped = (bytesExpected - bytesPlayed) / dsw->dsw_OutputSize;
+ if( buffersWrapped > 0 )
+ {
+ playCursor += (buffersWrapped * dsw->dsw_OutputSize);
+ bytesPlayed += (buffersWrapped * dsw->dsw_OutputSize);
+ }
+ /* Maintain frame output cursor. */
+ dsw->dsw_FramesPlayed += (bytesPlayed / dsw->dsw_BytesPerOutputFrame);
+ }
+ numBytesEmpty = playCursor - dsw->dsw_WriteOffset;
+ if( numBytesEmpty < 0 ) numBytesEmpty += dsw->dsw_OutputSize; // unwrap offset
+ /* Have we underflowed? */
+ if( numBytesEmpty > (dsw->dsw_OutputSize - playWriteGap) )
+ {
+ if( dsw->dsw_OutputRunning )
+ {
+ dsw->dsw_OutputUnderflows += 1;
+ }
+ dsw->dsw_WriteOffset = writeCursor;
+ numBytesEmpty = dsw->dsw_OutputSize - playWriteGap;
+ }
+ *bytesEmpty = numBytesEmpty;
+ return hr;
+}
+
+/************************************************************************************/
+HRESULT DSW_ZeroEmptySpace( DSoundWrapper *dsw )
+{
+ HRESULT hr;
+ LPBYTE lpbuf1 = NULL;
+ LPBYTE lpbuf2 = NULL;
+ DWORD dwsize1 = 0;
+ DWORD dwsize2 = 0;
+ long bytesEmpty;
+ hr = DSW_QueryOutputSpace( dsw, &bytesEmpty ); // updates dsw_FramesPlayed
+ if (hr != DS_OK) return hr;
+ if( bytesEmpty == 0 ) return DS_OK;
+ // Lock free space in the DS
+ hr = IDirectSoundBuffer_Lock( dsw->dsw_OutputBuffer, dsw->dsw_WriteOffset, bytesEmpty, (void **) &lpbuf1, &dwsize1,
+ (void **) &lpbuf2, &dwsize2, 0);
+ if (hr == DS_OK)
+ {
+ // Copy the buffer into the DS
+ ZeroMemory(lpbuf1, dwsize1);
+ if(lpbuf2 != NULL)
+ {
+ ZeroMemory(lpbuf2, dwsize2);
+ }
+ // Update our buffer offset and unlock sound buffer
+ dsw->dsw_WriteOffset = (dsw->dsw_WriteOffset + dwsize1 + dwsize2) % dsw->dsw_OutputSize;
+ IDirectSoundBuffer_Unlock( dsw->dsw_OutputBuffer, lpbuf1, dwsize1, lpbuf2, dwsize2);
+ dsw->dsw_FramesWritten += bytesEmpty / dsw->dsw_BytesPerOutputFrame;
+ }
+ return hr;
+}
+
+/************************************************************************************/
+HRESULT DSW_WriteBlock( DSoundWrapper *dsw, char *buf, long numBytes )
+{
+ HRESULT hr;
+ LPBYTE lpbuf1 = NULL;
+ LPBYTE lpbuf2 = NULL;
+ DWORD dwsize1 = 0;
+ DWORD dwsize2 = 0;
+ // Lock free space in the DS
+ hr = IDirectSoundBuffer_Lock( dsw->dsw_OutputBuffer, dsw->dsw_WriteOffset, numBytes, (void **) &lpbuf1, &dwsize1,
+ (void **) &lpbuf2, &dwsize2, 0);
+ if (hr == DS_OK)
+ {
+ // Copy the buffer into the DS
+ CopyMemory(lpbuf1, buf, dwsize1);
+ if(lpbuf2 != NULL)
+ {
+ CopyMemory(lpbuf2, buf+dwsize1, dwsize2);
+ }
+ // Update our buffer offset and unlock sound buffer
+ dsw->dsw_WriteOffset = (dsw->dsw_WriteOffset + dwsize1 + dwsize2) % dsw->dsw_OutputSize;
+ IDirectSoundBuffer_Unlock( dsw->dsw_OutputBuffer, lpbuf1, dwsize1, lpbuf2, dwsize2);
+ dsw->dsw_FramesWritten += numBytes / dsw->dsw_BytesPerOutputFrame;
+ }
+ return hr;
+}
+
+/************************************************************************************/
+DWORD DSW_GetOutputStatus( DSoundWrapper *dsw )
+{
+ DWORD status;
+ if (IDirectSoundBuffer_GetStatus( dsw->dsw_OutputBuffer, &status ) != DS_OK)
+ return( DSERR_INVALIDPARAM );
+ else
+ return( status );
+}
+
+/* These routines are used to support audio input.
+ * Do NOT compile these calls when using NT4 because it does
+ * not support the entry points.
+ */
+/************************************************************************************/
+HRESULT DSW_InitInputDevice( DSoundWrapper *dsw, LPGUID lpGUID )
+{
+ HRESULT hr = dswDSoundEntryPoints.DirectSoundCaptureCreate( lpGUID, &dsw->dsw_pDirectSoundCapture, NULL );
+ if( hr != DS_OK ) return hr;
+ return hr;
+}
+/************************************************************************************/
+HRESULT DSW_InitInputBuffer( DSoundWrapper *dsw, unsigned long nFrameRate, WORD nChannels, int bytesPerBuffer )
+{
+ DSCBUFFERDESC captureDesc;
+ WAVEFORMATEX wfFormat;
+ HRESULT result;
+
+ dsw->dsw_BytesPerInputFrame = nChannels * sizeof(short);
+
+ // Define the buffer format
+ wfFormat.wFormatTag = WAVE_FORMAT_PCM;
+ wfFormat.nChannels = nChannels;
+ wfFormat.nSamplesPerSec = nFrameRate;
+ wfFormat.wBitsPerSample = 8 * sizeof(short);
+ wfFormat.nBlockAlign = (WORD)(wfFormat.nChannels * (wfFormat.wBitsPerSample / 8));
+ wfFormat.nAvgBytesPerSec = wfFormat.nSamplesPerSec * wfFormat.nBlockAlign;
+ wfFormat.cbSize = 0; /* No extended format info. */
+ dsw->dsw_InputSize = bytesPerBuffer;
+ // ----------------------------------------------------------------------
+ // Setup the secondary buffer description
+ ZeroMemory(&captureDesc, sizeof(DSCBUFFERDESC));
+ captureDesc.dwSize = sizeof(DSCBUFFERDESC);
+ captureDesc.dwFlags = 0;
+ captureDesc.dwBufferBytes = bytesPerBuffer;
+ captureDesc.lpwfxFormat = &wfFormat;
+ // Create the capture buffer
+ if ((result = IDirectSoundCapture_CreateCaptureBuffer( dsw->dsw_pDirectSoundCapture,
+ &captureDesc, &dsw->dsw_InputBuffer, NULL)) != DS_OK) return result;
+ dsw->dsw_ReadOffset = 0; // reset last read position to start of buffer
+ return DS_OK;
+}
+
+/************************************************************************************/
+HRESULT DSW_StartInput( DSoundWrapper *dsw )
+{
+ // Start the buffer playback
+ if( dsw->dsw_InputBuffer != NULL )
+ {
+ return IDirectSoundCaptureBuffer_Start( dsw->dsw_InputBuffer, DSCBSTART_LOOPING );
+ }
+ else return 0;
+}
+
+/************************************************************************************/
+HRESULT DSW_StopInput( DSoundWrapper *dsw )
+{
+ // Stop the buffer playback
+ if( dsw->dsw_InputBuffer != NULL )
+ {
+ return IDirectSoundCaptureBuffer_Stop( dsw->dsw_InputBuffer );
+ }
+ else return 0;
+}
+
+/************************************************************************************/
+HRESULT DSW_QueryInputFilled( DSoundWrapper *dsw, long *bytesFilled )
+{
+ HRESULT hr;
+ DWORD capturePos;
+ DWORD readPos;
+ long filled;
+ // Query to see how much data is in buffer.
+ // We don't need the capture position but sometimes DirectSound doesn't handle NULLS correctly
+ // so let's pass a pointer just to be safe.
+ hr = IDirectSoundCaptureBuffer_GetCurrentPosition( dsw->dsw_InputBuffer, &capturePos, &readPos );
+ if( hr != DS_OK )
+ {
+ return hr;
+ }
+ filled = readPos - dsw->dsw_ReadOffset;
+ if( filled < 0 ) filled += dsw->dsw_InputSize; // unwrap offset
+ *bytesFilled = filled;
+ return hr;
+}
+
+/************************************************************************************/
+HRESULT DSW_ReadBlock( DSoundWrapper *dsw, char *buf, long numBytes )
+{
+ HRESULT hr;
+ LPBYTE lpbuf1 = NULL;
+ LPBYTE lpbuf2 = NULL;
+ DWORD dwsize1 = 0;
+ DWORD dwsize2 = 0;
+ // Lock free space in the DS
+ hr = IDirectSoundCaptureBuffer_Lock ( dsw->dsw_InputBuffer, dsw->dsw_ReadOffset, numBytes, (void **) &lpbuf1, &dwsize1,
+ (void **) &lpbuf2, &dwsize2, 0);
+ if (hr == DS_OK)
+ {
+ // Copy from DS to the buffer
+ CopyMemory( buf, lpbuf1, dwsize1);
+ if(lpbuf2 != NULL)
+ {
+ CopyMemory( buf+dwsize1, lpbuf2, dwsize2);
+ }
+ // Update our buffer offset and unlock sound buffer
+ dsw->dsw_ReadOffset = (dsw->dsw_ReadOffset + dwsize1 + dwsize2) % dsw->dsw_InputSize;
+ IDirectSoundCaptureBuffer_Unlock ( dsw->dsw_InputBuffer, lpbuf1, dwsize1, lpbuf2, dwsize2);
+ }
+ return hr;
+}
+
diff --git a/pjmedia/src/pjmedia/portaudio/dsound_wrapper.h b/pjmedia/src/pjmedia/portaudio/dsound_wrapper.h
index 9e3f565f..e8954c19 100644
--- a/pjmedia/src/pjmedia/portaudio/dsound_wrapper.h
+++ b/pjmedia/src/pjmedia/portaudio/dsound_wrapper.h
@@ -1,130 +1,151 @@
-#ifndef __DSOUND_WRAPPER_H
-#define __DSOUND_WRAPPER_H
-/*
- * $Id: dsound_wrapper.h,v 1.1.1.1.2.8 2005/01/16 20:48:37 rossbencina Exp $
- * Simplified DirectSound interface.
- *
- * Author: Phil Burk & Robert Marsanyi
- *
- * For PortAudio Portable Real-Time Audio Library
- * For more information see: http://www.softsynth.com/portaudio/
- * DirectSound Implementation
- * Copyright (c) 1999-2000 Phil Burk & Robert Marsanyi
- *
- * Permission is hereby granted, free of charge, to any person obtaining
- * a copy of this software and associated documentation files
- * (the "Software"), to deal in the Software without restriction,
- * including without limitation the rights to use, copy, modify, merge,
- * publish, distribute, sublicense, and/or sell copies of the Software,
- * and to permit persons to whom the Software is furnished to do so,
- * subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be
- * included in all copies or substantial portions of the Software.
- *
- * Any person wishing to distribute modifications to the Software is
- * requested to send the modifications to the original developer so that
- * they can be incorporated into the canonical version.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
- * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
- * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
- * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
- * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
- *
- */
-
-/* on Borland compilers, WIN32 doesn't seem to be defined by default, which
- breaks DSound.h. Adding the define here fixes the problem. - rossb. */
-#ifdef __BORLANDC__
-#if !defined(WIN32)
-#define WIN32
-#endif
-#endif
-
-/*
- We are only using DX3 in here, no need to polute the namespace - davidv
-*/
-#define DIRECTSOUND_VERSION 0x0300
-
-#include <DSound.h>
-
-#ifdef __cplusplus
-extern "C"
-{
-#endif /* __cplusplus */
-
-
-typedef struct
-{
- HINSTANCE hInstance_;
-
- HRESULT (WINAPI *DirectSoundCreate)(LPGUID, LPDIRECTSOUND *, LPUNKNOWN);
- HRESULT (WINAPI *DirectSoundEnumerateW)(LPDSENUMCALLBACKW, LPVOID);
- HRESULT (WINAPI *DirectSoundEnumerateA)(LPDSENUMCALLBACKA, LPVOID);
-
- HRESULT (WINAPI *DirectSoundCaptureCreate)(LPGUID, LPDIRECTSOUNDCAPTURE *, LPUNKNOWN);
- HRESULT (WINAPI *DirectSoundCaptureEnumerateW)(LPDSENUMCALLBACKW, LPVOID);
- HRESULT (WINAPI *DirectSoundCaptureEnumerateA)(LPDSENUMCALLBACKA, LPVOID);
-}DSoundEntryPoints;
-
-extern DSoundEntryPoints dswDSoundEntryPoints;
-
-void DSW_InitializeDSoundEntryPoints(void);
-void DSW_TerminateDSoundEntryPoints(void);
-
-#define DSW_NUM_POSITIONS (4)
-#define DSW_NUM_EVENTS (5)
-#define DSW_TERMINATION_EVENT (DSW_NUM_POSITIONS)
-
-typedef struct
-{
-/* Output */
- LPDIRECTSOUND dsw_pDirectSound;
- LPDIRECTSOUNDBUFFER dsw_OutputBuffer;
- DWORD dsw_WriteOffset; /* last write position */
- INT dsw_OutputSize;
- INT dsw_BytesPerOutputFrame;
- /* Try to detect play buffer underflows. */
- LARGE_INTEGER dsw_CounterTicksPerBuffer; /* counter ticks it should take to play a full buffer */
- LARGE_INTEGER dsw_LastPlayTime;
- UINT dsw_LastPlayCursor;
- UINT dsw_OutputUnderflows;
- BOOL dsw_OutputRunning;
- /* use double which lets us can play for several thousand years with enough precision */
- double dsw_FramesWritten;
- double dsw_FramesPlayed;
-/* Input */
- INT dsw_BytesPerInputFrame;
- LPDIRECTSOUNDCAPTURE dsw_pDirectSoundCapture;
- LPDIRECTSOUNDCAPTUREBUFFER dsw_InputBuffer;
- UINT dsw_ReadOffset; /* last read position */
- UINT dsw_InputSize;
-} DSoundWrapper;
-
-HRESULT DSW_Init( DSoundWrapper *dsw );
-void DSW_Term( DSoundWrapper *dsw );
-HRESULT DSW_InitOutputBuffer( DSoundWrapper *dsw, unsigned long nFrameRate,
- WORD nChannels, int bufSize );
-HRESULT DSW_StartOutput( DSoundWrapper *dsw );
-HRESULT DSW_StopOutput( DSoundWrapper *dsw );
-DWORD DSW_GetOutputStatus( DSoundWrapper *dsw );
-HRESULT DSW_WriteBlock( DSoundWrapper *dsw, char *buf, long numBytes );
-HRESULT DSW_ZeroEmptySpace( DSoundWrapper *dsw );
-HRESULT DSW_QueryOutputSpace( DSoundWrapper *dsw, long *bytesEmpty );
-HRESULT DSW_Enumerate( DSoundWrapper *dsw );
-
-HRESULT DSW_InitInputBuffer( DSoundWrapper *dsw, unsigned long nFrameRate,
- WORD nChannels, int bufSize );
-HRESULT DSW_StartInput( DSoundWrapper *dsw );
-HRESULT DSW_StopInput( DSoundWrapper *dsw );
-HRESULT DSW_ReadBlock( DSoundWrapper *dsw, char *buf, long numBytes );
-HRESULT DSW_QueryInputFilled( DSoundWrapper *dsw, long *bytesFilled );
-HRESULT DSW_QueryOutputFilled( DSoundWrapper *dsw, long *bytesFilled );
-
-#ifdef __cplusplus
-}
-#endif /* __cplusplus */
-#endif /* __DSOUND_WRAPPER_H */
+/*
+ * PJMEDIA - Multimedia over IP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef __DSOUND_WRAPPER_H
+#define __DSOUND_WRAPPER_H
+/*
+ * $Id: dsound_wrapper.h,v 1.1.1.1.2.8 2005/01/16 20:48:37 rossbencina Exp $
+ * Simplified DirectSound interface.
+ *
+ * Author: Phil Burk & Robert Marsanyi
+ *
+ * For PortAudio Portable Real-Time Audio Library
+ * For more information see: http://www.softsynth.com/portaudio/
+ * DirectSound Implementation
+ * Copyright (c) 1999-2000 Phil Burk & Robert Marsanyi
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * Any person wishing to distribute modifications to the Software is
+ * requested to send the modifications to the original developer so that
+ * they can be incorporated into the canonical version.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+ * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+/* on Borland compilers, WIN32 doesn't seem to be defined by default, which
+ breaks DSound.h. Adding the define here fixes the problem. - rossb. */
+#ifdef __BORLANDC__
+#if !defined(WIN32)
+#define WIN32
+#endif
+#endif
+
+/*
+ We are only using DX3 in here, no need to polute the namespace - davidv
+*/
+#define DIRECTSOUND_VERSION 0x0300
+
+#include <DSound.h>
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif /* __cplusplus */
+
+
+typedef struct
+{
+ HINSTANCE hInstance_;
+
+ HRESULT (WINAPI *DirectSoundCreate)(LPGUID, LPDIRECTSOUND *, LPUNKNOWN);
+ HRESULT (WINAPI *DirectSoundEnumerateW)(LPDSENUMCALLBACKW, LPVOID);
+ HRESULT (WINAPI *DirectSoundEnumerateA)(LPDSENUMCALLBACKA, LPVOID);
+
+ HRESULT (WINAPI *DirectSoundCaptureCreate)(LPGUID, LPDIRECTSOUNDCAPTURE *, LPUNKNOWN);
+ HRESULT (WINAPI *DirectSoundCaptureEnumerateW)(LPDSENUMCALLBACKW, LPVOID);
+ HRESULT (WINAPI *DirectSoundCaptureEnumerateA)(LPDSENUMCALLBACKA, LPVOID);
+}DSoundEntryPoints;
+
+extern DSoundEntryPoints dswDSoundEntryPoints;
+
+void DSW_InitializeDSoundEntryPoints(void);
+void DSW_TerminateDSoundEntryPoints(void);
+
+#define DSW_NUM_POSITIONS (4)
+#define DSW_NUM_EVENTS (5)
+#define DSW_TERMINATION_EVENT (DSW_NUM_POSITIONS)
+
+typedef struct
+{
+/* Output */
+ LPDIRECTSOUND dsw_pDirectSound;
+ LPDIRECTSOUNDBUFFER dsw_OutputBuffer;
+ DWORD dsw_WriteOffset; /* last write position */
+ INT dsw_OutputSize;
+ INT dsw_BytesPerOutputFrame;
+ /* Try to detect play buffer underflows. */
+ LARGE_INTEGER dsw_CounterTicksPerBuffer; /* counter ticks it should take to play a full buffer */
+ LARGE_INTEGER dsw_LastPlayTime;
+ UINT dsw_LastPlayCursor;
+ UINT dsw_OutputUnderflows;
+ BOOL dsw_OutputRunning;
+ /* use double which lets us can play for several thousand years with enough precision */
+ double dsw_FramesWritten;
+ double dsw_FramesPlayed;
+/* Input */
+ INT dsw_BytesPerInputFrame;
+ LPDIRECTSOUNDCAPTURE dsw_pDirectSoundCapture;
+ LPDIRECTSOUNDCAPTUREBUFFER dsw_InputBuffer;
+ UINT dsw_ReadOffset; /* last read position */
+ UINT dsw_InputSize;
+} DSoundWrapper;
+
+HRESULT DSW_Init( DSoundWrapper *dsw );
+void DSW_Term( DSoundWrapper *dsw );
+HRESULT DSW_InitOutputBuffer( DSoundWrapper *dsw, unsigned long nFrameRate,
+ WORD nChannels, int bufSize );
+HRESULT DSW_StartOutput( DSoundWrapper *dsw );
+HRESULT DSW_StopOutput( DSoundWrapper *dsw );
+DWORD DSW_GetOutputStatus( DSoundWrapper *dsw );
+HRESULT DSW_WriteBlock( DSoundWrapper *dsw, char *buf, long numBytes );
+HRESULT DSW_ZeroEmptySpace( DSoundWrapper *dsw );
+HRESULT DSW_QueryOutputSpace( DSoundWrapper *dsw, long *bytesEmpty );
+HRESULT DSW_Enumerate( DSoundWrapper *dsw );
+
+HRESULT DSW_InitInputBuffer( DSoundWrapper *dsw, unsigned long nFrameRate,
+ WORD nChannels, int bufSize );
+HRESULT DSW_StartInput( DSoundWrapper *dsw );
+HRESULT DSW_StopInput( DSoundWrapper *dsw );
+HRESULT DSW_ReadBlock( DSoundWrapper *dsw, char *buf, long numBytes );
+HRESULT DSW_QueryInputFilled( DSoundWrapper *dsw, long *bytesFilled );
+HRESULT DSW_QueryOutputFilled( DSoundWrapper *dsw, long *bytesFilled );
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+#endif /* __DSOUND_WRAPPER_H */
diff --git a/pjmedia/src/pjmedia/portaudio/pa_allocation.c b/pjmedia/src/pjmedia/portaudio/pa_allocation.c
index 035b4d0b..d9f068c4 100644
--- a/pjmedia/src/pjmedia/portaudio/pa_allocation.c
+++ b/pjmedia/src/pjmedia/portaudio/pa_allocation.c
@@ -1,234 +1,255 @@
-/*
- * $Id: pa_allocation.c,v 1.1.2.6 2004/12/20 12:07:51 rossbencina Exp $
- * Portable Audio I/O Library allocation group implementation
- * memory allocation group for tracking allocation groups
- *
- * Based on the Open Source API proposed by Ross Bencina
- * Copyright (c) 1999-2002 Ross Bencina, Phil Burk
- *
- * Permission is hereby granted, free of charge, to any person obtaining
- * a copy of this software and associated documentation files
- * (the "Software"), to deal in the Software without restriction,
- * including without limitation the rights to use, copy, modify, merge,
- * publish, distribute, sublicense, and/or sell copies of the Software,
- * and to permit persons to whom the Software is furnished to do so,
- * subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be
- * included in all copies or substantial portions of the Software.
- *
- * Any person wishing to distribute modifications to the Software is
- * requested to send the modifications to the original developer so that
- * they can be incorporated into the canonical version.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
- * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
- * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
- * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
- * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
- */
-
-/** @file
- @brief Allocation Group implementation.
-*/
-
-
-#include "pa_allocation.h"
-#include "pa_util.h"
-
-
-/*
- Maintain 3 singly linked lists...
- linkBlocks: the buffers used to allocate the links
- spareLinks: links available for use in the allocations list
- allocations: the buffers currently allocated using PaUtil_ContextAllocateMemory()
-
- Link block size is doubled every time new links are allocated.
-*/
-
-
-#define PA_INITIAL_LINK_COUNT_ 16
-
-struct PaUtilAllocationGroupLink
-{
- struct PaUtilAllocationGroupLink *next;
- void *buffer;
-};
-
-/*
- Allocate a block of links. The first link will have it's buffer member
- pointing to the block, and it's next member set to <nextBlock>. The remaining
- links will have NULL buffer members, and each link will point to
- the next link except the last, which will point to <nextSpare>
-*/
-static struct PaUtilAllocationGroupLink *AllocateLinks( long count,
- struct PaUtilAllocationGroupLink *nextBlock,
- struct PaUtilAllocationGroupLink *nextSpare )
-{
- struct PaUtilAllocationGroupLink *result;
- int i;
-
- result = (struct PaUtilAllocationGroupLink *)PaUtil_AllocateMemory(
- sizeof(struct PaUtilAllocationGroupLink) * count );
- if( result )
- {
- /* the block link */
- result[0].buffer = result;
- result[0].next = nextBlock;
-
- /* the spare links */
- for( i=1; i<count; ++i )
- {
- result[i].buffer = 0;
- result[i].next = &result[i+1];
- }
- result[count-1].next = nextSpare;
- }
-
- return result;
-}
-
-
-PaUtilAllocationGroup* PaUtil_CreateAllocationGroup( void )
-{
- PaUtilAllocationGroup* result = 0;
- struct PaUtilAllocationGroupLink *links;
-
-
- links = AllocateLinks( PA_INITIAL_LINK_COUNT_, 0, 0 );
- if( links != 0 )
- {
- result = (PaUtilAllocationGroup*)PaUtil_AllocateMemory( sizeof(PaUtilAllocationGroup) );
- if( result )
- {
- result->linkCount = PA_INITIAL_LINK_COUNT_;
- result->linkBlocks = &links[0];
- result->spareLinks = &links[1];
- result->allocations = 0;
- }
- else
- {
- PaUtil_FreeMemory( links );
- }
- }
-
- return result;
-}
-
-
-void PaUtil_DestroyAllocationGroup( PaUtilAllocationGroup* group )
-{
- struct PaUtilAllocationGroupLink *current = group->linkBlocks;
- struct PaUtilAllocationGroupLink *next;
-
- while( current )
- {
- next = current->next;
- PaUtil_FreeMemory( current->buffer );
- current = next;
- }
-
- PaUtil_FreeMemory( group );
-}
-
-
-void* PaUtil_GroupAllocateMemory( PaUtilAllocationGroup* group, long size )
-{
- struct PaUtilAllocationGroupLink *links, *link;
- void *result = 0;
-
- /* allocate more links if necessary */
- if( !group->spareLinks )
- {
- /* double the link count on each block allocation */
- links = AllocateLinks( group->linkCount, group->linkBlocks, group->spareLinks );
- if( links )
- {
- group->linkCount += group->linkCount;
- group->linkBlocks = &links[0];
- group->spareLinks = &links[1];
- }
- }
-
- if( group->spareLinks )
- {
- result = PaUtil_AllocateMemory( size );
- if( result )
- {
- link = group->spareLinks;
- group->spareLinks = link->next;
-
- link->buffer = result;
- link->next = group->allocations;
-
- group->allocations = link;
- }
- }
-
- return result;
-}
-
-
-void PaUtil_GroupFreeMemory( PaUtilAllocationGroup* group, void *buffer )
-{
- struct PaUtilAllocationGroupLink *current = group->allocations;
- struct PaUtilAllocationGroupLink *previous = 0;
-
- if( buffer == 0 )
- return;
-
- /* find the right link and remove it */
- while( current )
- {
- if( current->buffer == buffer )
- {
- if( previous )
- {
- previous->next = current->next;
- }
- else
- {
- group->allocations = current->next;
- }
-
- current->buffer = 0;
- current->next = group->spareLinks;
- group->spareLinks = current;
-
- break;
- }
-
- previous = current;
- current = current->next;
- }
-
- PaUtil_FreeMemory( buffer ); /* free the memory whether we found it in the list or not */
-}
-
-
-void PaUtil_FreeAllAllocations( PaUtilAllocationGroup* group )
-{
- struct PaUtilAllocationGroupLink *current = group->allocations;
- struct PaUtilAllocationGroupLink *previous = 0;
-
- /* free all buffers in the allocations list */
- while( current )
- {
- PaUtil_FreeMemory( current->buffer );
- current->buffer = 0;
-
- previous = current;
- current = current->next;
- }
-
- /* link the former allocations list onto the front of the spareLinks list */
- if( previous )
- {
- previous->next = group->spareLinks;
- group->spareLinks = group->allocations;
- group->allocations = 0;
- }
-}
-
+/*
+ * $Id: pa_allocation.c,v 1.1.2.6 2004/12/20 12:07:51 rossbencina Exp $
+ * Portable Audio I/O Library allocation group implementation
+ * memory allocation group for tracking allocation groups
+ *
+ * Based on the Open Source API proposed by Ross Bencina
+ * Copyright (c) 1999-2002 Ross Bencina, Phil Burk
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * Any person wishing to distribute modifications to the Software is
+ * requested to send the modifications to the original developer so that
+ * they can be incorporated into the canonical version.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+ * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+/*
+ * PJMEDIA - Multimedia over IP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/** @file
+ @brief Allocation Group implementation.
+*/
+
+
+#include "pa_allocation.h"
+#include "pa_util.h"
+
+
+/*
+ Maintain 3 singly linked lists...
+ linkBlocks: the buffers used to allocate the links
+ spareLinks: links available for use in the allocations list
+ allocations: the buffers currently allocated using PaUtil_ContextAllocateMemory()
+
+ Link block size is doubled every time new links are allocated.
+*/
+
+
+#define PA_INITIAL_LINK_COUNT_ 16
+
+struct PaUtilAllocationGroupLink
+{
+ struct PaUtilAllocationGroupLink *next;
+ void *buffer;
+};
+
+/*
+ Allocate a block of links. The first link will have it's buffer member
+ pointing to the block, and it's next member set to <nextBlock>. The remaining
+ links will have NULL buffer members, and each link will point to
+ the next link except the last, which will point to <nextSpare>
+*/
+static struct PaUtilAllocationGroupLink *AllocateLinks( long count,
+ struct PaUtilAllocationGroupLink *nextBlock,
+ struct PaUtilAllocationGroupLink *nextSpare )
+{
+ struct PaUtilAllocationGroupLink *result;
+ int i;
+
+ result = (struct PaUtilAllocationGroupLink *)PaUtil_AllocateMemory(
+ sizeof(struct PaUtilAllocationGroupLink) * count );
+ if( result )
+ {
+ /* the block link */
+ result[0].buffer = result;
+ result[0].next = nextBlock;
+
+ /* the spare links */
+ for( i=1; i<count; ++i )
+ {
+ result[i].buffer = 0;
+ result[i].next = &result[i+1];
+ }
+ result[count-1].next = nextSpare;
+ }
+
+ return result;
+}
+
+
+PaUtilAllocationGroup* PaUtil_CreateAllocationGroup( void )
+{
+ PaUtilAllocationGroup* result = 0;
+ struct PaUtilAllocationGroupLink *links;
+
+
+ links = AllocateLinks( PA_INITIAL_LINK_COUNT_, 0, 0 );
+ if( links != 0 )
+ {
+ result = (PaUtilAllocationGroup*)PaUtil_AllocateMemory( sizeof(PaUtilAllocationGroup) );
+ if( result )
+ {
+ result->linkCount = PA_INITIAL_LINK_COUNT_;
+ result->linkBlocks = &links[0];
+ result->spareLinks = &links[1];
+ result->allocations = 0;
+ }
+ else
+ {
+ PaUtil_FreeMemory( links );
+ }
+ }
+
+ return result;
+}
+
+
+void PaUtil_DestroyAllocationGroup( PaUtilAllocationGroup* group )
+{
+ struct PaUtilAllocationGroupLink *current = group->linkBlocks;
+ struct PaUtilAllocationGroupLink *next;
+
+ while( current )
+ {
+ next = current->next;
+ PaUtil_FreeMemory( current->buffer );
+ current = next;
+ }
+
+ PaUtil_FreeMemory( group );
+}
+
+
+void* PaUtil_GroupAllocateMemory( PaUtilAllocationGroup* group, long size )
+{
+ struct PaUtilAllocationGroupLink *links, *link;
+ void *result = 0;
+
+ /* allocate more links if necessary */
+ if( !group->spareLinks )
+ {
+ /* double the link count on each block allocation */
+ links = AllocateLinks( group->linkCount, group->linkBlocks, group->spareLinks );
+ if( links )
+ {
+ group->linkCount += group->linkCount;
+ group->linkBlocks = &links[0];
+ group->spareLinks = &links[1];
+ }
+ }
+
+ if( group->spareLinks )
+ {
+ result = PaUtil_AllocateMemory( size );
+ if( result )
+ {
+ link = group->spareLinks;
+ group->spareLinks = link->next;
+
+ link->buffer = result;
+ link->next = group->allocations;
+
+ group->allocations = link;
+ }
+ }
+
+ return result;
+}
+
+
+void PaUtil_GroupFreeMemory( PaUtilAllocationGroup* group, void *buffer )
+{
+ struct PaUtilAllocationGroupLink *current = group->allocations;
+ struct PaUtilAllocationGroupLink *previous = 0;
+
+ if( buffer == 0 )
+ return;
+
+ /* find the right link and remove it */
+ while( current )
+ {
+ if( current->buffer == buffer )
+ {
+ if( previous )
+ {
+ previous->next = current->next;
+ }
+ else
+ {
+ group->allocations = current->next;
+ }
+
+ current->buffer = 0;
+ current->next = group->spareLinks;
+ group->spareLinks = current;
+
+ break;
+ }
+
+ previous = current;
+ current = current->next;
+ }
+
+ PaUtil_FreeMemory( buffer ); /* free the memory whether we found it in the list or not */
+}
+
+
+void PaUtil_FreeAllAllocations( PaUtilAllocationGroup* group )
+{
+ struct PaUtilAllocationGroupLink *current = group->allocations;
+ struct PaUtilAllocationGroupLink *previous = 0;
+
+ /* free all buffers in the allocations list */
+ while( current )
+ {
+ PaUtil_FreeMemory( current->buffer );
+ current->buffer = 0;
+
+ previous = current;
+ current = current->next;
+ }
+
+ /* link the former allocations list onto the front of the spareLinks list */
+ if( previous )
+ {
+ previous->next = group->spareLinks;
+ group->spareLinks = group->allocations;
+ group->allocations = 0;
+ }
+}
+
diff --git a/pjmedia/src/pjmedia/portaudio/pa_allocation.h b/pjmedia/src/pjmedia/portaudio/pa_allocation.h
index fb9321a0..788a8ae6 100644
--- a/pjmedia/src/pjmedia/portaudio/pa_allocation.h
+++ b/pjmedia/src/pjmedia/portaudio/pa_allocation.h
@@ -1,95 +1,116 @@
-#ifndef PA_ALLOCATION_H
-#define PA_ALLOCATION_H
-/*
- * $Id: pa_allocation.h,v 1.1.2.4 2003/09/20 21:04:44 rossbencina Exp $
- * Portable Audio I/O Library allocation context header
- * memory allocation context for tracking allocation groups
- *
- * Based on the Open Source API proposed by Ross Bencina
- * Copyright (c) 1999-2002 Ross Bencina, Phil Burk
- *
- * Permission is hereby granted, free of charge, to any person obtaining
- * a copy of this software and associated documentation files
- * (the "Software"), to deal in the Software without restriction,
- * including without limitation the rights to use, copy, modify, merge,
- * publish, distribute, sublicense, and/or sell copies of the Software,
- * and to permit persons to whom the Software is furnished to do so,
- * subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be
- * included in all copies or substantial portions of the Software.
- *
- * Any person wishing to distribute modifications to the Software is
- * requested to send the modifications to the original developer so that
- * they can be incorporated into the canonical version.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
- * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
- * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
- * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
- * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
- */
-
-/** @file
- @brief Allocation Group prototypes. An Allocation Group makes it easy to
- allocate multiple blocks of memory and free them all simultanously.
-
- An allocation group is useful for keeping track of multiple blocks
- of memory which are allocated at the same time (such as during initialization)
- and need to be deallocated at the same time. The allocation group maintains
- a list of allocated blocks, and can deallocate them all simultaneously which
- can be usefull for cleaning up after a partially initialized object fails.
-
- The allocation group implementation is built on top of the lower
- level allocation functions defined in pa_util.h
-*/
-
-
-#ifdef __cplusplus
-extern "C"
-{
-#endif /* __cplusplus */
-
-
-typedef struct
-{
- long linkCount;
- struct PaUtilAllocationGroupLink *linkBlocks;
- struct PaUtilAllocationGroupLink *spareLinks;
- struct PaUtilAllocationGroupLink *allocations;
-}PaUtilAllocationGroup;
-
-
-
-/** Create an allocation group.
-*/
-PaUtilAllocationGroup* PaUtil_CreateAllocationGroup( void );
-
-/** Destroy an allocation group, but not the memory allocated through the group.
-*/
-void PaUtil_DestroyAllocationGroup( PaUtilAllocationGroup* group );
-
-/** Allocate a block of memory though an allocation group.
-*/
-void* PaUtil_GroupAllocateMemory( PaUtilAllocationGroup* group, long size );
-
-/** Free a block of memory that was previously allocated though an allocation
- group. Calling this function is a relatively time consuming operation.
- Under normal circumstances clients should call PaUtil_FreeAllAllocations to
- free all allocated blocks simultaneously.
- @see PaUtil_FreeAllAllocations
-*/
-void PaUtil_GroupFreeMemory( PaUtilAllocationGroup* group, void *buffer );
-
-/** Free all blocks of memory which have been allocated through the allocation
- group. This function doesn't destroy the group itself.
-*/
-void PaUtil_FreeAllAllocations( PaUtilAllocationGroup* group );
-
-
-#ifdef __cplusplus
-}
-#endif /* __cplusplus */
-#endif /* PA_ALLOCATION_H */
+/*
+ * PJMEDIA - Multimedia over IP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef PA_ALLOCATION_H
+#define PA_ALLOCATION_H
+/*
+ * $Id: pa_allocation.h,v 1.1.2.4 2003/09/20 21:04:44 rossbencina Exp $
+ * Portable Audio I/O Library allocation context header
+ * memory allocation context for tracking allocation groups
+ *
+ * Based on the Open Source API proposed by Ross Bencina
+ * Copyright (c) 1999-2002 Ross Bencina, Phil Burk
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * Any person wishing to distribute modifications to the Software is
+ * requested to send the modifications to the original developer so that
+ * they can be incorporated into the canonical version.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+ * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+/** @file
+ @brief Allocation Group prototypes. An Allocation Group makes it easy to
+ allocate multiple blocks of memory and free them all simultanously.
+
+ An allocation group is useful for keeping track of multiple blocks
+ of memory which are allocated at the same time (such as during initialization)
+ and need to be deallocated at the same time. The allocation group maintains
+ a list of allocated blocks, and can deallocate them all simultaneously which
+ can be usefull for cleaning up after a partially initialized object fails.
+
+ The allocation group implementation is built on top of the lower
+ level allocation functions defined in pa_util.h
+*/
+
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif /* __cplusplus */
+
+
+typedef struct
+{
+ long linkCount;
+ struct PaUtilAllocationGroupLink *linkBlocks;
+ struct PaUtilAllocationGroupLink *spareLinks;
+ struct PaUtilAllocationGroupLink *allocations;
+}PaUtilAllocationGroup;
+
+
+
+/** Create an allocation group.
+*/
+PaUtilAllocationGroup* PaUtil_CreateAllocationGroup( void );
+
+/** Destroy an allocation group, but not the memory allocated through the group.
+*/
+void PaUtil_DestroyAllocationGroup( PaUtilAllocationGroup* group );
+
+/** Allocate a block of memory though an allocation group.
+*/
+void* PaUtil_GroupAllocateMemory( PaUtilAllocationGroup* group, long size );
+
+/** Free a block of memory that was previously allocated though an allocation
+ group. Calling this function is a relatively time consuming operation.
+ Under normal circumstances clients should call PaUtil_FreeAllAllocations to
+ free all allocated blocks simultaneously.
+ @see PaUtil_FreeAllAllocations
+*/
+void PaUtil_GroupFreeMemory( PaUtilAllocationGroup* group, void *buffer );
+
+/** Free all blocks of memory which have been allocated through the allocation
+ group. This function doesn't destroy the group itself.
+*/
+void PaUtil_FreeAllAllocations( PaUtilAllocationGroup* group );
+
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+#endif /* PA_ALLOCATION_H */
diff --git a/pjmedia/src/pjmedia/portaudio/pa_converters.c b/pjmedia/src/pjmedia/portaudio/pa_converters.c
index 4ee73c9a..33fe6a8a 100644
--- a/pjmedia/src/pjmedia/portaudio/pa_converters.c
+++ b/pjmedia/src/pjmedia/portaudio/pa_converters.c
@@ -1,1926 +1,1947 @@
-/*
- * $Id: pa_converters.c,v 1.1.2.26 2004/12/11 16:32:38 aknudsen Exp $
- * Portable Audio I/O Library sample conversion mechanism
- *
- * Based on the Open Source API proposed by Ross Bencina
- * Copyright (c) 1999-2002 Phil Burk, Ross Bencina
- *
- * Permission is hereby granted, free of charge, to any person obtaining
- * a copy of this software and associated documentation files
- * (the "Software"), to deal in the Software without restriction,
- * including without limitation the rights to use, copy, modify, merge,
- * publish, distribute, sublicense, and/or sell copies of the Software,
- * and to permit persons to whom the Software is furnished to do so,
- * subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be
- * included in all copies or substantial portions of the Software.
- *
- * Any person wishing to distribute modifications to the Software is
- * requested to send the modifications to the original developer so that
- * they can be incorporated into the canonical version.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
- * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
- * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
- * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
- * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
- */
-
-/** @file
- @brief Conversion functions implementations.
-
- If the C9x function lrintf() is available, define PA_USE_C99_LRINTF to use it
-
- @todo Consider whether functions which dither but don't clip should exist,
- V18 automatically enabled clipping whenever dithering was selected. Perhaps
- we should do the same.
-
- @todo implement the converters marked IMPLEMENT ME: Float32_To_UInt8_Dither,
- Float32_To_UInt8_Clip, Float32_To_UInt8_DitherClip, Int32_To_Int24_Dither,
- Int32_To_UInt8_Dither, Int24_To_Int16_Dither, Int24_To_Int8_Dither,
- Int24_To_UInt8_Dither, Int16_To_Int8_Dither, Int16_To_UInt8_Dither,
-
- @todo review the converters marked REVIEW: Float32_To_Int32,
- Float32_To_Int32_Dither, Float32_To_Int32_Clip, Float32_To_Int32_DitherClip,
- Int32_To_Int16_Dither, Int32_To_Int8_Dither, Int16_To_Int32
-*/
-
-
-#include "pa_converters.h"
-#include "pa_dither.h"
-#include "pa_endianness.h"
-#include "pa_types.h"
-
-
-PaSampleFormat PaUtil_SelectClosestAvailableFormat(
- PaSampleFormat availableFormats, PaSampleFormat format )
-{
- PaSampleFormat result;
-
- format &= ~paNonInterleaved;
- availableFormats &= ~paNonInterleaved;
-
- if( (format & availableFormats) == 0 )
- {
- /* NOTE: this code depends on the sample format constants being in
- descending order of quality - ie best quality is 0
- FIXME: should write an assert which checks that all of the
- known constants conform to that requirement.
- */
-
- if( format != 0x01 )
- {
- /* scan for better formats */
- result = format;
- do
- {
- result >>= 1;
- }
- while( (result & availableFormats) == 0 && result != 0 );
- }
- else
- {
- result = 0;
- }
-
- if( result == 0 ){
- /* scan for worse formats */
- result = format;
- do
- {
- result <<= 1;
- }
- while( (result & availableFormats) == 0 && result != paCustomFormat );
-
- if( (result & availableFormats) == 0 )
- result = paSampleFormatNotSupported;
- }
-
- }else{
- result = format;
- }
-
- return result;
-}
-
-/* -------------------------------------------------------------------------- */
-
-#define PA_SELECT_FORMAT_( format, float32, int32, int24, int16, int8, uint8 ) \
- switch( format & ~paNonInterleaved ){ \
- case paFloat32: \
- float32 \
- case paInt32: \
- int32 \
- case paInt24: \
- int24 \
- case paInt16: \
- int16 \
- case paInt8: \
- int8 \
- case paUInt8: \
- uint8 \
- default: return 0; \
- }
-
-/* -------------------------------------------------------------------------- */
-
-#define PA_SELECT_CONVERTER_DITHER_CLIP_( flags, source, destination ) \
- if( flags & paClipOff ){ /* no clip */ \
- if( flags & paDitherOff ){ /* no dither */ \
- return paConverters. source ## _To_ ## destination; \
- }else{ /* dither */ \
- return paConverters. source ## _To_ ## destination ## _Dither; \
- } \
- }else{ /* clip */ \
- if( flags & paDitherOff ){ /* no dither */ \
- return paConverters. source ## _To_ ## destination ## _Clip; \
- }else{ /* dither */ \
- return paConverters. source ## _To_ ## destination ## _DitherClip; \
- } \
- }
-
-/* -------------------------------------------------------------------------- */
-
-#define PA_SELECT_CONVERTER_DITHER_( flags, source, destination ) \
- if( flags & paDitherOff ){ /* no dither */ \
- return paConverters. source ## _To_ ## destination; \
- }else{ /* dither */ \
- return paConverters. source ## _To_ ## destination ## _Dither; \
- }
-
-/* -------------------------------------------------------------------------- */
-
-#define PA_USE_CONVERTER_( source, destination )\
- return paConverters. source ## _To_ ## destination;
-
-/* -------------------------------------------------------------------------- */
-
-#define PA_UNITY_CONVERSION_( wordlength )\
- return paConverters. Copy_ ## wordlength ## _To_ ## wordlength;
-
-/* -------------------------------------------------------------------------- */
-
-PaUtilConverter* PaUtil_SelectConverter( PaSampleFormat sourceFormat,
- PaSampleFormat destinationFormat, PaStreamFlags flags )
-{
- PA_SELECT_FORMAT_( sourceFormat,
- /* paFloat32: */
- PA_SELECT_FORMAT_( destinationFormat,
- /* paFloat32: */ PA_UNITY_CONVERSION_( 32 ),
- /* paInt32: */ PA_SELECT_CONVERTER_DITHER_CLIP_( flags, Float32, Int32 ),
- /* paInt24: */ PA_SELECT_CONVERTER_DITHER_CLIP_( flags, Float32, Int24 ),
- /* paInt16: */ PA_SELECT_CONVERTER_DITHER_CLIP_( flags, Float32, Int16 ),
- /* paInt8: */ PA_SELECT_CONVERTER_DITHER_CLIP_( flags, Float32, Int8 ),
- /* paUInt8: */ PA_SELECT_CONVERTER_DITHER_CLIP_( flags, Float32, UInt8 )
- ),
- /* paInt32: */
- PA_SELECT_FORMAT_( destinationFormat,
- /* paFloat32: */ PA_USE_CONVERTER_( Int32, Float32 ),
- /* paInt32: */ PA_UNITY_CONVERSION_( 32 ),
- /* paInt24: */ PA_SELECT_CONVERTER_DITHER_( flags, Int32, Int24 ),
- /* paInt16: */ PA_SELECT_CONVERTER_DITHER_( flags, Int32, Int16 ),
- /* paInt8: */ PA_SELECT_CONVERTER_DITHER_( flags, Int32, Int8 ),
- /* paUInt8: */ PA_SELECT_CONVERTER_DITHER_( flags, Int32, UInt8 )
- ),
- /* paInt24: */
- PA_SELECT_FORMAT_( destinationFormat,
- /* paFloat32: */ PA_USE_CONVERTER_( Int24, Float32 ),
- /* paInt32: */ PA_USE_CONVERTER_( Int24, Int32 ),
- /* paInt24: */ PA_UNITY_CONVERSION_( 24 ),
- /* paInt16: */ PA_SELECT_CONVERTER_DITHER_( flags, Int24, Int16 ),
- /* paInt8: */ PA_SELECT_CONVERTER_DITHER_( flags, Int24, Int8 ),
- /* paUInt8: */ PA_SELECT_CONVERTER_DITHER_( flags, Int24, UInt8 )
- ),
- /* paInt16: */
- PA_SELECT_FORMAT_( destinationFormat,
- /* paFloat32: */ PA_USE_CONVERTER_( Int16, Float32 ),
- /* paInt32: */ PA_USE_CONVERTER_( Int16, Int32 ),
- /* paInt24: */ PA_USE_CONVERTER_( Int16, Int24 ),
- /* paInt16: */ PA_UNITY_CONVERSION_( 16 ),
- /* paInt8: */ PA_SELECT_CONVERTER_DITHER_( flags, Int16, Int8 ),
- /* paUInt8: */ PA_SELECT_CONVERTER_DITHER_( flags, Int16, UInt8 )
- ),
- /* paInt8: */
- PA_SELECT_FORMAT_( destinationFormat,
- /* paFloat32: */ PA_USE_CONVERTER_( Int8, Float32 ),
- /* paInt32: */ PA_USE_CONVERTER_( Int8, Int32 ),
- /* paInt24: */ PA_USE_CONVERTER_( Int8, Int24 ),
- /* paInt16: */ PA_USE_CONVERTER_( Int8, Int16 ),
- /* paInt8: */ PA_UNITY_CONVERSION_( 8 ),
- /* paUInt8: */ PA_USE_CONVERTER_( Int8, UInt8 )
- ),
- /* paUInt8: */
- PA_SELECT_FORMAT_( destinationFormat,
- /* paFloat32: */ PA_USE_CONVERTER_( UInt8, Float32 ),
- /* paInt32: */ PA_USE_CONVERTER_( UInt8, Int32 ),
- /* paInt24: */ PA_USE_CONVERTER_( UInt8, Int24 ),
- /* paInt16: */ PA_USE_CONVERTER_( UInt8, Int16 ),
- /* paInt8: */ PA_USE_CONVERTER_( UInt8, Int8 ),
- /* paUInt8: */ PA_UNITY_CONVERSION_( 8 )
- )
- )
-}
-
-/* -------------------------------------------------------------------------- */
-
-#ifdef PA_NO_STANDARD_CONVERTERS
-
-/* -------------------------------------------------------------------------- */
-
-PaUtilConverterTable paConverters = {
- 0, /* PaUtilConverter *Float32_To_Int32; */
- 0, /* PaUtilConverter *Float32_To_Int32_Dither; */
- 0, /* PaUtilConverter *Float32_To_Int32_Clip; */
- 0, /* PaUtilConverter *Float32_To_Int32_DitherClip; */
-
- 0, /* PaUtilConverter *Float32_To_Int24; */
- 0, /* PaUtilConverter *Float32_To_Int24_Dither; */
- 0, /* PaUtilConverter *Float32_To_Int24_Clip; */
- 0, /* PaUtilConverter *Float32_To_Int24_DitherClip; */
-
- 0, /* PaUtilConverter *Float32_To_Int16; */
- 0, /* PaUtilConverter *Float32_To_Int16_Dither; */
- 0, /* PaUtilConverter *Float32_To_Int16_Clip; */
- 0, /* PaUtilConverter *Float32_To_Int16_DitherClip; */
-
- 0, /* PaUtilConverter *Float32_To_Int8; */
- 0, /* PaUtilConverter *Float32_To_Int8_Dither; */
- 0, /* PaUtilConverter *Float32_To_Int8_Clip; */
- 0, /* PaUtilConverter *Float32_To_Int8_DitherClip; */
-
- 0, /* PaUtilConverter *Float32_To_UInt8; */
- 0, /* PaUtilConverter *Float32_To_UInt8_Dither; */
- 0, /* PaUtilConverter *Float32_To_UInt8_Clip; */
- 0, /* PaUtilConverter *Float32_To_UInt8_DitherClip; */
-
- 0, /* PaUtilConverter *Int32_To_Float32; */
- 0, /* PaUtilConverter *Int32_To_Int24; */
- 0, /* PaUtilConverter *Int32_To_Int24_Dither; */
- 0, /* PaUtilConverter *Int32_To_Int16; */
- 0, /* PaUtilConverter *Int32_To_Int16_Dither; */
- 0, /* PaUtilConverter *Int32_To_Int8; */
- 0, /* PaUtilConverter *Int32_To_Int8_Dither; */
- 0, /* PaUtilConverter *Int32_To_UInt8; */
- 0, /* PaUtilConverter *Int32_To_UInt8_Dither; */
-
- 0, /* PaUtilConverter *Int24_To_Float32; */
- 0, /* PaUtilConverter *Int24_To_Int32; */
- 0, /* PaUtilConverter *Int24_To_Int16; */
- 0, /* PaUtilConverter *Int24_To_Int16_Dither; */
- 0, /* PaUtilConverter *Int24_To_Int8; */
- 0, /* PaUtilConverter *Int24_To_Int8_Dither; */
- 0, /* PaUtilConverter *Int24_To_UInt8; */
- 0, /* PaUtilConverter *Int24_To_UInt8_Dither; */
-
- 0, /* PaUtilConverter *Int16_To_Float32; */
- 0, /* PaUtilConverter *Int16_To_Int32; */
- 0, /* PaUtilConverter *Int16_To_Int24; */
- 0, /* PaUtilConverter *Int16_To_Int8; */
- 0, /* PaUtilConverter *Int16_To_Int8_Dither; */
- 0, /* PaUtilConverter *Int16_To_UInt8; */
- 0, /* PaUtilConverter *Int16_To_UInt8_Dither; */
-
- 0, /* PaUtilConverter *Int8_To_Float32; */
- 0, /* PaUtilConverter *Int8_To_Int32; */
- 0, /* PaUtilConverter *Int8_To_Int24 */
- 0, /* PaUtilConverter *Int8_To_Int16; */
- 0, /* PaUtilConverter *Int8_To_UInt8; */
-
- 0, /* PaUtilConverter *UInt8_To_Float32; */
- 0, /* PaUtilConverter *UInt8_To_Int32; */
- 0, /* PaUtilConverter *UInt8_To_Int24; */
- 0, /* PaUtilConverter *UInt8_To_Int16; */
- 0, /* PaUtilConverter *UInt8_To_Int8; */
-
- 0, /* PaUtilConverter *Copy_8_To_8; */
- 0, /* PaUtilConverter *Copy_16_To_16; */
- 0, /* PaUtilConverter *Copy_24_To_24; */
- 0 /* PaUtilConverter *Copy_32_To_32; */
-};
-
-/* -------------------------------------------------------------------------- */
-
-#else /* PA_NO_STANDARD_CONVERTERS is not defined */
-
-/* -------------------------------------------------------------------------- */
-
-#define PA_CLIP_( val, min, max )\
- { val = ((val) < (min)) ? (min) : (((val) > (max)) ? (max) : (val)); }
-
-
-static const float const_1_div_128_ = 1.0f / 128.0f; /* 8 bit multiplier */
-
-static const float const_1_div_32768_ = 1.0f / 32768.f; /* 16 bit multiplier */
-
-static const double const_1_div_2147483648_ = 1.0 / 2147483648.0; /* 32 bit multiplier */
-
-/* -------------------------------------------------------------------------- */
-
-static void Float32_To_Int32(
- void *destinationBuffer, signed int destinationStride,
- void *sourceBuffer, signed int sourceStride,
- unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
-{
- float *src = (float*)sourceBuffer;
- signed long *dest = (signed long*)destinationBuffer;
- (void)ditherGenerator; /* unused parameter */
-
- while( count-- )
- {
- /* REVIEW */
-#ifdef PA_USE_C99_LRINTF
- float scaled = *src * 0x7FFFFFFF;
- *dest = lrintf(scaled-0.5f);
-#else
- double scaled = *src * 0x7FFFFFFF;
- *dest = (signed long) scaled;
-#endif
-
- src += sourceStride;
- dest += destinationStride;
- }
-}
-
-/* -------------------------------------------------------------------------- */
-
-static void Float32_To_Int32_Dither(
- void *destinationBuffer, signed int destinationStride,
- void *sourceBuffer, signed int sourceStride,
- unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
-{
- float *src = (float*)sourceBuffer;
- signed long *dest = (signed long*)destinationBuffer;
-
- while( count-- )
- {
- /* REVIEW */
-#ifdef PA_USE_C99_LRINTF
- float dither = PaUtil_GenerateFloatTriangularDither( ditherGenerator );
- /* use smaller scaler to prevent overflow when we add the dither */
- float dithered = ((float)*src * (2147483646.0f)) + dither;
- *dest = lrintf(dithered - 0.5f);
-#else
- double dither = PaUtil_GenerateFloatTriangularDither( ditherGenerator );
- /* use smaller scaler to prevent overflow when we add the dither */
- double dithered = ((double)*src * (2147483646.0)) + dither;
- *dest = (signed long) dithered;
-#endif
- src += sourceStride;
- dest += destinationStride;
- }
-}
-
-/* -------------------------------------------------------------------------- */
-
-static void Float32_To_Int32_Clip(
- void *destinationBuffer, signed int destinationStride,
- void *sourceBuffer, signed int sourceStride,
- unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
-{
- float *src = (float*)sourceBuffer;
- signed long *dest = (signed long*)destinationBuffer;
- (void) ditherGenerator; /* unused parameter */
-
- while( count-- )
- {
- /* REVIEW */
-#ifdef PA_USE_C99_LRINTF
- float scaled = *src * 0x7FFFFFFF;
- PA_CLIP_( scaled, -2147483648.f, 2147483647.f );
- *dest = lrintf(scaled-0.5f);
-#else
- double scaled = *src * 0x7FFFFFFF;
- PA_CLIP_( scaled, -2147483648., 2147483647. );
- *dest = (signed long) scaled;
-#endif
-
- src += sourceStride;
- dest += destinationStride;
- }
-}
-
-/* -------------------------------------------------------------------------- */
-
-static void Float32_To_Int32_DitherClip(
- void *destinationBuffer, signed int destinationStride,
- void *sourceBuffer, signed int sourceStride,
- unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
-{
- float *src = (float*)sourceBuffer;
- signed long *dest = (signed long*)destinationBuffer;
-
- while( count-- )
- {
- /* REVIEW */
-#ifdef PA_USE_C99_LRINTF
- float dither = PaUtil_GenerateFloatTriangularDither( ditherGenerator );
- /* use smaller scaler to prevent overflow when we add the dither */
- float dithered = ((float)*src * (2147483646.0f)) + dither;
- PA_CLIP_( dithered, -2147483648.f, 2147483647.f );
- *dest = lrintf(dithered-0.5f);
-#else
- double dither = PaUtil_GenerateFloatTriangularDither( ditherGenerator );
- /* use smaller scaler to prevent overflow when we add the dither */
- double dithered = ((double)*src * (2147483646.0)) + dither;
- PA_CLIP_( dithered, -2147483648., 2147483647. );
- *dest = (signed long) dithered;
-#endif
-
- src += sourceStride;
- dest += destinationStride;
- }
-}
-
-/* -------------------------------------------------------------------------- */
-
-static void Float32_To_Int24(
- void *destinationBuffer, signed int destinationStride,
- void *sourceBuffer, signed int sourceStride,
- unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
-{
- float *src = (float*)sourceBuffer;
- unsigned char *dest = (unsigned char*)destinationBuffer;
- signed long temp;
-
- (void) ditherGenerator; /* unused parameter */
-
- while( count-- )
- {
- /* convert to 32 bit and drop the low 8 bits */
- double scaled = *src * 0x7FFFFFFF;
- temp = (signed long) scaled;
-
-#if defined(PA_LITTLE_ENDIAN)
- dest[0] = (unsigned char)(temp >> 8);
- dest[1] = (unsigned char)(temp >> 16);
- dest[2] = (unsigned char)(temp >> 24);
-#elif defined(PA_BIG_ENDIAN)
- dest[0] = (unsigned char)(temp >> 24);
- dest[1] = (unsigned char)(temp >> 16);
- dest[2] = (unsigned char)(temp >> 8);
-#endif
-
- src += sourceStride;
- dest += destinationStride * 3;
- }
-}
-
-/* -------------------------------------------------------------------------- */
-
-static void Float32_To_Int24_Dither(
- void *destinationBuffer, signed int destinationStride,
- void *sourceBuffer, signed int sourceStride,
- unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
-{
- float *src = (float*)sourceBuffer;
- unsigned char *dest = (unsigned char*)destinationBuffer;
- signed long temp;
-
- while( count-- )
- {
- /* convert to 32 bit and drop the low 8 bits */
-
- double dither = PaUtil_GenerateFloatTriangularDither( ditherGenerator );
- /* use smaller scaler to prevent overflow when we add the dither */
- double dithered = ((double)*src * (2147483646.0)) + dither;
-
- temp = (signed long) dithered;
-
-#if defined(PA_LITTLE_ENDIAN)
- dest[0] = (unsigned char)(temp >> 8);
- dest[1] = (unsigned char)(temp >> 16);
- dest[2] = (unsigned char)(temp >> 24);
-#elif defined(PA_BIG_ENDIAN)
- dest[0] = (unsigned char)(temp >> 24);
- dest[1] = (unsigned char)(temp >> 16);
- dest[2] = (unsigned char)(temp >> 8);
-#endif
-
- src += sourceStride;
- dest += destinationStride * 3;
- }
-}
-
-/* -------------------------------------------------------------------------- */
-
-static void Float32_To_Int24_Clip(
- void *destinationBuffer, signed int destinationStride,
- void *sourceBuffer, signed int sourceStride,
- unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
-{
- float *src = (float*)sourceBuffer;
- unsigned char *dest = (unsigned char*)destinationBuffer;
- signed long temp;
-
- (void) ditherGenerator; /* unused parameter */
-
- while( count-- )
- {
- /* convert to 32 bit and drop the low 8 bits */
- double scaled = *src * 0x7FFFFFFF;
- PA_CLIP_( scaled, -2147483648., 2147483647. );
- temp = (signed long) scaled;
-
-#if defined(PA_LITTLE_ENDIAN)
- dest[0] = (unsigned char)(temp >> 8);
- dest[1] = (unsigned char)(temp >> 16);
- dest[2] = (unsigned char)(temp >> 24);
-#elif defined(PA_BIG_ENDIAN)
- dest[0] = (unsigned char)(temp >> 24);
- dest[1] = (unsigned char)(temp >> 16);
- dest[2] = (unsigned char)(temp >> 8);
-#endif
-
- src += sourceStride;
- dest += destinationStride * 3;
- }
-}
-
-/* -------------------------------------------------------------------------- */
-
-static void Float32_To_Int24_DitherClip(
- void *destinationBuffer, signed int destinationStride,
- void *sourceBuffer, signed int sourceStride,
- unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
-{
- float *src = (float*)sourceBuffer;
- unsigned char *dest = (unsigned char*)destinationBuffer;
- signed long temp;
-
- while( count-- )
- {
- /* convert to 32 bit and drop the low 8 bits */
-
- double dither = PaUtil_GenerateFloatTriangularDither( ditherGenerator );
- /* use smaller scaler to prevent overflow when we add the dither */
- double dithered = ((double)*src * (2147483646.0)) + dither;
- PA_CLIP_( dithered, -2147483648., 2147483647. );
-
- temp = (signed long) dithered;
-
-#if defined(PA_LITTLE_ENDIAN)
- dest[0] = (unsigned char)(temp >> 8);
- dest[1] = (unsigned char)(temp >> 16);
- dest[2] = (unsigned char)(temp >> 24);
-#elif defined(PA_BIG_ENDIAN)
- dest[0] = (unsigned char)(temp >> 24);
- dest[1] = (unsigned char)(temp >> 16);
- dest[2] = (unsigned char)(temp >> 8);
-#endif
-
- src += sourceStride;
- dest += destinationStride * 3;
- }
-}
-
-/* -------------------------------------------------------------------------- */
-
-static void Float32_To_Int16(
- void *destinationBuffer, signed int destinationStride,
- void *sourceBuffer, signed int sourceStride,
- unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
-{
- float *src = (float*)sourceBuffer;
- signed short *dest = (signed short*)destinationBuffer;
- (void)ditherGenerator; /* unused parameter */
-
- while( count-- )
- {
-#ifdef PA_USE_C99_LRINTF
- float tempf = (*src * (32767.0f)) ;
- *dest = lrintf(tempf-0.5f);
-#else
- short samp = (short) (*src * (32767.0f));
- *dest = samp;
-#endif
-
- src += sourceStride;
- dest += destinationStride;
- }
-}
-
-/* -------------------------------------------------------------------------- */
-
-static void Float32_To_Int16_Dither(
- void *destinationBuffer, signed int destinationStride,
- void *sourceBuffer, signed int sourceStride,
- unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
-{
- float *src = (float*)sourceBuffer;
- signed short *dest = (signed short*)destinationBuffer;
-
- while( count-- )
- {
-
- float dither = PaUtil_GenerateFloatTriangularDither( ditherGenerator );
- /* use smaller scaler to prevent overflow when we add the dither */
- float dithered = (*src * (32766.0f)) + dither;
-
-#ifdef PA_USE_C99_LRINTF
- *dest = lrintf(dithered-0.5f);
-#else
- *dest = (signed short) dithered;
-#endif
-
- src += sourceStride;
- dest += destinationStride;
- }
-}
-
-/* -------------------------------------------------------------------------- */
-
-static void Float32_To_Int16_Clip(
- void *destinationBuffer, signed int destinationStride,
- void *sourceBuffer, signed int sourceStride,
- unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
-{
- float *src = (float*)sourceBuffer;
- signed short *dest = (signed short*)destinationBuffer;
- (void)ditherGenerator; /* unused parameter */
-
- while( count-- )
- {
-#ifdef PA_USE_C99_LRINTF
- long samp = lrintf((*src * (32767.0f)) -0.5f);
-#else
- long samp = (signed long) (*src * (32767.0f));
-#endif
- PA_CLIP_( samp, -0x8000, 0x7FFF );
- *dest = (signed short) samp;
-
- src += sourceStride;
- dest += destinationStride;
- }
-}
-
-/* -------------------------------------------------------------------------- */
-
-static void Float32_To_Int16_DitherClip(
- void *destinationBuffer, signed int destinationStride,
- void *sourceBuffer, signed int sourceStride,
- unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
-{
- float *src = (float*)sourceBuffer;
- signed short *dest = (signed short*)destinationBuffer;
- (void)ditherGenerator; /* unused parameter */
-
- while( count-- )
- {
-
- float dither = PaUtil_GenerateFloatTriangularDither( ditherGenerator );
- /* use smaller scaler to prevent overflow when we add the dither */
- float dithered = (*src * (32766.0f)) + dither;
- signed long samp = (signed long) dithered;
- PA_CLIP_( samp, -0x8000, 0x7FFF );
-#ifdef PA_USE_C99_LRINTF
- *dest = lrintf(samp-0.5f);
-#else
- *dest = (signed short) samp;
-#endif
-
- src += sourceStride;
- dest += destinationStride;
- }
-}
-
-/* -------------------------------------------------------------------------- */
-
-static void Float32_To_Int8(
- void *destinationBuffer, signed int destinationStride,
- void *sourceBuffer, signed int sourceStride,
- unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
-{
- float *src = (float*)sourceBuffer;
- signed char *dest = (signed char*)destinationBuffer;
- (void)ditherGenerator; /* unused parameter */
-
- while( count-- )
- {
- signed char samp = (signed char) (*src * (127.0f));
- *dest = samp;
-
- src += sourceStride;
- dest += destinationStride;
- }
-}
-
-/* -------------------------------------------------------------------------- */
-
-static void Float32_To_Int8_Dither(
- void *destinationBuffer, signed int destinationStride,
- void *sourceBuffer, signed int sourceStride,
- unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
-{
- float *src = (float*)sourceBuffer;
- signed char *dest = (signed char*)destinationBuffer;
- (void)ditherGenerator; /* unused parameter */
-
- while( count-- )
- {
- float dither = PaUtil_GenerateFloatTriangularDither( ditherGenerator );
- /* use smaller scaler to prevent overflow when we add the dither */
- float dithered = (*src * (126.0f)) + dither;
- signed long samp = (signed long) dithered;
- *dest = (signed char) samp;
-
- src += sourceStride;
- dest += destinationStride;
- }
-}
-
-/* -------------------------------------------------------------------------- */
-
-static void Float32_To_Int8_Clip(
- void *destinationBuffer, signed int destinationStride,
- void *sourceBuffer, signed int sourceStride,
- unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
-{
- float *src = (float*)sourceBuffer;
- signed char *dest = (signed char*)destinationBuffer;
- (void)ditherGenerator; /* unused parameter */
-
- while( count-- )
- {
- signed long samp = (signed long)(*src * (127.0f));
- PA_CLIP_( samp, -0x80, 0x7F );
- *dest = (signed char) samp;
-
- src += sourceStride;
- dest += destinationStride;
- }
-}
-
-/* -------------------------------------------------------------------------- */
-
-static void Float32_To_Int8_DitherClip(
- void *destinationBuffer, signed int destinationStride,
- void *sourceBuffer, signed int sourceStride,
- unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
-{
- float *src = (float*)sourceBuffer;
- signed char *dest = (signed char*)destinationBuffer;
- (void)ditherGenerator; /* unused parameter */
-
- while( count-- )
- {
- float dither = PaUtil_GenerateFloatTriangularDither( ditherGenerator );
- /* use smaller scaler to prevent overflow when we add the dither */
- float dithered = (*src * (126.0f)) + dither;
- signed long samp = (signed long) dithered;
- PA_CLIP_( samp, -0x80, 0x7F );
- *dest = (signed char) samp;
-
- src += sourceStride;
- dest += destinationStride;
- }
-}
-
-/* -------------------------------------------------------------------------- */
-
-static void Float32_To_UInt8(
- void *destinationBuffer, signed int destinationStride,
- void *sourceBuffer, signed int sourceStride,
- unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
-{
- float *src = (float*)sourceBuffer;
- unsigned char *dest = (unsigned char*)destinationBuffer;
- (void)ditherGenerator; /* unused parameter */
-
- while( count-- )
- {
- unsigned char samp = (unsigned char)(128 + ((unsigned char) (*src * (127.0f))));
- *dest = samp;
-
- src += sourceStride;
- dest += destinationStride;
- }
-}
-
-/* -------------------------------------------------------------------------- */
-
-static void Float32_To_UInt8_Dither(
- void *destinationBuffer, signed int destinationStride,
- void *sourceBuffer, signed int sourceStride,
- unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
-{
- float *src = (float*)sourceBuffer;
- unsigned char *dest = (unsigned char*)destinationBuffer;
- (void)ditherGenerator; /* unused parameter */
-
- while( count-- )
- {
- /* IMPLEMENT ME */
-
- src += sourceStride;
- dest += destinationStride;
- }
-}
-
-/* -------------------------------------------------------------------------- */
-
-static void Float32_To_UInt8_Clip(
- void *destinationBuffer, signed int destinationStride,
- void *sourceBuffer, signed int sourceStride,
- unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
-{
- float *src = (float*)sourceBuffer;
- unsigned char *dest = (unsigned char*)destinationBuffer;
- (void)ditherGenerator; /* unused parameter */
-
- while( count-- )
- {
- /* IMPLEMENT ME */
-
- src += sourceStride;
- dest += destinationStride;
- }
-}
-
-/* -------------------------------------------------------------------------- */
-
-static void Float32_To_UInt8_DitherClip(
- void *destinationBuffer, signed int destinationStride,
- void *sourceBuffer, signed int sourceStride,
- unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
-{
- float *src = (float*)sourceBuffer;
- unsigned char *dest = (unsigned char*)destinationBuffer;
- (void)ditherGenerator; /* unused parameter */
-
- while( count-- )
- {
- /* IMPLEMENT ME */
-
- src += sourceStride;
- dest += destinationStride;
- }
-}
-
-/* -------------------------------------------------------------------------- */
-
-static void Int32_To_Float32(
- void *destinationBuffer, signed int destinationStride,
- void *sourceBuffer, signed int sourceStride,
- unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
-{
- signed long *src = (signed long*)sourceBuffer;
- float *dest = (float*)destinationBuffer;
- (void)ditherGenerator; /* unused parameter */
-
- while( count-- )
- {
- *dest = (float) ((double)*src * const_1_div_2147483648_);
-
- src += sourceStride;
- dest += destinationStride;
- }
-}
-
-/* -------------------------------------------------------------------------- */
-
-static void Int32_To_Int24(
- void *destinationBuffer, signed int destinationStride,
- void *sourceBuffer, signed int sourceStride,
- unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
-{
- signed long *src = (signed long*)sourceBuffer;
- unsigned char *dest = (unsigned char*)destinationBuffer;
- (void) ditherGenerator; /* unused parameter */
-
- while( count-- )
- {
- /* REVIEW */
-#if defined(PA_LITTLE_ENDIAN)
- dest[0] = (unsigned char)(*src >> 8);
- dest[1] = (unsigned char)(*src >> 16);
- dest[2] = (unsigned char)(*src >> 24);
-#elif defined(PA_BIG_ENDIAN)
- dest[0] = (unsigned char)(*src >> 24);
- dest[1] = (unsigned char)(*src >> 16);
- dest[2] = (unsigned char)(*src >> 8);
-#endif
- src += sourceStride;
- dest += destinationStride * 3;
- }
-}
-
-/* -------------------------------------------------------------------------- */
-
-static void Int32_To_Int24_Dither(
- void *destinationBuffer, signed int destinationStride,
- void *sourceBuffer, signed int sourceStride,
- unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
-{
- (void) destinationBuffer; /* unused parameters */
- (void) destinationStride; /* unused parameters */
- (void) sourceBuffer; /* unused parameters */
- (void) sourceStride; /* unused parameters */
- (void) count; /* unused parameters */
- (void) ditherGenerator; /* unused parameters */
- /* IMPLEMENT ME */
-}
-
-/* -------------------------------------------------------------------------- */
-
-static void Int32_To_Int16(
- void *destinationBuffer, signed int destinationStride,
- void *sourceBuffer, signed int sourceStride,
- unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
-{
- signed long *src = (signed long*)sourceBuffer;
- signed short *dest = (signed short*)destinationBuffer;
- (void)ditherGenerator; /* unused parameter */
-
- while( count-- )
- {
- *dest = (signed short) ((*src) >> 16);
-
- src += sourceStride;
- dest += destinationStride;
- }
-}
-
-/* -------------------------------------------------------------------------- */
-
-static void Int32_To_Int16_Dither(
- void *destinationBuffer, signed int destinationStride,
- void *sourceBuffer, signed int sourceStride,
- unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
-{
- signed long *src = (signed long*)sourceBuffer;
- signed short *dest = (signed short*)destinationBuffer;
- signed long dither;
-
- while( count-- )
- {
- /* REVIEW */
- dither = PaUtil_Generate16BitTriangularDither( ditherGenerator );
- *dest = (signed short) ((((*src)>>1) + dither) >> 15);
-
- src += sourceStride;
- dest += destinationStride;
- }
-}
-
-/* -------------------------------------------------------------------------- */
-
-static void Int32_To_Int8(
- void *destinationBuffer, signed int destinationStride,
- void *sourceBuffer, signed int sourceStride,
- unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
-{
- signed long *src = (signed long*)sourceBuffer;
- signed char *dest = (signed char*)destinationBuffer;
- (void)ditherGenerator; /* unused parameter */
-
- while( count-- )
- {
- *dest = (signed char) ((*src) >> 24);
-
- src += sourceStride;
- dest += destinationStride;
- }
-}
-
-/* -------------------------------------------------------------------------- */
-
-static void Int32_To_Int8_Dither(
- void *destinationBuffer, signed int destinationStride,
- void *sourceBuffer, signed int sourceStride,
- unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
-{
- signed long *src = (signed long*)sourceBuffer;
- signed char *dest = (signed char*)destinationBuffer;
- signed long dither;
-
- while( count-- )
- {
- /* REVIEW */
- dither = PaUtil_Generate16BitTriangularDither( ditherGenerator );
- *dest = (signed char) ((((*src)>>1) + dither) >> 23);
-
- src += sourceStride;
- dest += destinationStride;
- }
-}
-
-/* -------------------------------------------------------------------------- */
-
-static void Int32_To_UInt8(
- void *destinationBuffer, signed int destinationStride,
- void *sourceBuffer, signed int sourceStride,
- unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
-{
- signed long *src = (signed long*)sourceBuffer;
- unsigned char *dest = (unsigned char*)destinationBuffer;
- (void)ditherGenerator; /* unused parameter */
-
- while( count-- )
- {
- (*dest) = (unsigned char)(((*src) >> 24) + 128);
-
- src += sourceStride;
- dest += destinationStride;
- }
-}
-
-/* -------------------------------------------------------------------------- */
-
-static void Int32_To_UInt8_Dither(
- void *destinationBuffer, signed int destinationStride,
- void *sourceBuffer, signed int sourceStride,
- unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
-{
- signed long *src = (signed long*)sourceBuffer;
- unsigned char *dest = (unsigned char*)destinationBuffer;
- (void)ditherGenerator; /* unused parameter */
-
- while( count-- )
- {
- /* IMPLEMENT ME */
-
- src += sourceStride;
- dest += destinationStride;
- }
-}
-
-/* -------------------------------------------------------------------------- */
-
-static void Int24_To_Float32(
- void *destinationBuffer, signed int destinationStride,
- void *sourceBuffer, signed int sourceStride,
- unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
-{
- unsigned char *src = (unsigned char*)sourceBuffer;
- float *dest = (float*)destinationBuffer;
- signed long temp;
-
- (void) ditherGenerator; /* unused parameter */
-
- while( count-- )
- {
-
-#if defined(PA_LITTLE_ENDIAN)
- temp = (((long)src[0]) << 8);
- temp = temp | (((long)src[1]) << 16);
- temp = temp | (((long)src[2]) << 24);
-#elif defined(PA_BIG_ENDIAN)
- temp = (((long)src[0]) << 24);
- temp = temp | (((long)src[1]) << 16);
- temp = temp | (((long)src[2]) << 8);
-#endif
-
- *dest = (float) ((double)temp * const_1_div_2147483648_);
-
- src += sourceStride * 3;
- dest += destinationStride;
- }
-}
-
-/* -------------------------------------------------------------------------- */
-
-static void Int24_To_Int32(
- void *destinationBuffer, signed int destinationStride,
- void *sourceBuffer, signed int sourceStride,
- unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
-{
- unsigned char *src = (unsigned char*)sourceBuffer;
- signed long *dest = (signed long*) destinationBuffer;
- signed long temp;
-
- (void) ditherGenerator; /* unused parameter */
-
- while( count-- )
- {
-
-#if defined(PA_LITTLE_ENDIAN)
- temp = (((long)src[0]) << 8);
- temp = temp | (((long)src[1]) << 16);
- temp = temp | (((long)src[2]) << 24);
-#elif defined(PA_BIG_ENDIAN)
- temp = (((long)src[0]) << 24);
- temp = temp | (((long)src[1]) << 16);
- temp = temp | (((long)src[2]) << 8);
-#endif
-
- *dest = temp;
-
- src += sourceStride * 3;
- dest += destinationStride;
- }
-}
-
-/* -------------------------------------------------------------------------- */
-
-static void Int24_To_Int16(
- void *destinationBuffer, signed int destinationStride,
- void *sourceBuffer, signed int sourceStride,
- unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
-{
- unsigned char *src = (unsigned char*)sourceBuffer;
- signed short *dest = (signed short*)destinationBuffer;
-
- signed short temp;
-
- (void) ditherGenerator; /* unused parameter */
-
- while( count-- )
- {
-
-#if defined(PA_LITTLE_ENDIAN)
- /* src[0] is discarded */
- temp = (((signed short)src[1]));
- temp = temp | (signed short)(((signed short)src[2]) << 8);
-#elif defined(PA_BIG_ENDIAN)
- /* src[2] is discarded */
- temp = (signed short)(((signed short)src[0]) << 8);
- temp = temp | (((signed short)src[1]));
-#endif
-
- *dest = temp;
-
- src += sourceStride * 3;
- dest += destinationStride;
- }
-}
-
-/* -------------------------------------------------------------------------- */
-
-static void Int24_To_Int16_Dither(
- void *destinationBuffer, signed int destinationStride,
- void *sourceBuffer, signed int sourceStride,
- unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
-{
- (void) destinationBuffer; /* unused parameters */
- (void) destinationStride; /* unused parameters */
- (void) sourceBuffer; /* unused parameters */
- (void) sourceStride; /* unused parameters */
- (void) count; /* unused parameters */
- (void) ditherGenerator; /* unused parameters */
- /* IMPLEMENT ME */
-}
-
-/* -------------------------------------------------------------------------- */
-
-static void Int24_To_Int8(
- void *destinationBuffer, signed int destinationStride,
- void *sourceBuffer, signed int sourceStride,
- unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
-{
- unsigned char *src = (unsigned char*)sourceBuffer;
- signed char *dest = (signed char*)destinationBuffer;
-
- (void) ditherGenerator; /* unused parameter */
-
- while( count-- )
- {
-
-#if defined(PA_LITTLE_ENDIAN)
- /* src[0] is discarded */
- /* src[1] is discarded */
- *dest = src[2];
-#elif defined(PA_BIG_ENDIAN)
- /* src[2] is discarded */
- /* src[1] is discarded */
- *dest = src[0];
-#endif
-
- src += sourceStride * 3;
- dest += destinationStride;
- }
-}
-
-/* -------------------------------------------------------------------------- */
-
-static void Int24_To_Int8_Dither(
- void *destinationBuffer, signed int destinationStride,
- void *sourceBuffer, signed int sourceStride,
- unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
-{
- (void) destinationBuffer; /* unused parameters */
- (void) destinationStride; /* unused parameters */
- (void) sourceBuffer; /* unused parameters */
- (void) sourceStride; /* unused parameters */
- (void) count; /* unused parameters */
- (void) ditherGenerator; /* unused parameters */
- /* IMPLEMENT ME */
-}
-
-/* -------------------------------------------------------------------------- */
-
-static void Int24_To_UInt8(
- void *destinationBuffer, signed int destinationStride,
- void *sourceBuffer, signed int sourceStride,
- unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
-{
- unsigned char *src = (unsigned char*)sourceBuffer;
- unsigned char *dest = (unsigned char*)destinationBuffer;
-
- (void) ditherGenerator; /* unused parameter */
-
- while( count-- )
- {
-
-#if defined(PA_LITTLE_ENDIAN)
- /* src[0] is discarded */
- /* src[1] is discarded */
- *dest = (unsigned char)(src[2] + 128);
-#elif defined(PA_BIG_ENDIAN)
- *dest = (unsigned char)(src[0] + 128);
- /* src[1] is discarded */
- /* src[2] is discarded */
-#endif
-
- src += sourceStride * 3;
- dest += destinationStride;
- }
-}
-
-/* -------------------------------------------------------------------------- */
-
-static void Int24_To_UInt8_Dither(
- void *destinationBuffer, signed int destinationStride,
- void *sourceBuffer, signed int sourceStride,
- unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
-{
- (void) destinationBuffer; /* unused parameters */
- (void) destinationStride; /* unused parameters */
- (void) sourceBuffer; /* unused parameters */
- (void) sourceStride; /* unused parameters */
- (void) count; /* unused parameters */
- (void) ditherGenerator; /* unused parameters */
- /* IMPLEMENT ME */
-}
-
-/* -------------------------------------------------------------------------- */
-
-static void Int16_To_Float32(
- void *destinationBuffer, signed int destinationStride,
- void *sourceBuffer, signed int sourceStride,
- unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
-{
- signed short *src = (signed short*)sourceBuffer;
- float *dest = (float*)destinationBuffer;
- (void)ditherGenerator; /* unused parameter */
-
- while( count-- )
- {
- float samp = *src * const_1_div_32768_; /* FIXME: i'm concerned about this being asymetrical with float->int16 -rb */
- *dest = samp;
-
- src += sourceStride;
- dest += destinationStride;
- }
-}
-
-/* -------------------------------------------------------------------------- */
-
-static void Int16_To_Int32(
- void *destinationBuffer, signed int destinationStride,
- void *sourceBuffer, signed int sourceStride,
- unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
-{
- signed short *src = (signed short*)sourceBuffer;
- signed long *dest = (signed long*)destinationBuffer;
- (void)ditherGenerator; /* unused parameter */
-
- while( count-- )
- {
- /* REVIEW: we should consider something like
- (*src << 16) | (*src & 0xFFFF)
- */
-
- *dest = *src << 16;
-
- src += sourceStride;
- dest += destinationStride;
- }
-}
-
-/* -------------------------------------------------------------------------- */
-
-static void Int16_To_Int24(
- void *destinationBuffer, signed int destinationStride,
- void *sourceBuffer, signed int sourceStride,
- unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
-{
- signed short *src = (signed short*) sourceBuffer;
- unsigned char *dest = (unsigned char*)destinationBuffer;
- signed short temp;
-
- (void) ditherGenerator; /* unused parameter */
-
- while( count-- )
- {
- temp = *src;
-
-#if defined(PA_LITTLE_ENDIAN)
- dest[0] = 0;
- dest[1] = (unsigned char)(temp);
- dest[2] = (unsigned char)(temp >> 8);
-#elif defined(PA_BIG_ENDIAN)
- dest[0] = (unsigned char)(temp >> 8);
- dest[1] = (unsigned char)(temp);
- dest[2] = 0;
-#endif
-
- src += sourceStride;
- dest += destinationStride * 3;
- }
-}
-
-/* -------------------------------------------------------------------------- */
-
-static void Int16_To_Int8(
- void *destinationBuffer, signed int destinationStride,
- void *sourceBuffer, signed int sourceStride,
- unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
-{
- signed short *src = (signed short*)sourceBuffer;
- signed char *dest = (signed char*)destinationBuffer;
- (void)ditherGenerator; /* unused parameter */
-
- while( count-- )
- {
- (*dest) = (signed char)((*src) >> 8);
-
- src += sourceStride;
- dest += destinationStride;
- }
-}
-
-/* -------------------------------------------------------------------------- */
-
-static void Int16_To_Int8_Dither(
- void *destinationBuffer, signed int destinationStride,
- void *sourceBuffer, signed int sourceStride,
- unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
-{
- signed short *src = (signed short*)sourceBuffer;
- signed char *dest = (signed char*)destinationBuffer;
- (void)ditherGenerator; /* unused parameter */
-
- while( count-- )
- {
- /* IMPLEMENT ME */
-
- src += sourceStride;
- dest += destinationStride;
- }
-}
-
-/* -------------------------------------------------------------------------- */
-
-static void Int16_To_UInt8(
- void *destinationBuffer, signed int destinationStride,
- void *sourceBuffer, signed int sourceStride,
- unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
-{
- signed short *src = (signed short*)sourceBuffer;
- unsigned char *dest = (unsigned char*)destinationBuffer;
- (void)ditherGenerator; /* unused parameter */
-
- while( count-- )
- {
- (*dest) = (unsigned char)(((*src) >> 8) + 128);
-
- src += sourceStride;
- dest += destinationStride;
- }
-}
-
-/* -------------------------------------------------------------------------- */
-
-static void Int16_To_UInt8_Dither(
- void *destinationBuffer, signed int destinationStride,
- void *sourceBuffer, signed int sourceStride,
- unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
-{
- signed short *src = (signed short*)sourceBuffer;
- unsigned char *dest = (unsigned char*)destinationBuffer;
- (void)ditherGenerator; /* unused parameter */
-
- while( count-- )
- {
- /* IMPLEMENT ME */
-
- src += sourceStride;
- dest += destinationStride;
- }
-}
-
-/* -------------------------------------------------------------------------- */
-
-static void Int8_To_Float32(
- void *destinationBuffer, signed int destinationStride,
- void *sourceBuffer, signed int sourceStride,
- unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
-{
- signed char *src = (signed char*)sourceBuffer;
- float *dest = (float*)destinationBuffer;
- (void)ditherGenerator; /* unused parameter */
-
- while( count-- )
- {
- float samp = *src * const_1_div_128_;
- *dest = samp;
-
- src += sourceStride;
- dest += destinationStride;
- }
-}
-
-/* -------------------------------------------------------------------------- */
-
-static void Int8_To_Int32(
- void *destinationBuffer, signed int destinationStride,
- void *sourceBuffer, signed int sourceStride,
- unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
-{
- signed char *src = (signed char*)sourceBuffer;
- signed long *dest = (signed long*)destinationBuffer;
- (void)ditherGenerator; /* unused parameter */
-
- while( count-- )
- {
- (*dest) = (*src) << 24;
-
- src += sourceStride;
- dest += destinationStride;
- }
-}
-
-/* -------------------------------------------------------------------------- */
-
-static void Int8_To_Int24(
- void *destinationBuffer, signed int destinationStride,
- void *sourceBuffer, signed int sourceStride,
- unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
-{
- signed char *src = (signed char*)sourceBuffer;
- unsigned char *dest = (unsigned char*)destinationBuffer;
- (void)ditherGenerator; /* unused parameter */
-
- while( count-- )
- {
-
-#if defined(PA_LITTLE_ENDIAN)
- dest[0] = 0;
- dest[1] = 0;
- dest[2] = (*src);
-#elif defined(PA_BIG_ENDIAN)
- dest[0] = (*src);
- dest[1] = 0;
- dest[2] = 0;
-#endif
-
- src += sourceStride;
- dest += destinationStride * 3;
- }
-}
-
-/* -------------------------------------------------------------------------- */
-
-static void Int8_To_Int16(
- void *destinationBuffer, signed int destinationStride,
- void *sourceBuffer, signed int sourceStride,
- unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
-{
- signed char *src = (signed char*)sourceBuffer;
- signed short *dest = (signed short*)destinationBuffer;
- (void)ditherGenerator; /* unused parameter */
-
- while( count-- )
- {
- (*dest) = (signed short)((*src) << 8);
-
- src += sourceStride;
- dest += destinationStride;
- }
-}
-
-/* -------------------------------------------------------------------------- */
-
-static void Int8_To_UInt8(
- void *destinationBuffer, signed int destinationStride,
- void *sourceBuffer, signed int sourceStride,
- unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
-{
- signed char *src = (signed char*)sourceBuffer;
- unsigned char *dest = (unsigned char*)destinationBuffer;
- (void)ditherGenerator; /* unused parameter */
-
- while( count-- )
- {
- (*dest) = (unsigned char)(*src + 128);
-
- src += sourceStride;
- dest += destinationStride;
- }
-}
-
-/* -------------------------------------------------------------------------- */
-
-static void UInt8_To_Float32(
- void *destinationBuffer, signed int destinationStride,
- void *sourceBuffer, signed int sourceStride,
- unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
-{
- unsigned char *src = (unsigned char*)sourceBuffer;
- float *dest = (float*)destinationBuffer;
- (void)ditherGenerator; /* unused parameter */
-
- while( count-- )
- {
- float samp = (*src - 128) * const_1_div_128_;
- *dest = samp;
-
- src += sourceStride;
- dest += destinationStride;
- }
-}
-
-/* -------------------------------------------------------------------------- */
-
-static void UInt8_To_Int32(
- void *destinationBuffer, signed int destinationStride,
- void *sourceBuffer, signed int sourceStride,
- unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
-{
- unsigned char *src = (unsigned char*)sourceBuffer;
- signed long *dest = (signed long*)destinationBuffer;
- (void)ditherGenerator; /* unused parameter */
-
- while( count-- )
- {
- (*dest) = (*src - 128) << 24;
-
- src += sourceStride;
- dest += destinationStride;
- }
-}
-
-/* -------------------------------------------------------------------------- */
-
-static void UInt8_To_Int24(
- void *destinationBuffer, signed int destinationStride,
- void *sourceBuffer, signed int sourceStride,
- unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
-{
- unsigned char *src = (unsigned char*)sourceBuffer;
- unsigned char *dest = (unsigned char*)destinationBuffer;
- (void) ditherGenerator; /* unused parameters */
-
- while( count-- )
- {
-
-#if defined(PA_LITTLE_ENDIAN)
- dest[0] = 0;
- dest[1] = 0;
- dest[2] = (unsigned char)(*src - 128);
-#elif defined(PA_BIG_ENDIAN)
- dest[0] = (unsigned char)(*src - 128);
- dest[1] = 0;
- dest[2] = 0;
-#endif
-
- src += sourceStride;
- dest += destinationStride * 3;
- }
-}
-
-/* -------------------------------------------------------------------------- */
-
-static void UInt8_To_Int16(
- void *destinationBuffer, signed int destinationStride,
- void *sourceBuffer, signed int sourceStride,
- unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
-{
- unsigned char *src = (unsigned char*)sourceBuffer;
- signed short *dest = (signed short*)destinationBuffer;
- (void)ditherGenerator; /* unused parameter */
-
- while( count-- )
- {
- (*dest) = (signed short)((*src - 128) << 8);
-
- src += sourceStride;
- dest += destinationStride;
- }
-}
-
-/* -------------------------------------------------------------------------- */
-
-static void UInt8_To_Int8(
- void *destinationBuffer, signed int destinationStride,
- void *sourceBuffer, signed int sourceStride,
- unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
-{
- unsigned char *src = (unsigned char*)sourceBuffer;
- signed char *dest = (signed char*)destinationBuffer;
- (void)ditherGenerator; /* unused parameter */
-
- while( count-- )
- {
- (*dest) = (signed char)(*src - 128);
-
- src += sourceStride;
- dest += destinationStride;
- }
-}
-
-/* -------------------------------------------------------------------------- */
-
-static void Copy_8_To_8(
- void *destinationBuffer, signed int destinationStride,
- void *sourceBuffer, signed int sourceStride,
- unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
-{
- unsigned char *src = (unsigned char*)sourceBuffer;
- unsigned char *dest = (unsigned char*)destinationBuffer;
-
- (void) ditherGenerator; /* unused parameter */
-
- while( count-- )
- {
- *dest = *src;
-
- src += sourceStride;
- dest += destinationStride;
- }
-}
-
-/* -------------------------------------------------------------------------- */
-
-static void Copy_16_To_16(
- void *destinationBuffer, signed int destinationStride,
- void *sourceBuffer, signed int sourceStride,
- unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
-{
- PaUint16 *src = (PaUint16 *)sourceBuffer;
- PaUint16 *dest = (PaUint16 *)destinationBuffer;
-
- (void) ditherGenerator; /* unused parameter */
-
- while( count-- )
- {
- *dest = *src;
-
- src += sourceStride;
- dest += destinationStride;
- }
-}
-
-/* -------------------------------------------------------------------------- */
-
-static void Copy_24_To_24(
- void *destinationBuffer, signed int destinationStride,
- void *sourceBuffer, signed int sourceStride,
- unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
-{
- unsigned char *src = (unsigned char*)sourceBuffer;
- unsigned char *dest = (unsigned char*)destinationBuffer;
-
- (void) ditherGenerator; /* unused parameter */
-
- while( count-- )
- {
- dest[0] = src[0];
- dest[1] = src[1];
- dest[2] = src[2];
-
- src += sourceStride * 3;
- dest += destinationStride * 3;
- }
-}
-
-/* -------------------------------------------------------------------------- */
-
-static void Copy_32_To_32(
- void *destinationBuffer, signed int destinationStride,
- void *sourceBuffer, signed int sourceStride,
- unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
-{
- PaUint32 *dest = (PaUint32 *)destinationBuffer;
- PaUint32 *src = (PaUint32 *)sourceBuffer;
-
- (void) ditherGenerator; /* unused parameter */
-
- while( count-- )
- {
- *dest = *src;
-
- src += sourceStride;
- dest += destinationStride;
- }
-}
-
-/* -------------------------------------------------------------------------- */
-
-PaUtilConverterTable paConverters = {
- Float32_To_Int32, /* PaUtilConverter *Float32_To_Int32; */
- Float32_To_Int32_Dither, /* PaUtilConverter *Float32_To_Int32_Dither; */
- Float32_To_Int32_Clip, /* PaUtilConverter *Float32_To_Int32_Clip; */
- Float32_To_Int32_DitherClip, /* PaUtilConverter *Float32_To_Int32_DitherClip; */
-
- Float32_To_Int24, /* PaUtilConverter *Float32_To_Int24; */
- Float32_To_Int24_Dither, /* PaUtilConverter *Float32_To_Int24_Dither; */
- Float32_To_Int24_Clip, /* PaUtilConverter *Float32_To_Int24_Clip; */
- Float32_To_Int24_DitherClip, /* PaUtilConverter *Float32_To_Int24_DitherClip; */
-
- Float32_To_Int16, /* PaUtilConverter *Float32_To_Int16; */
- Float32_To_Int16_Dither, /* PaUtilConverter *Float32_To_Int16_Dither; */
- Float32_To_Int16_Clip, /* PaUtilConverter *Float32_To_Int16_Clip; */
- Float32_To_Int16_DitherClip, /* PaUtilConverter *Float32_To_Int16_DitherClip; */
-
- Float32_To_Int8, /* PaUtilConverter *Float32_To_Int8; */
- Float32_To_Int8_Dither, /* PaUtilConverter *Float32_To_Int8_Dither; */
- Float32_To_Int8_Clip, /* PaUtilConverter *Float32_To_Int8_Clip; */
- Float32_To_Int8_DitherClip, /* PaUtilConverter *Float32_To_Int8_DitherClip; */
-
- Float32_To_UInt8, /* PaUtilConverter *Float32_To_UInt8; */
- Float32_To_UInt8_Dither, /* PaUtilConverter *Float32_To_UInt8_Dither; */
- Float32_To_UInt8_Clip, /* PaUtilConverter *Float32_To_UInt8_Clip; */
- Float32_To_UInt8_DitherClip, /* PaUtilConverter *Float32_To_UInt8_DitherClip; */
-
- Int32_To_Float32, /* PaUtilConverter *Int32_To_Float32; */
- Int32_To_Int24, /* PaUtilConverter *Int32_To_Int24; */
- Int32_To_Int24_Dither, /* PaUtilConverter *Int32_To_Int24_Dither; */
- Int32_To_Int16, /* PaUtilConverter *Int32_To_Int16; */
- Int32_To_Int16_Dither, /* PaUtilConverter *Int32_To_Int16_Dither; */
- Int32_To_Int8, /* PaUtilConverter *Int32_To_Int8; */
- Int32_To_Int8_Dither, /* PaUtilConverter *Int32_To_Int8_Dither; */
- Int32_To_UInt8, /* PaUtilConverter *Int32_To_UInt8; */
- Int32_To_UInt8_Dither, /* PaUtilConverter *Int32_To_UInt8_Dither; */
-
- Int24_To_Float32, /* PaUtilConverter *Int24_To_Float32; */
- Int24_To_Int32, /* PaUtilConverter *Int24_To_Int32; */
- Int24_To_Int16, /* PaUtilConverter *Int24_To_Int16; */
- Int24_To_Int16_Dither, /* PaUtilConverter *Int24_To_Int16_Dither; */
- Int24_To_Int8, /* PaUtilConverter *Int24_To_Int8; */
- Int24_To_Int8_Dither, /* PaUtilConverter *Int24_To_Int8_Dither; */
- Int24_To_UInt8, /* PaUtilConverter *Int24_To_UInt8; */
- Int24_To_UInt8_Dither, /* PaUtilConverter *Int24_To_UInt8_Dither; */
-
- Int16_To_Float32, /* PaUtilConverter *Int16_To_Float32; */
- Int16_To_Int32, /* PaUtilConverter *Int16_To_Int32; */
- Int16_To_Int24, /* PaUtilConverter *Int16_To_Int24; */
- Int16_To_Int8, /* PaUtilConverter *Int16_To_Int8; */
- Int16_To_Int8_Dither, /* PaUtilConverter *Int16_To_Int8_Dither; */
- Int16_To_UInt8, /* PaUtilConverter *Int16_To_UInt8; */
- Int16_To_UInt8_Dither, /* PaUtilConverter *Int16_To_UInt8_Dither; */
-
- Int8_To_Float32, /* PaUtilConverter *Int8_To_Float32; */
- Int8_To_Int32, /* PaUtilConverter *Int8_To_Int32; */
- Int8_To_Int24, /* PaUtilConverter *Int8_To_Int24 */
- Int8_To_Int16, /* PaUtilConverter *Int8_To_Int16; */
- Int8_To_UInt8, /* PaUtilConverter *Int8_To_UInt8; */
-
- UInt8_To_Float32, /* PaUtilConverter *UInt8_To_Float32; */
- UInt8_To_Int32, /* PaUtilConverter *UInt8_To_Int32; */
- UInt8_To_Int24, /* PaUtilConverter *UInt8_To_Int24; */
- UInt8_To_Int16, /* PaUtilConverter *UInt8_To_Int16; */
- UInt8_To_Int8, /* PaUtilConverter *UInt8_To_Int8; */
-
- Copy_8_To_8, /* PaUtilConverter *Copy_8_To_8; */
- Copy_16_To_16, /* PaUtilConverter *Copy_16_To_16; */
- Copy_24_To_24, /* PaUtilConverter *Copy_24_To_24; */
- Copy_32_To_32 /* PaUtilConverter *Copy_32_To_32; */
-};
-
-/* -------------------------------------------------------------------------- */
-
-#endif /* PA_NO_STANDARD_CONVERTERS */
-
-/* -------------------------------------------------------------------------- */
-
-PaUtilZeroer* PaUtil_SelectZeroer( PaSampleFormat destinationFormat )
-{
- switch( destinationFormat & ~paNonInterleaved ){
- case paFloat32:
- return paZeroers.Zero32;
- case paInt32:
- return paZeroers.Zero32;
- case paInt24:
- return paZeroers.Zero24;
- case paInt16:
- return paZeroers.Zero16;
- case paInt8:
- return paZeroers.Zero8;
- case paUInt8:
- return paZeroers.ZeroU8;
- default: return 0;
- }
-}
-
-/* -------------------------------------------------------------------------- */
-
-#ifdef PA_NO_STANDARD_ZEROERS
-
-/* -------------------------------------------------------------------------- */
-
-PaUtilZeroerTable paZeroers = {
- 0, /* PaUtilZeroer *ZeroU8; */
- 0, /* PaUtilZeroer *Zero8; */
- 0, /* PaUtilZeroer *Zero16; */
- 0, /* PaUtilZeroer *Zero24; */
- 0, /* PaUtilZeroer *Zero32; */
-};
-
-/* -------------------------------------------------------------------------- */
-
-#else /* PA_NO_STANDARD_ZEROERS is not defined */
-
-/* -------------------------------------------------------------------------- */
-
-static void ZeroU8( void *destinationBuffer, signed int destinationStride,
- unsigned int count )
-{
- unsigned char *dest = (unsigned char*)destinationBuffer;
-
- while( count-- )
- {
- *dest = 128;
-
- dest += destinationStride;
- }
-}
-
-/* -------------------------------------------------------------------------- */
-
-static void Zero8( void *destinationBuffer, signed int destinationStride,
- unsigned int count )
-{
- unsigned char *dest = (unsigned char*)destinationBuffer;
-
- while( count-- )
- {
- *dest = 0;
-
- dest += destinationStride;
- }
-}
-
-/* -------------------------------------------------------------------------- */
-
-static void Zero16( void *destinationBuffer, signed int destinationStride,
- unsigned int count )
-{
- PaUint16 *dest = (PaUint16 *)destinationBuffer;
-
- while( count-- )
- {
- *dest = 0;
-
- dest += destinationStride;
- }
-}
-
-/* -------------------------------------------------------------------------- */
-
-static void Zero24( void *destinationBuffer, signed int destinationStride,
- unsigned int count )
-{
- unsigned char *dest = (unsigned char*)destinationBuffer;
-
- while( count-- )
- {
- dest[0] = 0;
- dest[1] = 0;
- dest[2] = 0;
-
- dest += destinationStride * 3;
- }
-}
-
-/* -------------------------------------------------------------------------- */
-
-static void Zero32( void *destinationBuffer, signed int destinationStride,
- unsigned int count )
-{
- PaUint32 *dest = (PaUint32 *)destinationBuffer;
-
- while( count-- )
- {
- *dest = 0;
-
- dest += destinationStride;
- }
-}
-
-/* -------------------------------------------------------------------------- */
-
-PaUtilZeroerTable paZeroers = {
- ZeroU8, /* PaUtilZeroer *ZeroU8; */
- Zero8, /* PaUtilZeroer *Zero8; */
- Zero16, /* PaUtilZeroer *Zero16; */
- Zero24, /* PaUtilZeroer *Zero24; */
- Zero32, /* PaUtilZeroer *Zero32; */
-};
-
-/* -------------------------------------------------------------------------- */
-
-#endif /* PA_NO_STANDARD_ZEROERS */
+/*
+ * $Id: pa_converters.c,v 1.1.2.26 2004/12/11 16:32:38 aknudsen Exp $
+ * Portable Audio I/O Library sample conversion mechanism
+ *
+ * Based on the Open Source API proposed by Ross Bencina
+ * Copyright (c) 1999-2002 Phil Burk, Ross Bencina
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * Any person wishing to distribute modifications to the Software is
+ * requested to send the modifications to the original developer so that
+ * they can be incorporated into the canonical version.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+ * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+/*
+ * PJMEDIA - Multimedia over IP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/** @file
+ @brief Conversion functions implementations.
+
+ If the C9x function lrintf() is available, define PA_USE_C99_LRINTF to use it
+
+ @todo Consider whether functions which dither but don't clip should exist,
+ V18 automatically enabled clipping whenever dithering was selected. Perhaps
+ we should do the same.
+
+ @todo implement the converters marked IMPLEMENT ME: Float32_To_UInt8_Dither,
+ Float32_To_UInt8_Clip, Float32_To_UInt8_DitherClip, Int32_To_Int24_Dither,
+ Int32_To_UInt8_Dither, Int24_To_Int16_Dither, Int24_To_Int8_Dither,
+ Int24_To_UInt8_Dither, Int16_To_Int8_Dither, Int16_To_UInt8_Dither,
+
+ @todo review the converters marked REVIEW: Float32_To_Int32,
+ Float32_To_Int32_Dither, Float32_To_Int32_Clip, Float32_To_Int32_DitherClip,
+ Int32_To_Int16_Dither, Int32_To_Int8_Dither, Int16_To_Int32
+*/
+
+
+#include "pa_converters.h"
+#include "pa_dither.h"
+#include "pa_endianness.h"
+#include "pa_types.h"
+
+
+PaSampleFormat PaUtil_SelectClosestAvailableFormat(
+ PaSampleFormat availableFormats, PaSampleFormat format )
+{
+ PaSampleFormat result;
+
+ format &= ~paNonInterleaved;
+ availableFormats &= ~paNonInterleaved;
+
+ if( (format & availableFormats) == 0 )
+ {
+ /* NOTE: this code depends on the sample format constants being in
+ descending order of quality - ie best quality is 0
+ FIXME: should write an assert which checks that all of the
+ known constants conform to that requirement.
+ */
+
+ if( format != 0x01 )
+ {
+ /* scan for better formats */
+ result = format;
+ do
+ {
+ result >>= 1;
+ }
+ while( (result & availableFormats) == 0 && result != 0 );
+ }
+ else
+ {
+ result = 0;
+ }
+
+ if( result == 0 ){
+ /* scan for worse formats */
+ result = format;
+ do
+ {
+ result <<= 1;
+ }
+ while( (result & availableFormats) == 0 && result != paCustomFormat );
+
+ if( (result & availableFormats) == 0 )
+ result = paSampleFormatNotSupported;
+ }
+
+ }else{
+ result = format;
+ }
+
+ return result;
+}
+
+/* -------------------------------------------------------------------------- */
+
+#define PA_SELECT_FORMAT_( format, float32, int32, int24, int16, int8, uint8 ) \
+ switch( format & ~paNonInterleaved ){ \
+ case paFloat32: \
+ float32 \
+ case paInt32: \
+ int32 \
+ case paInt24: \
+ int24 \
+ case paInt16: \
+ int16 \
+ case paInt8: \
+ int8 \
+ case paUInt8: \
+ uint8 \
+ default: return 0; \
+ }
+
+/* -------------------------------------------------------------------------- */
+
+#define PA_SELECT_CONVERTER_DITHER_CLIP_( flags, source, destination ) \
+ if( flags & paClipOff ){ /* no clip */ \
+ if( flags & paDitherOff ){ /* no dither */ \
+ return paConverters. source ## _To_ ## destination; \
+ }else{ /* dither */ \
+ return paConverters. source ## _To_ ## destination ## _Dither; \
+ } \
+ }else{ /* clip */ \
+ if( flags & paDitherOff ){ /* no dither */ \
+ return paConverters. source ## _To_ ## destination ## _Clip; \
+ }else{ /* dither */ \
+ return paConverters. source ## _To_ ## destination ## _DitherClip; \
+ } \
+ }
+
+/* -------------------------------------------------------------------------- */
+
+#define PA_SELECT_CONVERTER_DITHER_( flags, source, destination ) \
+ if( flags & paDitherOff ){ /* no dither */ \
+ return paConverters. source ## _To_ ## destination; \
+ }else{ /* dither */ \
+ return paConverters. source ## _To_ ## destination ## _Dither; \
+ }
+
+/* -------------------------------------------------------------------------- */
+
+#define PA_USE_CONVERTER_( source, destination )\
+ return paConverters. source ## _To_ ## destination;
+
+/* -------------------------------------------------------------------------- */
+
+#define PA_UNITY_CONVERSION_( wordlength )\
+ return paConverters. Copy_ ## wordlength ## _To_ ## wordlength;
+
+/* -------------------------------------------------------------------------- */
+
+PaUtilConverter* PaUtil_SelectConverter( PaSampleFormat sourceFormat,
+ PaSampleFormat destinationFormat, PaStreamFlags flags )
+{
+ PA_SELECT_FORMAT_( sourceFormat,
+ /* paFloat32: */
+ PA_SELECT_FORMAT_( destinationFormat,
+ /* paFloat32: */ PA_UNITY_CONVERSION_( 32 ),
+ /* paInt32: */ PA_SELECT_CONVERTER_DITHER_CLIP_( flags, Float32, Int32 ),
+ /* paInt24: */ PA_SELECT_CONVERTER_DITHER_CLIP_( flags, Float32, Int24 ),
+ /* paInt16: */ PA_SELECT_CONVERTER_DITHER_CLIP_( flags, Float32, Int16 ),
+ /* paInt8: */ PA_SELECT_CONVERTER_DITHER_CLIP_( flags, Float32, Int8 ),
+ /* paUInt8: */ PA_SELECT_CONVERTER_DITHER_CLIP_( flags, Float32, UInt8 )
+ ),
+ /* paInt32: */
+ PA_SELECT_FORMAT_( destinationFormat,
+ /* paFloat32: */ PA_USE_CONVERTER_( Int32, Float32 ),
+ /* paInt32: */ PA_UNITY_CONVERSION_( 32 ),
+ /* paInt24: */ PA_SELECT_CONVERTER_DITHER_( flags, Int32, Int24 ),
+ /* paInt16: */ PA_SELECT_CONVERTER_DITHER_( flags, Int32, Int16 ),
+ /* paInt8: */ PA_SELECT_CONVERTER_DITHER_( flags, Int32, Int8 ),
+ /* paUInt8: */ PA_SELECT_CONVERTER_DITHER_( flags, Int32, UInt8 )
+ ),
+ /* paInt24: */
+ PA_SELECT_FORMAT_( destinationFormat,
+ /* paFloat32: */ PA_USE_CONVERTER_( Int24, Float32 ),
+ /* paInt32: */ PA_USE_CONVERTER_( Int24, Int32 ),
+ /* paInt24: */ PA_UNITY_CONVERSION_( 24 ),
+ /* paInt16: */ PA_SELECT_CONVERTER_DITHER_( flags, Int24, Int16 ),
+ /* paInt8: */ PA_SELECT_CONVERTER_DITHER_( flags, Int24, Int8 ),
+ /* paUInt8: */ PA_SELECT_CONVERTER_DITHER_( flags, Int24, UInt8 )
+ ),
+ /* paInt16: */
+ PA_SELECT_FORMAT_( destinationFormat,
+ /* paFloat32: */ PA_USE_CONVERTER_( Int16, Float32 ),
+ /* paInt32: */ PA_USE_CONVERTER_( Int16, Int32 ),
+ /* paInt24: */ PA_USE_CONVERTER_( Int16, Int24 ),
+ /* paInt16: */ PA_UNITY_CONVERSION_( 16 ),
+ /* paInt8: */ PA_SELECT_CONVERTER_DITHER_( flags, Int16, Int8 ),
+ /* paUInt8: */ PA_SELECT_CONVERTER_DITHER_( flags, Int16, UInt8 )
+ ),
+ /* paInt8: */
+ PA_SELECT_FORMAT_( destinationFormat,
+ /* paFloat32: */ PA_USE_CONVERTER_( Int8, Float32 ),
+ /* paInt32: */ PA_USE_CONVERTER_( Int8, Int32 ),
+ /* paInt24: */ PA_USE_CONVERTER_( Int8, Int24 ),
+ /* paInt16: */ PA_USE_CONVERTER_( Int8, Int16 ),
+ /* paInt8: */ PA_UNITY_CONVERSION_( 8 ),
+ /* paUInt8: */ PA_USE_CONVERTER_( Int8, UInt8 )
+ ),
+ /* paUInt8: */
+ PA_SELECT_FORMAT_( destinationFormat,
+ /* paFloat32: */ PA_USE_CONVERTER_( UInt8, Float32 ),
+ /* paInt32: */ PA_USE_CONVERTER_( UInt8, Int32 ),
+ /* paInt24: */ PA_USE_CONVERTER_( UInt8, Int24 ),
+ /* paInt16: */ PA_USE_CONVERTER_( UInt8, Int16 ),
+ /* paInt8: */ PA_USE_CONVERTER_( UInt8, Int8 ),
+ /* paUInt8: */ PA_UNITY_CONVERSION_( 8 )
+ )
+ )
+}
+
+/* -------------------------------------------------------------------------- */
+
+#ifdef PA_NO_STANDARD_CONVERTERS
+
+/* -------------------------------------------------------------------------- */
+
+PaUtilConverterTable paConverters = {
+ 0, /* PaUtilConverter *Float32_To_Int32; */
+ 0, /* PaUtilConverter *Float32_To_Int32_Dither; */
+ 0, /* PaUtilConverter *Float32_To_Int32_Clip; */
+ 0, /* PaUtilConverter *Float32_To_Int32_DitherClip; */
+
+ 0, /* PaUtilConverter *Float32_To_Int24; */
+ 0, /* PaUtilConverter *Float32_To_Int24_Dither; */
+ 0, /* PaUtilConverter *Float32_To_Int24_Clip; */
+ 0, /* PaUtilConverter *Float32_To_Int24_DitherClip; */
+
+ 0, /* PaUtilConverter *Float32_To_Int16; */
+ 0, /* PaUtilConverter *Float32_To_Int16_Dither; */
+ 0, /* PaUtilConverter *Float32_To_Int16_Clip; */
+ 0, /* PaUtilConverter *Float32_To_Int16_DitherClip; */
+
+ 0, /* PaUtilConverter *Float32_To_Int8; */
+ 0, /* PaUtilConverter *Float32_To_Int8_Dither; */
+ 0, /* PaUtilConverter *Float32_To_Int8_Clip; */
+ 0, /* PaUtilConverter *Float32_To_Int8_DitherClip; */
+
+ 0, /* PaUtilConverter *Float32_To_UInt8; */
+ 0, /* PaUtilConverter *Float32_To_UInt8_Dither; */
+ 0, /* PaUtilConverter *Float32_To_UInt8_Clip; */
+ 0, /* PaUtilConverter *Float32_To_UInt8_DitherClip; */
+
+ 0, /* PaUtilConverter *Int32_To_Float32; */
+ 0, /* PaUtilConverter *Int32_To_Int24; */
+ 0, /* PaUtilConverter *Int32_To_Int24_Dither; */
+ 0, /* PaUtilConverter *Int32_To_Int16; */
+ 0, /* PaUtilConverter *Int32_To_Int16_Dither; */
+ 0, /* PaUtilConverter *Int32_To_Int8; */
+ 0, /* PaUtilConverter *Int32_To_Int8_Dither; */
+ 0, /* PaUtilConverter *Int32_To_UInt8; */
+ 0, /* PaUtilConverter *Int32_To_UInt8_Dither; */
+
+ 0, /* PaUtilConverter *Int24_To_Float32; */
+ 0, /* PaUtilConverter *Int24_To_Int32; */
+ 0, /* PaUtilConverter *Int24_To_Int16; */
+ 0, /* PaUtilConverter *Int24_To_Int16_Dither; */
+ 0, /* PaUtilConverter *Int24_To_Int8; */
+ 0, /* PaUtilConverter *Int24_To_Int8_Dither; */
+ 0, /* PaUtilConverter *Int24_To_UInt8; */
+ 0, /* PaUtilConverter *Int24_To_UInt8_Dither; */
+
+ 0, /* PaUtilConverter *Int16_To_Float32; */
+ 0, /* PaUtilConverter *Int16_To_Int32; */
+ 0, /* PaUtilConverter *Int16_To_Int24; */
+ 0, /* PaUtilConverter *Int16_To_Int8; */
+ 0, /* PaUtilConverter *Int16_To_Int8_Dither; */
+ 0, /* PaUtilConverter *Int16_To_UInt8; */
+ 0, /* PaUtilConverter *Int16_To_UInt8_Dither; */
+
+ 0, /* PaUtilConverter *Int8_To_Float32; */
+ 0, /* PaUtilConverter *Int8_To_Int32; */
+ 0, /* PaUtilConverter *Int8_To_Int24 */
+ 0, /* PaUtilConverter *Int8_To_Int16; */
+ 0, /* PaUtilConverter *Int8_To_UInt8; */
+
+ 0, /* PaUtilConverter *UInt8_To_Float32; */
+ 0, /* PaUtilConverter *UInt8_To_Int32; */
+ 0, /* PaUtilConverter *UInt8_To_Int24; */
+ 0, /* PaUtilConverter *UInt8_To_Int16; */
+ 0, /* PaUtilConverter *UInt8_To_Int8; */
+
+ 0, /* PaUtilConverter *Copy_8_To_8; */
+ 0, /* PaUtilConverter *Copy_16_To_16; */
+ 0, /* PaUtilConverter *Copy_24_To_24; */
+ 0 /* PaUtilConverter *Copy_32_To_32; */
+};
+
+/* -------------------------------------------------------------------------- */
+
+#else /* PA_NO_STANDARD_CONVERTERS is not defined */
+
+/* -------------------------------------------------------------------------- */
+
+#define PA_CLIP_( val, min, max )\
+ { val = ((val) < (min)) ? (min) : (((val) > (max)) ? (max) : (val)); }
+
+
+static const float const_1_div_128_ = 1.0f / 128.0f; /* 8 bit multiplier */
+
+static const float const_1_div_32768_ = 1.0f / 32768.f; /* 16 bit multiplier */
+
+static const double const_1_div_2147483648_ = 1.0 / 2147483648.0; /* 32 bit multiplier */
+
+/* -------------------------------------------------------------------------- */
+
+static void Float32_To_Int32(
+ void *destinationBuffer, signed int destinationStride,
+ void *sourceBuffer, signed int sourceStride,
+ unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+ float *src = (float*)sourceBuffer;
+ signed long *dest = (signed long*)destinationBuffer;
+ (void)ditherGenerator; /* unused parameter */
+
+ while( count-- )
+ {
+ /* REVIEW */
+#ifdef PA_USE_C99_LRINTF
+ float scaled = *src * 0x7FFFFFFF;
+ *dest = lrintf(scaled-0.5f);
+#else
+ double scaled = *src * 0x7FFFFFFF;
+ *dest = (signed long) scaled;
+#endif
+
+ src += sourceStride;
+ dest += destinationStride;
+ }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Float32_To_Int32_Dither(
+ void *destinationBuffer, signed int destinationStride,
+ void *sourceBuffer, signed int sourceStride,
+ unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+ float *src = (float*)sourceBuffer;
+ signed long *dest = (signed long*)destinationBuffer;
+
+ while( count-- )
+ {
+ /* REVIEW */
+#ifdef PA_USE_C99_LRINTF
+ float dither = PaUtil_GenerateFloatTriangularDither( ditherGenerator );
+ /* use smaller scaler to prevent overflow when we add the dither */
+ float dithered = ((float)*src * (2147483646.0f)) + dither;
+ *dest = lrintf(dithered - 0.5f);
+#else
+ double dither = PaUtil_GenerateFloatTriangularDither( ditherGenerator );
+ /* use smaller scaler to prevent overflow when we add the dither */
+ double dithered = ((double)*src * (2147483646.0)) + dither;
+ *dest = (signed long) dithered;
+#endif
+ src += sourceStride;
+ dest += destinationStride;
+ }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Float32_To_Int32_Clip(
+ void *destinationBuffer, signed int destinationStride,
+ void *sourceBuffer, signed int sourceStride,
+ unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+ float *src = (float*)sourceBuffer;
+ signed long *dest = (signed long*)destinationBuffer;
+ (void) ditherGenerator; /* unused parameter */
+
+ while( count-- )
+ {
+ /* REVIEW */
+#ifdef PA_USE_C99_LRINTF
+ float scaled = *src * 0x7FFFFFFF;
+ PA_CLIP_( scaled, -2147483648.f, 2147483647.f );
+ *dest = lrintf(scaled-0.5f);
+#else
+ double scaled = *src * 0x7FFFFFFF;
+ PA_CLIP_( scaled, -2147483648., 2147483647. );
+ *dest = (signed long) scaled;
+#endif
+
+ src += sourceStride;
+ dest += destinationStride;
+ }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Float32_To_Int32_DitherClip(
+ void *destinationBuffer, signed int destinationStride,
+ void *sourceBuffer, signed int sourceStride,
+ unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+ float *src = (float*)sourceBuffer;
+ signed long *dest = (signed long*)destinationBuffer;
+
+ while( count-- )
+ {
+ /* REVIEW */
+#ifdef PA_USE_C99_LRINTF
+ float dither = PaUtil_GenerateFloatTriangularDither( ditherGenerator );
+ /* use smaller scaler to prevent overflow when we add the dither */
+ float dithered = ((float)*src * (2147483646.0f)) + dither;
+ PA_CLIP_( dithered, -2147483648.f, 2147483647.f );
+ *dest = lrintf(dithered-0.5f);
+#else
+ double dither = PaUtil_GenerateFloatTriangularDither( ditherGenerator );
+ /* use smaller scaler to prevent overflow when we add the dither */
+ double dithered = ((double)*src * (2147483646.0)) + dither;
+ PA_CLIP_( dithered, -2147483648., 2147483647. );
+ *dest = (signed long) dithered;
+#endif
+
+ src += sourceStride;
+ dest += destinationStride;
+ }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Float32_To_Int24(
+ void *destinationBuffer, signed int destinationStride,
+ void *sourceBuffer, signed int sourceStride,
+ unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+ float *src = (float*)sourceBuffer;
+ unsigned char *dest = (unsigned char*)destinationBuffer;
+ signed long temp;
+
+ (void) ditherGenerator; /* unused parameter */
+
+ while( count-- )
+ {
+ /* convert to 32 bit and drop the low 8 bits */
+ double scaled = *src * 0x7FFFFFFF;
+ temp = (signed long) scaled;
+
+#if defined(PA_LITTLE_ENDIAN)
+ dest[0] = (unsigned char)(temp >> 8);
+ dest[1] = (unsigned char)(temp >> 16);
+ dest[2] = (unsigned char)(temp >> 24);
+#elif defined(PA_BIG_ENDIAN)
+ dest[0] = (unsigned char)(temp >> 24);
+ dest[1] = (unsigned char)(temp >> 16);
+ dest[2] = (unsigned char)(temp >> 8);
+#endif
+
+ src += sourceStride;
+ dest += destinationStride * 3;
+ }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Float32_To_Int24_Dither(
+ void *destinationBuffer, signed int destinationStride,
+ void *sourceBuffer, signed int sourceStride,
+ unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+ float *src = (float*)sourceBuffer;
+ unsigned char *dest = (unsigned char*)destinationBuffer;
+ signed long temp;
+
+ while( count-- )
+ {
+ /* convert to 32 bit and drop the low 8 bits */
+
+ double dither = PaUtil_GenerateFloatTriangularDither( ditherGenerator );
+ /* use smaller scaler to prevent overflow when we add the dither */
+ double dithered = ((double)*src * (2147483646.0)) + dither;
+
+ temp = (signed long) dithered;
+
+#if defined(PA_LITTLE_ENDIAN)
+ dest[0] = (unsigned char)(temp >> 8);
+ dest[1] = (unsigned char)(temp >> 16);
+ dest[2] = (unsigned char)(temp >> 24);
+#elif defined(PA_BIG_ENDIAN)
+ dest[0] = (unsigned char)(temp >> 24);
+ dest[1] = (unsigned char)(temp >> 16);
+ dest[2] = (unsigned char)(temp >> 8);
+#endif
+
+ src += sourceStride;
+ dest += destinationStride * 3;
+ }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Float32_To_Int24_Clip(
+ void *destinationBuffer, signed int destinationStride,
+ void *sourceBuffer, signed int sourceStride,
+ unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+ float *src = (float*)sourceBuffer;
+ unsigned char *dest = (unsigned char*)destinationBuffer;
+ signed long temp;
+
+ (void) ditherGenerator; /* unused parameter */
+
+ while( count-- )
+ {
+ /* convert to 32 bit and drop the low 8 bits */
+ double scaled = *src * 0x7FFFFFFF;
+ PA_CLIP_( scaled, -2147483648., 2147483647. );
+ temp = (signed long) scaled;
+
+#if defined(PA_LITTLE_ENDIAN)
+ dest[0] = (unsigned char)(temp >> 8);
+ dest[1] = (unsigned char)(temp >> 16);
+ dest[2] = (unsigned char)(temp >> 24);
+#elif defined(PA_BIG_ENDIAN)
+ dest[0] = (unsigned char)(temp >> 24);
+ dest[1] = (unsigned char)(temp >> 16);
+ dest[2] = (unsigned char)(temp >> 8);
+#endif
+
+ src += sourceStride;
+ dest += destinationStride * 3;
+ }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Float32_To_Int24_DitherClip(
+ void *destinationBuffer, signed int destinationStride,
+ void *sourceBuffer, signed int sourceStride,
+ unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+ float *src = (float*)sourceBuffer;
+ unsigned char *dest = (unsigned char*)destinationBuffer;
+ signed long temp;
+
+ while( count-- )
+ {
+ /* convert to 32 bit and drop the low 8 bits */
+
+ double dither = PaUtil_GenerateFloatTriangularDither( ditherGenerator );
+ /* use smaller scaler to prevent overflow when we add the dither */
+ double dithered = ((double)*src * (2147483646.0)) + dither;
+ PA_CLIP_( dithered, -2147483648., 2147483647. );
+
+ temp = (signed long) dithered;
+
+#if defined(PA_LITTLE_ENDIAN)
+ dest[0] = (unsigned char)(temp >> 8);
+ dest[1] = (unsigned char)(temp >> 16);
+ dest[2] = (unsigned char)(temp >> 24);
+#elif defined(PA_BIG_ENDIAN)
+ dest[0] = (unsigned char)(temp >> 24);
+ dest[1] = (unsigned char)(temp >> 16);
+ dest[2] = (unsigned char)(temp >> 8);
+#endif
+
+ src += sourceStride;
+ dest += destinationStride * 3;
+ }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Float32_To_Int16(
+ void *destinationBuffer, signed int destinationStride,
+ void *sourceBuffer, signed int sourceStride,
+ unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+ float *src = (float*)sourceBuffer;
+ signed short *dest = (signed short*)destinationBuffer;
+ (void)ditherGenerator; /* unused parameter */
+
+ while( count-- )
+ {
+#ifdef PA_USE_C99_LRINTF
+ float tempf = (*src * (32767.0f)) ;
+ *dest = lrintf(tempf-0.5f);
+#else
+ short samp = (short) (*src * (32767.0f));
+ *dest = samp;
+#endif
+
+ src += sourceStride;
+ dest += destinationStride;
+ }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Float32_To_Int16_Dither(
+ void *destinationBuffer, signed int destinationStride,
+ void *sourceBuffer, signed int sourceStride,
+ unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+ float *src = (float*)sourceBuffer;
+ signed short *dest = (signed short*)destinationBuffer;
+
+ while( count-- )
+ {
+
+ float dither = PaUtil_GenerateFloatTriangularDither( ditherGenerator );
+ /* use smaller scaler to prevent overflow when we add the dither */
+ float dithered = (*src * (32766.0f)) + dither;
+
+#ifdef PA_USE_C99_LRINTF
+ *dest = lrintf(dithered-0.5f);
+#else
+ *dest = (signed short) dithered;
+#endif
+
+ src += sourceStride;
+ dest += destinationStride;
+ }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Float32_To_Int16_Clip(
+ void *destinationBuffer, signed int destinationStride,
+ void *sourceBuffer, signed int sourceStride,
+ unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+ float *src = (float*)sourceBuffer;
+ signed short *dest = (signed short*)destinationBuffer;
+ (void)ditherGenerator; /* unused parameter */
+
+ while( count-- )
+ {
+#ifdef PA_USE_C99_LRINTF
+ long samp = lrintf((*src * (32767.0f)) -0.5f);
+#else
+ long samp = (signed long) (*src * (32767.0f));
+#endif
+ PA_CLIP_( samp, -0x8000, 0x7FFF );
+ *dest = (signed short) samp;
+
+ src += sourceStride;
+ dest += destinationStride;
+ }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Float32_To_Int16_DitherClip(
+ void *destinationBuffer, signed int destinationStride,
+ void *sourceBuffer, signed int sourceStride,
+ unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+ float *src = (float*)sourceBuffer;
+ signed short *dest = (signed short*)destinationBuffer;
+ (void)ditherGenerator; /* unused parameter */
+
+ while( count-- )
+ {
+
+ float dither = PaUtil_GenerateFloatTriangularDither( ditherGenerator );
+ /* use smaller scaler to prevent overflow when we add the dither */
+ float dithered = (*src * (32766.0f)) + dither;
+ signed long samp = (signed long) dithered;
+ PA_CLIP_( samp, -0x8000, 0x7FFF );
+#ifdef PA_USE_C99_LRINTF
+ *dest = lrintf(samp-0.5f);
+#else
+ *dest = (signed short) samp;
+#endif
+
+ src += sourceStride;
+ dest += destinationStride;
+ }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Float32_To_Int8(
+ void *destinationBuffer, signed int destinationStride,
+ void *sourceBuffer, signed int sourceStride,
+ unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+ float *src = (float*)sourceBuffer;
+ signed char *dest = (signed char*)destinationBuffer;
+ (void)ditherGenerator; /* unused parameter */
+
+ while( count-- )
+ {
+ signed char samp = (signed char) (*src * (127.0f));
+ *dest = samp;
+
+ src += sourceStride;
+ dest += destinationStride;
+ }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Float32_To_Int8_Dither(
+ void *destinationBuffer, signed int destinationStride,
+ void *sourceBuffer, signed int sourceStride,
+ unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+ float *src = (float*)sourceBuffer;
+ signed char *dest = (signed char*)destinationBuffer;
+ (void)ditherGenerator; /* unused parameter */
+
+ while( count-- )
+ {
+ float dither = PaUtil_GenerateFloatTriangularDither( ditherGenerator );
+ /* use smaller scaler to prevent overflow when we add the dither */
+ float dithered = (*src * (126.0f)) + dither;
+ signed long samp = (signed long) dithered;
+ *dest = (signed char) samp;
+
+ src += sourceStride;
+ dest += destinationStride;
+ }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Float32_To_Int8_Clip(
+ void *destinationBuffer, signed int destinationStride,
+ void *sourceBuffer, signed int sourceStride,
+ unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+ float *src = (float*)sourceBuffer;
+ signed char *dest = (signed char*)destinationBuffer;
+ (void)ditherGenerator; /* unused parameter */
+
+ while( count-- )
+ {
+ signed long samp = (signed long)(*src * (127.0f));
+ PA_CLIP_( samp, -0x80, 0x7F );
+ *dest = (signed char) samp;
+
+ src += sourceStride;
+ dest += destinationStride;
+ }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Float32_To_Int8_DitherClip(
+ void *destinationBuffer, signed int destinationStride,
+ void *sourceBuffer, signed int sourceStride,
+ unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+ float *src = (float*)sourceBuffer;
+ signed char *dest = (signed char*)destinationBuffer;
+ (void)ditherGenerator; /* unused parameter */
+
+ while( count-- )
+ {
+ float dither = PaUtil_GenerateFloatTriangularDither( ditherGenerator );
+ /* use smaller scaler to prevent overflow when we add the dither */
+ float dithered = (*src * (126.0f)) + dither;
+ signed long samp = (signed long) dithered;
+ PA_CLIP_( samp, -0x80, 0x7F );
+ *dest = (signed char) samp;
+
+ src += sourceStride;
+ dest += destinationStride;
+ }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Float32_To_UInt8(
+ void *destinationBuffer, signed int destinationStride,
+ void *sourceBuffer, signed int sourceStride,
+ unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+ float *src = (float*)sourceBuffer;
+ unsigned char *dest = (unsigned char*)destinationBuffer;
+ (void)ditherGenerator; /* unused parameter */
+
+ while( count-- )
+ {
+ unsigned char samp = (unsigned char)(128 + ((unsigned char) (*src * (127.0f))));
+ *dest = samp;
+
+ src += sourceStride;
+ dest += destinationStride;
+ }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Float32_To_UInt8_Dither(
+ void *destinationBuffer, signed int destinationStride,
+ void *sourceBuffer, signed int sourceStride,
+ unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+ float *src = (float*)sourceBuffer;
+ unsigned char *dest = (unsigned char*)destinationBuffer;
+ (void)ditherGenerator; /* unused parameter */
+
+ while( count-- )
+ {
+ /* IMPLEMENT ME */
+
+ src += sourceStride;
+ dest += destinationStride;
+ }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Float32_To_UInt8_Clip(
+ void *destinationBuffer, signed int destinationStride,
+ void *sourceBuffer, signed int sourceStride,
+ unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+ float *src = (float*)sourceBuffer;
+ unsigned char *dest = (unsigned char*)destinationBuffer;
+ (void)ditherGenerator; /* unused parameter */
+
+ while( count-- )
+ {
+ /* IMPLEMENT ME */
+
+ src += sourceStride;
+ dest += destinationStride;
+ }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Float32_To_UInt8_DitherClip(
+ void *destinationBuffer, signed int destinationStride,
+ void *sourceBuffer, signed int sourceStride,
+ unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+ float *src = (float*)sourceBuffer;
+ unsigned char *dest = (unsigned char*)destinationBuffer;
+ (void)ditherGenerator; /* unused parameter */
+
+ while( count-- )
+ {
+ /* IMPLEMENT ME */
+
+ src += sourceStride;
+ dest += destinationStride;
+ }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Int32_To_Float32(
+ void *destinationBuffer, signed int destinationStride,
+ void *sourceBuffer, signed int sourceStride,
+ unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+ signed long *src = (signed long*)sourceBuffer;
+ float *dest = (float*)destinationBuffer;
+ (void)ditherGenerator; /* unused parameter */
+
+ while( count-- )
+ {
+ *dest = (float) ((double)*src * const_1_div_2147483648_);
+
+ src += sourceStride;
+ dest += destinationStride;
+ }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Int32_To_Int24(
+ void *destinationBuffer, signed int destinationStride,
+ void *sourceBuffer, signed int sourceStride,
+ unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+ signed long *src = (signed long*)sourceBuffer;
+ unsigned char *dest = (unsigned char*)destinationBuffer;
+ (void) ditherGenerator; /* unused parameter */
+
+ while( count-- )
+ {
+ /* REVIEW */
+#if defined(PA_LITTLE_ENDIAN)
+ dest[0] = (unsigned char)(*src >> 8);
+ dest[1] = (unsigned char)(*src >> 16);
+ dest[2] = (unsigned char)(*src >> 24);
+#elif defined(PA_BIG_ENDIAN)
+ dest[0] = (unsigned char)(*src >> 24);
+ dest[1] = (unsigned char)(*src >> 16);
+ dest[2] = (unsigned char)(*src >> 8);
+#endif
+ src += sourceStride;
+ dest += destinationStride * 3;
+ }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Int32_To_Int24_Dither(
+ void *destinationBuffer, signed int destinationStride,
+ void *sourceBuffer, signed int sourceStride,
+ unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+ (void) destinationBuffer; /* unused parameters */
+ (void) destinationStride; /* unused parameters */
+ (void) sourceBuffer; /* unused parameters */
+ (void) sourceStride; /* unused parameters */
+ (void) count; /* unused parameters */
+ (void) ditherGenerator; /* unused parameters */
+ /* IMPLEMENT ME */
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Int32_To_Int16(
+ void *destinationBuffer, signed int destinationStride,
+ void *sourceBuffer, signed int sourceStride,
+ unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+ signed long *src = (signed long*)sourceBuffer;
+ signed short *dest = (signed short*)destinationBuffer;
+ (void)ditherGenerator; /* unused parameter */
+
+ while( count-- )
+ {
+ *dest = (signed short) ((*src) >> 16);
+
+ src += sourceStride;
+ dest += destinationStride;
+ }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Int32_To_Int16_Dither(
+ void *destinationBuffer, signed int destinationStride,
+ void *sourceBuffer, signed int sourceStride,
+ unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+ signed long *src = (signed long*)sourceBuffer;
+ signed short *dest = (signed short*)destinationBuffer;
+ signed long dither;
+
+ while( count-- )
+ {
+ /* REVIEW */
+ dither = PaUtil_Generate16BitTriangularDither( ditherGenerator );
+ *dest = (signed short) ((((*src)>>1) + dither) >> 15);
+
+ src += sourceStride;
+ dest += destinationStride;
+ }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Int32_To_Int8(
+ void *destinationBuffer, signed int destinationStride,
+ void *sourceBuffer, signed int sourceStride,
+ unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+ signed long *src = (signed long*)sourceBuffer;
+ signed char *dest = (signed char*)destinationBuffer;
+ (void)ditherGenerator; /* unused parameter */
+
+ while( count-- )
+ {
+ *dest = (signed char) ((*src) >> 24);
+
+ src += sourceStride;
+ dest += destinationStride;
+ }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Int32_To_Int8_Dither(
+ void *destinationBuffer, signed int destinationStride,
+ void *sourceBuffer, signed int sourceStride,
+ unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+ signed long *src = (signed long*)sourceBuffer;
+ signed char *dest = (signed char*)destinationBuffer;
+ signed long dither;
+
+ while( count-- )
+ {
+ /* REVIEW */
+ dither = PaUtil_Generate16BitTriangularDither( ditherGenerator );
+ *dest = (signed char) ((((*src)>>1) + dither) >> 23);
+
+ src += sourceStride;
+ dest += destinationStride;
+ }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Int32_To_UInt8(
+ void *destinationBuffer, signed int destinationStride,
+ void *sourceBuffer, signed int sourceStride,
+ unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+ signed long *src = (signed long*)sourceBuffer;
+ unsigned char *dest = (unsigned char*)destinationBuffer;
+ (void)ditherGenerator; /* unused parameter */
+
+ while( count-- )
+ {
+ (*dest) = (unsigned char)(((*src) >> 24) + 128);
+
+ src += sourceStride;
+ dest += destinationStride;
+ }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Int32_To_UInt8_Dither(
+ void *destinationBuffer, signed int destinationStride,
+ void *sourceBuffer, signed int sourceStride,
+ unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+ signed long *src = (signed long*)sourceBuffer;
+ unsigned char *dest = (unsigned char*)destinationBuffer;
+ (void)ditherGenerator; /* unused parameter */
+
+ while( count-- )
+ {
+ /* IMPLEMENT ME */
+
+ src += sourceStride;
+ dest += destinationStride;
+ }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Int24_To_Float32(
+ void *destinationBuffer, signed int destinationStride,
+ void *sourceBuffer, signed int sourceStride,
+ unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+ unsigned char *src = (unsigned char*)sourceBuffer;
+ float *dest = (float*)destinationBuffer;
+ signed long temp;
+
+ (void) ditherGenerator; /* unused parameter */
+
+ while( count-- )
+ {
+
+#if defined(PA_LITTLE_ENDIAN)
+ temp = (((long)src[0]) << 8);
+ temp = temp | (((long)src[1]) << 16);
+ temp = temp | (((long)src[2]) << 24);
+#elif defined(PA_BIG_ENDIAN)
+ temp = (((long)src[0]) << 24);
+ temp = temp | (((long)src[1]) << 16);
+ temp = temp | (((long)src[2]) << 8);
+#endif
+
+ *dest = (float) ((double)temp * const_1_div_2147483648_);
+
+ src += sourceStride * 3;
+ dest += destinationStride;
+ }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Int24_To_Int32(
+ void *destinationBuffer, signed int destinationStride,
+ void *sourceBuffer, signed int sourceStride,
+ unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+ unsigned char *src = (unsigned char*)sourceBuffer;
+ signed long *dest = (signed long*) destinationBuffer;
+ signed long temp;
+
+ (void) ditherGenerator; /* unused parameter */
+
+ while( count-- )
+ {
+
+#if defined(PA_LITTLE_ENDIAN)
+ temp = (((long)src[0]) << 8);
+ temp = temp | (((long)src[1]) << 16);
+ temp = temp | (((long)src[2]) << 24);
+#elif defined(PA_BIG_ENDIAN)
+ temp = (((long)src[0]) << 24);
+ temp = temp | (((long)src[1]) << 16);
+ temp = temp | (((long)src[2]) << 8);
+#endif
+
+ *dest = temp;
+
+ src += sourceStride * 3;
+ dest += destinationStride;
+ }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Int24_To_Int16(
+ void *destinationBuffer, signed int destinationStride,
+ void *sourceBuffer, signed int sourceStride,
+ unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+ unsigned char *src = (unsigned char*)sourceBuffer;
+ signed short *dest = (signed short*)destinationBuffer;
+
+ signed short temp;
+
+ (void) ditherGenerator; /* unused parameter */
+
+ while( count-- )
+ {
+
+#if defined(PA_LITTLE_ENDIAN)
+ /* src[0] is discarded */
+ temp = (((signed short)src[1]));
+ temp = temp | (signed short)(((signed short)src[2]) << 8);
+#elif defined(PA_BIG_ENDIAN)
+ /* src[2] is discarded */
+ temp = (signed short)(((signed short)src[0]) << 8);
+ temp = temp | (((signed short)src[1]));
+#endif
+
+ *dest = temp;
+
+ src += sourceStride * 3;
+ dest += destinationStride;
+ }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Int24_To_Int16_Dither(
+ void *destinationBuffer, signed int destinationStride,
+ void *sourceBuffer, signed int sourceStride,
+ unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+ (void) destinationBuffer; /* unused parameters */
+ (void) destinationStride; /* unused parameters */
+ (void) sourceBuffer; /* unused parameters */
+ (void) sourceStride; /* unused parameters */
+ (void) count; /* unused parameters */
+ (void) ditherGenerator; /* unused parameters */
+ /* IMPLEMENT ME */
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Int24_To_Int8(
+ void *destinationBuffer, signed int destinationStride,
+ void *sourceBuffer, signed int sourceStride,
+ unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+ unsigned char *src = (unsigned char*)sourceBuffer;
+ signed char *dest = (signed char*)destinationBuffer;
+
+ (void) ditherGenerator; /* unused parameter */
+
+ while( count-- )
+ {
+
+#if defined(PA_LITTLE_ENDIAN)
+ /* src[0] is discarded */
+ /* src[1] is discarded */
+ *dest = src[2];
+#elif defined(PA_BIG_ENDIAN)
+ /* src[2] is discarded */
+ /* src[1] is discarded */
+ *dest = src[0];
+#endif
+
+ src += sourceStride * 3;
+ dest += destinationStride;
+ }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Int24_To_Int8_Dither(
+ void *destinationBuffer, signed int destinationStride,
+ void *sourceBuffer, signed int sourceStride,
+ unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+ (void) destinationBuffer; /* unused parameters */
+ (void) destinationStride; /* unused parameters */
+ (void) sourceBuffer; /* unused parameters */
+ (void) sourceStride; /* unused parameters */
+ (void) count; /* unused parameters */
+ (void) ditherGenerator; /* unused parameters */
+ /* IMPLEMENT ME */
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Int24_To_UInt8(
+ void *destinationBuffer, signed int destinationStride,
+ void *sourceBuffer, signed int sourceStride,
+ unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+ unsigned char *src = (unsigned char*)sourceBuffer;
+ unsigned char *dest = (unsigned char*)destinationBuffer;
+
+ (void) ditherGenerator; /* unused parameter */
+
+ while( count-- )
+ {
+
+#if defined(PA_LITTLE_ENDIAN)
+ /* src[0] is discarded */
+ /* src[1] is discarded */
+ *dest = (unsigned char)(src[2] + 128);
+#elif defined(PA_BIG_ENDIAN)
+ *dest = (unsigned char)(src[0] + 128);
+ /* src[1] is discarded */
+ /* src[2] is discarded */
+#endif
+
+ src += sourceStride * 3;
+ dest += destinationStride;
+ }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Int24_To_UInt8_Dither(
+ void *destinationBuffer, signed int destinationStride,
+ void *sourceBuffer, signed int sourceStride,
+ unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+ (void) destinationBuffer; /* unused parameters */
+ (void) destinationStride; /* unused parameters */
+ (void) sourceBuffer; /* unused parameters */
+ (void) sourceStride; /* unused parameters */
+ (void) count; /* unused parameters */
+ (void) ditherGenerator; /* unused parameters */
+ /* IMPLEMENT ME */
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Int16_To_Float32(
+ void *destinationBuffer, signed int destinationStride,
+ void *sourceBuffer, signed int sourceStride,
+ unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+ signed short *src = (signed short*)sourceBuffer;
+ float *dest = (float*)destinationBuffer;
+ (void)ditherGenerator; /* unused parameter */
+
+ while( count-- )
+ {
+ float samp = *src * const_1_div_32768_; /* FIXME: i'm concerned about this being asymetrical with float->int16 -rb */
+ *dest = samp;
+
+ src += sourceStride;
+ dest += destinationStride;
+ }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Int16_To_Int32(
+ void *destinationBuffer, signed int destinationStride,
+ void *sourceBuffer, signed int sourceStride,
+ unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+ signed short *src = (signed short*)sourceBuffer;
+ signed long *dest = (signed long*)destinationBuffer;
+ (void)ditherGenerator; /* unused parameter */
+
+ while( count-- )
+ {
+ /* REVIEW: we should consider something like
+ (*src << 16) | (*src & 0xFFFF)
+ */
+
+ *dest = *src << 16;
+
+ src += sourceStride;
+ dest += destinationStride;
+ }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Int16_To_Int24(
+ void *destinationBuffer, signed int destinationStride,
+ void *sourceBuffer, signed int sourceStride,
+ unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+ signed short *src = (signed short*) sourceBuffer;
+ unsigned char *dest = (unsigned char*)destinationBuffer;
+ signed short temp;
+
+ (void) ditherGenerator; /* unused parameter */
+
+ while( count-- )
+ {
+ temp = *src;
+
+#if defined(PA_LITTLE_ENDIAN)
+ dest[0] = 0;
+ dest[1] = (unsigned char)(temp);
+ dest[2] = (unsigned char)(temp >> 8);
+#elif defined(PA_BIG_ENDIAN)
+ dest[0] = (unsigned char)(temp >> 8);
+ dest[1] = (unsigned char)(temp);
+ dest[2] = 0;
+#endif
+
+ src += sourceStride;
+ dest += destinationStride * 3;
+ }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Int16_To_Int8(
+ void *destinationBuffer, signed int destinationStride,
+ void *sourceBuffer, signed int sourceStride,
+ unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+ signed short *src = (signed short*)sourceBuffer;
+ signed char *dest = (signed char*)destinationBuffer;
+ (void)ditherGenerator; /* unused parameter */
+
+ while( count-- )
+ {
+ (*dest) = (signed char)((*src) >> 8);
+
+ src += sourceStride;
+ dest += destinationStride;
+ }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Int16_To_Int8_Dither(
+ void *destinationBuffer, signed int destinationStride,
+ void *sourceBuffer, signed int sourceStride,
+ unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+ signed short *src = (signed short*)sourceBuffer;
+ signed char *dest = (signed char*)destinationBuffer;
+ (void)ditherGenerator; /* unused parameter */
+
+ while( count-- )
+ {
+ /* IMPLEMENT ME */
+
+ src += sourceStride;
+ dest += destinationStride;
+ }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Int16_To_UInt8(
+ void *destinationBuffer, signed int destinationStride,
+ void *sourceBuffer, signed int sourceStride,
+ unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+ signed short *src = (signed short*)sourceBuffer;
+ unsigned char *dest = (unsigned char*)destinationBuffer;
+ (void)ditherGenerator; /* unused parameter */
+
+ while( count-- )
+ {
+ (*dest) = (unsigned char)(((*src) >> 8) + 128);
+
+ src += sourceStride;
+ dest += destinationStride;
+ }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Int16_To_UInt8_Dither(
+ void *destinationBuffer, signed int destinationStride,
+ void *sourceBuffer, signed int sourceStride,
+ unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+ signed short *src = (signed short*)sourceBuffer;
+ unsigned char *dest = (unsigned char*)destinationBuffer;
+ (void)ditherGenerator; /* unused parameter */
+
+ while( count-- )
+ {
+ /* IMPLEMENT ME */
+
+ src += sourceStride;
+ dest += destinationStride;
+ }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Int8_To_Float32(
+ void *destinationBuffer, signed int destinationStride,
+ void *sourceBuffer, signed int sourceStride,
+ unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+ signed char *src = (signed char*)sourceBuffer;
+ float *dest = (float*)destinationBuffer;
+ (void)ditherGenerator; /* unused parameter */
+
+ while( count-- )
+ {
+ float samp = *src * const_1_div_128_;
+ *dest = samp;
+
+ src += sourceStride;
+ dest += destinationStride;
+ }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Int8_To_Int32(
+ void *destinationBuffer, signed int destinationStride,
+ void *sourceBuffer, signed int sourceStride,
+ unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+ signed char *src = (signed char*)sourceBuffer;
+ signed long *dest = (signed long*)destinationBuffer;
+ (void)ditherGenerator; /* unused parameter */
+
+ while( count-- )
+ {
+ (*dest) = (*src) << 24;
+
+ src += sourceStride;
+ dest += destinationStride;
+ }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Int8_To_Int24(
+ void *destinationBuffer, signed int destinationStride,
+ void *sourceBuffer, signed int sourceStride,
+ unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+ signed char *src = (signed char*)sourceBuffer;
+ unsigned char *dest = (unsigned char*)destinationBuffer;
+ (void)ditherGenerator; /* unused parameter */
+
+ while( count-- )
+ {
+
+#if defined(PA_LITTLE_ENDIAN)
+ dest[0] = 0;
+ dest[1] = 0;
+ dest[2] = (*src);
+#elif defined(PA_BIG_ENDIAN)
+ dest[0] = (*src);
+ dest[1] = 0;
+ dest[2] = 0;
+#endif
+
+ src += sourceStride;
+ dest += destinationStride * 3;
+ }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Int8_To_Int16(
+ void *destinationBuffer, signed int destinationStride,
+ void *sourceBuffer, signed int sourceStride,
+ unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+ signed char *src = (signed char*)sourceBuffer;
+ signed short *dest = (signed short*)destinationBuffer;
+ (void)ditherGenerator; /* unused parameter */
+
+ while( count-- )
+ {
+ (*dest) = (signed short)((*src) << 8);
+
+ src += sourceStride;
+ dest += destinationStride;
+ }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Int8_To_UInt8(
+ void *destinationBuffer, signed int destinationStride,
+ void *sourceBuffer, signed int sourceStride,
+ unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+ signed char *src = (signed char*)sourceBuffer;
+ unsigned char *dest = (unsigned char*)destinationBuffer;
+ (void)ditherGenerator; /* unused parameter */
+
+ while( count-- )
+ {
+ (*dest) = (unsigned char)(*src + 128);
+
+ src += sourceStride;
+ dest += destinationStride;
+ }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void UInt8_To_Float32(
+ void *destinationBuffer, signed int destinationStride,
+ void *sourceBuffer, signed int sourceStride,
+ unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+ unsigned char *src = (unsigned char*)sourceBuffer;
+ float *dest = (float*)destinationBuffer;
+ (void)ditherGenerator; /* unused parameter */
+
+ while( count-- )
+ {
+ float samp = (*src - 128) * const_1_div_128_;
+ *dest = samp;
+
+ src += sourceStride;
+ dest += destinationStride;
+ }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void UInt8_To_Int32(
+ void *destinationBuffer, signed int destinationStride,
+ void *sourceBuffer, signed int sourceStride,
+ unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+ unsigned char *src = (unsigned char*)sourceBuffer;
+ signed long *dest = (signed long*)destinationBuffer;
+ (void)ditherGenerator; /* unused parameter */
+
+ while( count-- )
+ {
+ (*dest) = (*src - 128) << 24;
+
+ src += sourceStride;
+ dest += destinationStride;
+ }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void UInt8_To_Int24(
+ void *destinationBuffer, signed int destinationStride,
+ void *sourceBuffer, signed int sourceStride,
+ unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+ unsigned char *src = (unsigned char*)sourceBuffer;
+ unsigned char *dest = (unsigned char*)destinationBuffer;
+ (void) ditherGenerator; /* unused parameters */
+
+ while( count-- )
+ {
+
+#if defined(PA_LITTLE_ENDIAN)
+ dest[0] = 0;
+ dest[1] = 0;
+ dest[2] = (unsigned char)(*src - 128);
+#elif defined(PA_BIG_ENDIAN)
+ dest[0] = (unsigned char)(*src - 128);
+ dest[1] = 0;
+ dest[2] = 0;
+#endif
+
+ src += sourceStride;
+ dest += destinationStride * 3;
+ }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void UInt8_To_Int16(
+ void *destinationBuffer, signed int destinationStride,
+ void *sourceBuffer, signed int sourceStride,
+ unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+ unsigned char *src = (unsigned char*)sourceBuffer;
+ signed short *dest = (signed short*)destinationBuffer;
+ (void)ditherGenerator; /* unused parameter */
+
+ while( count-- )
+ {
+ (*dest) = (signed short)((*src - 128) << 8);
+
+ src += sourceStride;
+ dest += destinationStride;
+ }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void UInt8_To_Int8(
+ void *destinationBuffer, signed int destinationStride,
+ void *sourceBuffer, signed int sourceStride,
+ unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+ unsigned char *src = (unsigned char*)sourceBuffer;
+ signed char *dest = (signed char*)destinationBuffer;
+ (void)ditherGenerator; /* unused parameter */
+
+ while( count-- )
+ {
+ (*dest) = (signed char)(*src - 128);
+
+ src += sourceStride;
+ dest += destinationStride;
+ }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Copy_8_To_8(
+ void *destinationBuffer, signed int destinationStride,
+ void *sourceBuffer, signed int sourceStride,
+ unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+ unsigned char *src = (unsigned char*)sourceBuffer;
+ unsigned char *dest = (unsigned char*)destinationBuffer;
+
+ (void) ditherGenerator; /* unused parameter */
+
+ while( count-- )
+ {
+ *dest = *src;
+
+ src += sourceStride;
+ dest += destinationStride;
+ }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Copy_16_To_16(
+ void *destinationBuffer, signed int destinationStride,
+ void *sourceBuffer, signed int sourceStride,
+ unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+ PaUint16 *src = (PaUint16 *)sourceBuffer;
+ PaUint16 *dest = (PaUint16 *)destinationBuffer;
+
+ (void) ditherGenerator; /* unused parameter */
+
+ while( count-- )
+ {
+ *dest = *src;
+
+ src += sourceStride;
+ dest += destinationStride;
+ }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Copy_24_To_24(
+ void *destinationBuffer, signed int destinationStride,
+ void *sourceBuffer, signed int sourceStride,
+ unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+ unsigned char *src = (unsigned char*)sourceBuffer;
+ unsigned char *dest = (unsigned char*)destinationBuffer;
+
+ (void) ditherGenerator; /* unused parameter */
+
+ while( count-- )
+ {
+ dest[0] = src[0];
+ dest[1] = src[1];
+ dest[2] = src[2];
+
+ src += sourceStride * 3;
+ dest += destinationStride * 3;
+ }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Copy_32_To_32(
+ void *destinationBuffer, signed int destinationStride,
+ void *sourceBuffer, signed int sourceStride,
+ unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+ PaUint32 *dest = (PaUint32 *)destinationBuffer;
+ PaUint32 *src = (PaUint32 *)sourceBuffer;
+
+ (void) ditherGenerator; /* unused parameter */
+
+ while( count-- )
+ {
+ *dest = *src;
+
+ src += sourceStride;
+ dest += destinationStride;
+ }
+}
+
+/* -------------------------------------------------------------------------- */
+
+PaUtilConverterTable paConverters = {
+ Float32_To_Int32, /* PaUtilConverter *Float32_To_Int32; */
+ Float32_To_Int32_Dither, /* PaUtilConverter *Float32_To_Int32_Dither; */
+ Float32_To_Int32_Clip, /* PaUtilConverter *Float32_To_Int32_Clip; */
+ Float32_To_Int32_DitherClip, /* PaUtilConverter *Float32_To_Int32_DitherClip; */
+
+ Float32_To_Int24, /* PaUtilConverter *Float32_To_Int24; */
+ Float32_To_Int24_Dither, /* PaUtilConverter *Float32_To_Int24_Dither; */
+ Float32_To_Int24_Clip, /* PaUtilConverter *Float32_To_Int24_Clip; */
+ Float32_To_Int24_DitherClip, /* PaUtilConverter *Float32_To_Int24_DitherClip; */
+
+ Float32_To_Int16, /* PaUtilConverter *Float32_To_Int16; */
+ Float32_To_Int16_Dither, /* PaUtilConverter *Float32_To_Int16_Dither; */
+ Float32_To_Int16_Clip, /* PaUtilConverter *Float32_To_Int16_Clip; */
+ Float32_To_Int16_DitherClip, /* PaUtilConverter *Float32_To_Int16_DitherClip; */
+
+ Float32_To_Int8, /* PaUtilConverter *Float32_To_Int8; */
+ Float32_To_Int8_Dither, /* PaUtilConverter *Float32_To_Int8_Dither; */
+ Float32_To_Int8_Clip, /* PaUtilConverter *Float32_To_Int8_Clip; */
+ Float32_To_Int8_DitherClip, /* PaUtilConverter *Float32_To_Int8_DitherClip; */
+
+ Float32_To_UInt8, /* PaUtilConverter *Float32_To_UInt8; */
+ Float32_To_UInt8_Dither, /* PaUtilConverter *Float32_To_UInt8_Dither; */
+ Float32_To_UInt8_Clip, /* PaUtilConverter *Float32_To_UInt8_Clip; */
+ Float32_To_UInt8_DitherClip, /* PaUtilConverter *Float32_To_UInt8_DitherClip; */
+
+ Int32_To_Float32, /* PaUtilConverter *Int32_To_Float32; */
+ Int32_To_Int24, /* PaUtilConverter *Int32_To_Int24; */
+ Int32_To_Int24_Dither, /* PaUtilConverter *Int32_To_Int24_Dither; */
+ Int32_To_Int16, /* PaUtilConverter *Int32_To_Int16; */
+ Int32_To_Int16_Dither, /* PaUtilConverter *Int32_To_Int16_Dither; */
+ Int32_To_Int8, /* PaUtilConverter *Int32_To_Int8; */
+ Int32_To_Int8_Dither, /* PaUtilConverter *Int32_To_Int8_Dither; */
+ Int32_To_UInt8, /* PaUtilConverter *Int32_To_UInt8; */
+ Int32_To_UInt8_Dither, /* PaUtilConverter *Int32_To_UInt8_Dither; */
+
+ Int24_To_Float32, /* PaUtilConverter *Int24_To_Float32; */
+ Int24_To_Int32, /* PaUtilConverter *Int24_To_Int32; */
+ Int24_To_Int16, /* PaUtilConverter *Int24_To_Int16; */
+ Int24_To_Int16_Dither, /* PaUtilConverter *Int24_To_Int16_Dither; */
+ Int24_To_Int8, /* PaUtilConverter *Int24_To_Int8; */
+ Int24_To_Int8_Dither, /* PaUtilConverter *Int24_To_Int8_Dither; */
+ Int24_To_UInt8, /* PaUtilConverter *Int24_To_UInt8; */
+ Int24_To_UInt8_Dither, /* PaUtilConverter *Int24_To_UInt8_Dither; */
+
+ Int16_To_Float32, /* PaUtilConverter *Int16_To_Float32; */
+ Int16_To_Int32, /* PaUtilConverter *Int16_To_Int32; */
+ Int16_To_Int24, /* PaUtilConverter *Int16_To_Int24; */
+ Int16_To_Int8, /* PaUtilConverter *Int16_To_Int8; */
+ Int16_To_Int8_Dither, /* PaUtilConverter *Int16_To_Int8_Dither; */
+ Int16_To_UInt8, /* PaUtilConverter *Int16_To_UInt8; */
+ Int16_To_UInt8_Dither, /* PaUtilConverter *Int16_To_UInt8_Dither; */
+
+ Int8_To_Float32, /* PaUtilConverter *Int8_To_Float32; */
+ Int8_To_Int32, /* PaUtilConverter *Int8_To_Int32; */
+ Int8_To_Int24, /* PaUtilConverter *Int8_To_Int24 */
+ Int8_To_Int16, /* PaUtilConverter *Int8_To_Int16; */
+ Int8_To_UInt8, /* PaUtilConverter *Int8_To_UInt8; */
+
+ UInt8_To_Float32, /* PaUtilConverter *UInt8_To_Float32; */
+ UInt8_To_Int32, /* PaUtilConverter *UInt8_To_Int32; */
+ UInt8_To_Int24, /* PaUtilConverter *UInt8_To_Int24; */
+ UInt8_To_Int16, /* PaUtilConverter *UInt8_To_Int16; */
+ UInt8_To_Int8, /* PaUtilConverter *UInt8_To_Int8; */
+
+ Copy_8_To_8, /* PaUtilConverter *Copy_8_To_8; */
+ Copy_16_To_16, /* PaUtilConverter *Copy_16_To_16; */
+ Copy_24_To_24, /* PaUtilConverter *Copy_24_To_24; */
+ Copy_32_To_32 /* PaUtilConverter *Copy_32_To_32; */
+};
+
+/* -------------------------------------------------------------------------- */
+
+#endif /* PA_NO_STANDARD_CONVERTERS */
+
+/* -------------------------------------------------------------------------- */
+
+PaUtilZeroer* PaUtil_SelectZeroer( PaSampleFormat destinationFormat )
+{
+ switch( destinationFormat & ~paNonInterleaved ){
+ case paFloat32:
+ return paZeroers.Zero32;
+ case paInt32:
+ return paZeroers.Zero32;
+ case paInt24:
+ return paZeroers.Zero24;
+ case paInt16:
+ return paZeroers.Zero16;
+ case paInt8:
+ return paZeroers.Zero8;
+ case paUInt8:
+ return paZeroers.ZeroU8;
+ default: return 0;
+ }
+}
+
+/* -------------------------------------------------------------------------- */
+
+#ifdef PA_NO_STANDARD_ZEROERS
+
+/* -------------------------------------------------------------------------- */
+
+PaUtilZeroerTable paZeroers = {
+ 0, /* PaUtilZeroer *ZeroU8; */
+ 0, /* PaUtilZeroer *Zero8; */
+ 0, /* PaUtilZeroer *Zero16; */
+ 0, /* PaUtilZeroer *Zero24; */
+ 0, /* PaUtilZeroer *Zero32; */
+};
+
+/* -------------------------------------------------------------------------- */
+
+#else /* PA_NO_STANDARD_ZEROERS is not defined */
+
+/* -------------------------------------------------------------------------- */
+
+static void ZeroU8( void *destinationBuffer, signed int destinationStride,
+ unsigned int count )
+{
+ unsigned char *dest = (unsigned char*)destinationBuffer;
+
+ while( count-- )
+ {
+ *dest = 128;
+
+ dest += destinationStride;
+ }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Zero8( void *destinationBuffer, signed int destinationStride,
+ unsigned int count )
+{
+ unsigned char *dest = (unsigned char*)destinationBuffer;
+
+ while( count-- )
+ {
+ *dest = 0;
+
+ dest += destinationStride;
+ }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Zero16( void *destinationBuffer, signed int destinationStride,
+ unsigned int count )
+{
+ PaUint16 *dest = (PaUint16 *)destinationBuffer;
+
+ while( count-- )
+ {
+ *dest = 0;
+
+ dest += destinationStride;
+ }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Zero24( void *destinationBuffer, signed int destinationStride,
+ unsigned int count )
+{
+ unsigned char *dest = (unsigned char*)destinationBuffer;
+
+ while( count-- )
+ {
+ dest[0] = 0;
+ dest[1] = 0;
+ dest[2] = 0;
+
+ dest += destinationStride * 3;
+ }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Zero32( void *destinationBuffer, signed int destinationStride,
+ unsigned int count )
+{
+ PaUint32 *dest = (PaUint32 *)destinationBuffer;
+
+ while( count-- )
+ {
+ *dest = 0;
+
+ dest += destinationStride;
+ }
+}
+
+/* -------------------------------------------------------------------------- */
+
+PaUtilZeroerTable paZeroers = {
+ ZeroU8, /* PaUtilZeroer *ZeroU8; */
+ Zero8, /* PaUtilZeroer *Zero8; */
+ Zero16, /* PaUtilZeroer *Zero16; */
+ Zero24, /* PaUtilZeroer *Zero24; */
+ Zero32, /* PaUtilZeroer *Zero32; */
+};
+
+/* -------------------------------------------------------------------------- */
+
+#endif /* PA_NO_STANDARD_ZEROERS */
diff --git a/pjmedia/src/pjmedia/portaudio/pa_converters.h b/pjmedia/src/pjmedia/portaudio/pa_converters.h
index 831c9c64..b196293d 100644
--- a/pjmedia/src/pjmedia/portaudio/pa_converters.h
+++ b/pjmedia/src/pjmedia/portaudio/pa_converters.h
@@ -1,254 +1,275 @@
-#ifndef PA_CONVERTERS_H
-#define PA_CONVERTERS_H
-/*
- * $Id: pa_converters.h,v 1.1.2.9 2003/09/20 21:05:14 rossbencina Exp $
- * Portable Audio I/O Library sample conversion mechanism
- *
- * Based on the Open Source API proposed by Ross Bencina
- * Copyright (c) 1999-2002 Phil Burk, Ross Bencina
- *
- * Permission is hereby granted, free of charge, to any person obtaining
- * a copy of this software and associated documentation files
- * (the "Software"), to deal in the Software without restriction,
- * including without limitation the rights to use, copy, modify, merge,
- * publish, distribute, sublicense, and/or sell copies of the Software,
- * and to permit persons to whom the Software is furnished to do so,
- * subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be
- * included in all copies or substantial portions of the Software.
- *
- * Any person wishing to distribute modifications to the Software is
- * requested to send the modifications to the original developer so that
- * they can be incorporated into the canonical version.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
- * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
- * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
- * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
- * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
- */
-
-/** @file
- @brief Conversion functions used to convert buffers of samples from one
- format to another.
-*/
-
-
-#include "portaudio.h" /* for PaSampleFormat */
-
-#ifdef __cplusplus
-extern "C"
-{
-#endif /* __cplusplus */
-
-
-struct PaUtilTriangularDitherGenerator;
-
-
-/** Choose an available sample format which is most appropriate for
- representing the requested format. If the requested format is not available
- higher quality formats are considered before lower quality formates.
- @param availableFormats A variable containing the logical OR of all available
- formats.
- @param format The desired format.
- @return The most appropriate available format for representing the requested
- format.
-*/
-PaSampleFormat PaUtil_SelectClosestAvailableFormat(
- PaSampleFormat availableFormats, PaSampleFormat format );
-
-
-/* high level conversions functions for use by implementations */
-
-
-/** The generic sample converter prototype. Sample converters convert count
- samples from sourceBuffer to destinationBuffer. The actual type of the data
- pointed to by these parameters varys for different converter functions.
- @param destinationBuffer A pointer to the first sample of the destination.
- @param destinationStride An offset between successive destination samples
- expressed in samples (not bytes.) It may be negative.
- @param sourceBuffer A pointer to the first sample of the source.
- @param sourceStride An offset between successive source samples
- expressed in samples (not bytes.) It may be negative.
- @param count The number of samples to convert.
- @param ditherState State information used to calculate dither. Converters
- that do not perform dithering will ignore this parameter, in which case
- NULL or invalid dither state may be passed.
-*/
-typedef void PaUtilConverter(
- void *destinationBuffer, signed int destinationStride,
- void *sourceBuffer, signed int sourceStride,
- unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator );
-
-
-/** Find a sample converter function for the given source and destinations
- formats and flags (clip and dither.)
- @return
- A pointer to a PaUtilConverter which will perform the requested
- conversion, or NULL if the given format conversion is not supported.
- For conversions where clipping or dithering is not necessary, the
- clip and dither flags are ignored and a non-clipping or dithering
- version is returned.
- If the source and destination formats are the same, a function which
- copies data of the appropriate size will be returned.
-*/
-PaUtilConverter* PaUtil_SelectConverter( PaSampleFormat sourceFormat,
- PaSampleFormat destinationFormat, PaStreamFlags flags );
-
-
-/** The generic buffer zeroer prototype. Buffer zeroers copy count zeros to
- destinationBuffer. The actual type of the data pointed to varys for
- different zeroer functions.
- @param destinationBuffer A pointer to the first sample of the destination.
- @param destinationStride An offset between successive destination samples
- expressed in samples (not bytes.) It may be negative.
- @param count The number of samples to zero.
-*/
-typedef void PaUtilZeroer(
- void *destinationBuffer, signed int destinationStride, unsigned int count );
-
-
-/** Find a buffer zeroer function for the given destination format.
- @return
- A pointer to a PaUtilZeroer which will perform the requested
- zeroing.
-*/
-PaUtilZeroer* PaUtil_SelectZeroer( PaSampleFormat destinationFormat );
-
-/*----------------------------------------------------------------------------*/
-/* low level functions and data structures which may be used for
- substituting conversion functions */
-
-
-/** The type used to store all sample conversion functions.
- @see paConverters;
-*/
-typedef struct{
- PaUtilConverter *Float32_To_Int32;
- PaUtilConverter *Float32_To_Int32_Dither;
- PaUtilConverter *Float32_To_Int32_Clip;
- PaUtilConverter *Float32_To_Int32_DitherClip;
-
- PaUtilConverter *Float32_To_Int24;
- PaUtilConverter *Float32_To_Int24_Dither;
- PaUtilConverter *Float32_To_Int24_Clip;
- PaUtilConverter *Float32_To_Int24_DitherClip;
-
- PaUtilConverter *Float32_To_Int16;
- PaUtilConverter *Float32_To_Int16_Dither;
- PaUtilConverter *Float32_To_Int16_Clip;
- PaUtilConverter *Float32_To_Int16_DitherClip;
-
- PaUtilConverter *Float32_To_Int8;
- PaUtilConverter *Float32_To_Int8_Dither;
- PaUtilConverter *Float32_To_Int8_Clip;
- PaUtilConverter *Float32_To_Int8_DitherClip;
-
- PaUtilConverter *Float32_To_UInt8;
- PaUtilConverter *Float32_To_UInt8_Dither;
- PaUtilConverter *Float32_To_UInt8_Clip;
- PaUtilConverter *Float32_To_UInt8_DitherClip;
-
- PaUtilConverter *Int32_To_Float32;
- PaUtilConverter *Int32_To_Int24;
- PaUtilConverter *Int32_To_Int24_Dither;
- PaUtilConverter *Int32_To_Int16;
- PaUtilConverter *Int32_To_Int16_Dither;
- PaUtilConverter *Int32_To_Int8;
- PaUtilConverter *Int32_To_Int8_Dither;
- PaUtilConverter *Int32_To_UInt8;
- PaUtilConverter *Int32_To_UInt8_Dither;
-
- PaUtilConverter *Int24_To_Float32;
- PaUtilConverter *Int24_To_Int32;
- PaUtilConverter *Int24_To_Int16;
- PaUtilConverter *Int24_To_Int16_Dither;
- PaUtilConverter *Int24_To_Int8;
- PaUtilConverter *Int24_To_Int8_Dither;
- PaUtilConverter *Int24_To_UInt8;
- PaUtilConverter *Int24_To_UInt8_Dither;
-
- PaUtilConverter *Int16_To_Float32;
- PaUtilConverter *Int16_To_Int32;
- PaUtilConverter *Int16_To_Int24;
- PaUtilConverter *Int16_To_Int8;
- PaUtilConverter *Int16_To_Int8_Dither;
- PaUtilConverter *Int16_To_UInt8;
- PaUtilConverter *Int16_To_UInt8_Dither;
-
- PaUtilConverter *Int8_To_Float32;
- PaUtilConverter *Int8_To_Int32;
- PaUtilConverter *Int8_To_Int24;
- PaUtilConverter *Int8_To_Int16;
- PaUtilConverter *Int8_To_UInt8;
-
- PaUtilConverter *UInt8_To_Float32;
- PaUtilConverter *UInt8_To_Int32;
- PaUtilConverter *UInt8_To_Int24;
- PaUtilConverter *UInt8_To_Int16;
- PaUtilConverter *UInt8_To_Int8;
-
- PaUtilConverter *Copy_8_To_8; /* copy without any conversion */
- PaUtilConverter *Copy_16_To_16; /* copy without any conversion */
- PaUtilConverter *Copy_24_To_24; /* copy without any conversion */
- PaUtilConverter *Copy_32_To_32; /* copy without any conversion */
-} PaUtilConverterTable;
-
-
-/** A table of pointers to all required converter functions.
- PaUtil_SelectConverter() uses this table to lookup the appropriate
- conversion functions. The fields of this structure are initialized
- with default conversion functions. Fields may be NULL, indicating that
- no conversion function is available. User code may substitue optimised
- conversion functions by assigning different function pointers to
- these fields.
-
- @note
- If the PA_NO_STANDARD_CONVERTERS preprocessor variable is defined,
- PortAudio's standard converters will not be compiled, and all fields
- of this structure will be initialized to NULL. In such cases, users
- should supply their own conversion functions if the require PortAudio
- to open a stream that requires sample conversion.
-
- @see PaUtilConverterTable, PaUtilConverter, PaUtil_SelectConverter
-*/
-extern PaUtilConverterTable paConverters;
-
-
-/** The type used to store all buffer zeroing functions.
- @see paZeroers;
-*/
-typedef struct{
- PaUtilZeroer *ZeroU8; /* unsigned 8 bit, zero == 128 */
- PaUtilZeroer *Zero8;
- PaUtilZeroer *Zero16;
- PaUtilZeroer *Zero24;
- PaUtilZeroer *Zero32;
-} PaUtilZeroerTable;
-
-
-/** A table of pointers to all required zeroer functions.
- PaUtil_SelectZeroer() uses this table to lookup the appropriate
- conversion functions. The fields of this structure are initialized
- with default conversion functions. User code may substitue optimised
- conversion functions by assigning different function pointers to
- these fields.
-
- @note
- If the PA_NO_STANDARD_ZEROERS preprocessor variable is defined,
- PortAudio's standard zeroers will not be compiled, and all fields
- of this structure will be initialized to NULL. In such cases, users
- should supply their own zeroing functions for the sample sizes which
- they intend to use.
-
- @see PaUtilZeroerTable, PaUtilZeroer, PaUtil_SelectZeroer
-*/
-extern PaUtilZeroerTable paZeroers;
-
-#ifdef __cplusplus
-}
-#endif /* __cplusplus */
-#endif /* PA_CONVERTERS_H */
+/*
+ * PJMEDIA - Multimedia over IP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef PA_CONVERTERS_H
+#define PA_CONVERTERS_H
+/*
+ * $Id: pa_converters.h,v 1.1.2.9 2003/09/20 21:05:14 rossbencina Exp $
+ * Portable Audio I/O Library sample conversion mechanism
+ *
+ * Based on the Open Source API proposed by Ross Bencina
+ * Copyright (c) 1999-2002 Phil Burk, Ross Bencina
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * Any person wishing to distribute modifications to the Software is
+ * requested to send the modifications to the original developer so that
+ * they can be incorporated into the canonical version.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+ * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+/** @file
+ @brief Conversion functions used to convert buffers of samples from one
+ format to another.
+*/
+
+
+#include "portaudio.h" /* for PaSampleFormat */
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif /* __cplusplus */
+
+
+struct PaUtilTriangularDitherGenerator;
+
+
+/** Choose an available sample format which is most appropriate for
+ representing the requested format. If the requested format is not available
+ higher quality formats are considered before lower quality formates.
+ @param availableFormats A variable containing the logical OR of all available
+ formats.
+ @param format The desired format.
+ @return The most appropriate available format for representing the requested
+ format.
+*/
+PaSampleFormat PaUtil_SelectClosestAvailableFormat(
+ PaSampleFormat availableFormats, PaSampleFormat format );
+
+
+/* high level conversions functions for use by implementations */
+
+
+/** The generic sample converter prototype. Sample converters convert count
+ samples from sourceBuffer to destinationBuffer. The actual type of the data
+ pointed to by these parameters varys for different converter functions.
+ @param destinationBuffer A pointer to the first sample of the destination.
+ @param destinationStride An offset between successive destination samples
+ expressed in samples (not bytes.) It may be negative.
+ @param sourceBuffer A pointer to the first sample of the source.
+ @param sourceStride An offset between successive source samples
+ expressed in samples (not bytes.) It may be negative.
+ @param count The number of samples to convert.
+ @param ditherState State information used to calculate dither. Converters
+ that do not perform dithering will ignore this parameter, in which case
+ NULL or invalid dither state may be passed.
+*/
+typedef void PaUtilConverter(
+ void *destinationBuffer, signed int destinationStride,
+ void *sourceBuffer, signed int sourceStride,
+ unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator );
+
+
+/** Find a sample converter function for the given source and destinations
+ formats and flags (clip and dither.)
+ @return
+ A pointer to a PaUtilConverter which will perform the requested
+ conversion, or NULL if the given format conversion is not supported.
+ For conversions where clipping or dithering is not necessary, the
+ clip and dither flags are ignored and a non-clipping or dithering
+ version is returned.
+ If the source and destination formats are the same, a function which
+ copies data of the appropriate size will be returned.
+*/
+PaUtilConverter* PaUtil_SelectConverter( PaSampleFormat sourceFormat,
+ PaSampleFormat destinationFormat, PaStreamFlags flags );
+
+
+/** The generic buffer zeroer prototype. Buffer zeroers copy count zeros to
+ destinationBuffer. The actual type of the data pointed to varys for
+ different zeroer functions.
+ @param destinationBuffer A pointer to the first sample of the destination.
+ @param destinationStride An offset between successive destination samples
+ expressed in samples (not bytes.) It may be negative.
+ @param count The number of samples to zero.
+*/
+typedef void PaUtilZeroer(
+ void *destinationBuffer, signed int destinationStride, unsigned int count );
+
+
+/** Find a buffer zeroer function for the given destination format.
+ @return
+ A pointer to a PaUtilZeroer which will perform the requested
+ zeroing.
+*/
+PaUtilZeroer* PaUtil_SelectZeroer( PaSampleFormat destinationFormat );
+
+/*----------------------------------------------------------------------------*/
+/* low level functions and data structures which may be used for
+ substituting conversion functions */
+
+
+/** The type used to store all sample conversion functions.
+ @see paConverters;
+*/
+typedef struct{
+ PaUtilConverter *Float32_To_Int32;
+ PaUtilConverter *Float32_To_Int32_Dither;
+ PaUtilConverter *Float32_To_Int32_Clip;
+ PaUtilConverter *Float32_To_Int32_DitherClip;
+
+ PaUtilConverter *Float32_To_Int24;
+ PaUtilConverter *Float32_To_Int24_Dither;
+ PaUtilConverter *Float32_To_Int24_Clip;
+ PaUtilConverter *Float32_To_Int24_DitherClip;
+
+ PaUtilConverter *Float32_To_Int16;
+ PaUtilConverter *Float32_To_Int16_Dither;
+ PaUtilConverter *Float32_To_Int16_Clip;
+ PaUtilConverter *Float32_To_Int16_DitherClip;
+
+ PaUtilConverter *Float32_To_Int8;
+ PaUtilConverter *Float32_To_Int8_Dither;
+ PaUtilConverter *Float32_To_Int8_Clip;
+ PaUtilConverter *Float32_To_Int8_DitherClip;
+
+ PaUtilConverter *Float32_To_UInt8;
+ PaUtilConverter *Float32_To_UInt8_Dither;
+ PaUtilConverter *Float32_To_UInt8_Clip;
+ PaUtilConverter *Float32_To_UInt8_DitherClip;
+
+ PaUtilConverter *Int32_To_Float32;
+ PaUtilConverter *Int32_To_Int24;
+ PaUtilConverter *Int32_To_Int24_Dither;
+ PaUtilConverter *Int32_To_Int16;
+ PaUtilConverter *Int32_To_Int16_Dither;
+ PaUtilConverter *Int32_To_Int8;
+ PaUtilConverter *Int32_To_Int8_Dither;
+ PaUtilConverter *Int32_To_UInt8;
+ PaUtilConverter *Int32_To_UInt8_Dither;
+
+ PaUtilConverter *Int24_To_Float32;
+ PaUtilConverter *Int24_To_Int32;
+ PaUtilConverter *Int24_To_Int16;
+ PaUtilConverter *Int24_To_Int16_Dither;
+ PaUtilConverter *Int24_To_Int8;
+ PaUtilConverter *Int24_To_Int8_Dither;
+ PaUtilConverter *Int24_To_UInt8;
+ PaUtilConverter *Int24_To_UInt8_Dither;
+
+ PaUtilConverter *Int16_To_Float32;
+ PaUtilConverter *Int16_To_Int32;
+ PaUtilConverter *Int16_To_Int24;
+ PaUtilConverter *Int16_To_Int8;
+ PaUtilConverter *Int16_To_Int8_Dither;
+ PaUtilConverter *Int16_To_UInt8;
+ PaUtilConverter *Int16_To_UInt8_Dither;
+
+ PaUtilConverter *Int8_To_Float32;
+ PaUtilConverter *Int8_To_Int32;
+ PaUtilConverter *Int8_To_Int24;
+ PaUtilConverter *Int8_To_Int16;
+ PaUtilConverter *Int8_To_UInt8;
+
+ PaUtilConverter *UInt8_To_Float32;
+ PaUtilConverter *UInt8_To_Int32;
+ PaUtilConverter *UInt8_To_Int24;
+ PaUtilConverter *UInt8_To_Int16;
+ PaUtilConverter *UInt8_To_Int8;
+
+ PaUtilConverter *Copy_8_To_8; /* copy without any conversion */
+ PaUtilConverter *Copy_16_To_16; /* copy without any conversion */
+ PaUtilConverter *Copy_24_To_24; /* copy without any conversion */
+ PaUtilConverter *Copy_32_To_32; /* copy without any conversion */
+} PaUtilConverterTable;
+
+
+/** A table of pointers to all required converter functions.
+ PaUtil_SelectConverter() uses this table to lookup the appropriate
+ conversion functions. The fields of this structure are initialized
+ with default conversion functions. Fields may be NULL, indicating that
+ no conversion function is available. User code may substitue optimised
+ conversion functions by assigning different function pointers to
+ these fields.
+
+ @note
+ If the PA_NO_STANDARD_CONVERTERS preprocessor variable is defined,
+ PortAudio's standard converters will not be compiled, and all fields
+ of this structure will be initialized to NULL. In such cases, users
+ should supply their own conversion functions if the require PortAudio
+ to open a stream that requires sample conversion.
+
+ @see PaUtilConverterTable, PaUtilConverter, PaUtil_SelectConverter
+*/
+extern PaUtilConverterTable paConverters;
+
+
+/** The type used to store all buffer zeroing functions.
+ @see paZeroers;
+*/
+typedef struct{
+ PaUtilZeroer *ZeroU8; /* unsigned 8 bit, zero == 128 */
+ PaUtilZeroer *Zero8;
+ PaUtilZeroer *Zero16;
+ PaUtilZeroer *Zero24;
+ PaUtilZeroer *Zero32;
+} PaUtilZeroerTable;
+
+
+/** A table of pointers to all required zeroer functions.
+ PaUtil_SelectZeroer() uses this table to lookup the appropriate
+ conversion functions. The fields of this structure are initialized
+ with default conversion functions. User code may substitue optimised
+ conversion functions by assigning different function pointers to
+ these fields.
+
+ @note
+ If the PA_NO_STANDARD_ZEROERS preprocessor variable is defined,
+ PortAudio's standard zeroers will not be compiled, and all fields
+ of this structure will be initialized to NULL. In such cases, users
+ should supply their own zeroing functions for the sample sizes which
+ they intend to use.
+
+ @see PaUtilZeroerTable, PaUtilZeroer, PaUtil_SelectZeroer
+*/
+extern PaUtilZeroerTable paZeroers;
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+#endif /* PA_CONVERTERS_H */
diff --git a/pjmedia/src/pjmedia/portaudio/pa_cpuload.c b/pjmedia/src/pjmedia/portaudio/pa_cpuload.c
index e70fbf4e..2def09df 100644
--- a/pjmedia/src/pjmedia/portaudio/pa_cpuload.c
+++ b/pjmedia/src/pjmedia/portaudio/pa_cpuload.c
@@ -1,96 +1,117 @@
-/*
- * $Id: pa_cpuload.c,v 1.1.2.14 2004/01/08 22:01:12 rossbencina Exp $
- * Portable Audio I/O Library CPU Load measurement functions
- * Portable CPU load measurement facility.
- *
- * Based on the Open Source API proposed by Ross Bencina
- * Copyright (c) 2002 Ross Bencina
- *
- * Permission is hereby granted, free of charge, to any person obtaining
- * a copy of this software and associated documentation files
- * (the "Software"), to deal in the Software without restriction,
- * including without limitation the rights to use, copy, modify, merge,
- * publish, distribute, sublicense, and/or sell copies of the Software,
- * and to permit persons to whom the Software is furnished to do so,
- * subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be
- * included in all copies or substantial portions of the Software.
- *
- * Any person wishing to distribute modifications to the Software is
- * requested to send the modifications to the original developer so that
- * they can be incorporated into the canonical version.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
- * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
- * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
- * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
- * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
- */
-
-/** @file
- @brief Functions to assist in measuring the CPU utilization of a callback
- stream. Used to implement the Pa_GetStreamCpuLoad() function.
-
- @todo Dynamically calculate the coefficients used to smooth the CPU Load
- Measurements over time to provide a uniform characterisation of CPU Load
- independent of rate at which PaUtil_BeginCpuLoadMeasurement /
- PaUtil_EndCpuLoadMeasurement are called.
-*/
-
-
-#include "pa_cpuload.h"
-
-#include <assert.h>
-
-#include "pa_util.h" /* for PaUtil_GetTime() */
-
-
-void PaUtil_InitializeCpuLoadMeasurer( PaUtilCpuLoadMeasurer* measurer, double sampleRate )
-{
- assert( sampleRate > 0 );
-
- measurer->samplingPeriod = 1. / sampleRate;
- measurer->averageLoad = 0.;
-}
-
-void PaUtil_ResetCpuLoadMeasurer( PaUtilCpuLoadMeasurer* measurer )
-{
- measurer->averageLoad = 0.;
-}
-
-void PaUtil_BeginCpuLoadMeasurement( PaUtilCpuLoadMeasurer* measurer )
-{
- measurer->measurementStartTime = PaUtil_GetTime();
-}
-
-
-void PaUtil_EndCpuLoadMeasurement( PaUtilCpuLoadMeasurer* measurer, unsigned long framesProcessed )
-{
- double measurementEndTime, secondsFor100Percent, measuredLoad;
-
- if( framesProcessed > 0 ){
- measurementEndTime = PaUtil_GetTime();
-
- assert( framesProcessed > 0 );
- secondsFor100Percent = framesProcessed * measurer->samplingPeriod;
-
- measuredLoad = (measurementEndTime - measurer->measurementStartTime) / secondsFor100Percent;
-
- /* Low pass filter the calculated CPU load to reduce jitter using a simple IIR low pass filter. */
- /** FIXME @todo these coefficients shouldn't be hardwired */
-#define LOWPASS_COEFFICIENT_0 (0.9)
-#define LOWPASS_COEFFICIENT_1 (0.99999 - LOWPASS_COEFFICIENT_0)
-
- measurer->averageLoad = (LOWPASS_COEFFICIENT_0 * measurer->averageLoad) +
- (LOWPASS_COEFFICIENT_1 * measuredLoad);
- }
-}
-
-
-double PaUtil_GetCpuLoad( PaUtilCpuLoadMeasurer* measurer )
-{
- return measurer->averageLoad;
-}
+/*
+ * $Id: pa_cpuload.c,v 1.1.2.14 2004/01/08 22:01:12 rossbencina Exp $
+ * Portable Audio I/O Library CPU Load measurement functions
+ * Portable CPU load measurement facility.
+ *
+ * Based on the Open Source API proposed by Ross Bencina
+ * Copyright (c) 2002 Ross Bencina
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * Any person wishing to distribute modifications to the Software is
+ * requested to send the modifications to the original developer so that
+ * they can be incorporated into the canonical version.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+ * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+/*
+ * PJMEDIA - Multimedia over IP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/** @file
+ @brief Functions to assist in measuring the CPU utilization of a callback
+ stream. Used to implement the Pa_GetStreamCpuLoad() function.
+
+ @todo Dynamically calculate the coefficients used to smooth the CPU Load
+ Measurements over time to provide a uniform characterisation of CPU Load
+ independent of rate at which PaUtil_BeginCpuLoadMeasurement /
+ PaUtil_EndCpuLoadMeasurement are called.
+*/
+
+
+#include "pa_cpuload.h"
+
+#include <assert.h>
+
+#include "pa_util.h" /* for PaUtil_GetTime() */
+
+
+void PaUtil_InitializeCpuLoadMeasurer( PaUtilCpuLoadMeasurer* measurer, double sampleRate )
+{
+ assert( sampleRate > 0 );
+
+ measurer->samplingPeriod = 1. / sampleRate;
+ measurer->averageLoad = 0.;
+}
+
+void PaUtil_ResetCpuLoadMeasurer( PaUtilCpuLoadMeasurer* measurer )
+{
+ measurer->averageLoad = 0.;
+}
+
+void PaUtil_BeginCpuLoadMeasurement( PaUtilCpuLoadMeasurer* measurer )
+{
+ measurer->measurementStartTime = PaUtil_GetTime();
+}
+
+
+void PaUtil_EndCpuLoadMeasurement( PaUtilCpuLoadMeasurer* measurer, unsigned long framesProcessed )
+{
+ double measurementEndTime, secondsFor100Percent, measuredLoad;
+
+ if( framesProcessed > 0 ){
+ measurementEndTime = PaUtil_GetTime();
+
+ assert( framesProcessed > 0 );
+ secondsFor100Percent = framesProcessed * measurer->samplingPeriod;
+
+ measuredLoad = (measurementEndTime - measurer->measurementStartTime) / secondsFor100Percent;
+
+ /* Low pass filter the calculated CPU load to reduce jitter using a simple IIR low pass filter. */
+ /** FIXME @todo these coefficients shouldn't be hardwired */
+#define LOWPASS_COEFFICIENT_0 (0.9)
+#define LOWPASS_COEFFICIENT_1 (0.99999 - LOWPASS_COEFFICIENT_0)
+
+ measurer->averageLoad = (LOWPASS_COEFFICIENT_0 * measurer->averageLoad) +
+ (LOWPASS_COEFFICIENT_1 * measuredLoad);
+ }
+}
+
+
+double PaUtil_GetCpuLoad( PaUtilCpuLoadMeasurer* measurer )
+{
+ return measurer->averageLoad;
+}
diff --git a/pjmedia/src/pjmedia/portaudio/pa_cpuload.h b/pjmedia/src/pjmedia/portaudio/pa_cpuload.h
index f77d9199..fd7988b6 100644
--- a/pjmedia/src/pjmedia/portaudio/pa_cpuload.h
+++ b/pjmedia/src/pjmedia/portaudio/pa_cpuload.h
@@ -1,63 +1,84 @@
-#ifndef PA_CPULOAD_H
-#define PA_CPULOAD_H
-/*
- * $Id: pa_cpuload.h,v 1.1.2.10 2004/01/08 22:01:12 rossbencina Exp $
- * Portable Audio I/O Library CPU Load measurement functions
- * Portable CPU load measurement facility.
- *
- * Based on the Open Source API proposed by Ross Bencina
- * Copyright (c) 2002 Ross Bencina
- *
- * Permission is hereby granted, free of charge, to any person obtaining
- * a copy of this software and associated documentation files
- * (the "Software"), to deal in the Software without restriction,
- * including without limitation the rights to use, copy, modify, merge,
- * publish, distribute, sublicense, and/or sell copies of the Software,
- * and to permit persons to whom the Software is furnished to do so,
- * subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be
- * included in all copies or substantial portions of the Software.
- *
- * Any person wishing to distribute modifications to the Software is
- * requested to send the modifications to the original developer so that
- * they can be incorporated into the canonical version.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
- * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
- * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
- * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
- * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
- */
-
-/** @file
- @brief Functions to assist in measuring the CPU utilization of a callback
- stream. Used to implement the Pa_GetStreamCpuLoad() function.
-*/
-
-
-#ifdef __cplusplus
-extern "C"
-{
-#endif /* __cplusplus */
-
-
-typedef struct {
- double samplingPeriod;
- double measurementStartTime;
- double averageLoad;
-} PaUtilCpuLoadMeasurer; /**< @todo need better name than measurer */
-
-void PaUtil_InitializeCpuLoadMeasurer( PaUtilCpuLoadMeasurer* measurer, double sampleRate );
-void PaUtil_BeginCpuLoadMeasurement( PaUtilCpuLoadMeasurer* measurer );
-void PaUtil_EndCpuLoadMeasurement( PaUtilCpuLoadMeasurer* measurer, unsigned long framesProcessed );
-void PaUtil_ResetCpuLoadMeasurer( PaUtilCpuLoadMeasurer* measurer );
-double PaUtil_GetCpuLoad( PaUtilCpuLoadMeasurer* measurer );
-
-
-#ifdef __cplusplus
-}
-#endif /* __cplusplus */
-#endif /* PA_CPULOAD_H */
+/*
+ * PJMEDIA - Multimedia over IP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef PA_CPULOAD_H
+#define PA_CPULOAD_H
+/*
+ * $Id: pa_cpuload.h,v 1.1.2.10 2004/01/08 22:01:12 rossbencina Exp $
+ * Portable Audio I/O Library CPU Load measurement functions
+ * Portable CPU load measurement facility.
+ *
+ * Based on the Open Source API proposed by Ross Bencina
+ * Copyright (c) 2002 Ross Bencina
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * Any person wishing to distribute modifications to the Software is
+ * requested to send the modifications to the original developer so that
+ * they can be incorporated into the canonical version.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+ * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+/** @file
+ @brief Functions to assist in measuring the CPU utilization of a callback
+ stream. Used to implement the Pa_GetStreamCpuLoad() function.
+*/
+
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif /* __cplusplus */
+
+
+typedef struct {
+ double samplingPeriod;
+ double measurementStartTime;
+ double averageLoad;
+} PaUtilCpuLoadMeasurer; /**< @todo need better name than measurer */
+
+void PaUtil_InitializeCpuLoadMeasurer( PaUtilCpuLoadMeasurer* measurer, double sampleRate );
+void PaUtil_BeginCpuLoadMeasurement( PaUtilCpuLoadMeasurer* measurer );
+void PaUtil_EndCpuLoadMeasurement( PaUtilCpuLoadMeasurer* measurer, unsigned long framesProcessed );
+void PaUtil_ResetCpuLoadMeasurer( PaUtilCpuLoadMeasurer* measurer );
+double PaUtil_GetCpuLoad( PaUtilCpuLoadMeasurer* measurer );
+
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+#endif /* PA_CPULOAD_H */
diff --git a/pjmedia/src/pjmedia/portaudio/pa_dither.c b/pjmedia/src/pjmedia/portaudio/pa_dither.c
index 0600db62..0c20a957 100644
--- a/pjmedia/src/pjmedia/portaudio/pa_dither.c
+++ b/pjmedia/src/pjmedia/portaudio/pa_dither.c
@@ -1,204 +1,225 @@
-/*
- * $Id: pa_dither.c,v 1.1.2.6 2005/05/28 22:49:02 rossbencina Exp $
- * Portable Audio I/O Library triangular dither generator
- *
- * Based on the Open Source API proposed by Ross Bencina
- * Copyright (c) 1999-2002 Phil Burk, Ross Bencina
- *
- * Permission is hereby granted, free of charge, to any person obtaining
- * a copy of this software and associated documentation files
- * (the "Software"), to deal in the Software without restriction,
- * including without limitation the rights to use, copy, modify, merge,
- * publish, distribute, sublicense, and/or sell copies of the Software,
- * and to permit persons to whom the Software is furnished to do so,
- * subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be
- * included in all copies or substantial portions of the Software.
- *
- * Any person wishing to distribute modifications to the Software is
- * requested to send the modifications to the original developer so that
- * they can be incorporated into the canonical version.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
- * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
- * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
- * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
- * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
- */
-
-/** @file
- @brief Functions for generating dither noise
-*/
-
-
-#include "pa_dither.h"
-#include "pa_types.h"
-
-#define PA_DITHER_BITS_ (15)
-
-
-void PaUtil_InitializeTriangularDitherState( PaUtilTriangularDitherGenerator *state )
-{
- state->previous = 0;
- state->randSeed1 = 22222;
- state->randSeed2 = 5555555;
-}
-
-
-signed long PaUtil_Generate16BitTriangularDither( PaUtilTriangularDitherGenerator *state )
-{
- signed long current, highPass;
-
- /* Generate two random numbers. */
- state->randSeed1 = (state->randSeed1 * 196314165) + 907633515;
- state->randSeed2 = (state->randSeed2 * 196314165) + 907633515;
-
- /* Generate triangular distribution about 0.
- * Shift before adding to prevent overflow which would skew the distribution.
- * Also shift an extra bit for the high pass filter.
- */
-#define DITHER_SHIFT_ ((SIZEOF_LONG*8 - PA_DITHER_BITS_) + 1)
- current = (((signed long)state->randSeed1)>>DITHER_SHIFT_) +
- (((signed long)state->randSeed2)>>DITHER_SHIFT_);
-
- /* High pass filter to reduce audibility. */
- highPass = current - state->previous;
- state->previous = current;
- return highPass;
-}
-
-
-/* Multiply by PA_FLOAT_DITHER_SCALE_ to get a float between -2.0 and +1.99999 */
-#define PA_FLOAT_DITHER_SCALE_ (1.0f / ((1<<PA_DITHER_BITS_)-1))
-static const float const_float_dither_scale_ = PA_FLOAT_DITHER_SCALE_;
-
-float PaUtil_GenerateFloatTriangularDither( PaUtilTriangularDitherGenerator *state )
-{
- signed long current, highPass;
-
- /* Generate two random numbers. */
- state->randSeed1 = (state->randSeed1 * 196314165) + 907633515;
- state->randSeed2 = (state->randSeed2 * 196314165) + 907633515;
-
- /* Generate triangular distribution about 0.
- * Shift before adding to prevent overflow which would skew the distribution.
- * Also shift an extra bit for the high pass filter.
- */
-#define DITHER_SHIFT_ ((SIZEOF_LONG*8 - PA_DITHER_BITS_) + 1)
- current = (((signed long)state->randSeed1)>>DITHER_SHIFT_) +
- (((signed long)state->randSeed2)>>DITHER_SHIFT_);
-
- /* High pass filter to reduce audibility. */
- highPass = current - state->previous;
- state->previous = current;
- return ((float)highPass) * const_float_dither_scale_;
-}
-
-
-/*
-The following alternate dither algorithms (from musicdsp.org) could be
-considered
-*/
-
-/*Noise shaped dither (March 2000)
--------------------
-
-This is a simple implementation of highpass triangular-PDF dither with
-2nd-order noise shaping, for use when truncating floating point audio
-data to fixed point.
-
-The noise shaping lowers the noise floor by 11dB below 5kHz (@ 44100Hz
-sample rate) compared to triangular-PDF dither. The code below assumes
-input data is in the range +1 to -1 and doesn't check for overloads!
-
-To save time when generating dither for multiple channels you can do
-things like this: r3=(r1 & 0x7F)<<8; instead of calling rand() again.
-
-
-
- int r1, r2; //rectangular-PDF random numbers
- float s1, s2; //error feedback buffers
- float s = 0.5f; //set to 0.0f for no noise shaping
- float w = pow(2.0,bits-1); //word length (usually bits=16)
- float wi= 1.0f/w;
- float d = wi / RAND_MAX; //dither amplitude (2 lsb)
- float o = wi * 0.5f; //remove dc offset
- float in, tmp;
- int out;
-
-
-//for each sample...
-
- r2=r1; //can make HP-TRI dither by
- r1=rand(); //subtracting previous rand()
-
- in += s * (s1 + s1 - s2); //error feedback
- tmp = in + o + d * (float)(r1 - r2); //dc offset and dither
-
- out = (int)(w * tmp); //truncate downwards
- if(tmp<0.0f) out--; //this is faster than floor()
-
- s2 = s1;
- s1 = in - wi * (float)out; //error
-
-
-
---
-paul.kellett@maxim.abel.co.uk
-http://www.maxim.abel.co.uk
-*/
-
-
-/*
-16-to-8-bit first-order dither
-
-Type : First order error feedforward dithering code
-References : Posted by Jon Watte
-
-Notes :
-This is about as simple a dithering algorithm as you can implement, but it's
-likely to sound better than just truncating to N bits.
-
-Note that you might not want to carry forward the full difference for infinity.
-It's probably likely that the worst performance hit comes from the saturation
-conditionals, which can be avoided with appropriate instructions on many DSPs
-and integer SIMD type instructions, or CMOV.
-
-Last, if sound quality is paramount (such as when going from > 16 bits to 16
-bits) you probably want to use a higher-order dither function found elsewhere
-on this site.
-
-
-Code :
-// This code will down-convert and dither a 16-bit signed short
-// mono signal into an 8-bit unsigned char signal, using a first
-// order forward-feeding error term dither.
-
-#define uchar unsigned char
-
-void dither_one_channel_16_to_8( short * input, uchar * output, int count, int * memory )
-{
- int m = *memory;
- while( count-- > 0 ) {
- int i = *input++;
- i += m;
- int j = i + 32768 - 128;
- uchar o;
- if( j < 0 ) {
- o = 0;
- }
- else if( j > 65535 ) {
- o = 255;
- }
- else {
- o = (uchar)((j>>8)&0xff);
- }
- m = ((j-32768+128)-i);
- *output++ = o;
- }
- *memory = m;
-}
-*/
+/*
+ * $Id: pa_dither.c,v 1.1.2.6 2005/05/28 22:49:02 rossbencina Exp $
+ * Portable Audio I/O Library triangular dither generator
+ *
+ * Based on the Open Source API proposed by Ross Bencina
+ * Copyright (c) 1999-2002 Phil Burk, Ross Bencina
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * Any person wishing to distribute modifications to the Software is
+ * requested to send the modifications to the original developer so that
+ * they can be incorporated into the canonical version.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+ * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+/*
+ * PJMEDIA - Multimedia over IP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/** @file
+ @brief Functions for generating dither noise
+*/
+
+
+#include "pa_dither.h"
+#include "pa_types.h"
+
+#define PA_DITHER_BITS_ (15)
+
+
+void PaUtil_InitializeTriangularDitherState( PaUtilTriangularDitherGenerator *state )
+{
+ state->previous = 0;
+ state->randSeed1 = 22222;
+ state->randSeed2 = 5555555;
+}
+
+
+signed long PaUtil_Generate16BitTriangularDither( PaUtilTriangularDitherGenerator *state )
+{
+ signed long current, highPass;
+
+ /* Generate two random numbers. */
+ state->randSeed1 = (state->randSeed1 * 196314165) + 907633515;
+ state->randSeed2 = (state->randSeed2 * 196314165) + 907633515;
+
+ /* Generate triangular distribution about 0.
+ * Shift before adding to prevent overflow which would skew the distribution.
+ * Also shift an extra bit for the high pass filter.
+ */
+#define DITHER_SHIFT_ ((SIZEOF_LONG*8 - PA_DITHER_BITS_) + 1)
+ current = (((signed long)state->randSeed1)>>DITHER_SHIFT_) +
+ (((signed long)state->randSeed2)>>DITHER_SHIFT_);
+
+ /* High pass filter to reduce audibility. */
+ highPass = current - state->previous;
+ state->previous = current;
+ return highPass;
+}
+
+
+/* Multiply by PA_FLOAT_DITHER_SCALE_ to get a float between -2.0 and +1.99999 */
+#define PA_FLOAT_DITHER_SCALE_ (1.0f / ((1<<PA_DITHER_BITS_)-1))
+static const float const_float_dither_scale_ = PA_FLOAT_DITHER_SCALE_;
+
+float PaUtil_GenerateFloatTriangularDither( PaUtilTriangularDitherGenerator *state )
+{
+ signed long current, highPass;
+
+ /* Generate two random numbers. */
+ state->randSeed1 = (state->randSeed1 * 196314165) + 907633515;
+ state->randSeed2 = (state->randSeed2 * 196314165) + 907633515;
+
+ /* Generate triangular distribution about 0.
+ * Shift before adding to prevent overflow which would skew the distribution.
+ * Also shift an extra bit for the high pass filter.
+ */
+#define DITHER_SHIFT_ ((SIZEOF_LONG*8 - PA_DITHER_BITS_) + 1)
+ current = (((signed long)state->randSeed1)>>DITHER_SHIFT_) +
+ (((signed long)state->randSeed2)>>DITHER_SHIFT_);
+
+ /* High pass filter to reduce audibility. */
+ highPass = current - state->previous;
+ state->previous = current;
+ return ((float)highPass) * const_float_dither_scale_;
+}
+
+
+/*
+The following alternate dither algorithms (from musicdsp.org) could be
+considered
+*/
+
+/*Noise shaped dither (March 2000)
+-------------------
+
+This is a simple implementation of highpass triangular-PDF dither with
+2nd-order noise shaping, for use when truncating floating point audio
+data to fixed point.
+
+The noise shaping lowers the noise floor by 11dB below 5kHz (@ 44100Hz
+sample rate) compared to triangular-PDF dither. The code below assumes
+input data is in the range +1 to -1 and doesn't check for overloads!
+
+To save time when generating dither for multiple channels you can do
+things like this: r3=(r1 & 0x7F)<<8; instead of calling rand() again.
+
+
+
+ int r1, r2; //rectangular-PDF random numbers
+ float s1, s2; //error feedback buffers
+ float s = 0.5f; //set to 0.0f for no noise shaping
+ float w = pow(2.0,bits-1); //word length (usually bits=16)
+ float wi= 1.0f/w;
+ float d = wi / RAND_MAX; //dither amplitude (2 lsb)
+ float o = wi * 0.5f; //remove dc offset
+ float in, tmp;
+ int out;
+
+
+//for each sample...
+
+ r2=r1; //can make HP-TRI dither by
+ r1=rand(); //subtracting previous rand()
+
+ in += s * (s1 + s1 - s2); //error feedback
+ tmp = in + o + d * (float)(r1 - r2); //dc offset and dither
+
+ out = (int)(w * tmp); //truncate downwards
+ if(tmp<0.0f) out--; //this is faster than floor()
+
+ s2 = s1;
+ s1 = in - wi * (float)out; //error
+
+
+
+--
+paul.kellett@maxim.abel.co.uk
+http://www.maxim.abel.co.uk
+*/
+
+
+/*
+16-to-8-bit first-order dither
+
+Type : First order error feedforward dithering code
+References : Posted by Jon Watte
+
+Notes :
+This is about as simple a dithering algorithm as you can implement, but it's
+likely to sound better than just truncating to N bits.
+
+Note that you might not want to carry forward the full difference for infinity.
+It's probably likely that the worst performance hit comes from the saturation
+conditionals, which can be avoided with appropriate instructions on many DSPs
+and integer SIMD type instructions, or CMOV.
+
+Last, if sound quality is paramount (such as when going from > 16 bits to 16
+bits) you probably want to use a higher-order dither function found elsewhere
+on this site.
+
+
+Code :
+// This code will down-convert and dither a 16-bit signed short
+// mono signal into an 8-bit unsigned char signal, using a first
+// order forward-feeding error term dither.
+
+#define uchar unsigned char
+
+void dither_one_channel_16_to_8( short * input, uchar * output, int count, int * memory )
+{
+ int m = *memory;
+ while( count-- > 0 ) {
+ int i = *input++;
+ i += m;
+ int j = i + 32768 - 128;
+ uchar o;
+ if( j < 0 ) {
+ o = 0;
+ }
+ else if( j > 65535 ) {
+ o = 255;
+ }
+ else {
+ o = (uchar)((j>>8)&0xff);
+ }
+ m = ((j-32768+128)-i);
+ *output++ = o;
+ }
+ *memory = m;
+}
+*/
diff --git a/pjmedia/src/pjmedia/portaudio/pa_dither.h b/pjmedia/src/pjmedia/portaudio/pa_dither.h
index 70369e18..24bbe2a2 100644
--- a/pjmedia/src/pjmedia/portaudio/pa_dither.h
+++ b/pjmedia/src/pjmedia/portaudio/pa_dither.h
@@ -1,91 +1,112 @@
-#ifndef PA_DITHER_H
-#define PA_DITHER_H
-/*
- * $Id: pa_dither.h,v 1.1.2.4 2003/09/20 21:06:19 rossbencina Exp $
- * Portable Audio I/O Library triangular dither generator
- *
- * Based on the Open Source API proposed by Ross Bencina
- * Copyright (c) 1999-2002 Phil Burk, Ross Bencina
- *
- * Permission is hereby granted, free of charge, to any person obtaining
- * a copy of this software and associated documentation files
- * (the "Software"), to deal in the Software without restriction,
- * including without limitation the rights to use, copy, modify, merge,
- * publish, distribute, sublicense, and/or sell copies of the Software,
- * and to permit persons to whom the Software is furnished to do so,
- * subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be
- * included in all copies or substantial portions of the Software.
- *
- * Any person wishing to distribute modifications to the Software is
- * requested to send the modifications to the original developer so that
- * they can be incorporated into the canonical version.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
- * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
- * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
- * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
- * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
- */
-
-/** @file
- @brief Functions for generating dither noise
-*/
-
-
-#ifdef __cplusplus
-extern "C"
-{
-#endif /* __cplusplus */
-
-
-/** @brief State needed to generate a dither signal */
-typedef struct PaUtilTriangularDitherGenerator{
- unsigned long previous;
- unsigned long randSeed1;
- unsigned long randSeed2;
-} PaUtilTriangularDitherGenerator;
-
-
-/** @brief Initialize dither state */
-void PaUtil_InitializeTriangularDitherState( PaUtilTriangularDitherGenerator *ditherState );
-
-
-/**
- @brief Calculate 2 LSB dither signal with a triangular distribution.
- Ranged for adding to a 1 bit right-shifted 32 bit integer
- prior to >>15. eg:
-<pre>
- signed long in = *
- signed long dither = PaUtil_Generate16BitTriangularDither( ditherState );
- signed short out = (signed short)(((in>>1) + dither) >> 15);
-</pre>
- @return
- A signed long with a range of +32767 to -32768
-*/
-signed long PaUtil_Generate16BitTriangularDither( PaUtilTriangularDitherGenerator *ditherState );
-
-
-/**
- @brief Calculate 2 LSB dither signal with a triangular distribution.
- Ranged for adding to a pre-scaled float.
-<pre>
- float in = *
- float dither = PaUtil_GenerateFloatTriangularDither( ditherState );
- // use smaller scaler to prevent overflow when we add the dither
- signed short out = (signed short)(in*(32766.0f) + dither );
-</pre>
- @return
- A float with a range of -2.0 to +1.99999.
-*/
-float PaUtil_GenerateFloatTriangularDither( PaUtilTriangularDitherGenerator *ditherState );
-
-
-
-#ifdef __cplusplus
-}
-#endif /* __cplusplus */
-#endif /* PA_DITHER_H */
+/*
+ * PJMEDIA - Multimedia over IP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef PA_DITHER_H
+#define PA_DITHER_H
+/*
+ * $Id: pa_dither.h,v 1.1.2.4 2003/09/20 21:06:19 rossbencina Exp $
+ * Portable Audio I/O Library triangular dither generator
+ *
+ * Based on the Open Source API proposed by Ross Bencina
+ * Copyright (c) 1999-2002 Phil Burk, Ross Bencina
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * Any person wishing to distribute modifications to the Software is
+ * requested to send the modifications to the original developer so that
+ * they can be incorporated into the canonical version.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+ * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+/** @file
+ @brief Functions for generating dither noise
+*/
+
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif /* __cplusplus */
+
+
+/** @brief State needed to generate a dither signal */
+typedef struct PaUtilTriangularDitherGenerator{
+ unsigned long previous;
+ unsigned long randSeed1;
+ unsigned long randSeed2;
+} PaUtilTriangularDitherGenerator;
+
+
+/** @brief Initialize dither state */
+void PaUtil_InitializeTriangularDitherState( PaUtilTriangularDitherGenerator *ditherState );
+
+
+/**
+ @brief Calculate 2 LSB dither signal with a triangular distribution.
+ Ranged for adding to a 1 bit right-shifted 32 bit integer
+ prior to >>15. eg:
+<pre>
+ signed long in = *
+ signed long dither = PaUtil_Generate16BitTriangularDither( ditherState );
+ signed short out = (signed short)(((in>>1) + dither) >> 15);
+</pre>
+ @return
+ A signed long with a range of +32767 to -32768
+*/
+signed long PaUtil_Generate16BitTriangularDither( PaUtilTriangularDitherGenerator *ditherState );
+
+
+/**
+ @brief Calculate 2 LSB dither signal with a triangular distribution.
+ Ranged for adding to a pre-scaled float.
+<pre>
+ float in = *
+ float dither = PaUtil_GenerateFloatTriangularDither( ditherState );
+ // use smaller scaler to prevent overflow when we add the dither
+ signed short out = (signed short)(in*(32766.0f) + dither );
+</pre>
+ @return
+ A float with a range of -2.0 to +1.99999.
+*/
+float PaUtil_GenerateFloatTriangularDither( PaUtilTriangularDitherGenerator *ditherState );
+
+
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+#endif /* PA_DITHER_H */
diff --git a/pjmedia/src/pjmedia/portaudio/pa_endianness.h b/pjmedia/src/pjmedia/portaudio/pa_endianness.h
index 052bced6..2b546c35 100644
--- a/pjmedia/src/pjmedia/portaudio/pa_endianness.h
+++ b/pjmedia/src/pjmedia/portaudio/pa_endianness.h
@@ -1,111 +1,132 @@
-#ifndef PA_ENDIANNESS_H
-#define PA_ENDIANNESS_H
-/*
- * $Id: pa_endianness.h,v 1.1.2.3 2003/09/20 21:06:19 rossbencina Exp $
- * Portable Audio I/O Library current platform endianness macros
- *
- * Based on the Open Source API proposed by Ross Bencina
- * Copyright (c) 1999-2002 Phil Burk, Ross Bencina
- *
- * Permission is hereby granted, free of charge, to any person obtaining
- * a copy of this software and associated documentation files
- * (the "Software"), to deal in the Software without restriction,
- * including without limitation the rights to use, copy, modify, merge,
- * publish, distribute, sublicense, and/or sell copies of the Software,
- * and to permit persons to whom the Software is furnished to do so,
- * subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be
- * included in all copies or substantial portions of the Software.
- *
- * Any person wishing to distribute modifications to the Software is
- * requested to send the modifications to the original developer so that
- * they can be incorporated into the canonical version.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
- * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
- * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
- * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
- * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
- */
-
-/** @file
- @brief Configure endianness symbols for the target processor.
-
- Arrange for either the PA_LITTLE_ENDIAN or PA_BIG_ENDIAN preprocessor symbols
- to be defined. The one that is defined reflects the endianness of the target
- platform and may be used to implement conditional compilation of byte-order
- dependent code.
-
- If either PA_LITTLE_ENDIAN or PA_BIG_ENDIAN is defined already, then no attempt
- is made to override that setting. This may be useful if you have a better way
- of determining the platform's endianness. The autoconf mechanism uses this for
- example.
-
- A PA_VALIDATE_ENDIANNESS macro is provided to compare the compile time
- and runtime endiannes and raise an assertion if they don't match.
-*/
-
-
-#ifdef __cplusplus
-extern "C"
-{
-#endif /* __cplusplus */
-
-
-#if defined(PA_LITTLE_ENDIAN) || defined(PA_BIG_ENDIAN)
- /* endianness define has been set externally, such as by autoconf */
-
- #if defined(PA_LITTLE_ENDIAN) && defined(PA_BIG_ENDIAN)
- #error both PA_LITTLE_ENDIAN and PA_BIG_ENDIAN have been defined externally to pa_endianness.h - only one endianness at a time please
- #endif
-
-#else
- /* endianness define has not been set externally */
-
- /* set PA_LITTLE_ENDIAN or PA_BIG_ENDIAN by testing well known platform specific defines */
-
- #if defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__NT__)
-
- #define PA_LITTLE_ENDIAN /* win32, assume intel byte order */
-
- #else
-
-#endif
-
- #if !defined(PA_LITTLE_ENDIAN) && !defined(PA_BIG_ENDIAN)
- /*
- If the following error is raised, you either need to modify the code above
- to automatically determine the endianness from other symbols defined on your
- platform, or define either PA_LITTLE_ENDIAN or PA_BIG_ENDIAN externally.
- */
- #error pa_endianness.h was unable to automatically determine the endianness of the target platform
- #endif
-
-#endif
-
-/* PA_VALIDATE_ENDIANNESS compares the compile time and runtime endianness,
- and raises an assertion if they don't match. <assert.h> must be included in
- the context in which this macro is used.
-*/
-#if defined(PA_LITTLE_ENDIAN)
- #define PA_VALIDATE_ENDIANNESS \
- { \
- const long nativeOne = 1; \
- assert( "PortAudio: compile time and runtime endianness don't match" && (((char *)&nativeOne)[0]) == 1 ); \
- }
-#elif defined(PA_BIG_ENDIAN)
- #define PA_VALIDATE_ENDIANNESS \
- { \
- const long nativeOne = 1; \
- assert( "PortAudio: compile time and runtime endianness don't match" && (((char *)&nativeOne)[0]) == 0 ); \
- }
-#endif
-
-
-#ifdef __cplusplus
-}
-#endif /* __cplusplus */
-#endif /* PA_ENDIANNESS_H */
+/*
+ * PJMEDIA - Multimedia over IP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef PA_ENDIANNESS_H
+#define PA_ENDIANNESS_H
+/*
+ * $Id: pa_endianness.h,v 1.1.2.3 2003/09/20 21:06:19 rossbencina Exp $
+ * Portable Audio I/O Library current platform endianness macros
+ *
+ * Based on the Open Source API proposed by Ross Bencina
+ * Copyright (c) 1999-2002 Phil Burk, Ross Bencina
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * Any person wishing to distribute modifications to the Software is
+ * requested to send the modifications to the original developer so that
+ * they can be incorporated into the canonical version.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+ * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+/** @file
+ @brief Configure endianness symbols for the target processor.
+
+ Arrange for either the PA_LITTLE_ENDIAN or PA_BIG_ENDIAN preprocessor symbols
+ to be defined. The one that is defined reflects the endianness of the target
+ platform and may be used to implement conditional compilation of byte-order
+ dependent code.
+
+ If either PA_LITTLE_ENDIAN or PA_BIG_ENDIAN is defined already, then no attempt
+ is made to override that setting. This may be useful if you have a better way
+ of determining the platform's endianness. The autoconf mechanism uses this for
+ example.
+
+ A PA_VALIDATE_ENDIANNESS macro is provided to compare the compile time
+ and runtime endiannes and raise an assertion if they don't match.
+*/
+
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif /* __cplusplus */
+
+
+#if defined(PA_LITTLE_ENDIAN) || defined(PA_BIG_ENDIAN)
+ /* endianness define has been set externally, such as by autoconf */
+
+ #if defined(PA_LITTLE_ENDIAN) && defined(PA_BIG_ENDIAN)
+ #error both PA_LITTLE_ENDIAN and PA_BIG_ENDIAN have been defined externally to pa_endianness.h - only one endianness at a time please
+ #endif
+
+#else
+ /* endianness define has not been set externally */
+
+ /* set PA_LITTLE_ENDIAN or PA_BIG_ENDIAN by testing well known platform specific defines */
+
+ #if defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__NT__)
+
+ #define PA_LITTLE_ENDIAN /* win32, assume intel byte order */
+
+ #else
+
+#endif
+
+ #if !defined(PA_LITTLE_ENDIAN) && !defined(PA_BIG_ENDIAN)
+ /*
+ If the following error is raised, you either need to modify the code above
+ to automatically determine the endianness from other symbols defined on your
+ platform, or define either PA_LITTLE_ENDIAN or PA_BIG_ENDIAN externally.
+ */
+ #error pa_endianness.h was unable to automatically determine the endianness of the target platform
+ #endif
+
+#endif
+
+/* PA_VALIDATE_ENDIANNESS compares the compile time and runtime endianness,
+ and raises an assertion if they don't match. <assert.h> must be included in
+ the context in which this macro is used.
+*/
+#if defined(PA_LITTLE_ENDIAN)
+ #define PA_VALIDATE_ENDIANNESS \
+ { \
+ const long nativeOne = 1; \
+ assert( "PortAudio: compile time and runtime endianness don't match" && (((char *)&nativeOne)[0]) == 1 ); \
+ }
+#elif defined(PA_BIG_ENDIAN)
+ #define PA_VALIDATE_ENDIANNESS \
+ { \
+ const long nativeOne = 1; \
+ assert( "PortAudio: compile time and runtime endianness don't match" && (((char *)&nativeOne)[0]) == 0 ); \
+ }
+#endif
+
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+#endif /* PA_ENDIANNESS_H */
diff --git a/pjmedia/src/pjmedia/portaudio/pa_front.c b/pjmedia/src/pjmedia/portaudio/pa_front.c
index 3a267bbc..17905825 100644
--- a/pjmedia/src/pjmedia/portaudio/pa_front.c
+++ b/pjmedia/src/pjmedia/portaudio/pa_front.c
@@ -1,1976 +1,1997 @@
-/*
- * $Id: pa_front.c,v 1.1.2.51 2005/02/05 15:52:12 rossbencina Exp $
- * Portable Audio I/O Library Multi-Host API front end
- * Validate function parameters and manage multiple host APIs.
- *
- * Based on the Open Source API proposed by Ross Bencina
- * Copyright (c) 1999-2002 Ross Bencina, Phil Burk
- *
- * Permission is hereby granted, free of charge, to any person obtaining
- * a copy of this software and associated documentation files
- * (the "Software"), to deal in the Software without restriction,
- * including without limitation the rights to use, copy, modify, merge,
- * publish, distribute, sublicense, and/or sell copies of the Software,
- * and to permit persons to whom the Software is furnished to do so,
- * subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be
- * included in all copies or substantial portions of the Software.
- *
- * Any person wishing to distribute modifications to the Software is
- * requested to send the modifications to the original developer so that
- * they can be incorporated into the canonical version.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
- * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
- * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
- * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
- * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
- */
-
-/* doxygen index page */
-/** @mainpage
-
-PortAudio is an open-source cross-platform †CË library for audio input
-and output. It is designed to simplify the porting of audio applications
-between various platforms, and also to simplify the development of audio
-software in general by hiding the complexities of device interfacing.
-
-See the PortAudio website for further information http://www.portaudio.com/
-
-This documentation pertains to PortAudio V19, API version 2.0 which is
-currently under development. API version 2.0 differs in a number of ways from
-previous versions, please consult the enhancement proposals for further details:
-http://www.portaudio.com/docs/proposals/index.html
-
-This documentation is under construction. Things you might be interested in
-include:
-
-- The PortAudio API 2.0, as documented in portaudio.h
-
-- The <a href="todo.html">TODO List</a>
-
-Feel free to pick an item off TODO list and fix/implement it. You may want to
-enquire about status on the PortAudio mailing list first.
-*/
-
-
-/** @file
- @brief Implements public PortAudio API, checks some errors, forwards to
- host API implementations.
-
- Implements the functions defined in the PortAudio API, checks for
- some parameter and state inconsistencies and forwards API requests to
- specific Host API implementations (via the interface declared in
- pa_hostapi.h), and Streams (via the interface declared in pa_stream.h).
-
- This file handles initialization and termination of Host API
- implementations via initializers stored in the paHostApiInitializers
- global variable.
-
- Some utility functions declared in pa_util.h are implemented in this file.
-
- All PortAudio API functions can be conditionally compiled with logging code.
- To compile with logging, define the PA_LOG_API_CALLS precompiler symbol.
-
- @todo Consider adding host API specific error text in Pa_GetErrorText() for
- paUnanticipatedHostError
-
- @todo Consider adding a new error code for when (inputParameters == NULL)
- && (outputParameters == NULL)
-
- @todo review whether Pa_CloseStream() should call the interface's
- CloseStream function if aborting the stream returns an error code.
-
- @todo Create new error codes if a NULL buffer pointer, or a
- zero frame count is passed to Pa_ReadStream or Pa_WriteStream.
-*/
-
-
-#include <stdio.h>
-#include <stdarg.h>
-#include <memory.h>
-#include <string.h>
-#include <assert.h> /* needed by PA_VALIDATE_ENDIANNESS */
-
-#include "portaudio.h"
-#include "pa_util.h"
-#include "pa_endianness.h"
-#include "pa_types.h"
-#include "pa_hostapi.h"
-#include "pa_stream.h"
-
-#include "pa_trace.h"
-
-
-#define PA_VERSION_ 1899
-#define PA_VERSION_TEXT_ "PortAudio V19-devel"
-
-
-
-/* #define PA_LOG_API_CALLS */
-
-/*
- The basic format for log messages is described below. If you need to
- add any log messages, please follow this format.
-
- Function entry (void function):
-
- "FunctionName called.\n"
-
- Function entry (non void function):
-
- "FunctionName called:\n"
- "\tParam1Type param1: param1Value\n"
- "\tParam2Type param2: param2Value\n" (etc...)
-
-
- Function exit (no return value):
-
- "FunctionName returned.\n"
-
- Function exit (simple return value):
-
- "FunctionName returned:\n"
- "\tReturnType: returnValue\n\n"
-
- If the return type is an error code, the error text is displayed in ()
-
- If the return type is not an error code, but has taken a special value
- because an error occurred, then the reason for the error is shown in []
-
- If the return type is a struct ptr, the struct is dumped.
-
- See the code below for examples
-*/
-
-
-int Pa_GetVersion( void )
-{
- return PA_VERSION_;
-}
-
-
-const char* Pa_GetVersionText( void )
-{
- return PA_VERSION_TEXT_;
-}
-
-
-
-#define PA_LAST_HOST_ERROR_TEXT_LENGTH_ 1024
-
-static char lastHostErrorText_[ PA_LAST_HOST_ERROR_TEXT_LENGTH_ + 1 ] = {0};
-
-static PaHostErrorInfo lastHostErrorInfo_ = { (PaHostApiTypeId)-1, 0, lastHostErrorText_ };
-
-
-void PaUtil_SetLastHostErrorInfo( PaHostApiTypeId hostApiType, long errorCode,
- const char *errorText )
-{
- lastHostErrorInfo_.hostApiType = hostApiType;
- lastHostErrorInfo_.errorCode = errorCode;
-
- strncpy( lastHostErrorText_, errorText, PA_LAST_HOST_ERROR_TEXT_LENGTH_ );
-}
-
-
-void PaUtil_DebugPrint( const char *format, ... )
-{
- va_list ap;
-
- va_start( ap, format );
- vfprintf( stderr, format, ap );
- va_end( ap );
-
- fflush( stderr );
-}
-
-
-static PaUtilHostApiRepresentation **hostApis_ = 0;
-static int hostApisCount_ = 0;
-static int initializationCount_ = 0;
-static int deviceCount_ = 0;
-
-PaUtilStreamRepresentation *firstOpenStream_ = NULL;
-
-
-#define PA_IS_INITIALISED_ (initializationCount_ != 0)
-
-
-static int CountHostApiInitializers( void )
-{
- int result = 0;
-
- while( paHostApiInitializers[ result ] != 0 )
- ++result;
- return result;
-}
-
-
-static void TerminateHostApis( void )
-{
- /* terminate in reverse order from initialization */
-
- while( hostApisCount_ > 0 )
- {
- --hostApisCount_;
- hostApis_[hostApisCount_]->Terminate( hostApis_[hostApisCount_] );
- }
- hostApisCount_ = 0;
- deviceCount_ = 0;
-
- if( hostApis_ != 0 )
- PaUtil_FreeMemory( hostApis_ );
- hostApis_ = 0;
-}
-
-
-static PaError InitializeHostApis( void )
-{
- PaError result = paNoError;
- int i, initializerCount, baseDeviceIndex;
-
- initializerCount = CountHostApiInitializers();
-
- hostApis_ = (PaUtilHostApiRepresentation**)PaUtil_AllocateMemory(
- sizeof(PaUtilHostApiRepresentation*) * initializerCount );
- if( !hostApis_ )
- {
- result = paInsufficientMemory;
- goto error;
- }
-
- hostApisCount_ = 0;
- deviceCount_ = 0;
- baseDeviceIndex = 0;
-
- for( i=0; i< initializerCount; ++i )
- {
- hostApis_[hostApisCount_] = NULL;
- result = paHostApiInitializers[i]( &hostApis_[hostApisCount_], hostApisCount_ );
- if( result != paNoError )
- goto error;
-
- if( hostApis_[hostApisCount_] )
- {
-
- hostApis_[hostApisCount_]->privatePaFrontInfo.baseDeviceIndex = baseDeviceIndex;
-
- if( hostApis_[hostApisCount_]->info.defaultInputDevice != paNoDevice )
- hostApis_[hostApisCount_]->info.defaultInputDevice += baseDeviceIndex;
-
- if( hostApis_[hostApisCount_]->info.defaultOutputDevice != paNoDevice )
- hostApis_[hostApisCount_]->info.defaultOutputDevice += baseDeviceIndex;
-
- baseDeviceIndex += hostApis_[hostApisCount_]->info.deviceCount;
- deviceCount_ += hostApis_[hostApisCount_]->info.deviceCount;
-
- ++hostApisCount_;
- }
- }
-
- return result;
-
-error:
- TerminateHostApis();
- return result;
-}
-
-
-/*
- FindHostApi() finds the index of the host api to which
- <device> belongs and returns it. if <hostSpecificDeviceIndex> is
- non-null, the host specific device index is returned in it.
- returns -1 if <device> is out of range.
-
-*/
-static int FindHostApi( PaDeviceIndex device, int *hostSpecificDeviceIndex )
-{
- int i=0;
-
- if( !PA_IS_INITIALISED_ )
- return -1;
-
- if( device < 0 )
- return -1;
-
- while( i < hostApisCount_
- && device >= hostApis_[i]->info.deviceCount )
- {
-
- device -= hostApis_[i]->info.deviceCount;
- ++i;
- }
-
- if( i >= hostApisCount_ )
- return -1;
-
- if( hostSpecificDeviceIndex )
- *hostSpecificDeviceIndex = device;
-
- return i;
-}
-
-
-static void AddOpenStream( PaStream* stream )
-{
- ((PaUtilStreamRepresentation*)stream)->nextOpenStream = firstOpenStream_;
- firstOpenStream_ = (PaUtilStreamRepresentation*)stream;
-}
-
-
-static void RemoveOpenStream( PaStream* stream )
-{
- PaUtilStreamRepresentation *previous = NULL;
- PaUtilStreamRepresentation *current = firstOpenStream_;
-
- while( current != NULL )
- {
- if( ((PaStream*)current) == stream )
- {
- if( previous == NULL )
- {
- firstOpenStream_ = current->nextOpenStream;
- }
- else
- {
- previous->nextOpenStream = current->nextOpenStream;
- }
- return;
- }
- else
- {
- previous = current;
- current = current->nextOpenStream;
- }
- }
-}
-
-
-static void CloseOpenStreams( void )
-{
- /* we call Pa_CloseStream() here to ensure that the same destruction
- logic is used for automatically closed streams */
-
- while( firstOpenStream_ != NULL )
- Pa_CloseStream( firstOpenStream_ );
-}
-
-
-PaError Pa_Initialize( void )
-{
- PaError result;
-
-#ifdef PA_LOG_API_CALLS
- PaUtil_DebugPrint( "Pa_Initialize called.\n" );
-#endif
-
- if( PA_IS_INITIALISED_ )
- {
- ++initializationCount_;
- result = paNoError;
- }
- else
- {
- PA_VALIDATE_TYPE_SIZES;
- PA_VALIDATE_ENDIANNESS;
-
- PaUtil_InitializeClock();
- PaUtil_ResetTraceMessages();
-
- result = InitializeHostApis();
- if( result == paNoError )
- ++initializationCount_;
- }
-
-#ifdef PA_LOG_API_CALLS
- PaUtil_DebugPrint( "Pa_Initialize returned:\n" );
- PaUtil_DebugPrint( "\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) );
-#endif
-
- return result;
-}
-
-
-PaError Pa_Terminate( void )
-{
- PaError result;
-
-#ifdef PA_LOG_API_CALLS
- PaUtil_DebugPrint("Pa_Terminate called.\n" );
-#endif
-
- if( PA_IS_INITIALISED_ )
- {
- if( --initializationCount_ == 0 )
- {
- CloseOpenStreams();
-
- TerminateHostApis();
-
- PaUtil_DumpTraceMessages();
- }
- result = paNoError;
- }
- else
- {
- result= paNotInitialized;
- }
-
-#ifdef PA_LOG_API_CALLS
- PaUtil_DebugPrint("Pa_Terminate returned:\n" );
- PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) );
-#endif
-
- return result;
-}
-
-
-const PaHostErrorInfo* Pa_GetLastHostErrorInfo( void )
-{
- return &lastHostErrorInfo_;
-}
-
-
-const char *Pa_GetErrorText( PaError errorCode )
-{
- const char *result;
-
- switch( errorCode )
- {
- case paNoError: result = "Success"; break;
- case paNotInitialized: result = "PortAudio not initialized"; break;
- /** @todo could catenate the last host error text to result in the case of paUnanticipatedHostError */
- case paUnanticipatedHostError: result = "Unanticipated host error"; break;
- case paInvalidChannelCount: result = "Invalid number of channels"; break;
- case paInvalidSampleRate: result = "Invalid sample rate"; break;
- case paInvalidDevice: result = "Invalid device"; break;
- case paInvalidFlag: result = "Invalid flag"; break;
- case paSampleFormatNotSupported: result = "Sample format not supported"; break;
- case paBadIODeviceCombination: result = "Illegal combination of I/O devices"; break;
- case paInsufficientMemory: result = "Insufficient memory"; break;
- case paBufferTooBig: result = "Buffer too big"; break;
- case paBufferTooSmall: result = "Buffer too small"; break;
- case paNullCallback: result = "No callback routine specified"; break;
- case paBadStreamPtr: result = "Invalid stream pointer"; break;
- case paTimedOut: result = "Wait timed out"; break;
- case paInternalError: result = "Internal PortAudio error"; break;
- case paDeviceUnavailable: result = "Device unavailable"; break;
- case paIncompatibleHostApiSpecificStreamInfo: result = "Incompatible host API specific stream info"; break;
- case paStreamIsStopped: result = "Stream is stopped"; break;
- case paStreamIsNotStopped: result = "Stream is not stopped"; break;
- case paInputOverflowed: result = "Input overflowed"; break;
- case paOutputUnderflowed: result = "Output underflowed"; break;
- case paHostApiNotFound: result = "Host API not found"; break;
- case paInvalidHostApi: result = "Invalid host API"; break;
- case paCanNotReadFromACallbackStream: result = "Can't read from a callback stream"; break;
- case paCanNotWriteToACallbackStream: result = "Can't write to a callback stream"; break;
- case paCanNotReadFromAnOutputOnlyStream: result = "Can't read from an output only stream"; break;
- case paCanNotWriteToAnInputOnlyStream: result = "Can't write to an input only stream"; break;
- default: result = "Illegal error number"; break;
- }
- return result;
-}
-
-
-PaHostApiIndex Pa_HostApiTypeIdToHostApiIndex( PaHostApiTypeId type )
-{
- PaHostApiIndex result;
- int i;
-
-#ifdef PA_LOG_API_CALLS
- PaUtil_DebugPrint("Pa_HostApiTypeIdToHostApiIndex called:\n" );
- PaUtil_DebugPrint("\tPaHostApiTypeId type: %d\n", type );
-#endif
-
- if( !PA_IS_INITIALISED_ )
- {
- result = paNotInitialized;
- }
- else
- {
- result = paHostApiNotFound;
-
- for( i=0; i < hostApisCount_; ++i )
- {
- if( hostApis_[i]->info.type == type )
- {
- result = i;
- break;
- }
- }
- }
-
-#ifdef PA_LOG_API_CALLS
- PaUtil_DebugPrint("Pa_HostApiTypeIdToHostApiIndex returned:\n" );
- if( result < 0 )
- PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) );
- else
- PaUtil_DebugPrint("\tPaHostApiIndex: %d\n\n", result );
-#endif
-
- return result;
-}
-
-
-PaError PaUtil_GetHostApiRepresentation( struct PaUtilHostApiRepresentation **hostApi,
- PaHostApiTypeId type )
-{
- PaError result;
- int i;
-
- if( !PA_IS_INITIALISED_ )
- {
- result = paNotInitialized;
- }
- else
- {
- result = paHostApiNotFound;
-
- for( i=0; i < hostApisCount_; ++i )
- {
- if( hostApis_[i]->info.type == type )
- {
- *hostApi = hostApis_[i];
- result = paNoError;
- break;
- }
- }
- }
-
- return result;
-}
-
-
-PaError PaUtil_DeviceIndexToHostApiDeviceIndex(
- PaDeviceIndex *hostApiDevice, PaDeviceIndex device, struct PaUtilHostApiRepresentation *hostApi )
-{
- PaError result;
- PaDeviceIndex x;
-
- x = device - hostApi->privatePaFrontInfo.baseDeviceIndex;
-
- if( x < 0 || x >= hostApi->info.deviceCount )
- {
- result = paInvalidDevice;
- }
- else
- {
- *hostApiDevice = x;
- result = paNoError;
- }
-
- return result;
-}
-
-
-PaHostApiIndex Pa_GetHostApiCount( void )
-{
- int result;
-
-#ifdef PA_LOG_API_CALLS
- PaUtil_DebugPrint("Pa_GetHostApiCount called.\n" );
-#endif
-
- if( !PA_IS_INITIALISED_ )
- {
- result = paNotInitialized;
- }
- else
- {
- result = hostApisCount_;
- }
-
-#ifdef PA_LOG_API_CALLS
- PaUtil_DebugPrint("Pa_GetHostApiCount returned:\n" );
- if( result < 0 )
- PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) );
- else
- PaUtil_DebugPrint("\tPaHostApiIndex %d\n\n", result );
-#endif
-
- return result;
-}
-
-
-PaHostApiIndex Pa_GetDefaultHostApi( void )
-{
- int result;
-
-#ifdef PA_LOG_API_CALLS
- PaUtil_DebugPrint("Pa_GetDefaultHostApi called.\n" );
-#endif
-
- if( !PA_IS_INITIALISED_ )
- {
- result = paNotInitialized;
- }
- else
- {
- result = paDefaultHostApiIndex;
-
- /* internal consistency check: make sure that the default host api
- index is within range */
-
- if( result < 0 || result >= hostApisCount_ )
- {
- result = paInternalError;
- }
- }
-
-#ifdef PA_LOG_API_CALLS
- PaUtil_DebugPrint("Pa_GetDefaultHostApi returned:\n" );
- if( result < 0 )
- PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) );
- else
- PaUtil_DebugPrint("\tPaHostApiIndex %d\n\n", result );
-#endif
-
- return result;
-}
-
-
-const PaHostApiInfo* Pa_GetHostApiInfo( PaHostApiIndex hostApi )
-{
- PaHostApiInfo *info;
-
-#ifdef PA_LOG_API_CALLS
- PaUtil_DebugPrint("Pa_GetHostApiInfo called:\n" );
- PaUtil_DebugPrint("\tPaHostApiIndex hostApi: %d\n", hostApi );
-#endif
-
- if( !PA_IS_INITIALISED_ )
- {
- info = NULL;
-
-#ifdef PA_LOG_API_CALLS
- PaUtil_DebugPrint("Pa_GetHostApiInfo returned:\n" );
- PaUtil_DebugPrint("\tPaHostApiInfo*: NULL [ PortAudio not initialized ]\n\n" );
-#endif
-
- }
- else if( hostApi < 0 || hostApi >= hostApisCount_ )
- {
- info = NULL;
-
-#ifdef PA_LOG_API_CALLS
- PaUtil_DebugPrint("Pa_GetHostApiInfo returned:\n" );
- PaUtil_DebugPrint("\tPaHostApiInfo*: NULL [ hostApi out of range ]\n\n" );
-#endif
-
- }
- else
- {
- info = &hostApis_[hostApi]->info;
-
-#ifdef PA_LOG_API_CALLS
- PaUtil_DebugPrint("Pa_GetHostApiInfo returned:\n" );
- PaUtil_DebugPrint("\tPaHostApiInfo*: 0x%p\n", info );
- PaUtil_DebugPrint("\t{" );
- PaUtil_DebugPrint("\t\tint structVersion: %d\n", info->structVersion );
- PaUtil_DebugPrint("\t\tPaHostApiTypeId type: %d\n", info->type );
- PaUtil_DebugPrint("\t\tconst char *name: %s\n\n", info->name );
- PaUtil_DebugPrint("\t}\n\n" );
-#endif
-
- }
-
- return info;
-}
-
-
-PaDeviceIndex Pa_HostApiDeviceIndexToDeviceIndex( PaHostApiIndex hostApi, int hostApiDeviceIndex )
-{
- PaDeviceIndex result;
-
-#ifdef PA_LOG_API_CALLS
- PaUtil_DebugPrint("Pa_HostApiDeviceIndexToPaDeviceIndex called:\n" );
- PaUtil_DebugPrint("\tPaHostApiIndex hostApi: %d\n", hostApi );
- PaUtil_DebugPrint("\tint hostApiDeviceIndex: %d\n", hostApiDeviceIndex );
-#endif
-
- if( !PA_IS_INITIALISED_ )
- {
- result = paNotInitialized;
- }
- else
- {
- if( hostApi < 0 || hostApi >= hostApisCount_ )
- {
- result = paInvalidHostApi;
- }
- else
- {
- if( hostApiDeviceIndex < 0 ||
- hostApiDeviceIndex >= hostApis_[hostApi]->info.deviceCount )
- {
- result = paInvalidDevice;
- }
- else
- {
- result = hostApis_[hostApi]->privatePaFrontInfo.baseDeviceIndex + hostApiDeviceIndex;
- }
- }
- }
-
-#ifdef PA_LOG_API_CALLS
- PaUtil_DebugPrint("Pa_HostApiDeviceIndexToPaDeviceIndex returned:\n" );
- if( result < 0 )
- PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) );
- else
- PaUtil_DebugPrint("\tPaDeviceIndex: %d\n\n", result );
-#endif
-
- return result;
-}
-
-
-PaDeviceIndex Pa_GetDeviceCount( void )
-{
- PaDeviceIndex result;
-
-#ifdef PA_LOG_API_CALLS
- PaUtil_DebugPrint("Pa_GetDeviceCount called.\n" );
-#endif
-
- if( !PA_IS_INITIALISED_ )
- {
- result = paNotInitialized;
- }
- else
- {
- result = deviceCount_;
- }
-
-#ifdef PA_LOG_API_CALLS
- PaUtil_DebugPrint("Pa_GetDeviceCount returned:\n" );
- if( result < 0 )
- PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) );
- else
- PaUtil_DebugPrint("\tPaDeviceIndex: %d\n\n", result );
-#endif
-
- return result;
-}
-
-
-PaDeviceIndex Pa_GetDefaultInputDevice( void )
-{
- PaHostApiIndex hostApi;
- PaDeviceIndex result;
-
-#ifdef PA_LOG_API_CALLS
- PaUtil_DebugPrint("Pa_GetDefaultInputDevice called.\n" );
-#endif
-
- hostApi = Pa_GetDefaultHostApi();
- if( hostApi < 0 )
- {
- result = paNoDevice;
- }
- else
- {
- result = hostApis_[hostApi]->info.defaultInputDevice;
- }
-
-#ifdef PA_LOG_API_CALLS
- PaUtil_DebugPrint("Pa_GetDefaultInputDevice returned:\n" );
- PaUtil_DebugPrint("\tPaDeviceIndex: %d\n\n", result );
-#endif
-
- return result;
-}
-
-
-PaDeviceIndex Pa_GetDefaultOutputDevice( void )
-{
- PaHostApiIndex hostApi;
- PaDeviceIndex result;
-
-#ifdef PA_LOG_API_CALLS
- PaUtil_DebugPrint("Pa_GetDefaultOutputDevice called.\n" );
-#endif
-
- hostApi = Pa_GetDefaultHostApi();
- if( hostApi < 0 )
- {
- result = paNoDevice;
- }
- else
- {
- result = hostApis_[hostApi]->info.defaultOutputDevice;
- }
-
-#ifdef PA_LOG_API_CALLS
- PaUtil_DebugPrint("Pa_GetDefaultOutputDevice returned:\n" );
- PaUtil_DebugPrint("\tPaDeviceIndex: %d\n\n", result );
-#endif
-
- return result;
-}
-
-
-const PaDeviceInfo* Pa_GetDeviceInfo( PaDeviceIndex device )
-{
- int hostSpecificDeviceIndex;
- int hostApiIndex = FindHostApi( device, &hostSpecificDeviceIndex );
- PaDeviceInfo *result;
-
-
-#ifdef PA_LOG_API_CALLS
- PaUtil_DebugPrint("Pa_GetDeviceInfo called:\n" );
- PaUtil_DebugPrint("\tPaDeviceIndex device: %d\n", device );
-#endif
-
- if( hostApiIndex < 0 )
- {
- result = NULL;
-
-#ifdef PA_LOG_API_CALLS
- PaUtil_DebugPrint("Pa_GetDeviceInfo returned:\n" );
- PaUtil_DebugPrint("\tPaDeviceInfo* NULL [ invalid device index ]\n\n" );
-#endif
-
- }
- else
- {
- result = hostApis_[hostApiIndex]->deviceInfos[ hostSpecificDeviceIndex ];
-
-#ifdef PA_LOG_API_CALLS
- PaUtil_DebugPrint("Pa_GetDeviceInfo returned:\n" );
- PaUtil_DebugPrint("\tPaDeviceInfo*: 0x%p:\n", result );
- PaUtil_DebugPrint("\t{\n" );
-
- PaUtil_DebugPrint("\t\tint structVersion: %d\n", result->structVersion );
- PaUtil_DebugPrint("\t\tconst char *name: %s\n", result->name );
- PaUtil_DebugPrint("\t\tPaHostApiIndex hostApi: %d\n", result->hostApi );
- PaUtil_DebugPrint("\t\tint maxInputChannels: %d\n", result->maxInputChannels );
- PaUtil_DebugPrint("\t\tint maxOutputChannels: %d\n", result->maxOutputChannels );
- PaUtil_DebugPrint("\t}\n\n" );
-#endif
-
- }
-
- return result;
-}
-
-
-/*
- SampleFormatIsValid() returns 1 if sampleFormat is a sample format
- defined in portaudio.h, or 0 otherwise.
-*/
-static int SampleFormatIsValid( PaSampleFormat format )
-{
- switch( format & ~paNonInterleaved )
- {
- case paFloat32: return 1;
- case paInt16: return 1;
- case paInt32: return 1;
- case paInt24: return 1;
- case paInt8: return 1;
- case paUInt8: return 1;
- case paCustomFormat: return 1;
- default: return 0;
- }
-}
-
-/*
- NOTE: make sure this validation list is kept syncronised with the one in
- pa_hostapi.h
-
- ValidateOpenStreamParameters() checks that parameters to Pa_OpenStream()
- conform to the expected values as described below. This function is
- also designed to be used with the proposed Pa_IsFormatSupported() function.
-
- There are basically two types of validation that could be performed:
- Generic conformance validation, and device capability mismatch
- validation. This function performs only generic conformance validation.
- Validation that would require knowledge of device capabilities is
- not performed because of potentially complex relationships between
- combinations of parameters - for example, even if the sampleRate
- seems ok, it might not be for a duplex stream - we have no way of
- checking this in an API-neutral way, so we don't try.
-
- On success the function returns PaNoError and fills in hostApi,
- hostApiInputDeviceID, and hostApiOutputDeviceID fields. On failure
- the function returns an error code indicating the first encountered
- parameter error.
-
-
- If ValidateOpenStreamParameters() returns paNoError, the following
- assertions are guaranteed to be true.
-
- - at least one of inputParameters & outputParmeters is valid (not NULL)
-
- - if inputParameters & outputParmeters are both valid, that
- inputParameters->device & outputParmeters->device both use the same host api
-
- PaDeviceIndex inputParameters->device
- - is within range (0 to Pa_GetDeviceCount-1) Or:
- - is paUseHostApiSpecificDeviceSpecification and
- inputParameters->hostApiSpecificStreamInfo is non-NULL and refers
- to a valid host api
-
- int inputParameters->channelCount
- - if inputParameters->device is not paUseHostApiSpecificDeviceSpecification, channelCount is > 0
- - upper bound is NOT validated against device capabilities
-
- PaSampleFormat inputParameters->sampleFormat
- - is one of the sample formats defined in portaudio.h
-
- void *inputParameters->hostApiSpecificStreamInfo
- - if supplied its hostApi field matches the input device's host Api
-
- PaDeviceIndex outputParmeters->device
- - is within range (0 to Pa_GetDeviceCount-1)
-
- int outputParmeters->channelCount
- - if inputDevice is valid, channelCount is > 0
- - upper bound is NOT validated against device capabilities
-
- PaSampleFormat outputParmeters->sampleFormat
- - is one of the sample formats defined in portaudio.h
-
- void *outputParmeters->hostApiSpecificStreamInfo
- - if supplied its hostApi field matches the output device's host Api
-
- double sampleRate
- - is not an 'absurd' rate (less than 1000. or greater than 200000.)
- - sampleRate is NOT validated against device capabilities
-
- PaStreamFlags streamFlags
- - unused platform neutral flags are zero
- - paNeverDropInput is only used for full-duplex callback streams with
- variable buffer size (paFramesPerBufferUnspecified)
-*/
-static PaError ValidateOpenStreamParameters(
- const PaStreamParameters *inputParameters,
- const PaStreamParameters *outputParameters,
- double sampleRate,
- unsigned long framesPerBuffer,
- PaStreamFlags streamFlags,
- PaStreamCallback *streamCallback,
- PaUtilHostApiRepresentation **hostApi,
- PaDeviceIndex *hostApiInputDevice,
- PaDeviceIndex *hostApiOutputDevice )
-{
- int inputHostApiIndex = -1, /* Surpress uninitialised var warnings: compiler does */
- outputHostApiIndex = -1; /* not see that if inputParameters and outputParame- */
- /* ters are both nonzero, these indices are set. */
-
- if( (inputParameters == NULL) && (outputParameters == NULL) )
- {
- return paInvalidDevice; /** @todo should be a new error code "invalid device parameters" or something */
- }
- else
- {
- if( inputParameters == NULL )
- {
- *hostApiInputDevice = paNoDevice;
- }
- else if( inputParameters->device == paUseHostApiSpecificDeviceSpecification )
- {
- if( inputParameters->hostApiSpecificStreamInfo )
- {
- inputHostApiIndex = Pa_HostApiTypeIdToHostApiIndex(
- ((PaUtilHostApiSpecificStreamInfoHeader*)inputParameters->hostApiSpecificStreamInfo)->hostApiType );
-
- if( inputHostApiIndex != -1 )
- {
- *hostApiInputDevice = paUseHostApiSpecificDeviceSpecification;
- *hostApi = hostApis_[inputHostApiIndex];
- }
- else
- {
- return paInvalidDevice;
- }
- }
- else
- {
- return paInvalidDevice;
- }
- }
- else
- {
- if( inputParameters->device < 0 || inputParameters->device >= deviceCount_ )
- return paInvalidDevice;
-
- inputHostApiIndex = FindHostApi( inputParameters->device, hostApiInputDevice );
- if( inputHostApiIndex < 0 )
- return paInternalError;
-
- *hostApi = hostApis_[inputHostApiIndex];
-
- if( inputParameters->channelCount <= 0 )
- return paInvalidChannelCount;
-
- if( !SampleFormatIsValid( inputParameters->sampleFormat ) )
- return paSampleFormatNotSupported;
-
- if( inputParameters->hostApiSpecificStreamInfo != NULL )
- {
- if( ((PaUtilHostApiSpecificStreamInfoHeader*)inputParameters->hostApiSpecificStreamInfo)->hostApiType
- != (*hostApi)->info.type )
- return paIncompatibleHostApiSpecificStreamInfo;
- }
- }
-
- if( outputParameters == NULL )
- {
- *hostApiOutputDevice = paNoDevice;
- }
- else if( outputParameters->device == paUseHostApiSpecificDeviceSpecification )
- {
- if( outputParameters->hostApiSpecificStreamInfo )
- {
- outputHostApiIndex = Pa_HostApiTypeIdToHostApiIndex(
- ((PaUtilHostApiSpecificStreamInfoHeader*)outputParameters->hostApiSpecificStreamInfo)->hostApiType );
-
- if( outputHostApiIndex != -1 )
- {
- *hostApiOutputDevice = paUseHostApiSpecificDeviceSpecification;
- *hostApi = hostApis_[outputHostApiIndex];
- }
- else
- {
- return paInvalidDevice;
- }
- }
- else
- {
- return paInvalidDevice;
- }
- }
- else
- {
- if( outputParameters->device < 0 || outputParameters->device >= deviceCount_ )
- return paInvalidDevice;
-
- outputHostApiIndex = FindHostApi( outputParameters->device, hostApiOutputDevice );
- if( outputHostApiIndex < 0 )
- return paInternalError;
-
- *hostApi = hostApis_[outputHostApiIndex];
-
- if( outputParameters->channelCount <= 0 )
- return paInvalidChannelCount;
-
- if( !SampleFormatIsValid( outputParameters->sampleFormat ) )
- return paSampleFormatNotSupported;
-
- if( outputParameters->hostApiSpecificStreamInfo != NULL )
- {
- if( ((PaUtilHostApiSpecificStreamInfoHeader*)outputParameters->hostApiSpecificStreamInfo)->hostApiType
- != (*hostApi)->info.type )
- return paIncompatibleHostApiSpecificStreamInfo;
- }
- }
-
- if( (inputParameters != NULL) && (outputParameters != NULL) )
- {
- /* ensure that both devices use the same API */
- if( inputHostApiIndex != outputHostApiIndex )
- return paBadIODeviceCombination;
- }
- }
-
-
- /* Check for absurd sample rates. */
- if( (sampleRate < 1000.0) || (sampleRate > 200000.0) )
- return paInvalidSampleRate;
-
- if( ((streamFlags & ~paPlatformSpecificFlags) & ~(paClipOff | paDitherOff | paNeverDropInput | paPrimeOutputBuffersUsingStreamCallback ) ) != 0 )
- return paInvalidFlag;
-
- if( streamFlags & paNeverDropInput )
- {
- /* must be a callback stream */
- if( !streamCallback )
- return paInvalidFlag;
-
- /* must be a full duplex stream */
- if( (inputParameters == NULL) || (outputParameters == NULL) )
- return paInvalidFlag;
-
- /* must use paFramesPerBufferUnspecified */
- if( framesPerBuffer != paFramesPerBufferUnspecified )
- return paInvalidFlag;
- }
-
- return paNoError;
-}
-
-
-PaError Pa_IsFormatSupported( const PaStreamParameters *inputParameters,
- const PaStreamParameters *outputParameters,
- double sampleRate )
-{
- PaError result;
- PaUtilHostApiRepresentation *hostApi;
- PaDeviceIndex hostApiInputDevice, hostApiOutputDevice;
- PaStreamParameters hostApiInputParameters, hostApiOutputParameters;
- PaStreamParameters *hostApiInputParametersPtr, *hostApiOutputParametersPtr;
-
-
-#ifdef PA_LOG_API_CALLS
- PaUtil_DebugPrint("Pa_IsFormatSupported called:\n" );
-
- if( inputParameters == NULL ){
- PaUtil_DebugPrint("\tPaStreamParameters *inputParameters: NULL\n" );
- }else{
- PaUtil_DebugPrint("\tPaStreamParameters *inputParameters: 0x%p\n", inputParameters );
- PaUtil_DebugPrint("\tPaDeviceIndex inputParameters->device: %d\n", inputParameters->device );
- PaUtil_DebugPrint("\tint inputParameters->channelCount: %d\n", inputParameters->channelCount );
- PaUtil_DebugPrint("\tPaSampleFormat inputParameters->sampleFormat: %d\n", inputParameters->sampleFormat );
- PaUtil_DebugPrint("\tPaTime inputParameters->suggestedLatency: %f\n", inputParameters->suggestedLatency );
- PaUtil_DebugPrint("\tvoid *inputParameters->hostApiSpecificStreamInfo: 0x%p\n", inputParameters->hostApiSpecificStreamInfo );
- }
-
- if( outputParameters == NULL ){
- PaUtil_DebugPrint("\tPaStreamParameters *outputParameters: NULL\n" );
- }else{
- PaUtil_DebugPrint("\tPaStreamParameters *outputParameters: 0x%p\n", outputParameters );
- PaUtil_DebugPrint("\tPaDeviceIndex outputParameters->device: %d\n", outputParameters->device );
- PaUtil_DebugPrint("\tint outputParameters->channelCount: %d\n", outputParameters->channelCount );
- PaUtil_DebugPrint("\tPaSampleFormat outputParameters->sampleFormat: %d\n", outputParameters->sampleFormat );
- PaUtil_DebugPrint("\tPaTime outputParameters->suggestedLatency: %f\n", outputParameters->suggestedLatency );
- PaUtil_DebugPrint("\tvoid *outputParameters->hostApiSpecificStreamInfo: 0x%p\n", outputParameters->hostApiSpecificStreamInfo );
- }
-
- PaUtil_DebugPrint("\tdouble sampleRate: %g\n", sampleRate );
-#endif
-
- if( !PA_IS_INITIALISED_ )
- {
- result = paNotInitialized;
-
-#ifdef PA_LOG_API_CALLS
- PaUtil_DebugPrint("Pa_IsFormatSupported returned:\n" );
- PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) );
-#endif
- return result;
- }
-
- result = ValidateOpenStreamParameters( inputParameters,
- outputParameters,
- sampleRate, 0, paNoFlag, 0,
- &hostApi,
- &hostApiInputDevice,
- &hostApiOutputDevice );
- if( result != paNoError )
- {
-#ifdef PA_LOG_API_CALLS
- PaUtil_DebugPrint("Pa_IsFormatSupported returned:\n" );
- PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) );
-#endif
- return result;
- }
-
-
- if( inputParameters )
- {
- hostApiInputParameters.device = hostApiInputDevice;
- hostApiInputParameters.channelCount = inputParameters->channelCount;
- hostApiInputParameters.sampleFormat = inputParameters->sampleFormat;
- hostApiInputParameters.suggestedLatency = inputParameters->suggestedLatency;
- hostApiInputParameters.hostApiSpecificStreamInfo = inputParameters->hostApiSpecificStreamInfo;
- hostApiInputParametersPtr = &hostApiInputParameters;
- }
- else
- {
- hostApiInputParametersPtr = NULL;
- }
-
- if( outputParameters )
- {
- hostApiOutputParameters.device = hostApiOutputDevice;
- hostApiOutputParameters.channelCount = outputParameters->channelCount;
- hostApiOutputParameters.sampleFormat = outputParameters->sampleFormat;
- hostApiOutputParameters.suggestedLatency = outputParameters->suggestedLatency;
- hostApiOutputParameters.hostApiSpecificStreamInfo = outputParameters->hostApiSpecificStreamInfo;
- hostApiOutputParametersPtr = &hostApiOutputParameters;
- }
- else
- {
- hostApiOutputParametersPtr = NULL;
- }
-
- result = hostApi->IsFormatSupported( hostApi,
- hostApiInputParametersPtr, hostApiOutputParametersPtr,
- sampleRate );
-
-#ifdef PA_LOG_API_CALLS
- PaUtil_DebugPrint("Pa_OpenStream returned:\n" );
- if( result == paFormatIsSupported )
- PaUtil_DebugPrint("\tPaError: 0 [ paFormatIsSupported ]\n\n" );
- else
- PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) );
-#endif
-
- return result;
-}
-
-
-PaError Pa_OpenStream( PaStream** stream,
- const PaStreamParameters *inputParameters,
- const PaStreamParameters *outputParameters,
- double sampleRate,
- unsigned long framesPerBuffer,
- PaStreamFlags streamFlags,
- PaStreamCallback *streamCallback,
- void *userData )
-{
- PaError result;
- PaUtilHostApiRepresentation *hostApi;
- PaDeviceIndex hostApiInputDevice, hostApiOutputDevice;
- PaStreamParameters hostApiInputParameters, hostApiOutputParameters;
- PaStreamParameters *hostApiInputParametersPtr, *hostApiOutputParametersPtr;
-
-
-#ifdef PA_LOG_API_CALLS
- PaUtil_DebugPrint("Pa_OpenStream called:\n" );
- PaUtil_DebugPrint("\tPaStream** stream: 0x%p\n", stream );
-
- if( inputParameters == NULL ){
- PaUtil_DebugPrint("\tPaStreamParameters *inputParameters: NULL\n" );
- }else{
- PaUtil_DebugPrint("\tPaStreamParameters *inputParameters: 0x%p\n", inputParameters );
- PaUtil_DebugPrint("\tPaDeviceIndex inputParameters->device: %d\n", inputParameters->device );
- PaUtil_DebugPrint("\tint inputParameters->channelCount: %d\n", inputParameters->channelCount );
- PaUtil_DebugPrint("\tPaSampleFormat inputParameters->sampleFormat: %d\n", inputParameters->sampleFormat );
- PaUtil_DebugPrint("\tPaTime inputParameters->suggestedLatency: %f\n", inputParameters->suggestedLatency );
- PaUtil_DebugPrint("\tvoid *inputParameters->hostApiSpecificStreamInfo: 0x%p\n", inputParameters->hostApiSpecificStreamInfo );
- }
-
- if( outputParameters == NULL ){
- PaUtil_DebugPrint("\tPaStreamParameters *outputParameters: NULL\n" );
- }else{
- PaUtil_DebugPrint("\tPaStreamParameters *outputParameters: 0x%p\n", outputParameters );
- PaUtil_DebugPrint("\tPaDeviceIndex outputParameters->device: %d\n", outputParameters->device );
- PaUtil_DebugPrint("\tint outputParameters->channelCount: %d\n", outputParameters->channelCount );
- PaUtil_DebugPrint("\tPaSampleFormat outputParameters->sampleFormat: %d\n", outputParameters->sampleFormat );
- PaUtil_DebugPrint("\tPaTime outputParameters->suggestedLatency: %f\n", outputParameters->suggestedLatency );
- PaUtil_DebugPrint("\tvoid *outputParameters->hostApiSpecificStreamInfo: 0x%p\n", outputParameters->hostApiSpecificStreamInfo );
- }
-
- PaUtil_DebugPrint("\tdouble sampleRate: %g\n", sampleRate );
- PaUtil_DebugPrint("\tunsigned long framesPerBuffer: %d\n", framesPerBuffer );
- PaUtil_DebugPrint("\tPaStreamFlags streamFlags: 0x%x\n", streamFlags );
- PaUtil_DebugPrint("\tPaStreamCallback *streamCallback: 0x%p\n", streamCallback );
- PaUtil_DebugPrint("\tvoid *userData: 0x%p\n", userData );
-#endif
-
- if( !PA_IS_INITIALISED_ )
- {
- result = paNotInitialized;
-
-#ifdef PA_LOG_API_CALLS
- PaUtil_DebugPrint("Pa_OpenStream returned:\n" );
- PaUtil_DebugPrint("\t*(PaStream** stream): undefined\n" );
- PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) );
-#endif
- return result;
- }
-
- /* Check for parameter errors.
- NOTE: make sure this validation list is kept syncronised with the one
- in pa_hostapi.h
- */
-
- if( stream == NULL )
- {
- result = paBadStreamPtr;
-
-#ifdef PA_LOG_API_CALLS
- PaUtil_DebugPrint("Pa_OpenStream returned:\n" );
- PaUtil_DebugPrint("\t*(PaStream** stream): undefined\n" );
- PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) );
-#endif
- return result;
- }
-
- result = ValidateOpenStreamParameters( inputParameters,
- outputParameters,
- sampleRate, framesPerBuffer,
- streamFlags, streamCallback,
- &hostApi,
- &hostApiInputDevice,
- &hostApiOutputDevice );
- if( result != paNoError )
- {
-#ifdef PA_LOG_API_CALLS
- PaUtil_DebugPrint("Pa_OpenStream returned:\n" );
- PaUtil_DebugPrint("\t*(PaStream** stream): undefined\n" );
- PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) );
-#endif
- return result;
- }
-
-
- if( inputParameters )
- {
- hostApiInputParameters.device = hostApiInputDevice;
- hostApiInputParameters.channelCount = inputParameters->channelCount;
- hostApiInputParameters.sampleFormat = inputParameters->sampleFormat;
- hostApiInputParameters.suggestedLatency = inputParameters->suggestedLatency;
- hostApiInputParameters.hostApiSpecificStreamInfo = inputParameters->hostApiSpecificStreamInfo;
- hostApiInputParametersPtr = &hostApiInputParameters;
- }
- else
- {
- hostApiInputParametersPtr = NULL;
- }
-
- if( outputParameters )
- {
- hostApiOutputParameters.device = hostApiOutputDevice;
- hostApiOutputParameters.channelCount = outputParameters->channelCount;
- hostApiOutputParameters.sampleFormat = outputParameters->sampleFormat;
- hostApiOutputParameters.suggestedLatency = outputParameters->suggestedLatency;
- hostApiOutputParameters.hostApiSpecificStreamInfo = outputParameters->hostApiSpecificStreamInfo;
- hostApiOutputParametersPtr = &hostApiOutputParameters;
- }
- else
- {
- hostApiOutputParametersPtr = NULL;
- }
-
- result = hostApi->OpenStream( hostApi, stream,
- hostApiInputParametersPtr, hostApiOutputParametersPtr,
- sampleRate, framesPerBuffer, streamFlags, streamCallback, userData );
-
- if( result == paNoError )
- AddOpenStream( *stream );
-
-
-#ifdef PA_LOG_API_CALLS
- PaUtil_DebugPrint("Pa_OpenStream returned:\n" );
- PaUtil_DebugPrint("\t*(PaStream** stream): 0x%p\n", *stream );
- PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) );
-#endif
-
- return result;
-}
-
-
-PaError Pa_OpenDefaultStream( PaStream** stream,
- int inputChannelCount,
- int outputChannelCount,
- PaSampleFormat sampleFormat,
- double sampleRate,
- unsigned long framesPerBuffer,
- PaStreamCallback *streamCallback,
- void *userData )
-{
- PaError result;
- PaStreamParameters hostApiInputParameters, hostApiOutputParameters;
- PaStreamParameters *hostApiInputParametersPtr, *hostApiOutputParametersPtr;
-
-#ifdef PA_LOG_API_CALLS
- PaUtil_DebugPrint("Pa_OpenDefaultStream called:\n" );
- PaUtil_DebugPrint("\tPaStream** stream: 0x%p\n", stream );
- PaUtil_DebugPrint("\tint inputChannelCount: %d\n", inputChannelCount );
- PaUtil_DebugPrint("\tint outputChannelCount: %d\n", outputChannelCount );
- PaUtil_DebugPrint("\tPaSampleFormat sampleFormat: %d\n", sampleFormat );
- PaUtil_DebugPrint("\tdouble sampleRate: %g\n", sampleRate );
- PaUtil_DebugPrint("\tunsigned long framesPerBuffer: %d\n", framesPerBuffer );
- PaUtil_DebugPrint("\tPaStreamCallback *streamCallback: 0x%p\n", streamCallback );
- PaUtil_DebugPrint("\tvoid *userData: 0x%p\n", userData );
-#endif
-
-
- if( inputChannelCount > 0 )
- {
- hostApiInputParameters.device = Pa_GetDefaultInputDevice();
- hostApiInputParameters.channelCount = inputChannelCount;
- hostApiInputParameters.sampleFormat = sampleFormat;
- /* defaultHighInputLatency is used below instead of
- defaultLowInputLatency because it is more important for the default
- stream to work reliably than it is for it to work with the lowest
- latency.
- */
- hostApiInputParameters.suggestedLatency =
- Pa_GetDeviceInfo( hostApiInputParameters.device )->defaultHighInputLatency;
- hostApiInputParameters.hostApiSpecificStreamInfo = NULL;
- hostApiInputParametersPtr = &hostApiInputParameters;
- }
- else
- {
- hostApiInputParametersPtr = NULL;
- }
-
- if( outputChannelCount > 0 )
- {
- hostApiOutputParameters.device = Pa_GetDefaultOutputDevice();
- hostApiOutputParameters.channelCount = outputChannelCount;
- hostApiOutputParameters.sampleFormat = sampleFormat;
- /* defaultHighOutputLatency is used below instead of
- defaultLowOutputLatency because it is more important for the default
- stream to work reliably than it is for it to work with the lowest
- latency.
- */
- hostApiOutputParameters.suggestedLatency =
- Pa_GetDeviceInfo( hostApiOutputParameters.device )->defaultHighOutputLatency;
- hostApiOutputParameters.hostApiSpecificStreamInfo = NULL;
- hostApiOutputParametersPtr = &hostApiOutputParameters;
- }
- else
- {
- hostApiOutputParametersPtr = NULL;
- }
-
-
- result = Pa_OpenStream(
- stream, hostApiInputParametersPtr, hostApiOutputParametersPtr,
- sampleRate, framesPerBuffer, paNoFlag, streamCallback, userData );
-
-#ifdef PA_LOG_API_CALLS
- PaUtil_DebugPrint("Pa_OpenDefaultStream returned:\n" );
- PaUtil_DebugPrint("\t*(PaStream** stream): 0x%p", *stream );
- PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) );
-#endif
-
- return result;
-}
-
-
-PaError PaUtil_ValidateStreamPointer( PaStream* stream )
-{
- if( !PA_IS_INITIALISED_ ) return paNotInitialized;
-
- if( stream == NULL ) return paBadStreamPtr;
-
- if( ((PaUtilStreamRepresentation*)stream)->magic != PA_STREAM_MAGIC )
- return paBadStreamPtr;
-
- return paNoError;
-}
-
-
-PaError Pa_CloseStream( PaStream* stream )
-{
- PaUtilStreamInterface *interface;
- PaError result = PaUtil_ValidateStreamPointer( stream );
-
-#ifdef PA_LOG_API_CALLS
- PaUtil_DebugPrint("Pa_CloseStream called:\n" );
- PaUtil_DebugPrint("\tPaStream* stream: 0x%p\n", stream );
-#endif
-
- /* always remove the open stream from our list, even if this function
- eventually returns an error. Otherwise CloseOpenStreams() will
- get stuck in an infinite loop */
- RemoveOpenStream( stream ); /* be sure to call this _before_ closing the stream */
-
- if( result == paNoError )
- {
- interface = PA_STREAM_INTERFACE(stream);
-
- /* abort the stream if it isn't stopped */
- result = interface->IsStopped( stream );
- if( result == 1 )
- result = paNoError;
- else if( result == 0 )
- result = interface->Abort( stream );
-
- if( result == paNoError ) /** @todo REVIEW: shouldn't we close anyway? */
- result = interface->Close( stream );
- }
-
-#ifdef PA_LOG_API_CALLS
- PaUtil_DebugPrint("Pa_CloseStream returned:\n" );
- PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) );
-#endif
-
- return result;
-}
-
-
-PaError Pa_SetStreamFinishedCallback( PaStream *stream, PaStreamFinishedCallback* streamFinishedCallback )
-{
- PaError result = PaUtil_ValidateStreamPointer( stream );
-
-#ifdef PA_LOG_API_CALLS
- PaUtil_DebugPrint("Pa_SetStreamFinishedCallback called:\n" );
- PaUtil_DebugPrint("\tPaStream* stream: 0x%p\n", stream );
- PaUtil_DebugPrint("\tPaStreamFinishedCallback* streamFinishedCallback: 0x%p\n", streamFinishedCallback );
-#endif
-
- if( result == paNoError )
- {
- result = PA_STREAM_INTERFACE(stream)->IsStopped( stream );
- if( result == 0 )
- {
- result = paStreamIsNotStopped ;
- }
- if( result == 1 )
- {
- PA_STREAM_REP( stream )->streamFinishedCallback = streamFinishedCallback;
- result = paNoError;
- }
- }
-
-#ifdef PA_LOG_API_CALLS
- PaUtil_DebugPrint("Pa_SetStreamFinishedCallback returned:\n" );
- PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) );
-#endif
-
- return result;
-
-}
-
-
-PaError Pa_StartStream( PaStream *stream )
-{
- PaError result = PaUtil_ValidateStreamPointer( stream );
-
-#ifdef PA_LOG_API_CALLS
- PaUtil_DebugPrint("Pa_StartStream called:\n" );
- PaUtil_DebugPrint("\tPaStream* stream: 0x%p\n", stream );
-#endif
-
- if( result == paNoError )
- {
- result = PA_STREAM_INTERFACE(stream)->IsStopped( stream );
- if( result == 0 )
- {
- result = paStreamIsNotStopped ;
- }
- else if( result == 1 )
- {
- result = PA_STREAM_INTERFACE(stream)->Start( stream );
- }
- }
-
-#ifdef PA_LOG_API_CALLS
- PaUtil_DebugPrint("Pa_StartStream returned:\n" );
- PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) );
-#endif
-
- return result;
-}
-
-
-PaError Pa_StopStream( PaStream *stream )
-{
- PaError result = PaUtil_ValidateStreamPointer( stream );
-
-#ifdef PA_LOG_API_CALLS
- PaUtil_DebugPrint("Pa_StopStream called\n" );
- PaUtil_DebugPrint("\tPaStream* stream: 0x%p\n", stream );
-#endif
-
- if( result == paNoError )
- {
- result = PA_STREAM_INTERFACE(stream)->IsStopped( stream );
- if( result == 0 )
- {
- result = PA_STREAM_INTERFACE(stream)->Stop( stream );
- }
- else if( result == 1 )
- {
- result = paStreamIsStopped;
- }
- }
-
-#ifdef PA_LOG_API_CALLS
- PaUtil_DebugPrint("Pa_StopStream returned:\n" );
- PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) );
-#endif
-
- return result;
-}
-
-
-PaError Pa_AbortStream( PaStream *stream )
-{
- PaError result = PaUtil_ValidateStreamPointer( stream );
-
-#ifdef PA_LOG_API_CALLS
- PaUtil_DebugPrint("Pa_AbortStream called:\n" );
- PaUtil_DebugPrint("\tPaStream* stream: 0x%p\n", stream );
-#endif
-
- if( result == paNoError )
- {
- result = PA_STREAM_INTERFACE(stream)->IsStopped( stream );
- if( result == 0 )
- {
- result = PA_STREAM_INTERFACE(stream)->Abort( stream );
- }
- else if( result == 1 )
- {
- result = paStreamIsStopped;
- }
- }
-
-#ifdef PA_LOG_API_CALLS
- PaUtil_DebugPrint("Pa_AbortStream returned:\n" );
- PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) );
-#endif
-
- return result;
-}
-
-
-PaError Pa_IsStreamStopped( PaStream *stream )
-{
- PaError result = PaUtil_ValidateStreamPointer( stream );
-
-#ifdef PA_LOG_API_CALLS
- PaUtil_DebugPrint("Pa_IsStreamStopped called:\n" );
- PaUtil_DebugPrint("\tPaStream* stream: 0x%p\n", stream );
-#endif
-
- if( result == paNoError )
- result = PA_STREAM_INTERFACE(stream)->IsStopped( stream );
-
-#ifdef PA_LOG_API_CALLS
- PaUtil_DebugPrint("Pa_IsStreamStopped returned:\n" );
- PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) );
-#endif
-
- return result;
-}
-
-
-PaError Pa_IsStreamActive( PaStream *stream )
-{
- PaError result = PaUtil_ValidateStreamPointer( stream );
-
-#ifdef PA_LOG_API_CALLS
- PaUtil_DebugPrint("Pa_IsStreamActive called:\n" );
- PaUtil_DebugPrint("\tPaStream* stream: 0x%p\n", stream );
-#endif
-
- if( result == paNoError )
- result = PA_STREAM_INTERFACE(stream)->IsActive( stream );
-
-#ifdef PA_LOG_API_CALLS
- PaUtil_DebugPrint("Pa_IsStreamActive returned:\n" );
- PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) );
-#endif
-
- return result;
-}
-
-
-const PaStreamInfo* Pa_GetStreamInfo( PaStream *stream )
-{
- PaError error = PaUtil_ValidateStreamPointer( stream );
- const PaStreamInfo *result;
-
-#ifdef PA_LOG_API_CALLS
- PaUtil_DebugPrint("Pa_GetStreamInfo called:\n" );
- PaUtil_DebugPrint("\tPaStream* stream: 0x%p\n", stream );
-#endif
-
- if( error != paNoError )
- {
- result = 0;
-
-#ifdef PA_LOG_API_CALLS
- PaUtil_DebugPrint("Pa_GetStreamInfo returned:\n" );
- PaUtil_DebugPrint("\tconst PaStreamInfo*: 0 [PaError error:%d ( %s )]\n\n", result, error, Pa_GetErrorText( error ) );
-#endif
-
- }
- else
- {
- result = &PA_STREAM_REP( stream )->streamInfo;
-
-#ifdef PA_LOG_API_CALLS
- PaUtil_DebugPrint("Pa_GetStreamInfo returned:\n" );
- PaUtil_DebugPrint("\tconst PaStreamInfo*: 0x%p:\n", result );
- PaUtil_DebugPrint("\t{" );
-
- PaUtil_DebugPrint("\t\tint structVersion: %d\n", result->structVersion );
- PaUtil_DebugPrint("\t\tPaTime inputLatency: %f\n", result->inputLatency );
- PaUtil_DebugPrint("\t\tPaTime outputLatency: %f\n", result->outputLatency );
- PaUtil_DebugPrint("\t\tdouble sampleRate: %f\n", result->sampleRate );
- PaUtil_DebugPrint("\t}\n\n" );
-#endif
-
- }
-
- return result;
-}
-
-
-PaTime Pa_GetStreamTime( PaStream *stream )
-{
- PaError error = PaUtil_ValidateStreamPointer( stream );
- PaTime result;
-
-#ifdef PA_LOG_API_CALLS
- PaUtil_DebugPrint("Pa_GetStreamTime called:\n" );
- PaUtil_DebugPrint("\tPaStream* stream: 0x%p\n", stream );
-#endif
-
- if( error != paNoError )
- {
- result = 0;
-
-#ifdef PA_LOG_API_CALLS
- PaUtil_DebugPrint("Pa_GetStreamTime returned:\n" );
- PaUtil_DebugPrint("\tPaTime: 0 [PaError error:%d ( %s )]\n\n", result, error, Pa_GetErrorText( error ) );
-#endif
-
- }
- else
- {
- result = PA_STREAM_INTERFACE(stream)->GetTime( stream );
-
-#ifdef PA_LOG_API_CALLS
- PaUtil_DebugPrint("Pa_GetStreamTime returned:\n" );
- PaUtil_DebugPrint("\tPaTime: %g\n\n", result );
-#endif
-
- }
-
- return result;
-}
-
-
-double Pa_GetStreamCpuLoad( PaStream* stream )
-{
- PaError error = PaUtil_ValidateStreamPointer( stream );
- double result;
-
-#ifdef PA_LOG_API_CALLS
- PaUtil_DebugPrint("Pa_GetStreamCpuLoad called:\n" );
- PaUtil_DebugPrint("\tPaStream* stream: 0x%p\n", stream );
-#endif
-
- if( error != paNoError )
- {
-
- result = 0.0;
-
-#ifdef PA_LOG_API_CALLS
- PaUtil_DebugPrint("Pa_GetStreamCpuLoad returned:\n" );
- PaUtil_DebugPrint("\tdouble: 0.0 [PaError error: %d ( %s )]\n\n", error, Pa_GetErrorText( error ) );
-#endif
-
- }
- else
- {
- result = PA_STREAM_INTERFACE(stream)->GetCpuLoad( stream );
-
-#ifdef PA_LOG_API_CALLS
- PaUtil_DebugPrint("Pa_GetStreamCpuLoad returned:\n" );
- PaUtil_DebugPrint("\tdouble: %g\n\n", result );
-#endif
-
- }
-
- return result;
-}
-
-
-PaError Pa_ReadStream( PaStream* stream,
- void *buffer,
- unsigned long frames )
-{
- PaError result = PaUtil_ValidateStreamPointer( stream );
-
-#ifdef PA_LOG_API_CALLS
- PaUtil_DebugPrint("Pa_ReadStream called:\n" );
- PaUtil_DebugPrint("\tPaStream* stream: 0x%p\n", stream );
-#endif
-
- if( result == paNoError )
- {
- if( frames == 0 )
- {
- result = paInternalError; /** @todo should return a different error code */
- }
- else if( buffer == 0 )
- {
- result = paInternalError; /** @todo should return a different error code */
- }
- else
- {
- result = PA_STREAM_INTERFACE(stream)->IsStopped( stream );
- if( result == 0 )
- {
- result = PA_STREAM_INTERFACE(stream)->Read( stream, buffer, frames );
- }
- else if( result == 1 )
- {
- result = paStreamIsStopped;
- }
- }
- }
-
-#ifdef PA_LOG_API_CALLS
- PaUtil_DebugPrint("Pa_ReadStream returned:\n" );
- PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) );
-#endif
-
- return result;
-}
-
-
-PaError Pa_WriteStream( PaStream* stream,
- const void *buffer,
- unsigned long frames )
-{
- PaError result = PaUtil_ValidateStreamPointer( stream );
-
-#ifdef PA_LOG_API_CALLS
- PaUtil_DebugPrint("Pa_WriteStream called:\n" );
- PaUtil_DebugPrint("\tPaStream* stream: 0x%p\n", stream );
-#endif
-
- if( result == paNoError )
- {
- if( frames == 0 )
- {
- result = paInternalError; /** @todo should return a different error code */
- }
- else if( buffer == 0 )
- {
- result = paInternalError; /** @todo should return a different error code */
- }
- else
- {
- result = PA_STREAM_INTERFACE(stream)->IsStopped( stream );
- if( result == 0 )
- {
- result = PA_STREAM_INTERFACE(stream)->Write( stream, buffer, frames );
- }
- else if( result == 1 )
- {
- result = paStreamIsStopped;
- }
- }
- }
-
-#ifdef PA_LOG_API_CALLS
- PaUtil_DebugPrint("Pa_WriteStream returned:\n" );
- PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) );
-#endif
-
- return result;
-}
-
-signed long Pa_GetStreamReadAvailable( PaStream* stream )
-{
- PaError error = PaUtil_ValidateStreamPointer( stream );
- signed long result;
-
-#ifdef PA_LOG_API_CALLS
- PaUtil_DebugPrint("Pa_GetStreamReadAvailable called:\n" );
- PaUtil_DebugPrint("\tPaStream* stream: 0x%p\n", stream );
-#endif
-
- if( error != paNoError )
- {
- result = 0;
-
-#ifdef PA_LOG_API_CALLS
- PaUtil_DebugPrint("Pa_GetStreamReadAvailable returned:\n" );
- PaUtil_DebugPrint("\tunsigned long: 0 [ PaError error: %d ( %s ) ]\n\n", error, Pa_GetErrorText( error ) );
-#endif
-
- }
- else
- {
- result = PA_STREAM_INTERFACE(stream)->GetReadAvailable( stream );
-
-#ifdef PA_LOG_API_CALLS
- PaUtil_DebugPrint("Pa_GetStreamReadAvailable returned:\n" );
- PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) );
-#endif
-
- }
-
- return result;
-}
-
-
-signed long Pa_GetStreamWriteAvailable( PaStream* stream )
-{
- PaError error = PaUtil_ValidateStreamPointer( stream );
- signed long result;
-
-#ifdef PA_LOG_API_CALLS
- PaUtil_DebugPrint("Pa_GetStreamWriteAvailable called:\n" );
- PaUtil_DebugPrint("\tPaStream* stream: 0x%p\n", stream );
-#endif
-
- if( error != paNoError )
- {
- result = 0;
-
-#ifdef PA_LOG_API_CALLS
- PaUtil_DebugPrint("Pa_GetStreamWriteAvailable returned:\n" );
- PaUtil_DebugPrint("\tunsigned long: 0 [ PaError error: %d ( %s ) ]\n\n", error, Pa_GetErrorText( error ) );
-#endif
-
- }
- else
- {
- result = PA_STREAM_INTERFACE(stream)->GetWriteAvailable( stream );
-
-#ifdef PA_LOG_API_CALLS
- PaUtil_DebugPrint("Pa_GetStreamWriteAvailable returned:\n" );
- PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) );
-#endif
-
- }
-
- return result;
-}
-
-
-PaError Pa_GetSampleSize( PaSampleFormat format )
-{
- int result;
-
-#ifdef PA_LOG_API_CALLS
- PaUtil_DebugPrint("Pa_GetSampleSize called:\n" );
- PaUtil_DebugPrint("\tPaSampleFormat format: %d\n", format );
-#endif
-
- switch( format & ~paNonInterleaved )
- {
-
- case paUInt8:
- case paInt8:
- result = 1;
- break;
-
- case paInt16:
- result = 2;
- break;
-
- case paInt24:
- result = 3;
- break;
-
- case paFloat32:
- case paInt32:
- result = 4;
- break;
-
- default:
- result = paSampleFormatNotSupported;
- break;
- }
-
-#ifdef PA_LOG_API_CALLS
- PaUtil_DebugPrint("Pa_GetSampleSize returned:\n" );
- if( result > 0 )
- PaUtil_DebugPrint("\tint: %d\n\n", result );
- else
- PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) );
-#endif
-
- return (PaError) result;
-}
-
+/*
+ * $Id: pa_front.c,v 1.1.2.51 2005/02/05 15:52:12 rossbencina Exp $
+ * Portable Audio I/O Library Multi-Host API front end
+ * Validate function parameters and manage multiple host APIs.
+ *
+ * Based on the Open Source API proposed by Ross Bencina
+ * Copyright (c) 1999-2002 Ross Bencina, Phil Burk
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * Any person wishing to distribute modifications to the Software is
+ * requested to send the modifications to the original developer so that
+ * they can be incorporated into the canonical version.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+ * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+/*
+ * PJMEDIA - Multimedia over IP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/* doxygen index page */
+/** @mainpage
+
+PortAudio is an open-source cross-platform †CË library for audio input
+and output. It is designed to simplify the porting of audio applications
+between various platforms, and also to simplify the development of audio
+software in general by hiding the complexities of device interfacing.
+
+See the PortAudio website for further information http://www.portaudio.com/
+
+This documentation pertains to PortAudio V19, API version 2.0 which is
+currently under development. API version 2.0 differs in a number of ways from
+previous versions, please consult the enhancement proposals for further details:
+http://www.portaudio.com/docs/proposals/index.html
+
+This documentation is under construction. Things you might be interested in
+include:
+
+- The PortAudio API 2.0, as documented in portaudio.h
+
+- The <a href="todo.html">TODO List</a>
+
+Feel free to pick an item off TODO list and fix/implement it. You may want to
+enquire about status on the PortAudio mailing list first.
+*/
+
+
+/** @file
+ @brief Implements public PortAudio API, checks some errors, forwards to
+ host API implementations.
+
+ Implements the functions defined in the PortAudio API, checks for
+ some parameter and state inconsistencies and forwards API requests to
+ specific Host API implementations (via the interface declared in
+ pa_hostapi.h), and Streams (via the interface declared in pa_stream.h).
+
+ This file handles initialization and termination of Host API
+ implementations via initializers stored in the paHostApiInitializers
+ global variable.
+
+ Some utility functions declared in pa_util.h are implemented in this file.
+
+ All PortAudio API functions can be conditionally compiled with logging code.
+ To compile with logging, define the PA_LOG_API_CALLS precompiler symbol.
+
+ @todo Consider adding host API specific error text in Pa_GetErrorText() for
+ paUnanticipatedHostError
+
+ @todo Consider adding a new error code for when (inputParameters == NULL)
+ && (outputParameters == NULL)
+
+ @todo review whether Pa_CloseStream() should call the interface's
+ CloseStream function if aborting the stream returns an error code.
+
+ @todo Create new error codes if a NULL buffer pointer, or a
+ zero frame count is passed to Pa_ReadStream or Pa_WriteStream.
+*/
+
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <memory.h>
+#include <string.h>
+#include <assert.h> /* needed by PA_VALIDATE_ENDIANNESS */
+
+#include "portaudio.h"
+#include "pa_util.h"
+#include "pa_endianness.h"
+#include "pa_types.h"
+#include "pa_hostapi.h"
+#include "pa_stream.h"
+
+#include "pa_trace.h"
+
+
+#define PA_VERSION_ 1899
+#define PA_VERSION_TEXT_ "PortAudio V19-devel"
+
+
+
+/* #define PA_LOG_API_CALLS */
+
+/*
+ The basic format for log messages is described below. If you need to
+ add any log messages, please follow this format.
+
+ Function entry (void function):
+
+ "FunctionName called.\n"
+
+ Function entry (non void function):
+
+ "FunctionName called:\n"
+ "\tParam1Type param1: param1Value\n"
+ "\tParam2Type param2: param2Value\n" (etc...)
+
+
+ Function exit (no return value):
+
+ "FunctionName returned.\n"
+
+ Function exit (simple return value):
+
+ "FunctionName returned:\n"
+ "\tReturnType: returnValue\n\n"
+
+ If the return type is an error code, the error text is displayed in ()
+
+ If the return type is not an error code, but has taken a special value
+ because an error occurred, then the reason for the error is shown in []
+
+ If the return type is a struct ptr, the struct is dumped.
+
+ See the code below for examples
+*/
+
+
+int Pa_GetVersion( void )
+{
+ return PA_VERSION_;
+}
+
+
+const char* Pa_GetVersionText( void )
+{
+ return PA_VERSION_TEXT_;
+}
+
+
+
+#define PA_LAST_HOST_ERROR_TEXT_LENGTH_ 1024
+
+static char lastHostErrorText_[ PA_LAST_HOST_ERROR_TEXT_LENGTH_ + 1 ] = {0};
+
+static PaHostErrorInfo lastHostErrorInfo_ = { (PaHostApiTypeId)-1, 0, lastHostErrorText_ };
+
+
+void PaUtil_SetLastHostErrorInfo( PaHostApiTypeId hostApiType, long errorCode,
+ const char *errorText )
+{
+ lastHostErrorInfo_.hostApiType = hostApiType;
+ lastHostErrorInfo_.errorCode = errorCode;
+
+ strncpy( lastHostErrorText_, errorText, PA_LAST_HOST_ERROR_TEXT_LENGTH_ );
+}
+
+
+void PaUtil_DebugPrint( const char *format, ... )
+{
+ va_list ap;
+
+ va_start( ap, format );
+ vfprintf( stderr, format, ap );
+ va_end( ap );
+
+ fflush( stderr );
+}
+
+
+static PaUtilHostApiRepresentation **hostApis_ = 0;
+static int hostApisCount_ = 0;
+static int initializationCount_ = 0;
+static int deviceCount_ = 0;
+
+PaUtilStreamRepresentation *firstOpenStream_ = NULL;
+
+
+#define PA_IS_INITIALISED_ (initializationCount_ != 0)
+
+
+static int CountHostApiInitializers( void )
+{
+ int result = 0;
+
+ while( paHostApiInitializers[ result ] != 0 )
+ ++result;
+ return result;
+}
+
+
+static void TerminateHostApis( void )
+{
+ /* terminate in reverse order from initialization */
+
+ while( hostApisCount_ > 0 )
+ {
+ --hostApisCount_;
+ hostApis_[hostApisCount_]->Terminate( hostApis_[hostApisCount_] );
+ }
+ hostApisCount_ = 0;
+ deviceCount_ = 0;
+
+ if( hostApis_ != 0 )
+ PaUtil_FreeMemory( hostApis_ );
+ hostApis_ = 0;
+}
+
+
+static PaError InitializeHostApis( void )
+{
+ PaError result = paNoError;
+ int i, initializerCount, baseDeviceIndex;
+
+ initializerCount = CountHostApiInitializers();
+
+ hostApis_ = (PaUtilHostApiRepresentation**)PaUtil_AllocateMemory(
+ sizeof(PaUtilHostApiRepresentation*) * initializerCount );
+ if( !hostApis_ )
+ {
+ result = paInsufficientMemory;
+ goto error;
+ }
+
+ hostApisCount_ = 0;
+ deviceCount_ = 0;
+ baseDeviceIndex = 0;
+
+ for( i=0; i< initializerCount; ++i )
+ {
+ hostApis_[hostApisCount_] = NULL;
+ result = paHostApiInitializers[i]( &hostApis_[hostApisCount_], hostApisCount_ );
+ if( result != paNoError )
+ goto error;
+
+ if( hostApis_[hostApisCount_] )
+ {
+
+ hostApis_[hostApisCount_]->privatePaFrontInfo.baseDeviceIndex = baseDeviceIndex;
+
+ if( hostApis_[hostApisCount_]->info.defaultInputDevice != paNoDevice )
+ hostApis_[hostApisCount_]->info.defaultInputDevice += baseDeviceIndex;
+
+ if( hostApis_[hostApisCount_]->info.defaultOutputDevice != paNoDevice )
+ hostApis_[hostApisCount_]->info.defaultOutputDevice += baseDeviceIndex;
+
+ baseDeviceIndex += hostApis_[hostApisCount_]->info.deviceCount;
+ deviceCount_ += hostApis_[hostApisCount_]->info.deviceCount;
+
+ ++hostApisCount_;
+ }
+ }
+
+ return result;
+
+error:
+ TerminateHostApis();
+ return result;
+}
+
+
+/*
+ FindHostApi() finds the index of the host api to which
+ <device> belongs and returns it. if <hostSpecificDeviceIndex> is
+ non-null, the host specific device index is returned in it.
+ returns -1 if <device> is out of range.
+
+*/
+static int FindHostApi( PaDeviceIndex device, int *hostSpecificDeviceIndex )
+{
+ int i=0;
+
+ if( !PA_IS_INITIALISED_ )
+ return -1;
+
+ if( device < 0 )
+ return -1;
+
+ while( i < hostApisCount_
+ && device >= hostApis_[i]->info.deviceCount )
+ {
+
+ device -= hostApis_[i]->info.deviceCount;
+ ++i;
+ }
+
+ if( i >= hostApisCount_ )
+ return -1;
+
+ if( hostSpecificDeviceIndex )
+ *hostSpecificDeviceIndex = device;
+
+ return i;
+}
+
+
+static void AddOpenStream( PaStream* stream )
+{
+ ((PaUtilStreamRepresentation*)stream)->nextOpenStream = firstOpenStream_;
+ firstOpenStream_ = (PaUtilStreamRepresentation*)stream;
+}
+
+
+static void RemoveOpenStream( PaStream* stream )
+{
+ PaUtilStreamRepresentation *previous = NULL;
+ PaUtilStreamRepresentation *current = firstOpenStream_;
+
+ while( current != NULL )
+ {
+ if( ((PaStream*)current) == stream )
+ {
+ if( previous == NULL )
+ {
+ firstOpenStream_ = current->nextOpenStream;
+ }
+ else
+ {
+ previous->nextOpenStream = current->nextOpenStream;
+ }
+ return;
+ }
+ else
+ {
+ previous = current;
+ current = current->nextOpenStream;
+ }
+ }
+}
+
+
+static void CloseOpenStreams( void )
+{
+ /* we call Pa_CloseStream() here to ensure that the same destruction
+ logic is used for automatically closed streams */
+
+ while( firstOpenStream_ != NULL )
+ Pa_CloseStream( firstOpenStream_ );
+}
+
+
+PaError Pa_Initialize( void )
+{
+ PaError result;
+
+#ifdef PA_LOG_API_CALLS
+ PaUtil_DebugPrint( "Pa_Initialize called.\n" );
+#endif
+
+ if( PA_IS_INITIALISED_ )
+ {
+ ++initializationCount_;
+ result = paNoError;
+ }
+ else
+ {
+ PA_VALIDATE_TYPE_SIZES;
+ PA_VALIDATE_ENDIANNESS;
+
+ PaUtil_InitializeClock();
+ PaUtil_ResetTraceMessages();
+
+ result = InitializeHostApis();
+ if( result == paNoError )
+ ++initializationCount_;
+ }
+
+#ifdef PA_LOG_API_CALLS
+ PaUtil_DebugPrint( "Pa_Initialize returned:\n" );
+ PaUtil_DebugPrint( "\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) );
+#endif
+
+ return result;
+}
+
+
+PaError Pa_Terminate( void )
+{
+ PaError result;
+
+#ifdef PA_LOG_API_CALLS
+ PaUtil_DebugPrint("Pa_Terminate called.\n" );
+#endif
+
+ if( PA_IS_INITIALISED_ )
+ {
+ if( --initializationCount_ == 0 )
+ {
+ CloseOpenStreams();
+
+ TerminateHostApis();
+
+ PaUtil_DumpTraceMessages();
+ }
+ result = paNoError;
+ }
+ else
+ {
+ result= paNotInitialized;
+ }
+
+#ifdef PA_LOG_API_CALLS
+ PaUtil_DebugPrint("Pa_Terminate returned:\n" );
+ PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) );
+#endif
+
+ return result;
+}
+
+
+const PaHostErrorInfo* Pa_GetLastHostErrorInfo( void )
+{
+ return &lastHostErrorInfo_;
+}
+
+
+const char *Pa_GetErrorText( PaError errorCode )
+{
+ const char *result;
+
+ switch( errorCode )
+ {
+ case paNoError: result = "Success"; break;
+ case paNotInitialized: result = "PortAudio not initialized"; break;
+ /** @todo could catenate the last host error text to result in the case of paUnanticipatedHostError */
+ case paUnanticipatedHostError: result = "Unanticipated host error"; break;
+ case paInvalidChannelCount: result = "Invalid number of channels"; break;
+ case paInvalidSampleRate: result = "Invalid sample rate"; break;
+ case paInvalidDevice: result = "Invalid device"; break;
+ case paInvalidFlag: result = "Invalid flag"; break;
+ case paSampleFormatNotSupported: result = "Sample format not supported"; break;
+ case paBadIODeviceCombination: result = "Illegal combination of I/O devices"; break;
+ case paInsufficientMemory: result = "Insufficient memory"; break;
+ case paBufferTooBig: result = "Buffer too big"; break;
+ case paBufferTooSmall: result = "Buffer too small"; break;
+ case paNullCallback: result = "No callback routine specified"; break;
+ case paBadStreamPtr: result = "Invalid stream pointer"; break;
+ case paTimedOut: result = "Wait timed out"; break;
+ case paInternalError: result = "Internal PortAudio error"; break;
+ case paDeviceUnavailable: result = "Device unavailable"; break;
+ case paIncompatibleHostApiSpecificStreamInfo: result = "Incompatible host API specific stream info"; break;
+ case paStreamIsStopped: result = "Stream is stopped"; break;
+ case paStreamIsNotStopped: result = "Stream is not stopped"; break;
+ case paInputOverflowed: result = "Input overflowed"; break;
+ case paOutputUnderflowed: result = "Output underflowed"; break;
+ case paHostApiNotFound: result = "Host API not found"; break;
+ case paInvalidHostApi: result = "Invalid host API"; break;
+ case paCanNotReadFromACallbackStream: result = "Can't read from a callback stream"; break;
+ case paCanNotWriteToACallbackStream: result = "Can't write to a callback stream"; break;
+ case paCanNotReadFromAnOutputOnlyStream: result = "Can't read from an output only stream"; break;
+ case paCanNotWriteToAnInputOnlyStream: result = "Can't write to an input only stream"; break;
+ default: result = "Illegal error number"; break;
+ }
+ return result;
+}
+
+
+PaHostApiIndex Pa_HostApiTypeIdToHostApiIndex( PaHostApiTypeId type )
+{
+ PaHostApiIndex result;
+ int i;
+
+#ifdef PA_LOG_API_CALLS
+ PaUtil_DebugPrint("Pa_HostApiTypeIdToHostApiIndex called:\n" );
+ PaUtil_DebugPrint("\tPaHostApiTypeId type: %d\n", type );
+#endif
+
+ if( !PA_IS_INITIALISED_ )
+ {
+ result = paNotInitialized;
+ }
+ else
+ {
+ result = paHostApiNotFound;
+
+ for( i=0; i < hostApisCount_; ++i )
+ {
+ if( hostApis_[i]->info.type == type )
+ {
+ result = i;
+ break;
+ }
+ }
+ }
+
+#ifdef PA_LOG_API_CALLS
+ PaUtil_DebugPrint("Pa_HostApiTypeIdToHostApiIndex returned:\n" );
+ if( result < 0 )
+ PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) );
+ else
+ PaUtil_DebugPrint("\tPaHostApiIndex: %d\n\n", result );
+#endif
+
+ return result;
+}
+
+
+PaError PaUtil_GetHostApiRepresentation( struct PaUtilHostApiRepresentation **hostApi,
+ PaHostApiTypeId type )
+{
+ PaError result;
+ int i;
+
+ if( !PA_IS_INITIALISED_ )
+ {
+ result = paNotInitialized;
+ }
+ else
+ {
+ result = paHostApiNotFound;
+
+ for( i=0; i < hostApisCount_; ++i )
+ {
+ if( hostApis_[i]->info.type == type )
+ {
+ *hostApi = hostApis_[i];
+ result = paNoError;
+ break;
+ }
+ }
+ }
+
+ return result;
+}
+
+
+PaError PaUtil_DeviceIndexToHostApiDeviceIndex(
+ PaDeviceIndex *hostApiDevice, PaDeviceIndex device, struct PaUtilHostApiRepresentation *hostApi )
+{
+ PaError result;
+ PaDeviceIndex x;
+
+ x = device - hostApi->privatePaFrontInfo.baseDeviceIndex;
+
+ if( x < 0 || x >= hostApi->info.deviceCount )
+ {
+ result = paInvalidDevice;
+ }
+ else
+ {
+ *hostApiDevice = x;
+ result = paNoError;
+ }
+
+ return result;
+}
+
+
+PaHostApiIndex Pa_GetHostApiCount( void )
+{
+ int result;
+
+#ifdef PA_LOG_API_CALLS
+ PaUtil_DebugPrint("Pa_GetHostApiCount called.\n" );
+#endif
+
+ if( !PA_IS_INITIALISED_ )
+ {
+ result = paNotInitialized;
+ }
+ else
+ {
+ result = hostApisCount_;
+ }
+
+#ifdef PA_LOG_API_CALLS
+ PaUtil_DebugPrint("Pa_GetHostApiCount returned:\n" );
+ if( result < 0 )
+ PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) );
+ else
+ PaUtil_DebugPrint("\tPaHostApiIndex %d\n\n", result );
+#endif
+
+ return result;
+}
+
+
+PaHostApiIndex Pa_GetDefaultHostApi( void )
+{
+ int result;
+
+#ifdef PA_LOG_API_CALLS
+ PaUtil_DebugPrint("Pa_GetDefaultHostApi called.\n" );
+#endif
+
+ if( !PA_IS_INITIALISED_ )
+ {
+ result = paNotInitialized;
+ }
+ else
+ {
+ result = paDefaultHostApiIndex;
+
+ /* internal consistency check: make sure that the default host api
+ index is within range */
+
+ if( result < 0 || result >= hostApisCount_ )
+ {
+ result = paInternalError;
+ }
+ }
+
+#ifdef PA_LOG_API_CALLS
+ PaUtil_DebugPrint("Pa_GetDefaultHostApi returned:\n" );
+ if( result < 0 )
+ PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) );
+ else
+ PaUtil_DebugPrint("\tPaHostApiIndex %d\n\n", result );
+#endif
+
+ return result;
+}
+
+
+const PaHostApiInfo* Pa_GetHostApiInfo( PaHostApiIndex hostApi )
+{
+ PaHostApiInfo *info;
+
+#ifdef PA_LOG_API_CALLS
+ PaUtil_DebugPrint("Pa_GetHostApiInfo called:\n" );
+ PaUtil_DebugPrint("\tPaHostApiIndex hostApi: %d\n", hostApi );
+#endif
+
+ if( !PA_IS_INITIALISED_ )
+ {
+ info = NULL;
+
+#ifdef PA_LOG_API_CALLS
+ PaUtil_DebugPrint("Pa_GetHostApiInfo returned:\n" );
+ PaUtil_DebugPrint("\tPaHostApiInfo*: NULL [ PortAudio not initialized ]\n\n" );
+#endif
+
+ }
+ else if( hostApi < 0 || hostApi >= hostApisCount_ )
+ {
+ info = NULL;
+
+#ifdef PA_LOG_API_CALLS
+ PaUtil_DebugPrint("Pa_GetHostApiInfo returned:\n" );
+ PaUtil_DebugPrint("\tPaHostApiInfo*: NULL [ hostApi out of range ]\n\n" );
+#endif
+
+ }
+ else
+ {
+ info = &hostApis_[hostApi]->info;
+
+#ifdef PA_LOG_API_CALLS
+ PaUtil_DebugPrint("Pa_GetHostApiInfo returned:\n" );
+ PaUtil_DebugPrint("\tPaHostApiInfo*: 0x%p\n", info );
+ PaUtil_DebugPrint("\t{" );
+ PaUtil_DebugPrint("\t\tint structVersion: %d\n", info->structVersion );
+ PaUtil_DebugPrint("\t\tPaHostApiTypeId type: %d\n", info->type );
+ PaUtil_DebugPrint("\t\tconst char *name: %s\n\n", info->name );
+ PaUtil_DebugPrint("\t}\n\n" );
+#endif
+
+ }
+
+ return info;
+}
+
+
+PaDeviceIndex Pa_HostApiDeviceIndexToDeviceIndex( PaHostApiIndex hostApi, int hostApiDeviceIndex )
+{
+ PaDeviceIndex result;
+
+#ifdef PA_LOG_API_CALLS
+ PaUtil_DebugPrint("Pa_HostApiDeviceIndexToPaDeviceIndex called:\n" );
+ PaUtil_DebugPrint("\tPaHostApiIndex hostApi: %d\n", hostApi );
+ PaUtil_DebugPrint("\tint hostApiDeviceIndex: %d\n", hostApiDeviceIndex );
+#endif
+
+ if( !PA_IS_INITIALISED_ )
+ {
+ result = paNotInitialized;
+ }
+ else
+ {
+ if( hostApi < 0 || hostApi >= hostApisCount_ )
+ {
+ result = paInvalidHostApi;
+ }
+ else
+ {
+ if( hostApiDeviceIndex < 0 ||
+ hostApiDeviceIndex >= hostApis_[hostApi]->info.deviceCount )
+ {
+ result = paInvalidDevice;
+ }
+ else
+ {
+ result = hostApis_[hostApi]->privatePaFrontInfo.baseDeviceIndex + hostApiDeviceIndex;
+ }
+ }
+ }
+
+#ifdef PA_LOG_API_CALLS
+ PaUtil_DebugPrint("Pa_HostApiDeviceIndexToPaDeviceIndex returned:\n" );
+ if( result < 0 )
+ PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) );
+ else
+ PaUtil_DebugPrint("\tPaDeviceIndex: %d\n\n", result );
+#endif
+
+ return result;
+}
+
+
+PaDeviceIndex Pa_GetDeviceCount( void )
+{
+ PaDeviceIndex result;
+
+#ifdef PA_LOG_API_CALLS
+ PaUtil_DebugPrint("Pa_GetDeviceCount called.\n" );
+#endif
+
+ if( !PA_IS_INITIALISED_ )
+ {
+ result = paNotInitialized;
+ }
+ else
+ {
+ result = deviceCount_;
+ }
+
+#ifdef PA_LOG_API_CALLS
+ PaUtil_DebugPrint("Pa_GetDeviceCount returned:\n" );
+ if( result < 0 )
+ PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) );
+ else
+ PaUtil_DebugPrint("\tPaDeviceIndex: %d\n\n", result );
+#endif
+
+ return result;
+}
+
+
+PaDeviceIndex Pa_GetDefaultInputDevice( void )
+{
+ PaHostApiIndex hostApi;
+ PaDeviceIndex result;
+
+#ifdef PA_LOG_API_CALLS
+ PaUtil_DebugPrint("Pa_GetDefaultInputDevice called.\n" );
+#endif
+
+ hostApi = Pa_GetDefaultHostApi();
+ if( hostApi < 0 )
+ {
+ result = paNoDevice;
+ }
+ else
+ {
+ result = hostApis_[hostApi]->info.defaultInputDevice;
+ }
+
+#ifdef PA_LOG_API_CALLS
+ PaUtil_DebugPrint("Pa_GetDefaultInputDevice returned:\n" );
+ PaUtil_DebugPrint("\tPaDeviceIndex: %d\n\n", result );
+#endif
+
+ return result;
+}
+
+
+PaDeviceIndex Pa_GetDefaultOutputDevice( void )
+{
+ PaHostApiIndex hostApi;
+ PaDeviceIndex result;
+
+#ifdef PA_LOG_API_CALLS
+ PaUtil_DebugPrint("Pa_GetDefaultOutputDevice called.\n" );
+#endif
+
+ hostApi = Pa_GetDefaultHostApi();
+ if( hostApi < 0 )
+ {
+ result = paNoDevice;
+ }
+ else
+ {
+ result = hostApis_[hostApi]->info.defaultOutputDevice;
+ }
+
+#ifdef PA_LOG_API_CALLS
+ PaUtil_DebugPrint("Pa_GetDefaultOutputDevice returned:\n" );
+ PaUtil_DebugPrint("\tPaDeviceIndex: %d\n\n", result );
+#endif
+
+ return result;
+}
+
+
+const PaDeviceInfo* Pa_GetDeviceInfo( PaDeviceIndex device )
+{
+ int hostSpecificDeviceIndex;
+ int hostApiIndex = FindHostApi( device, &hostSpecificDeviceIndex );
+ PaDeviceInfo *result;
+
+
+#ifdef PA_LOG_API_CALLS
+ PaUtil_DebugPrint("Pa_GetDeviceInfo called:\n" );
+ PaUtil_DebugPrint("\tPaDeviceIndex device: %d\n", device );
+#endif
+
+ if( hostApiIndex < 0 )
+ {
+ result = NULL;
+
+#ifdef PA_LOG_API_CALLS
+ PaUtil_DebugPrint("Pa_GetDeviceInfo returned:\n" );
+ PaUtil_DebugPrint("\tPaDeviceInfo* NULL [ invalid device index ]\n\n" );
+#endif
+
+ }
+ else
+ {
+ result = hostApis_[hostApiIndex]->deviceInfos[ hostSpecificDeviceIndex ];
+
+#ifdef PA_LOG_API_CALLS
+ PaUtil_DebugPrint("Pa_GetDeviceInfo returned:\n" );
+ PaUtil_DebugPrint("\tPaDeviceInfo*: 0x%p:\n", result );
+ PaUtil_DebugPrint("\t{\n" );
+
+ PaUtil_DebugPrint("\t\tint structVersion: %d\n", result->structVersion );
+ PaUtil_DebugPrint("\t\tconst char *name: %s\n", result->name );
+ PaUtil_DebugPrint("\t\tPaHostApiIndex hostApi: %d\n", result->hostApi );
+ PaUtil_DebugPrint("\t\tint maxInputChannels: %d\n", result->maxInputChannels );
+ PaUtil_DebugPrint("\t\tint maxOutputChannels: %d\n", result->maxOutputChannels );
+ PaUtil_DebugPrint("\t}\n\n" );
+#endif
+
+ }
+
+ return result;
+}
+
+
+/*
+ SampleFormatIsValid() returns 1 if sampleFormat is a sample format
+ defined in portaudio.h, or 0 otherwise.
+*/
+static int SampleFormatIsValid( PaSampleFormat format )
+{
+ switch( format & ~paNonInterleaved )
+ {
+ case paFloat32: return 1;
+ case paInt16: return 1;
+ case paInt32: return 1;
+ case paInt24: return 1;
+ case paInt8: return 1;
+ case paUInt8: return 1;
+ case paCustomFormat: return 1;
+ default: return 0;
+ }
+}
+
+/*
+ NOTE: make sure this validation list is kept syncronised with the one in
+ pa_hostapi.h
+
+ ValidateOpenStreamParameters() checks that parameters to Pa_OpenStream()
+ conform to the expected values as described below. This function is
+ also designed to be used with the proposed Pa_IsFormatSupported() function.
+
+ There are basically two types of validation that could be performed:
+ Generic conformance validation, and device capability mismatch
+ validation. This function performs only generic conformance validation.
+ Validation that would require knowledge of device capabilities is
+ not performed because of potentially complex relationships between
+ combinations of parameters - for example, even if the sampleRate
+ seems ok, it might not be for a duplex stream - we have no way of
+ checking this in an API-neutral way, so we don't try.
+
+ On success the function returns PaNoError and fills in hostApi,
+ hostApiInputDeviceID, and hostApiOutputDeviceID fields. On failure
+ the function returns an error code indicating the first encountered
+ parameter error.
+
+
+ If ValidateOpenStreamParameters() returns paNoError, the following
+ assertions are guaranteed to be true.
+
+ - at least one of inputParameters & outputParmeters is valid (not NULL)
+
+ - if inputParameters & outputParmeters are both valid, that
+ inputParameters->device & outputParmeters->device both use the same host api
+
+ PaDeviceIndex inputParameters->device
+ - is within range (0 to Pa_GetDeviceCount-1) Or:
+ - is paUseHostApiSpecificDeviceSpecification and
+ inputParameters->hostApiSpecificStreamInfo is non-NULL and refers
+ to a valid host api
+
+ int inputParameters->channelCount
+ - if inputParameters->device is not paUseHostApiSpecificDeviceSpecification, channelCount is > 0
+ - upper bound is NOT validated against device capabilities
+
+ PaSampleFormat inputParameters->sampleFormat
+ - is one of the sample formats defined in portaudio.h
+
+ void *inputParameters->hostApiSpecificStreamInfo
+ - if supplied its hostApi field matches the input device's host Api
+
+ PaDeviceIndex outputParmeters->device
+ - is within range (0 to Pa_GetDeviceCount-1)
+
+ int outputParmeters->channelCount
+ - if inputDevice is valid, channelCount is > 0
+ - upper bound is NOT validated against device capabilities
+
+ PaSampleFormat outputParmeters->sampleFormat
+ - is one of the sample formats defined in portaudio.h
+
+ void *outputParmeters->hostApiSpecificStreamInfo
+ - if supplied its hostApi field matches the output device's host Api
+
+ double sampleRate
+ - is not an 'absurd' rate (less than 1000. or greater than 200000.)
+ - sampleRate is NOT validated against device capabilities
+
+ PaStreamFlags streamFlags
+ - unused platform neutral flags are zero
+ - paNeverDropInput is only used for full-duplex callback streams with
+ variable buffer size (paFramesPerBufferUnspecified)
+*/
+static PaError ValidateOpenStreamParameters(
+ const PaStreamParameters *inputParameters,
+ const PaStreamParameters *outputParameters,
+ double sampleRate,
+ unsigned long framesPerBuffer,
+ PaStreamFlags streamFlags,
+ PaStreamCallback *streamCallback,
+ PaUtilHostApiRepresentation **hostApi,
+ PaDeviceIndex *hostApiInputDevice,
+ PaDeviceIndex *hostApiOutputDevice )
+{
+ int inputHostApiIndex = -1, /* Surpress uninitialised var warnings: compiler does */
+ outputHostApiIndex = -1; /* not see that if inputParameters and outputParame- */
+ /* ters are both nonzero, these indices are set. */
+
+ if( (inputParameters == NULL) && (outputParameters == NULL) )
+ {
+ return paInvalidDevice; /** @todo should be a new error code "invalid device parameters" or something */
+ }
+ else
+ {
+ if( inputParameters == NULL )
+ {
+ *hostApiInputDevice = paNoDevice;
+ }
+ else if( inputParameters->device == paUseHostApiSpecificDeviceSpecification )
+ {
+ if( inputParameters->hostApiSpecificStreamInfo )
+ {
+ inputHostApiIndex = Pa_HostApiTypeIdToHostApiIndex(
+ ((PaUtilHostApiSpecificStreamInfoHeader*)inputParameters->hostApiSpecificStreamInfo)->hostApiType );
+
+ if( inputHostApiIndex != -1 )
+ {
+ *hostApiInputDevice = paUseHostApiSpecificDeviceSpecification;
+ *hostApi = hostApis_[inputHostApiIndex];
+ }
+ else
+ {
+ return paInvalidDevice;
+ }
+ }
+ else
+ {
+ return paInvalidDevice;
+ }
+ }
+ else
+ {
+ if( inputParameters->device < 0 || inputParameters->device >= deviceCount_ )
+ return paInvalidDevice;
+
+ inputHostApiIndex = FindHostApi( inputParameters->device, hostApiInputDevice );
+ if( inputHostApiIndex < 0 )
+ return paInternalError;
+
+ *hostApi = hostApis_[inputHostApiIndex];
+
+ if( inputParameters->channelCount <= 0 )
+ return paInvalidChannelCount;
+
+ if( !SampleFormatIsValid( inputParameters->sampleFormat ) )
+ return paSampleFormatNotSupported;
+
+ if( inputParameters->hostApiSpecificStreamInfo != NULL )
+ {
+ if( ((PaUtilHostApiSpecificStreamInfoHeader*)inputParameters->hostApiSpecificStreamInfo)->hostApiType
+ != (*hostApi)->info.type )
+ return paIncompatibleHostApiSpecificStreamInfo;
+ }
+ }
+
+ if( outputParameters == NULL )
+ {
+ *hostApiOutputDevice = paNoDevice;
+ }
+ else if( outputParameters->device == paUseHostApiSpecificDeviceSpecification )
+ {
+ if( outputParameters->hostApiSpecificStreamInfo )
+ {
+ outputHostApiIndex = Pa_HostApiTypeIdToHostApiIndex(
+ ((PaUtilHostApiSpecificStreamInfoHeader*)outputParameters->hostApiSpecificStreamInfo)->hostApiType );
+
+ if( outputHostApiIndex != -1 )
+ {
+ *hostApiOutputDevice = paUseHostApiSpecificDeviceSpecification;
+ *hostApi = hostApis_[outputHostApiIndex];
+ }
+ else
+ {
+ return paInvalidDevice;
+ }
+ }
+ else
+ {
+ return paInvalidDevice;
+ }
+ }
+ else
+ {
+ if( outputParameters->device < 0 || outputParameters->device >= deviceCount_ )
+ return paInvalidDevice;
+
+ outputHostApiIndex = FindHostApi( outputParameters->device, hostApiOutputDevice );
+ if( outputHostApiIndex < 0 )
+ return paInternalError;
+
+ *hostApi = hostApis_[outputHostApiIndex];
+
+ if( outputParameters->channelCount <= 0 )
+ return paInvalidChannelCount;
+
+ if( !SampleFormatIsValid( outputParameters->sampleFormat ) )
+ return paSampleFormatNotSupported;
+
+ if( outputParameters->hostApiSpecificStreamInfo != NULL )
+ {
+ if( ((PaUtilHostApiSpecificStreamInfoHeader*)outputParameters->hostApiSpecificStreamInfo)->hostApiType
+ != (*hostApi)->info.type )
+ return paIncompatibleHostApiSpecificStreamInfo;
+ }
+ }
+
+ if( (inputParameters != NULL) && (outputParameters != NULL) )
+ {
+ /* ensure that both devices use the same API */
+ if( inputHostApiIndex != outputHostApiIndex )
+ return paBadIODeviceCombination;
+ }
+ }
+
+
+ /* Check for absurd sample rates. */
+ if( (sampleRate < 1000.0) || (sampleRate > 200000.0) )
+ return paInvalidSampleRate;
+
+ if( ((streamFlags & ~paPlatformSpecificFlags) & ~(paClipOff | paDitherOff | paNeverDropInput | paPrimeOutputBuffersUsingStreamCallback ) ) != 0 )
+ return paInvalidFlag;
+
+ if( streamFlags & paNeverDropInput )
+ {
+ /* must be a callback stream */
+ if( !streamCallback )
+ return paInvalidFlag;
+
+ /* must be a full duplex stream */
+ if( (inputParameters == NULL) || (outputParameters == NULL) )
+ return paInvalidFlag;
+
+ /* must use paFramesPerBufferUnspecified */
+ if( framesPerBuffer != paFramesPerBufferUnspecified )
+ return paInvalidFlag;
+ }
+
+ return paNoError;
+}
+
+
+PaError Pa_IsFormatSupported( const PaStreamParameters *inputParameters,
+ const PaStreamParameters *outputParameters,
+ double sampleRate )
+{
+ PaError result;
+ PaUtilHostApiRepresentation *hostApi;
+ PaDeviceIndex hostApiInputDevice, hostApiOutputDevice;
+ PaStreamParameters hostApiInputParameters, hostApiOutputParameters;
+ PaStreamParameters *hostApiInputParametersPtr, *hostApiOutputParametersPtr;
+
+
+#ifdef PA_LOG_API_CALLS
+ PaUtil_DebugPrint("Pa_IsFormatSupported called:\n" );
+
+ if( inputParameters == NULL ){
+ PaUtil_DebugPrint("\tPaStreamParameters *inputParameters: NULL\n" );
+ }else{
+ PaUtil_DebugPrint("\tPaStreamParameters *inputParameters: 0x%p\n", inputParameters );
+ PaUtil_DebugPrint("\tPaDeviceIndex inputParameters->device: %d\n", inputParameters->device );
+ PaUtil_DebugPrint("\tint inputParameters->channelCount: %d\n", inputParameters->channelCount );
+ PaUtil_DebugPrint("\tPaSampleFormat inputParameters->sampleFormat: %d\n", inputParameters->sampleFormat );
+ PaUtil_DebugPrint("\tPaTime inputParameters->suggestedLatency: %f\n", inputParameters->suggestedLatency );
+ PaUtil_DebugPrint("\tvoid *inputParameters->hostApiSpecificStreamInfo: 0x%p\n", inputParameters->hostApiSpecificStreamInfo );
+ }
+
+ if( outputParameters == NULL ){
+ PaUtil_DebugPrint("\tPaStreamParameters *outputParameters: NULL\n" );
+ }else{
+ PaUtil_DebugPrint("\tPaStreamParameters *outputParameters: 0x%p\n", outputParameters );
+ PaUtil_DebugPrint("\tPaDeviceIndex outputParameters->device: %d\n", outputParameters->device );
+ PaUtil_DebugPrint("\tint outputParameters->channelCount: %d\n", outputParameters->channelCount );
+ PaUtil_DebugPrint("\tPaSampleFormat outputParameters->sampleFormat: %d\n", outputParameters->sampleFormat );
+ PaUtil_DebugPrint("\tPaTime outputParameters->suggestedLatency: %f\n", outputParameters->suggestedLatency );
+ PaUtil_DebugPrint("\tvoid *outputParameters->hostApiSpecificStreamInfo: 0x%p\n", outputParameters->hostApiSpecificStreamInfo );
+ }
+
+ PaUtil_DebugPrint("\tdouble sampleRate: %g\n", sampleRate );
+#endif
+
+ if( !PA_IS_INITIALISED_ )
+ {
+ result = paNotInitialized;
+
+#ifdef PA_LOG_API_CALLS
+ PaUtil_DebugPrint("Pa_IsFormatSupported returned:\n" );
+ PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) );
+#endif
+ return result;
+ }
+
+ result = ValidateOpenStreamParameters( inputParameters,
+ outputParameters,
+ sampleRate, 0, paNoFlag, 0,
+ &hostApi,
+ &hostApiInputDevice,
+ &hostApiOutputDevice );
+ if( result != paNoError )
+ {
+#ifdef PA_LOG_API_CALLS
+ PaUtil_DebugPrint("Pa_IsFormatSupported returned:\n" );
+ PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) );
+#endif
+ return result;
+ }
+
+
+ if( inputParameters )
+ {
+ hostApiInputParameters.device = hostApiInputDevice;
+ hostApiInputParameters.channelCount = inputParameters->channelCount;
+ hostApiInputParameters.sampleFormat = inputParameters->sampleFormat;
+ hostApiInputParameters.suggestedLatency = inputParameters->suggestedLatency;
+ hostApiInputParameters.hostApiSpecificStreamInfo = inputParameters->hostApiSpecificStreamInfo;
+ hostApiInputParametersPtr = &hostApiInputParameters;
+ }
+ else
+ {
+ hostApiInputParametersPtr = NULL;
+ }
+
+ if( outputParameters )
+ {
+ hostApiOutputParameters.device = hostApiOutputDevice;
+ hostApiOutputParameters.channelCount = outputParameters->channelCount;
+ hostApiOutputParameters.sampleFormat = outputParameters->sampleFormat;
+ hostApiOutputParameters.suggestedLatency = outputParameters->suggestedLatency;
+ hostApiOutputParameters.hostApiSpecificStreamInfo = outputParameters->hostApiSpecificStreamInfo;
+ hostApiOutputParametersPtr = &hostApiOutputParameters;
+ }
+ else
+ {
+ hostApiOutputParametersPtr = NULL;
+ }
+
+ result = hostApi->IsFormatSupported( hostApi,
+ hostApiInputParametersPtr, hostApiOutputParametersPtr,
+ sampleRate );
+
+#ifdef PA_LOG_API_CALLS
+ PaUtil_DebugPrint("Pa_OpenStream returned:\n" );
+ if( result == paFormatIsSupported )
+ PaUtil_DebugPrint("\tPaError: 0 [ paFormatIsSupported ]\n\n" );
+ else
+ PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) );
+#endif
+
+ return result;
+}
+
+
+PaError Pa_OpenStream( PaStream** stream,
+ const PaStreamParameters *inputParameters,
+ const PaStreamParameters *outputParameters,
+ double sampleRate,
+ unsigned long framesPerBuffer,
+ PaStreamFlags streamFlags,
+ PaStreamCallback *streamCallback,
+ void *userData )
+{
+ PaError result;
+ PaUtilHostApiRepresentation *hostApi;
+ PaDeviceIndex hostApiInputDevice, hostApiOutputDevice;
+ PaStreamParameters hostApiInputParameters, hostApiOutputParameters;
+ PaStreamParameters *hostApiInputParametersPtr, *hostApiOutputParametersPtr;
+
+
+#ifdef PA_LOG_API_CALLS
+ PaUtil_DebugPrint("Pa_OpenStream called:\n" );
+ PaUtil_DebugPrint("\tPaStream** stream: 0x%p\n", stream );
+
+ if( inputParameters == NULL ){
+ PaUtil_DebugPrint("\tPaStreamParameters *inputParameters: NULL\n" );
+ }else{
+ PaUtil_DebugPrint("\tPaStreamParameters *inputParameters: 0x%p\n", inputParameters );
+ PaUtil_DebugPrint("\tPaDeviceIndex inputParameters->device: %d\n", inputParameters->device );
+ PaUtil_DebugPrint("\tint inputParameters->channelCount: %d\n", inputParameters->channelCount );
+ PaUtil_DebugPrint("\tPaSampleFormat inputParameters->sampleFormat: %d\n", inputParameters->sampleFormat );
+ PaUtil_DebugPrint("\tPaTime inputParameters->suggestedLatency: %f\n", inputParameters->suggestedLatency );
+ PaUtil_DebugPrint("\tvoid *inputParameters->hostApiSpecificStreamInfo: 0x%p\n", inputParameters->hostApiSpecificStreamInfo );
+ }
+
+ if( outputParameters == NULL ){
+ PaUtil_DebugPrint("\tPaStreamParameters *outputParameters: NULL\n" );
+ }else{
+ PaUtil_DebugPrint("\tPaStreamParameters *outputParameters: 0x%p\n", outputParameters );
+ PaUtil_DebugPrint("\tPaDeviceIndex outputParameters->device: %d\n", outputParameters->device );
+ PaUtil_DebugPrint("\tint outputParameters->channelCount: %d\n", outputParameters->channelCount );
+ PaUtil_DebugPrint("\tPaSampleFormat outputParameters->sampleFormat: %d\n", outputParameters->sampleFormat );
+ PaUtil_DebugPrint("\tPaTime outputParameters->suggestedLatency: %f\n", outputParameters->suggestedLatency );
+ PaUtil_DebugPrint("\tvoid *outputParameters->hostApiSpecificStreamInfo: 0x%p\n", outputParameters->hostApiSpecificStreamInfo );
+ }
+
+ PaUtil_DebugPrint("\tdouble sampleRate: %g\n", sampleRate );
+ PaUtil_DebugPrint("\tunsigned long framesPerBuffer: %d\n", framesPerBuffer );
+ PaUtil_DebugPrint("\tPaStreamFlags streamFlags: 0x%x\n", streamFlags );
+ PaUtil_DebugPrint("\tPaStreamCallback *streamCallback: 0x%p\n", streamCallback );
+ PaUtil_DebugPrint("\tvoid *userData: 0x%p\n", userData );
+#endif
+
+ if( !PA_IS_INITIALISED_ )
+ {
+ result = paNotInitialized;
+
+#ifdef PA_LOG_API_CALLS
+ PaUtil_DebugPrint("Pa_OpenStream returned:\n" );
+ PaUtil_DebugPrint("\t*(PaStream** stream): undefined\n" );
+ PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) );
+#endif
+ return result;
+ }
+
+ /* Check for parameter errors.
+ NOTE: make sure this validation list is kept syncronised with the one
+ in pa_hostapi.h
+ */
+
+ if( stream == NULL )
+ {
+ result = paBadStreamPtr;
+
+#ifdef PA_LOG_API_CALLS
+ PaUtil_DebugPrint("Pa_OpenStream returned:\n" );
+ PaUtil_DebugPrint("\t*(PaStream** stream): undefined\n" );
+ PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) );
+#endif
+ return result;
+ }
+
+ result = ValidateOpenStreamParameters( inputParameters,
+ outputParameters,
+ sampleRate, framesPerBuffer,
+ streamFlags, streamCallback,
+ &hostApi,
+ &hostApiInputDevice,
+ &hostApiOutputDevice );
+ if( result != paNoError )
+ {
+#ifdef PA_LOG_API_CALLS
+ PaUtil_DebugPrint("Pa_OpenStream returned:\n" );
+ PaUtil_DebugPrint("\t*(PaStream** stream): undefined\n" );
+ PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) );
+#endif
+ return result;
+ }
+
+
+ if( inputParameters )
+ {
+ hostApiInputParameters.device = hostApiInputDevice;
+ hostApiInputParameters.channelCount = inputParameters->channelCount;
+ hostApiInputParameters.sampleFormat = inputParameters->sampleFormat;
+ hostApiInputParameters.suggestedLatency = inputParameters->suggestedLatency;
+ hostApiInputParameters.hostApiSpecificStreamInfo = inputParameters->hostApiSpecificStreamInfo;
+ hostApiInputParametersPtr = &hostApiInputParameters;
+ }
+ else
+ {
+ hostApiInputParametersPtr = NULL;
+ }
+
+ if( outputParameters )
+ {
+ hostApiOutputParameters.device = hostApiOutputDevice;
+ hostApiOutputParameters.channelCount = outputParameters->channelCount;
+ hostApiOutputParameters.sampleFormat = outputParameters->sampleFormat;
+ hostApiOutputParameters.suggestedLatency = outputParameters->suggestedLatency;
+ hostApiOutputParameters.hostApiSpecificStreamInfo = outputParameters->hostApiSpecificStreamInfo;
+ hostApiOutputParametersPtr = &hostApiOutputParameters;
+ }
+ else
+ {
+ hostApiOutputParametersPtr = NULL;
+ }
+
+ result = hostApi->OpenStream( hostApi, stream,
+ hostApiInputParametersPtr, hostApiOutputParametersPtr,
+ sampleRate, framesPerBuffer, streamFlags, streamCallback, userData );
+
+ if( result == paNoError )
+ AddOpenStream( *stream );
+
+
+#ifdef PA_LOG_API_CALLS
+ PaUtil_DebugPrint("Pa_OpenStream returned:\n" );
+ PaUtil_DebugPrint("\t*(PaStream** stream): 0x%p\n", *stream );
+ PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) );
+#endif
+
+ return result;
+}
+
+
+PaError Pa_OpenDefaultStream( PaStream** stream,
+ int inputChannelCount,
+ int outputChannelCount,
+ PaSampleFormat sampleFormat,
+ double sampleRate,
+ unsigned long framesPerBuffer,
+ PaStreamCallback *streamCallback,
+ void *userData )
+{
+ PaError result;
+ PaStreamParameters hostApiInputParameters, hostApiOutputParameters;
+ PaStreamParameters *hostApiInputParametersPtr, *hostApiOutputParametersPtr;
+
+#ifdef PA_LOG_API_CALLS
+ PaUtil_DebugPrint("Pa_OpenDefaultStream called:\n" );
+ PaUtil_DebugPrint("\tPaStream** stream: 0x%p\n", stream );
+ PaUtil_DebugPrint("\tint inputChannelCount: %d\n", inputChannelCount );
+ PaUtil_DebugPrint("\tint outputChannelCount: %d\n", outputChannelCount );
+ PaUtil_DebugPrint("\tPaSampleFormat sampleFormat: %d\n", sampleFormat );
+ PaUtil_DebugPrint("\tdouble sampleRate: %g\n", sampleRate );
+ PaUtil_DebugPrint("\tunsigned long framesPerBuffer: %d\n", framesPerBuffer );
+ PaUtil_DebugPrint("\tPaStreamCallback *streamCallback: 0x%p\n", streamCallback );
+ PaUtil_DebugPrint("\tvoid *userData: 0x%p\n", userData );
+#endif
+
+
+ if( inputChannelCount > 0 )
+ {
+ hostApiInputParameters.device = Pa_GetDefaultInputDevice();
+ hostApiInputParameters.channelCount = inputChannelCount;
+ hostApiInputParameters.sampleFormat = sampleFormat;
+ /* defaultHighInputLatency is used below instead of
+ defaultLowInputLatency because it is more important for the default
+ stream to work reliably than it is for it to work with the lowest
+ latency.
+ */
+ hostApiInputParameters.suggestedLatency =
+ Pa_GetDeviceInfo( hostApiInputParameters.device )->defaultHighInputLatency;
+ hostApiInputParameters.hostApiSpecificStreamInfo = NULL;
+ hostApiInputParametersPtr = &hostApiInputParameters;
+ }
+ else
+ {
+ hostApiInputParametersPtr = NULL;
+ }
+
+ if( outputChannelCount > 0 )
+ {
+ hostApiOutputParameters.device = Pa_GetDefaultOutputDevice();
+ hostApiOutputParameters.channelCount = outputChannelCount;
+ hostApiOutputParameters.sampleFormat = sampleFormat;
+ /* defaultHighOutputLatency is used below instead of
+ defaultLowOutputLatency because it is more important for the default
+ stream to work reliably than it is for it to work with the lowest
+ latency.
+ */
+ hostApiOutputParameters.suggestedLatency =
+ Pa_GetDeviceInfo( hostApiOutputParameters.device )->defaultHighOutputLatency;
+ hostApiOutputParameters.hostApiSpecificStreamInfo = NULL;
+ hostApiOutputParametersPtr = &hostApiOutputParameters;
+ }
+ else
+ {
+ hostApiOutputParametersPtr = NULL;
+ }
+
+
+ result = Pa_OpenStream(
+ stream, hostApiInputParametersPtr, hostApiOutputParametersPtr,
+ sampleRate, framesPerBuffer, paNoFlag, streamCallback, userData );
+
+#ifdef PA_LOG_API_CALLS
+ PaUtil_DebugPrint("Pa_OpenDefaultStream returned:\n" );
+ PaUtil_DebugPrint("\t*(PaStream** stream): 0x%p", *stream );
+ PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) );
+#endif
+
+ return result;
+}
+
+
+PaError PaUtil_ValidateStreamPointer( PaStream* stream )
+{
+ if( !PA_IS_INITIALISED_ ) return paNotInitialized;
+
+ if( stream == NULL ) return paBadStreamPtr;
+
+ if( ((PaUtilStreamRepresentation*)stream)->magic != PA_STREAM_MAGIC )
+ return paBadStreamPtr;
+
+ return paNoError;
+}
+
+
+PaError Pa_CloseStream( PaStream* stream )
+{
+ PaUtilStreamInterface *interface;
+ PaError result = PaUtil_ValidateStreamPointer( stream );
+
+#ifdef PA_LOG_API_CALLS
+ PaUtil_DebugPrint("Pa_CloseStream called:\n" );
+ PaUtil_DebugPrint("\tPaStream* stream: 0x%p\n", stream );
+#endif
+
+ /* always remove the open stream from our list, even if this function
+ eventually returns an error. Otherwise CloseOpenStreams() will
+ get stuck in an infinite loop */
+ RemoveOpenStream( stream ); /* be sure to call this _before_ closing the stream */
+
+ if( result == paNoError )
+ {
+ interface = PA_STREAM_INTERFACE(stream);
+
+ /* abort the stream if it isn't stopped */
+ result = interface->IsStopped( stream );
+ if( result == 1 )
+ result = paNoError;
+ else if( result == 0 )
+ result = interface->Abort( stream );
+
+ if( result == paNoError ) /** @todo REVIEW: shouldn't we close anyway? */
+ result = interface->Close( stream );
+ }
+
+#ifdef PA_LOG_API_CALLS
+ PaUtil_DebugPrint("Pa_CloseStream returned:\n" );
+ PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) );
+#endif
+
+ return result;
+}
+
+
+PaError Pa_SetStreamFinishedCallback( PaStream *stream, PaStreamFinishedCallback* streamFinishedCallback )
+{
+ PaError result = PaUtil_ValidateStreamPointer( stream );
+
+#ifdef PA_LOG_API_CALLS
+ PaUtil_DebugPrint("Pa_SetStreamFinishedCallback called:\n" );
+ PaUtil_DebugPrint("\tPaStream* stream: 0x%p\n", stream );
+ PaUtil_DebugPrint("\tPaStreamFinishedCallback* streamFinishedCallback: 0x%p\n", streamFinishedCallback );
+#endif
+
+ if( result == paNoError )
+ {
+ result = PA_STREAM_INTERFACE(stream)->IsStopped( stream );
+ if( result == 0 )
+ {
+ result = paStreamIsNotStopped ;
+ }
+ if( result == 1 )
+ {
+ PA_STREAM_REP( stream )->streamFinishedCallback = streamFinishedCallback;
+ result = paNoError;
+ }
+ }
+
+#ifdef PA_LOG_API_CALLS
+ PaUtil_DebugPrint("Pa_SetStreamFinishedCallback returned:\n" );
+ PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) );
+#endif
+
+ return result;
+
+}
+
+
+PaError Pa_StartStream( PaStream *stream )
+{
+ PaError result = PaUtil_ValidateStreamPointer( stream );
+
+#ifdef PA_LOG_API_CALLS
+ PaUtil_DebugPrint("Pa_StartStream called:\n" );
+ PaUtil_DebugPrint("\tPaStream* stream: 0x%p\n", stream );
+#endif
+
+ if( result == paNoError )
+ {
+ result = PA_STREAM_INTERFACE(stream)->IsStopped( stream );
+ if( result == 0 )
+ {
+ result = paStreamIsNotStopped ;
+ }
+ else if( result == 1 )
+ {
+ result = PA_STREAM_INTERFACE(stream)->Start( stream );
+ }
+ }
+
+#ifdef PA_LOG_API_CALLS
+ PaUtil_DebugPrint("Pa_StartStream returned:\n" );
+ PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) );
+#endif
+
+ return result;
+}
+
+
+PaError Pa_StopStream( PaStream *stream )
+{
+ PaError result = PaUtil_ValidateStreamPointer( stream );
+
+#ifdef PA_LOG_API_CALLS
+ PaUtil_DebugPrint("Pa_StopStream called\n" );
+ PaUtil_DebugPrint("\tPaStream* stream: 0x%p\n", stream );
+#endif
+
+ if( result == paNoError )
+ {
+ result = PA_STREAM_INTERFACE(stream)->IsStopped( stream );
+ if( result == 0 )
+ {
+ result = PA_STREAM_INTERFACE(stream)->Stop( stream );
+ }
+ else if( result == 1 )
+ {
+ result = paStreamIsStopped;
+ }
+ }
+
+#ifdef PA_LOG_API_CALLS
+ PaUtil_DebugPrint("Pa_StopStream returned:\n" );
+ PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) );
+#endif
+
+ return result;
+}
+
+
+PaError Pa_AbortStream( PaStream *stream )
+{
+ PaError result = PaUtil_ValidateStreamPointer( stream );
+
+#ifdef PA_LOG_API_CALLS
+ PaUtil_DebugPrint("Pa_AbortStream called:\n" );
+ PaUtil_DebugPrint("\tPaStream* stream: 0x%p\n", stream );
+#endif
+
+ if( result == paNoError )
+ {
+ result = PA_STREAM_INTERFACE(stream)->IsStopped( stream );
+ if( result == 0 )
+ {
+ result = PA_STREAM_INTERFACE(stream)->Abort( stream );
+ }
+ else if( result == 1 )
+ {
+ result = paStreamIsStopped;
+ }
+ }
+
+#ifdef PA_LOG_API_CALLS
+ PaUtil_DebugPrint("Pa_AbortStream returned:\n" );
+ PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) );
+#endif
+
+ return result;
+}
+
+
+PaError Pa_IsStreamStopped( PaStream *stream )
+{
+ PaError result = PaUtil_ValidateStreamPointer( stream );
+
+#ifdef PA_LOG_API_CALLS
+ PaUtil_DebugPrint("Pa_IsStreamStopped called:\n" );
+ PaUtil_DebugPrint("\tPaStream* stream: 0x%p\n", stream );
+#endif
+
+ if( result == paNoError )
+ result = PA_STREAM_INTERFACE(stream)->IsStopped( stream );
+
+#ifdef PA_LOG_API_CALLS
+ PaUtil_DebugPrint("Pa_IsStreamStopped returned:\n" );
+ PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) );
+#endif
+
+ return result;
+}
+
+
+PaError Pa_IsStreamActive( PaStream *stream )
+{
+ PaError result = PaUtil_ValidateStreamPointer( stream );
+
+#ifdef PA_LOG_API_CALLS
+ PaUtil_DebugPrint("Pa_IsStreamActive called:\n" );
+ PaUtil_DebugPrint("\tPaStream* stream: 0x%p\n", stream );
+#endif
+
+ if( result == paNoError )
+ result = PA_STREAM_INTERFACE(stream)->IsActive( stream );
+
+#ifdef PA_LOG_API_CALLS
+ PaUtil_DebugPrint("Pa_IsStreamActive returned:\n" );
+ PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) );
+#endif
+
+ return result;
+}
+
+
+const PaStreamInfo* Pa_GetStreamInfo( PaStream *stream )
+{
+ PaError error = PaUtil_ValidateStreamPointer( stream );
+ const PaStreamInfo *result;
+
+#ifdef PA_LOG_API_CALLS
+ PaUtil_DebugPrint("Pa_GetStreamInfo called:\n" );
+ PaUtil_DebugPrint("\tPaStream* stream: 0x%p\n", stream );
+#endif
+
+ if( error != paNoError )
+ {
+ result = 0;
+
+#ifdef PA_LOG_API_CALLS
+ PaUtil_DebugPrint("Pa_GetStreamInfo returned:\n" );
+ PaUtil_DebugPrint("\tconst PaStreamInfo*: 0 [PaError error:%d ( %s )]\n\n", result, error, Pa_GetErrorText( error ) );
+#endif
+
+ }
+ else
+ {
+ result = &PA_STREAM_REP( stream )->streamInfo;
+
+#ifdef PA_LOG_API_CALLS
+ PaUtil_DebugPrint("Pa_GetStreamInfo returned:\n" );
+ PaUtil_DebugPrint("\tconst PaStreamInfo*: 0x%p:\n", result );
+ PaUtil_DebugPrint("\t{" );
+
+ PaUtil_DebugPrint("\t\tint structVersion: %d\n", result->structVersion );
+ PaUtil_DebugPrint("\t\tPaTime inputLatency: %f\n", result->inputLatency );
+ PaUtil_DebugPrint("\t\tPaTime outputLatency: %f\n", result->outputLatency );
+ PaUtil_DebugPrint("\t\tdouble sampleRate: %f\n", result->sampleRate );
+ PaUtil_DebugPrint("\t}\n\n" );
+#endif
+
+ }
+
+ return result;
+}
+
+
+PaTime Pa_GetStreamTime( PaStream *stream )
+{
+ PaError error = PaUtil_ValidateStreamPointer( stream );
+ PaTime result;
+
+#ifdef PA_LOG_API_CALLS
+ PaUtil_DebugPrint("Pa_GetStreamTime called:\n" );
+ PaUtil_DebugPrint("\tPaStream* stream: 0x%p\n", stream );
+#endif
+
+ if( error != paNoError )
+ {
+ result = 0;
+
+#ifdef PA_LOG_API_CALLS
+ PaUtil_DebugPrint("Pa_GetStreamTime returned:\n" );
+ PaUtil_DebugPrint("\tPaTime: 0 [PaError error:%d ( %s )]\n\n", result, error, Pa_GetErrorText( error ) );
+#endif
+
+ }
+ else
+ {
+ result = PA_STREAM_INTERFACE(stream)->GetTime( stream );
+
+#ifdef PA_LOG_API_CALLS
+ PaUtil_DebugPrint("Pa_GetStreamTime returned:\n" );
+ PaUtil_DebugPrint("\tPaTime: %g\n\n", result );
+#endif
+
+ }
+
+ return result;
+}
+
+
+double Pa_GetStreamCpuLoad( PaStream* stream )
+{
+ PaError error = PaUtil_ValidateStreamPointer( stream );
+ double result;
+
+#ifdef PA_LOG_API_CALLS
+ PaUtil_DebugPrint("Pa_GetStreamCpuLoad called:\n" );
+ PaUtil_DebugPrint("\tPaStream* stream: 0x%p\n", stream );
+#endif
+
+ if( error != paNoError )
+ {
+
+ result = 0.0;
+
+#ifdef PA_LOG_API_CALLS
+ PaUtil_DebugPrint("Pa_GetStreamCpuLoad returned:\n" );
+ PaUtil_DebugPrint("\tdouble: 0.0 [PaError error: %d ( %s )]\n\n", error, Pa_GetErrorText( error ) );
+#endif
+
+ }
+ else
+ {
+ result = PA_STREAM_INTERFACE(stream)->GetCpuLoad( stream );
+
+#ifdef PA_LOG_API_CALLS
+ PaUtil_DebugPrint("Pa_GetStreamCpuLoad returned:\n" );
+ PaUtil_DebugPrint("\tdouble: %g\n\n", result );
+#endif
+
+ }
+
+ return result;
+}
+
+
+PaError Pa_ReadStream( PaStream* stream,
+ void *buffer,
+ unsigned long frames )
+{
+ PaError result = PaUtil_ValidateStreamPointer( stream );
+
+#ifdef PA_LOG_API_CALLS
+ PaUtil_DebugPrint("Pa_ReadStream called:\n" );
+ PaUtil_DebugPrint("\tPaStream* stream: 0x%p\n", stream );
+#endif
+
+ if( result == paNoError )
+ {
+ if( frames == 0 )
+ {
+ result = paInternalError; /** @todo should return a different error code */
+ }
+ else if( buffer == 0 )
+ {
+ result = paInternalError; /** @todo should return a different error code */
+ }
+ else
+ {
+ result = PA_STREAM_INTERFACE(stream)->IsStopped( stream );
+ if( result == 0 )
+ {
+ result = PA_STREAM_INTERFACE(stream)->Read( stream, buffer, frames );
+ }
+ else if( result == 1 )
+ {
+ result = paStreamIsStopped;
+ }
+ }
+ }
+
+#ifdef PA_LOG_API_CALLS
+ PaUtil_DebugPrint("Pa_ReadStream returned:\n" );
+ PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) );
+#endif
+
+ return result;
+}
+
+
+PaError Pa_WriteStream( PaStream* stream,
+ const void *buffer,
+ unsigned long frames )
+{
+ PaError result = PaUtil_ValidateStreamPointer( stream );
+
+#ifdef PA_LOG_API_CALLS
+ PaUtil_DebugPrint("Pa_WriteStream called:\n" );
+ PaUtil_DebugPrint("\tPaStream* stream: 0x%p\n", stream );
+#endif
+
+ if( result == paNoError )
+ {
+ if( frames == 0 )
+ {
+ result = paInternalError; /** @todo should return a different error code */
+ }
+ else if( buffer == 0 )
+ {
+ result = paInternalError; /** @todo should return a different error code */
+ }
+ else
+ {
+ result = PA_STREAM_INTERFACE(stream)->IsStopped( stream );
+ if( result == 0 )
+ {
+ result = PA_STREAM_INTERFACE(stream)->Write( stream, buffer, frames );
+ }
+ else if( result == 1 )
+ {
+ result = paStreamIsStopped;
+ }
+ }
+ }
+
+#ifdef PA_LOG_API_CALLS
+ PaUtil_DebugPrint("Pa_WriteStream returned:\n" );
+ PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) );
+#endif
+
+ return result;
+}
+
+signed long Pa_GetStreamReadAvailable( PaStream* stream )
+{
+ PaError error = PaUtil_ValidateStreamPointer( stream );
+ signed long result;
+
+#ifdef PA_LOG_API_CALLS
+ PaUtil_DebugPrint("Pa_GetStreamReadAvailable called:\n" );
+ PaUtil_DebugPrint("\tPaStream* stream: 0x%p\n", stream );
+#endif
+
+ if( error != paNoError )
+ {
+ result = 0;
+
+#ifdef PA_LOG_API_CALLS
+ PaUtil_DebugPrint("Pa_GetStreamReadAvailable returned:\n" );
+ PaUtil_DebugPrint("\tunsigned long: 0 [ PaError error: %d ( %s ) ]\n\n", error, Pa_GetErrorText( error ) );
+#endif
+
+ }
+ else
+ {
+ result = PA_STREAM_INTERFACE(stream)->GetReadAvailable( stream );
+
+#ifdef PA_LOG_API_CALLS
+ PaUtil_DebugPrint("Pa_GetStreamReadAvailable returned:\n" );
+ PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) );
+#endif
+
+ }
+
+ return result;
+}
+
+
+signed long Pa_GetStreamWriteAvailable( PaStream* stream )
+{
+ PaError error = PaUtil_ValidateStreamPointer( stream );
+ signed long result;
+
+#ifdef PA_LOG_API_CALLS
+ PaUtil_DebugPrint("Pa_GetStreamWriteAvailable called:\n" );
+ PaUtil_DebugPrint("\tPaStream* stream: 0x%p\n", stream );
+#endif
+
+ if( error != paNoError )
+ {
+ result = 0;
+
+#ifdef PA_LOG_API_CALLS
+ PaUtil_DebugPrint("Pa_GetStreamWriteAvailable returned:\n" );
+ PaUtil_DebugPrint("\tunsigned long: 0 [ PaError error: %d ( %s ) ]\n\n", error, Pa_GetErrorText( error ) );
+#endif
+
+ }
+ else
+ {
+ result = PA_STREAM_INTERFACE(stream)->GetWriteAvailable( stream );
+
+#ifdef PA_LOG_API_CALLS
+ PaUtil_DebugPrint("Pa_GetStreamWriteAvailable returned:\n" );
+ PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) );
+#endif
+
+ }
+
+ return result;
+}
+
+
+PaError Pa_GetSampleSize( PaSampleFormat format )
+{
+ int result;
+
+#ifdef PA_LOG_API_CALLS
+ PaUtil_DebugPrint("Pa_GetSampleSize called:\n" );
+ PaUtil_DebugPrint("\tPaSampleFormat format: %d\n", format );
+#endif
+
+ switch( format & ~paNonInterleaved )
+ {
+
+ case paUInt8:
+ case paInt8:
+ result = 1;
+ break;
+
+ case paInt16:
+ result = 2;
+ break;
+
+ case paInt24:
+ result = 3;
+ break;
+
+ case paFloat32:
+ case paInt32:
+ result = 4;
+ break;
+
+ default:
+ result = paSampleFormatNotSupported;
+ break;
+ }
+
+#ifdef PA_LOG_API_CALLS
+ PaUtil_DebugPrint("Pa_GetSampleSize returned:\n" );
+ if( result > 0 )
+ PaUtil_DebugPrint("\tint: %d\n\n", result );
+ else
+ PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) );
+#endif
+
+ return (PaError) result;
+}
+
diff --git a/pjmedia/src/pjmedia/portaudio/pa_hostapi.h b/pjmedia/src/pjmedia/portaudio/pa_hostapi.h
index d0550706..aee06357 100644
--- a/pjmedia/src/pjmedia/portaudio/pa_hostapi.h
+++ b/pjmedia/src/pjmedia/portaudio/pa_hostapi.h
@@ -1,244 +1,265 @@
-#ifndef PA_HOSTAPI_H
-#define PA_HOSTAPI_H
-/*
- * $Id: pa_hostapi.h,v 1.1.2.14 2004/01/08 22:01:12 rossbencina Exp $
- * Portable Audio I/O Library
- * host api representation
- *
- * Based on the Open Source API proposed by Ross Bencina
- * Copyright (c) 1999-2002 Ross Bencina, Phil Burk
- *
- * Permission is hereby granted, free of charge, to any person obtaining
- * a copy of this software and associated documentation files
- * (the "Software"), to deal in the Software without restriction,
- * including without limitation the rights to use, copy, modify, merge,
- * publish, distribute, sublicense, and/or sell copies of the Software,
- * and to permit persons to whom the Software is furnished to do so,
- * subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be
- * included in all copies or substantial portions of the Software.
- *
- * Any person wishing to distribute modifications to the Software is
- * requested to send the modifications to the original developer so that
- * they can be incorporated into the canonical version.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
- * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
- * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
- * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
- * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
- */
-
-/** @file
- @brief Interface used by pa_front to virtualize functions which operate on
- host APIs.
-*/
-
-
-#include "portaudio.h"
-
-#ifdef __cplusplus
-extern "C"
-{
-#endif /* __cplusplus */
-
-
-/** **FOR THE USE OF pa_front.c ONLY**
- Do NOT use fields in this structure, they my change at any time.
- Use functions defined in pa_util.h if you think you need functionality
- which can be derived from here.
-*/
-typedef struct PaUtilPrivatePaFrontHostApiInfo {
-
-
- unsigned long baseDeviceIndex;
-}PaUtilPrivatePaFrontHostApiInfo;
-
-
-/** The common header for all data structures whose pointers are passed through
- the hostApiSpecificStreamInfo field of the PaStreamParameters structure.
- Note that in order to keep the public PortAudio interface clean, this structure
- is not used explicitly when declaring hostApiSpecificStreamInfo data structures.
- However, some code in pa_front depends on the first 3 members being equivalent
- with this structure.
- @see PaStreamParameters
-*/
-typedef struct PaUtilHostApiSpecificStreamInfoHeader
-{
- unsigned long size; /**< size of whole structure including this header */
- PaHostApiTypeId hostApiType; /**< host API for which this data is intended */
- unsigned long version; /**< structure version */
-} PaUtilHostApiSpecificStreamInfoHeader;
-
-
-
-/** A structure representing the interface to a host API. Contains both
- concrete data and pointers to functions which implement the interface.
-*/
-typedef struct PaUtilHostApiRepresentation {
- PaUtilPrivatePaFrontHostApiInfo privatePaFrontInfo;
-
- /** The host api implementation should populate the info field. In the
- case of info.defaultInputDevice and info.defaultOutputDevice the
- values stored should be 0 based indices within the host api's own
- device index range (0 to deviceCount). These values will be converted
- to global device indices by pa_front after PaUtilHostApiInitializer()
- returns.
- */
- PaHostApiInfo info;
-
- PaDeviceInfo** deviceInfos;
-
- /**
- (*Terminate)() is guaranteed to be called with a valid <hostApi>
- parameter, which was previously returned from the same implementation's
- initializer.
- */
- void (*Terminate)( struct PaUtilHostApiRepresentation *hostApi );
-
- /**
- The inputParameters and outputParameters pointers should not be saved
- as they will not remain valid after OpenStream is called.
-
-
- The following guarantees are made about parameters to (*OpenStream)():
-
- [NOTE: the following list up to *END PA FRONT VALIDATIONS* should be
- kept in sync with the one for ValidateOpenStreamParameters and
- Pa_OpenStream in pa_front.c]
-
- PaHostApiRepresentation *hostApi
- - is valid for this implementation
-
- PaStream** stream
- - is non-null
-
- - at least one of inputParameters & outputParmeters is valid (not NULL)
-
- - if inputParameters & outputParmeters are both valid, that
- inputParameters->device & outputParmeters->device both use the same host api
-
- PaDeviceIndex inputParameters->device
- - is within range (0 to Pa_CountDevices-1) Or:
- - is paUseHostApiSpecificDeviceSpecification and
- inputParameters->hostApiSpecificStreamInfo is non-NULL and refers
- to a valid host api
-
- int inputParameters->numChannels
- - if inputParameters->device is not paUseHostApiSpecificDeviceSpecification, numInputChannels is > 0
- - upper bound is NOT validated against device capabilities
-
- PaSampleFormat inputParameters->sampleFormat
- - is one of the sample formats defined in portaudio.h
-
- void *inputParameters->hostApiSpecificStreamInfo
- - if supplied its hostApi field matches the input device's host Api
-
- PaDeviceIndex outputParmeters->device
- - is within range (0 to Pa_CountDevices-1)
-
- int outputParmeters->numChannels
- - if inputDevice is valid, numInputChannels is > 0
- - upper bound is NOT validated against device capabilities
-
- PaSampleFormat outputParmeters->sampleFormat
- - is one of the sample formats defined in portaudio.h
-
- void *outputParmeters->hostApiSpecificStreamInfo
- - if supplied its hostApi field matches the output device's host Api
-
- double sampleRate
- - is not an 'absurd' rate (less than 1000. or greater than 200000.)
- - sampleRate is NOT validated against device capabilities
-
- PaStreamFlags streamFlags
- - unused platform neutral flags are zero
- - paNeverDropInput is only used for full-duplex callback streams
- with variable buffer size (paFramesPerBufferUnspecified)
-
- [*END PA FRONT VALIDATIONS*]
-
-
- The following validations MUST be performed by (*OpenStream)():
-
- - check that input device can support numInputChannels
-
- - check that input device can support inputSampleFormat, or that
- we have the capability to convert from outputSampleFormat to
- a native format
-
- - if inputStreamInfo is supplied, validate its contents,
- or return an error if no inputStreamInfo is expected
-
- - check that output device can support numOutputChannels
-
- - check that output device can support outputSampleFormat, or that
- we have the capability to convert from outputSampleFormat to
- a native format
-
- - if outputStreamInfo is supplied, validate its contents,
- or return an error if no outputStreamInfo is expected
-
- - if a full duplex stream is requested, check that the combination
- of input and output parameters is supported
-
- - check that the device supports sampleRate
-
- - alter sampleRate to a close allowable rate if necessary
-
- - validate inputLatency and outputLatency
-
- - validate any platform specific flags, if flags are supplied they
- must be valid.
- */
- PaError (*OpenStream)( struct PaUtilHostApiRepresentation *hostApi,
- PaStream** stream,
- const PaStreamParameters *inputParameters,
- const PaStreamParameters *outputParameters,
- double sampleRate,
- unsigned long framesPerCallback,
- PaStreamFlags streamFlags,
- PaStreamCallback *streamCallback,
- void *userData );
-
-
- PaError (*IsFormatSupported)( struct PaUtilHostApiRepresentation *hostApi,
- const PaStreamParameters *inputParameters,
- const PaStreamParameters *outputParameters,
- double sampleRate );
-} PaUtilHostApiRepresentation;
-
-
-/** Prototype for the initialization function which must be implemented by every
- host API.
-
- @see paHostApiInitializers
-*/
-typedef PaError PaUtilHostApiInitializer( PaUtilHostApiRepresentation**, PaHostApiIndex );
-
-
-/** paHostApiInitializers is a NULL-terminated array of host API initialization
- functions. These functions are called by pa_front to initialize the host APIs
- when the client calls Pa_Initialize().
-
- There is a platform specific file which defines paHostApiInitializers for that
- platform, pa_win/pa_win_hostapis.c contains the Win32 definitions for example.
-*/
-extern PaUtilHostApiInitializer *paHostApiInitializers[];
-
-
-/** The index of the default host API in the paHostApiInitializers array.
-
- There is a platform specific file which defines paDefaultHostApiIndex for that
- platform, see pa_win/pa_win_hostapis.c for example.
-*/
-extern int paDefaultHostApiIndex;
-
-
-#ifdef __cplusplus
-}
-#endif /* __cplusplus */
-#endif /* PA_HOSTAPI_H */
+/*
+ * PJMEDIA - Multimedia over IP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef PA_HOSTAPI_H
+#define PA_HOSTAPI_H
+/*
+ * $Id: pa_hostapi.h,v 1.1.2.14 2004/01/08 22:01:12 rossbencina Exp $
+ * Portable Audio I/O Library
+ * host api representation
+ *
+ * Based on the Open Source API proposed by Ross Bencina
+ * Copyright (c) 1999-2002 Ross Bencina, Phil Burk
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * Any person wishing to distribute modifications to the Software is
+ * requested to send the modifications to the original developer so that
+ * they can be incorporated into the canonical version.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+ * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+/** @file
+ @brief Interface used by pa_front to virtualize functions which operate on
+ host APIs.
+*/
+
+
+#include "portaudio.h"
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif /* __cplusplus */
+
+
+/** **FOR THE USE OF pa_front.c ONLY**
+ Do NOT use fields in this structure, they my change at any time.
+ Use functions defined in pa_util.h if you think you need functionality
+ which can be derived from here.
+*/
+typedef struct PaUtilPrivatePaFrontHostApiInfo {
+
+
+ unsigned long baseDeviceIndex;
+}PaUtilPrivatePaFrontHostApiInfo;
+
+
+/** The common header for all data structures whose pointers are passed through
+ the hostApiSpecificStreamInfo field of the PaStreamParameters structure.
+ Note that in order to keep the public PortAudio interface clean, this structure
+ is not used explicitly when declaring hostApiSpecificStreamInfo data structures.
+ However, some code in pa_front depends on the first 3 members being equivalent
+ with this structure.
+ @see PaStreamParameters
+*/
+typedef struct PaUtilHostApiSpecificStreamInfoHeader
+{
+ unsigned long size; /**< size of whole structure including this header */
+ PaHostApiTypeId hostApiType; /**< host API for which this data is intended */
+ unsigned long version; /**< structure version */
+} PaUtilHostApiSpecificStreamInfoHeader;
+
+
+
+/** A structure representing the interface to a host API. Contains both
+ concrete data and pointers to functions which implement the interface.
+*/
+typedef struct PaUtilHostApiRepresentation {
+ PaUtilPrivatePaFrontHostApiInfo privatePaFrontInfo;
+
+ /** The host api implementation should populate the info field. In the
+ case of info.defaultInputDevice and info.defaultOutputDevice the
+ values stored should be 0 based indices within the host api's own
+ device index range (0 to deviceCount). These values will be converted
+ to global device indices by pa_front after PaUtilHostApiInitializer()
+ returns.
+ */
+ PaHostApiInfo info;
+
+ PaDeviceInfo** deviceInfos;
+
+ /**
+ (*Terminate)() is guaranteed to be called with a valid <hostApi>
+ parameter, which was previously returned from the same implementation's
+ initializer.
+ */
+ void (*Terminate)( struct PaUtilHostApiRepresentation *hostApi );
+
+ /**
+ The inputParameters and outputParameters pointers should not be saved
+ as they will not remain valid after OpenStream is called.
+
+
+ The following guarantees are made about parameters to (*OpenStream)():
+
+ [NOTE: the following list up to *END PA FRONT VALIDATIONS* should be
+ kept in sync with the one for ValidateOpenStreamParameters and
+ Pa_OpenStream in pa_front.c]
+
+ PaHostApiRepresentation *hostApi
+ - is valid for this implementation
+
+ PaStream** stream
+ - is non-null
+
+ - at least one of inputParameters & outputParmeters is valid (not NULL)
+
+ - if inputParameters & outputParmeters are both valid, that
+ inputParameters->device & outputParmeters->device both use the same host api
+
+ PaDeviceIndex inputParameters->device
+ - is within range (0 to Pa_CountDevices-1) Or:
+ - is paUseHostApiSpecificDeviceSpecification and
+ inputParameters->hostApiSpecificStreamInfo is non-NULL and refers
+ to a valid host api
+
+ int inputParameters->numChannels
+ - if inputParameters->device is not paUseHostApiSpecificDeviceSpecification, numInputChannels is > 0
+ - upper bound is NOT validated against device capabilities
+
+ PaSampleFormat inputParameters->sampleFormat
+ - is one of the sample formats defined in portaudio.h
+
+ void *inputParameters->hostApiSpecificStreamInfo
+ - if supplied its hostApi field matches the input device's host Api
+
+ PaDeviceIndex outputParmeters->device
+ - is within range (0 to Pa_CountDevices-1)
+
+ int outputParmeters->numChannels
+ - if inputDevice is valid, numInputChannels is > 0
+ - upper bound is NOT validated against device capabilities
+
+ PaSampleFormat outputParmeters->sampleFormat
+ - is one of the sample formats defined in portaudio.h
+
+ void *outputParmeters->hostApiSpecificStreamInfo
+ - if supplied its hostApi field matches the output device's host Api
+
+ double sampleRate
+ - is not an 'absurd' rate (less than 1000. or greater than 200000.)
+ - sampleRate is NOT validated against device capabilities
+
+ PaStreamFlags streamFlags
+ - unused platform neutral flags are zero
+ - paNeverDropInput is only used for full-duplex callback streams
+ with variable buffer size (paFramesPerBufferUnspecified)
+
+ [*END PA FRONT VALIDATIONS*]
+
+
+ The following validations MUST be performed by (*OpenStream)():
+
+ - check that input device can support numInputChannels
+
+ - check that input device can support inputSampleFormat, or that
+ we have the capability to convert from outputSampleFormat to
+ a native format
+
+ - if inputStreamInfo is supplied, validate its contents,
+ or return an error if no inputStreamInfo is expected
+
+ - check that output device can support numOutputChannels
+
+ - check that output device can support outputSampleFormat, or that
+ we have the capability to convert from outputSampleFormat to
+ a native format
+
+ - if outputStreamInfo is supplied, validate its contents,
+ or return an error if no outputStreamInfo is expected
+
+ - if a full duplex stream is requested, check that the combination
+ of input and output parameters is supported
+
+ - check that the device supports sampleRate
+
+ - alter sampleRate to a close allowable rate if necessary
+
+ - validate inputLatency and outputLatency
+
+ - validate any platform specific flags, if flags are supplied they
+ must be valid.
+ */
+ PaError (*OpenStream)( struct PaUtilHostApiRepresentation *hostApi,
+ PaStream** stream,
+ const PaStreamParameters *inputParameters,
+ const PaStreamParameters *outputParameters,
+ double sampleRate,
+ unsigned long framesPerCallback,
+ PaStreamFlags streamFlags,
+ PaStreamCallback *streamCallback,
+ void *userData );
+
+
+ PaError (*IsFormatSupported)( struct PaUtilHostApiRepresentation *hostApi,
+ const PaStreamParameters *inputParameters,
+ const PaStreamParameters *outputParameters,
+ double sampleRate );
+} PaUtilHostApiRepresentation;
+
+
+/** Prototype for the initialization function which must be implemented by every
+ host API.
+
+ @see paHostApiInitializers
+*/
+typedef PaError PaUtilHostApiInitializer( PaUtilHostApiRepresentation**, PaHostApiIndex );
+
+
+/** paHostApiInitializers is a NULL-terminated array of host API initialization
+ functions. These functions are called by pa_front to initialize the host APIs
+ when the client calls Pa_Initialize().
+
+ There is a platform specific file which defines paHostApiInitializers for that
+ platform, pa_win/pa_win_hostapis.c contains the Win32 definitions for example.
+*/
+extern PaUtilHostApiInitializer *paHostApiInitializers[];
+
+
+/** The index of the default host API in the paHostApiInitializers array.
+
+ There is a platform specific file which defines paDefaultHostApiIndex for that
+ platform, see pa_win/pa_win_hostapis.c for example.
+*/
+extern int paDefaultHostApiIndex;
+
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+#endif /* PA_HOSTAPI_H */
diff --git a/pjmedia/src/pjmedia/portaudio/pa_linux_alsa.c b/pjmedia/src/pjmedia/portaudio/pa_linux_alsa.c
index 3b047163..405ada15 100644
--- a/pjmedia/src/pjmedia/portaudio/pa_linux_alsa.c
+++ b/pjmedia/src/pjmedia/portaudio/pa_linux_alsa.c
@@ -1,3272 +1,3293 @@
-/*
- * $Id: pa_linux_alsa.c,v 1.1.2.71 2005/04/15 18:20:18 aknudsen Exp $
- * PortAudio Portable Real-Time Audio Library
- * Latest Version at: http://www.portaudio.com
- * ALSA implementation by Joshua Haberman and Arve Knudsen
- *
- * Copyright (c) 2002 Joshua Haberman <joshua@haberman.com>
- * Copyright (c) 2005 Arve Knudsen <aknuds-1@broadpark.no>
- *
- * Based on the Open Source API proposed by Ross Bencina
- * Copyright (c) 1999-2002 Ross Bencina, Phil Burk
- *
- * Permission is hereby granted, free of charge, to any person obtaining
- * a copy of this software and associated documentation files
- * (the "Software"), to deal in the Software without restriction,
- * including without limitation the rights to use, copy, modify, merge,
- * publish, distribute, sublicense, and/or sell copies of the Software,
- * and to permit persons to whom the Software is furnished to do so,
- * subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be
- * included in all copies or substantial portions of the Software.
- *
- * Any person wishing to distribute modifications to the Software is
- * requested to send the modifications to the original developer so that
- * they can be incorporated into the canonical version.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
- * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
- * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
- * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
- * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
- */
-
-#define ALSA_PCM_NEW_HW_PARAMS_API
-#define ALSA_PCM_NEW_SW_PARAMS_API
-#include <alsa/asoundlib.h>
-#undef ALSA_PCM_NEW_HW_PARAMS_API
-#undef ALSA_PCM_NEW_SW_PARAMS_API
-
-#include <sys/poll.h>
-#include <string.h> /* strlen() */
-#include <limits.h>
-#include <math.h>
-#include <pthread.h>
-#include <signal.h>
-#include <time.h>
-#include <sys/mman.h>
-#include <signal.h> /* For sig_atomic_t */
-
-#include "portaudio.h"
-#include "pa_util.h"
-#include "pa_unix_util.h"
-#include "pa_allocation.h"
-#include "pa_hostapi.h"
-#include "pa_stream.h"
-#include "pa_cpuload.h"
-#include "pa_process.h"
-
-#include "pa_linux_alsa.h"
-
-/* Check return value of ALSA function, and map it to PaError */
-#define ENSURE_(expr, code) \
- do { \
- if( UNLIKELY( (aErr_ = (expr)) < 0 ) ) \
- { \
- /* PaUtil_SetLastHostErrorInfo should only be used in the main thread */ \
- if( (code) == paUnanticipatedHostError && pthread_self() != callbackThread_ ) \
- { \
- PaUtil_SetLastHostErrorInfo( paALSA, aErr_, snd_strerror( aErr_ ) ); \
- } \
- PaUtil_DebugPrint(( "Expression '" #expr "' failed in '" __FILE__ "', line: " STRINGIZE( __LINE__ ) "\n" )); \
- result = (code); \
- goto error; \
- } \
- } while( 0 );
-
-#define ASSERT_CALL_(expr, success) \
- aErr_ = (expr); \
- assert( aErr_ == success );
-
-static int aErr_; /* Used with ENSURE_ */
-static pthread_t callbackThread_;
-
-typedef enum
-{
- StreamDirection_In,
- StreamDirection_Out
-} StreamDirection;
-
-/* Threading utility struct */
-typedef struct PaAlsaThreading
-{
- pthread_t watchdogThread;
- pthread_t callbackThread;
- int watchdogRunning;
- int rtSched;
- int rtPrio;
- int useWatchdog;
- unsigned long throttledSleepTime;
- volatile PaTime callbackTime;
- volatile PaTime callbackCpuTime;
- PaUtilCpuLoadMeasurer *cpuLoadMeasurer;
-} PaAlsaThreading;
-
-typedef struct
-{
- PaSampleFormat hostSampleFormat;
- unsigned long framesPerBuffer;
- int numUserChannels, numHostChannels;
- int userInterleaved, hostInterleaved;
-
- snd_pcm_t *pcm;
- snd_pcm_uframes_t bufferSize;
- snd_pcm_format_t nativeFormat;
- unsigned int nfds;
- int ready; /* Marked ready from poll */
- void **userBuffers;
- snd_pcm_uframes_t offset;
- StreamDirection streamDir;
-
- snd_pcm_channel_area_t *channelAreas; /* Needed for channel adaption */
-} PaAlsaStreamComponent;
-
-/* Implementation specific stream structure */
-typedef struct PaAlsaStream
-{
- PaUtilStreamRepresentation streamRepresentation;
- PaUtilCpuLoadMeasurer cpuLoadMeasurer;
- PaUtilBufferProcessor bufferProcessor;
- PaAlsaThreading threading;
-
- unsigned long framesPerUserBuffer;
-
- int primeBuffers;
- int callbackMode; /* bool: are we running in callback mode? */
- int pcmsSynced; /* Have we successfully synced pcms */
-
- /* the callback thread uses these to poll the sound device(s), waiting
- * for data to be ready/available */
- struct pollfd *pfds;
- int pollTimeout;
-
- /* Used in communication between threads */
- volatile sig_atomic_t callback_finished; /* bool: are we in the "callback finished" state? */
- volatile sig_atomic_t callbackAbort; /* Drop frames? */
- volatile sig_atomic_t callbackStop; /* Signal a stop */
- volatile sig_atomic_t isActive; /* Is stream in active state? (Between StartStream and StopStream || !paContinue) */
- pthread_mutex_t stateMtx; /* Used to synchronize access to stream state */
- pthread_mutex_t startMtx; /* Used to synchronize stream start in callback mode */
- pthread_cond_t startCond; /* Wait untill audio is started in callback thread */
-
- int neverDropInput;
-
- PaTime underrun;
- PaTime overrun;
-
- PaAlsaStreamComponent capture, playback;
-}
-PaAlsaStream;
-
-/* PaAlsaHostApiRepresentation - host api datastructure specific to this implementation */
-
-typedef struct PaAlsaHostApiRepresentation
-{
- PaUtilHostApiRepresentation commonHostApiRep;
- PaUtilStreamInterface callbackStreamInterface;
- PaUtilStreamInterface blockingStreamInterface;
-
- PaUtilAllocationGroup *allocations;
-
- PaHostApiIndex hostApiIndex;
-}
-PaAlsaHostApiRepresentation;
-
-typedef struct PaAlsaDeviceInfo
-{
- PaDeviceInfo commonDeviceInfo;
- char *alsaName;
- int isPlug;
- int minInputChannels;
- int minOutputChannels;
-}
-PaAlsaDeviceInfo;
-
-/* Threading utilities */
-
-static void InitializeThreading( PaAlsaThreading *th, PaUtilCpuLoadMeasurer *clm )
-{
- th->watchdogRunning = 0;
- th->rtSched = 0;
- th->callbackTime = 0;
- th->callbackCpuTime = 0;
- th->useWatchdog = 1;
- th->throttledSleepTime = 0;
- th->cpuLoadMeasurer = clm;
-
- th->rtPrio = (sched_get_priority_max( SCHED_FIFO ) - sched_get_priority_min( SCHED_FIFO )) / 2
- + sched_get_priority_min( SCHED_FIFO );
-}
-
-static PaError KillCallbackThread( PaAlsaThreading *th, int wait, PaError *exitResult, PaError *watchdogExitResult )
-{
- PaError result = paNoError;
- void *pret;
-
- if( exitResult )
- *exitResult = paNoError;
- if( watchdogExitResult )
- *watchdogExitResult = paNoError;
-
- if( th->watchdogRunning )
- {
- pthread_cancel( th->watchdogThread );
- ASSERT_CALL_( pthread_join( th->watchdogThread, &pret ), 0 );
-
- if( pret && pret != PTHREAD_CANCELED )
- {
- if( watchdogExitResult )
- *watchdogExitResult = *(PaError *) pret;
- free( pret );
- }
- }
-
- /* Only kill the thread if it isn't in the process of stopping (flushing adaptation buffers) */
- /* TODO: Make join time out */
- if( !wait )
- pthread_cancel( th->callbackThread ); /* XXX: Safe to call this if the thread has exited on its own? */
- ASSERT_CALL_( pthread_join( th->callbackThread, &pret ), 0 );
-
- if( pret && pret != PTHREAD_CANCELED )
- {
- if( exitResult )
- *exitResult = *(PaError *) pret;
- free( pret );
- }
-
- return result;
-}
-
-static void OnWatchdogExit( void *userData )
-{
- PaAlsaThreading *th = (PaAlsaThreading *) userData;
- struct sched_param spm = { 0 };
- assert( th );
-
- ASSERT_CALL_( pthread_setschedparam( th->callbackThread, SCHED_OTHER, &spm ), 0 ); /* Lower before exiting */
- PA_DEBUG(( "Watchdog exiting\n" ));
-}
-
-static PaError BoostPriority( PaAlsaThreading *th )
-{
- PaError result = paNoError;
- struct sched_param spm = { 0 };
- spm.sched_priority = th->rtPrio;
-
- assert( th );
-
- if( pthread_setschedparam( th->callbackThread, SCHED_FIFO, &spm ) != 0 )
- {
- PA_UNLESS( errno == EPERM, paInternalError ); /* Lack permission to raise priority */
- PA_DEBUG(( "Failed bumping priority\n" ));
- result = 0;
- }
- else
- result = 1; /* Success */
-error:
- return result;
-}
-
-static void *WatchdogFunc( void *userData )
-{
- PaError result = paNoError, *pres = NULL;
- int err;
- PaAlsaThreading *th = (PaAlsaThreading *) userData;
- unsigned intervalMsec = 500;
- const PaTime maxSeconds = 3.; /* Max seconds between callbacks */
- PaTime timeThen = PaUtil_GetTime(), timeNow, timeElapsed, cpuTimeThen, cpuTimeNow, cpuTimeElapsed;
- double cpuLoad, avgCpuLoad = 0.;
- int throttled = 0;
-
- assert( th );
-
- pthread_cleanup_push( &OnWatchdogExit, th ); /* Execute OnWatchdogExit when exiting */
-
- /* Boost priority of callback thread */
- PA_ENSURE( result = BoostPriority( th ) );
- if( !result )
- {
- pthread_exit( NULL ); /* Boost failed, might as well exit */
- }
-
- cpuTimeThen = th->callbackCpuTime;
- {
- int policy;
- struct sched_param spm = { 0 };
- pthread_getschedparam( pthread_self(), &policy, &spm );
- PA_DEBUG(( "%s: Watchdog priority is %d\n", __FUNCTION__, spm.sched_priority ));
- }
-
- while( 1 )
- {
- double lowpassCoeff = 0.9, lowpassCoeff1 = 0.99999 - lowpassCoeff;
-
- /* Test before and after in case whatever underlying sleep call isn't interrupted by pthread_cancel */
- pthread_testcancel();
- Pa_Sleep( intervalMsec );
- pthread_testcancel();
-
- if( PaUtil_GetTime() - th->callbackTime > maxSeconds )
- {
- PA_DEBUG(( "Watchdog: Terminating callback thread\n" ));
- /* Tell thread to terminate */
- err = pthread_kill( th->callbackThread, SIGKILL );
- pthread_exit( NULL );
- }
-
- PA_DEBUG(( "%s: PortAudio reports CPU load: %g\n", __FUNCTION__, PaUtil_GetCpuLoad( th->cpuLoadMeasurer ) ));
-
- /* Check if we should throttle, or unthrottle :P */
- cpuTimeNow = th->callbackCpuTime;
- cpuTimeElapsed = cpuTimeNow - cpuTimeThen;
- cpuTimeThen = cpuTimeNow;
-
- timeNow = PaUtil_GetTime();
- timeElapsed = timeNow - timeThen;
- timeThen = timeNow;
- cpuLoad = cpuTimeElapsed / timeElapsed;
- avgCpuLoad = avgCpuLoad * lowpassCoeff + cpuLoad * lowpassCoeff1;
- /*
- if( throttled )
- PA_DEBUG(( "Watchdog: CPU load: %g, %g\n", avgCpuLoad, cpuTimeElapsed ));
- */
- if( PaUtil_GetCpuLoad( th->cpuLoadMeasurer ) > .925 )
- {
- static int policy;
- static struct sched_param spm = { 0 };
- static const struct sched_param defaultSpm = { 0 };
- PA_DEBUG(( "%s: Throttling audio thread, priority %d\n", __FUNCTION__, spm.sched_priority ));
-
- pthread_getschedparam( th->callbackThread, &policy, &spm );
- if( !pthread_setschedparam( th->callbackThread, SCHED_OTHER, &defaultSpm ) )
- {
- throttled = 1;
- }
- else
- PA_DEBUG(( "Watchdog: Couldn't lower priority of audio thread: %s\n", strerror( errno ) ));
-
- /* Give other processes a go, before raising priority again */
- PA_DEBUG(( "%s: Watchdog sleeping for %lu msecs before unthrottling\n", __FUNCTION__, th->throttledSleepTime ));
- Pa_Sleep( th->throttledSleepTime );
-
- /* Reset callback priority */
- if( pthread_setschedparam( th->callbackThread, SCHED_FIFO, &spm ) != 0 )
- {
- PA_DEBUG(( "%s: Couldn't raise priority of audio thread: %s\n", __FUNCTION__, strerror( errno ) ));
- }
-
- if( PaUtil_GetCpuLoad( th->cpuLoadMeasurer ) >= .99 )
- intervalMsec = 50;
- else
- intervalMsec = 100;
-
- /*
- lowpassCoeff = .97;
- lowpassCoeff1 = .99999 - lowpassCoeff;
- */
- }
- else if( throttled && avgCpuLoad < .8 )
- {
- intervalMsec = 500;
- throttled = 0;
-
- /*
- lowpassCoeff = .9;
- lowpassCoeff1 = .99999 - lowpassCoeff;
- */
- }
- }
-
- pthread_cleanup_pop( 1 ); /* Execute cleanup on exit */
-
-error:
- /* Shouldn't get here in the normal case */
-
- /* Pass on error code */
- pres = malloc( sizeof (PaError) );
- *pres = result;
-
- pthread_exit( pres );
-}
-
-static PaError CreateCallbackThread( PaAlsaThreading *th, void *(*callbackThreadFunc)( void * ), PaStream *s )
-{
- PaError result = paNoError;
- pthread_attr_t attr;
- int started = 0;
-
-#if defined _POSIX_MEMLOCK && (_POSIX_MEMLOCK != -1)
- if( th->rtSched )
- {
- if( mlockall( MCL_CURRENT | MCL_FUTURE ) < 0 )
- {
- int savedErrno = errno; /* In case errno gets overwritten */
- assert( savedErrno != EINVAL ); /* Most likely a programmer error */
- PA_UNLESS( (savedErrno == EPERM), paInternalError );
- PA_DEBUG(( "%s: Failed locking memory\n", __FUNCTION__ ));
- }
- else
- PA_DEBUG(( "%s: Successfully locked memory\n", __FUNCTION__ ));
- }
-#endif
-
- PA_UNLESS( !pthread_attr_init( &attr ), paInternalError );
- /* Priority relative to other processes */
- PA_UNLESS( !pthread_attr_setscope( &attr, PTHREAD_SCOPE_SYSTEM ), paInternalError );
-
- PA_UNLESS( !pthread_create( &th->callbackThread, &attr, callbackThreadFunc, s ), paInternalError );
- started = 1;
-
- if( th->rtSched )
- {
- if( th->useWatchdog )
- {
- int err;
- struct sched_param wdSpm = { 0 };
- /* Launch watchdog, watchdog sets callback thread priority */
- int prio = PA_MIN( th->rtPrio + 4, sched_get_priority_max( SCHED_FIFO ) );
- wdSpm.sched_priority = prio;
-
- PA_UNLESS( !pthread_attr_init( &attr ), paInternalError );
- PA_UNLESS( !pthread_attr_setinheritsched( &attr, PTHREAD_EXPLICIT_SCHED ), paInternalError );
- PA_UNLESS( !pthread_attr_setscope( &attr, PTHREAD_SCOPE_SYSTEM ), paInternalError );
- PA_UNLESS( !pthread_attr_setschedpolicy( &attr, SCHED_FIFO ), paInternalError );
- PA_UNLESS( !pthread_attr_setschedparam( &attr, &wdSpm ), paInternalError );
- if( (err = pthread_create( &th->watchdogThread, &attr, &WatchdogFunc, th )) )
- {
- PA_UNLESS( err == EPERM, paInternalError );
- /* Permission error, go on without realtime privileges */
- PA_DEBUG(( "Failed bumping priority\n" ));
- }
- else
- {
- int policy;
- th->watchdogRunning = 1;
- ASSERT_CALL_( pthread_getschedparam( th->watchdogThread, &policy, &wdSpm ), 0 );
- /* Check if priority is right, policy could potentially differ from SCHED_FIFO (but that's alright) */
- if( wdSpm.sched_priority != prio )
- {
- PA_DEBUG(( "Watchdog priority not set correctly (%d)\n", wdSpm.sched_priority ));
- PA_ENSURE( paInternalError );
- }
- }
- }
- else
- PA_ENSURE( BoostPriority( th ) );
- }
-
-end:
- return result;
-error:
- if( started )
- KillCallbackThread( th, 0, NULL, NULL );
-
- goto end;
-}
-
-static void CallbackUpdate( PaAlsaThreading *th )
-{
- th->callbackTime = PaUtil_GetTime();
- th->callbackCpuTime = PaUtil_GetCpuLoad( th->cpuLoadMeasurer );
-}
-
-/* prototypes for functions declared in this file */
-
-static void Terminate( struct PaUtilHostApiRepresentation *hostApi );
-static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
- const PaStreamParameters *inputParameters,
- const PaStreamParameters *outputParameters,
- double sampleRate );
-static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
- PaStream** s,
- const PaStreamParameters *inputParameters,
- const PaStreamParameters *outputParameters,
- double sampleRate,
- unsigned long framesPerBuffer,
- PaStreamFlags streamFlags,
- PaStreamCallback *callback,
- void *userData );
-static PaError CloseStream( PaStream* stream );
-static PaError StartStream( PaStream *stream );
-static PaError StopStream( PaStream *stream );
-static PaError AbortStream( PaStream *stream );
-static PaError IsStreamStopped( PaStream *s );
-static PaError IsStreamActive( PaStream *stream );
-static PaTime GetStreamTime( PaStream *stream );
-static double GetStreamCpuLoad( PaStream* stream );
-static PaError BuildDeviceList( PaAlsaHostApiRepresentation *hostApi );
-static int SetApproximateSampleRate( snd_pcm_t *pcm, snd_pcm_hw_params_t *hwParams, double sampleRate );
-static int GetExactSampleRate( snd_pcm_hw_params_t *hwParams, double *sampleRate );
-
-/* Callback prototypes */
-static void *CallbackThreadFunc( void *userData );
-
-/* Blocking prototypes */
-static signed long GetStreamReadAvailable( PaStream* s );
-static signed long GetStreamWriteAvailable( PaStream* s );
-static PaError ReadStream( PaStream* stream, void *buffer, unsigned long frames );
-static PaError WriteStream( PaStream* stream, const void *buffer, unsigned long frames );
-
-
-static const PaAlsaDeviceInfo *GetDeviceInfo( const PaUtilHostApiRepresentation *hostApi, int device )
-{
- return (const PaAlsaDeviceInfo *)hostApi->deviceInfos[device];
-}
-
-PaError PaAlsa_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex hostApiIndex )
-{
- PaError result = paNoError;
- PaAlsaHostApiRepresentation *alsaHostApi = NULL;
-
- PA_UNLESS( alsaHostApi = (PaAlsaHostApiRepresentation*) PaUtil_AllocateMemory(
- sizeof(PaAlsaHostApiRepresentation) ), paInsufficientMemory );
- PA_UNLESS( alsaHostApi->allocations = PaUtil_CreateAllocationGroup(), paInsufficientMemory );
- alsaHostApi->hostApiIndex = hostApiIndex;
-
- *hostApi = (PaUtilHostApiRepresentation*)alsaHostApi;
- (*hostApi)->info.structVersion = 1;
- (*hostApi)->info.type = paALSA;
- (*hostApi)->info.name = "ALSA";
-
- (*hostApi)->Terminate = Terminate;
- (*hostApi)->OpenStream = OpenStream;
- (*hostApi)->IsFormatSupported = IsFormatSupported;
-
- PA_ENSURE( BuildDeviceList( alsaHostApi ) );
-
- PaUtil_InitializeStreamInterface( &alsaHostApi->callbackStreamInterface,
- CloseStream, StartStream,
- StopStream, AbortStream,
- IsStreamStopped, IsStreamActive,
- GetStreamTime, GetStreamCpuLoad,
- PaUtil_DummyRead, PaUtil_DummyWrite,
- PaUtil_DummyGetReadAvailable,
- PaUtil_DummyGetWriteAvailable );
-
- PaUtil_InitializeStreamInterface( &alsaHostApi->blockingStreamInterface,
- CloseStream, StartStream,
- StopStream, AbortStream,
- IsStreamStopped, IsStreamActive,
- GetStreamTime, PaUtil_DummyGetCpuLoad,
- ReadStream, WriteStream,
- GetStreamReadAvailable,
- GetStreamWriteAvailable );
-
- return result;
-
-error:
- if( alsaHostApi )
- {
- if( alsaHostApi->allocations )
- {
- PaUtil_FreeAllAllocations( alsaHostApi->allocations );
- PaUtil_DestroyAllocationGroup( alsaHostApi->allocations );
- }
-
- PaUtil_FreeMemory( alsaHostApi );
- }
-
- return result;
-}
-
-static void Terminate( struct PaUtilHostApiRepresentation *hostApi )
-{
- PaAlsaHostApiRepresentation *alsaHostApi = (PaAlsaHostApiRepresentation*)hostApi;
-
- assert( hostApi );
-
- if( alsaHostApi->allocations )
- {
- PaUtil_FreeAllAllocations( alsaHostApi->allocations );
- PaUtil_DestroyAllocationGroup( alsaHostApi->allocations );
- }
-
- PaUtil_FreeMemory( alsaHostApi );
- snd_config_update_free_global();
-}
-
-/*! Determine max channels and default latencies.
- *
- * This function provides functionality to grope an opened (might be opened for capture or playback) pcm device for
- * traits like max channels, suitable default latencies and default sample rate. Upon error, max channels is set to zero,
- * and a suitable result returned. The device is closed before returning.
- */
-static PaError GropeDevice( snd_pcm_t *pcm, int *minChannels, int *maxChannels, double *defaultLowLatency,
- double *defaultHighLatency, double *defaultSampleRate, int isPlug )
-{
- PaError result = paNoError;
- snd_pcm_hw_params_t *hwParams;
- snd_pcm_uframes_t lowLatency = 512, highLatency = 2048;
- unsigned int minChans, maxChans;
- double defaultSr = *defaultSampleRate;
-
- assert( pcm );
-
- ENSURE_( snd_pcm_nonblock( pcm, 0 ), paUnanticipatedHostError );
-
- snd_pcm_hw_params_alloca( &hwParams );
- snd_pcm_hw_params_any( pcm, hwParams );
-
- if( defaultSr >= 0 )
- {
- /* Could be that the device opened in one mode supports samplerates that the other mode wont have,
- * so try again .. */
- if( SetApproximateSampleRate( pcm, hwParams, defaultSr ) < 0 )
- {
- defaultSr = -1.;
- PA_DEBUG(( "%s: Original default samplerate failed, trying again ..\n", __FUNCTION__ ));
- }
- }
-
- if( defaultSr < 0. ) /* Default sample rate not set */
- {
- unsigned int sampleRate = 44100; /* Will contain approximate rate returned by alsa-lib */
- ENSURE_( snd_pcm_hw_params_set_rate_near( pcm, hwParams, &sampleRate, NULL ), paUnanticipatedHostError );
- ENSURE_( GetExactSampleRate( hwParams, &defaultSr ), paUnanticipatedHostError );
- }
-
- ENSURE_( snd_pcm_hw_params_get_channels_min( hwParams, &minChans ), paUnanticipatedHostError );
- ENSURE_( snd_pcm_hw_params_get_channels_max( hwParams, &maxChans ), paUnanticipatedHostError );
- assert( maxChans <= INT_MAX );
- assert( maxChans > 0 ); /* Weird linking issue could cause wrong version of ALSA symbols to be called,
- resulting in zeroed values */
-
- /* XXX: Limit to sensible number (ALSA plugins accept a crazy amount of channels)? */
- if( isPlug && maxChans > 128 )
- {
- maxChans = 128;
- PA_DEBUG(( "%s: Limiting number of plugin channels to %u\n", __FUNCTION__, maxChans ));
- }
-
- /* TWEAKME:
- *
- * Giving values for default min and max latency is not
- * straightforward. Here are our objectives:
- *
- * * for low latency, we want to give the lowest value
- * that will work reliably. This varies based on the
- * sound card, kernel, CPU, etc. I think it is better
- * to give sub-optimal latency than to give a number
- * too low and cause dropouts. My conservative
- * estimate at this point is to base it on 4096-sample
- * latency at 44.1 kHz, which gives a latency of 23ms.
- * * for high latency we want to give a large enough
- * value that dropouts are basically impossible. This
- * doesn't really require as much tweaking, since
- * providing too large a number will just cause us to
- * select the nearest setting that will work at stream
- * config time.
- */
- ENSURE_( snd_pcm_hw_params_set_buffer_size_near( pcm, hwParams, &lowLatency ), paUnanticipatedHostError );
-
- /* Have to reset hwParams, to set new buffer size */
- ENSURE_( snd_pcm_hw_params_any( pcm, hwParams ), paUnanticipatedHostError );
- ENSURE_( snd_pcm_hw_params_set_buffer_size_near( pcm, hwParams, &highLatency ), paUnanticipatedHostError );
-
- *minChannels = (int)minChans;
- *maxChannels = (int)maxChans;
- *defaultSampleRate = defaultSr;
- *defaultLowLatency = (double) lowLatency / *defaultSampleRate;
- *defaultHighLatency = (double) highLatency / *defaultSampleRate;
-
-end:
- snd_pcm_close( pcm );
- return result;
-
-error:
- goto end;
-}
-
-/* Initialize device info with invalid values (maxInputChannels and maxOutputChannels are set to zero since these indicate
- * wether input/output is available) */
-static void InitializeDeviceInfo( PaDeviceInfo *deviceInfo )
-{
- deviceInfo->structVersion = -1;
- deviceInfo->name = NULL;
- deviceInfo->hostApi = -1;
- deviceInfo->maxInputChannels = 0;
- deviceInfo->maxOutputChannels = 0;
- deviceInfo->defaultLowInputLatency = -1.;
- deviceInfo->defaultLowOutputLatency = -1.;
- deviceInfo->defaultHighInputLatency = -1.;
- deviceInfo->defaultHighOutputLatency = -1.;
- deviceInfo->defaultSampleRate = -1.;
-}
-
-/* Helper struct */
-typedef struct
-{
- char *alsaName;
- char *name;
- int isPlug;
- int hasPlayback;
- int hasCapture;
-} DeviceNames;
-
-static PaError PaAlsa_StrDup( PaAlsaHostApiRepresentation *alsaApi,
- char **dst,
- const char *src)
-{
- PaError result = paNoError;
- int len = strlen( src ) + 1;
-
- /* PA_DEBUG(("PaStrDup %s %d\n", src, len)); */
-
- PA_UNLESS( *dst = (char *)PaUtil_GroupAllocateMemory( alsaApi->allocations, len ),
- paInsufficientMemory );
- strncpy( *dst, src, len );
-
-error:
- return result;
-}
-
-/* Disregard standard plugins
- * XXX: Might want to make the "default" plugin available, if we can make it work
- */
-static int IgnorePlugin( const char *pluginId )
-{
-#define numIgnored 10
- static const char *ignoredPlugins[numIgnored] = {"hw", "plughw", "plug", "default", "dsnoop", "dmix", "tee",
- "file", "null", "shm"};
- int i;
-
- for( i = 0; i < numIgnored; ++i )
- {
- if( !strcmp( pluginId, ignoredPlugins[i] ) )
- {
- return 1;
- }
- }
-
- return 0;
-}
-
-/* Build PaDeviceInfo list, ignore devices for which we cannot determine capabilities (possibly busy, sigh) */
-static PaError BuildDeviceList( PaAlsaHostApiRepresentation *alsaApi )
-{
- PaUtilHostApiRepresentation *commonApi = &alsaApi->commonHostApiRep;
- PaAlsaDeviceInfo *deviceInfoArray;
- int cardIdx = -1, devIdx = 0;
- snd_ctl_card_info_t *cardInfo;
- PaError result = paNoError;
- size_t numDeviceNames = 0, maxDeviceNames = 1, i;
- DeviceNames *deviceNames = NULL;
- snd_config_t *topNode = NULL;
- snd_pcm_info_t *pcmInfo;
- int res;
- int blocking = SND_PCM_NONBLOCK;
- char alsaCardName[50];
- if( getenv( "PA_ALSA_INITIALIZE_BLOCK" ) && atoi( getenv( "PA_ALSA_INITIALIZE_BLOCK" ) ) )
- blocking = 0;
-
- /* These two will be set to the first working input and output device, respectively */
- commonApi->info.defaultInputDevice = paNoDevice;
- commonApi->info.defaultOutputDevice = paNoDevice;
-
- /* count the devices by enumerating all the card numbers */
-
- /* snd_card_next() modifies the integer passed to it to be:
- * the index of the first card if the parameter is -1
- * the index of the next card if the parameter is the index of a card
- * -1 if there are no more cards
- *
- * The function itself returns 0 if it succeeded. */
- cardIdx = -1;
- snd_ctl_card_info_alloca( &cardInfo );
- snd_pcm_info_alloca( &pcmInfo );
- while( snd_card_next( &cardIdx ) == 0 && cardIdx >= 0 )
- {
- char *cardName;
- int devIdx = -1;
- snd_ctl_t *ctl;
- char buf[50];
-
- snprintf( alsaCardName, sizeof (alsaCardName), "hw:%d", cardIdx );
-
- /* Acquire name of card */
- if( snd_ctl_open( &ctl, alsaCardName, 0 ) < 0 )
- continue; /* Unable to open card :( */
- snd_ctl_card_info( ctl, cardInfo );
-
- PA_ENSURE( PaAlsa_StrDup( alsaApi, &cardName, snd_ctl_card_info_get_name( cardInfo )) );
-
- while( snd_ctl_pcm_next_device( ctl, &devIdx ) == 0 && devIdx >= 0 )
- {
- char *alsaDeviceName, *deviceName;
- size_t len;
- int hasPlayback = 0, hasCapture = 0;
- snprintf( buf, sizeof (buf), "%s:%d,%d", "hw", cardIdx, devIdx );
-
- /* Obtain info about this particular device */
- snd_pcm_info_set_device( pcmInfo, devIdx );
- snd_pcm_info_set_subdevice( pcmInfo, 0 );
- snd_pcm_info_set_stream( pcmInfo, SND_PCM_STREAM_CAPTURE );
- if( snd_ctl_pcm_info( ctl, pcmInfo ) >= 0 )
- hasCapture = 1;
-
- snd_pcm_info_set_stream( pcmInfo, SND_PCM_STREAM_PLAYBACK );
- if( snd_ctl_pcm_info( ctl, pcmInfo ) >= 0 )
- hasPlayback = 1;
-
- if( !hasPlayback && !hasCapture )
- {
- continue; /* Error */
- }
-
- /* The length of the string written by snprintf plus terminating 0 */
- len = snprintf( NULL, 0, "%s: %s (%s)", cardName, snd_pcm_info_get_name( pcmInfo ), buf ) + 1;
- PA_UNLESS( deviceName = (char *)PaUtil_GroupAllocateMemory( alsaApi->allocations, len ),
- paInsufficientMemory );
- snprintf( deviceName, len, "%s: %s (%s)", cardName,
- snd_pcm_info_get_name( pcmInfo ), buf );
-
- ++numDeviceNames;
- if( !deviceNames || numDeviceNames > maxDeviceNames )
- {
- maxDeviceNames *= 2;
- PA_UNLESS( deviceNames = (DeviceNames *) realloc( deviceNames, maxDeviceNames * sizeof (DeviceNames) ),
- paInsufficientMemory );
- }
-
- PA_ENSURE( PaAlsa_StrDup( alsaApi, &alsaDeviceName, buf ) );
-
- deviceNames[ numDeviceNames - 1 ].alsaName = alsaDeviceName;
- deviceNames[ numDeviceNames - 1 ].name = deviceName;
- deviceNames[ numDeviceNames - 1 ].isPlug = 0;
- deviceNames[ numDeviceNames - 1 ].hasPlayback = hasPlayback;
- deviceNames[ numDeviceNames - 1 ].hasCapture = hasCapture;
- }
- snd_ctl_close( ctl );
- }
-
- /* Iterate over plugin devices */
- snd_config_update();
- if( (res = snd_config_search( snd_config, "pcm", &topNode )) >= 0 )
- {
- snd_config_iterator_t i, next;
-
- snd_config_for_each( i, next, topNode )
- {
- const char *tpStr = NULL, *idStr = NULL;
- char *alsaDeviceName, *deviceName;
- snd_config_t *n = snd_config_iterator_entry( i ), *tp = NULL;
- if( snd_config_get_type( n ) != SND_CONFIG_TYPE_COMPOUND )
- continue;
-
- ENSURE_( snd_config_search( n, "type", &tp ), paUnanticipatedHostError );
- ENSURE_( snd_config_get_string( tp, &tpStr ), paUnanticipatedHostError );
-
- ENSURE_( snd_config_get_id( n, &idStr ), paUnanticipatedHostError );
- if( IgnorePlugin( idStr ) )
- {
- PA_DEBUG(( "%s: Ignoring ALSA plugin device %s of type %s\n", __FUNCTION__, idStr, tpStr ));
- continue;
- }
-
- PA_DEBUG(( "%s: Found plugin %s of type %s\n", __FUNCTION__, idStr, tpStr ));
-
- PA_UNLESS( alsaDeviceName = (char*)PaUtil_GroupAllocateMemory( alsaApi->allocations,
- strlen(idStr) + 6 ), paInsufficientMemory );
- strcpy( alsaDeviceName, idStr );
- PA_UNLESS( deviceName = (char*)PaUtil_GroupAllocateMemory( alsaApi->allocations,
- strlen(idStr) + 1 ), paInsufficientMemory );
- strcpy( deviceName, idStr );
-
- ++numDeviceNames;
- if( !deviceNames || numDeviceNames > maxDeviceNames )
- {
- maxDeviceNames *= 2;
- PA_UNLESS( deviceNames = (DeviceNames *) realloc( deviceNames, maxDeviceNames * sizeof (DeviceNames) ),
- paInsufficientMemory );
- }
-
- deviceNames[numDeviceNames - 1].alsaName = alsaDeviceName;
- deviceNames[numDeviceNames - 1].name = deviceName;
- deviceNames[numDeviceNames - 1].isPlug = 1;
- deviceNames[numDeviceNames - 1].hasPlayback = 1;
- deviceNames[numDeviceNames - 1].hasCapture = 1;
- }
- }
- else
- PA_DEBUG(( "%s: Iterating over ALSA plugins failed: %s\n", __FUNCTION__, snd_strerror( res ) ));
-
- /* allocate deviceInfo memory based on the number of devices */
- PA_UNLESS( commonApi->deviceInfos = (PaDeviceInfo**)PaUtil_GroupAllocateMemory(
- alsaApi->allocations, sizeof(PaDeviceInfo*) * (numDeviceNames) ), paInsufficientMemory );
-
- /* allocate all device info structs in a contiguous block */
- PA_UNLESS( deviceInfoArray = (PaAlsaDeviceInfo*)PaUtil_GroupAllocateMemory(
- alsaApi->allocations, sizeof(PaAlsaDeviceInfo) * numDeviceNames ), paInsufficientMemory );
-
- /* Loop over list of cards, filling in info, if a device is deemed unavailable (can't get name),
- * it's ignored.
- */
- /* while( snd_card_next( &cardIdx ) == 0 && cardIdx >= 0 ) */
- for( i = 0, devIdx = 0; i < numDeviceNames; ++i )
- {
- snd_pcm_t *pcm;
- PaAlsaDeviceInfo *deviceInfo = &deviceInfoArray[devIdx];
- PaDeviceInfo *commonDeviceInfo = &deviceInfo->commonDeviceInfo;
-
- /* Zero fields */
- InitializeDeviceInfo( commonDeviceInfo );
-
- /* to determine device capabilities, we must open the device and query the
- * hardware parameter configuration space */
-
- /* Query capture */
- if( deviceNames[i].hasCapture &&
- snd_pcm_open( &pcm, deviceNames[i].alsaName, SND_PCM_STREAM_CAPTURE, blocking ) >= 0 )
- {
- if( GropeDevice( pcm, &deviceInfo->minInputChannels, &commonDeviceInfo->maxInputChannels,
- &commonDeviceInfo->defaultLowInputLatency, &commonDeviceInfo->defaultHighInputLatency,
- &commonDeviceInfo->defaultSampleRate, deviceNames[i].isPlug ) != paNoError )
- continue; /* Error */
- }
-
- /* Query playback */
- if( deviceNames[i].hasPlayback &&
- snd_pcm_open( &pcm, deviceNames[i].alsaName, SND_PCM_STREAM_PLAYBACK, blocking ) >= 0 )
- {
- if( GropeDevice( pcm, &deviceInfo->minOutputChannels, &commonDeviceInfo->maxOutputChannels,
- &commonDeviceInfo->defaultLowOutputLatency, &commonDeviceInfo->defaultHighOutputLatency,
- &commonDeviceInfo->defaultSampleRate, deviceNames[i].isPlug ) != paNoError )
- continue; /* Error */
- }
-
- commonDeviceInfo->structVersion = 2;
- commonDeviceInfo->hostApi = alsaApi->hostApiIndex;
- commonDeviceInfo->name = deviceNames[i].name;
- deviceInfo->alsaName = deviceNames[i].alsaName;
- deviceInfo->isPlug = deviceNames[i].isPlug;
-
- /* A: Storing pointer to PaAlsaDeviceInfo object as pointer to PaDeviceInfo object.
- * Should now be safe to add device info, unless the device supports neither capture nor playback
- */
- if( commonDeviceInfo->maxInputChannels > 0 || commonDeviceInfo->maxOutputChannels > 0 )
- {
- if( commonApi->info.defaultInputDevice == paNoDevice && commonDeviceInfo->maxInputChannels > 0 )
- commonApi->info.defaultInputDevice = devIdx;
- if( commonApi->info.defaultOutputDevice == paNoDevice && commonDeviceInfo->maxOutputChannels > 0 )
- commonApi->info.defaultOutputDevice = devIdx;
-
- commonApi->deviceInfos[devIdx++] = (PaDeviceInfo *) deviceInfo;
- }
- }
- free( deviceNames );
-
- commonApi->info.deviceCount = devIdx; /* Number of successfully queried devices */
-
-end:
- return result;
-
-error:
- goto end; /* No particular action */
-}
-
-/* Check against known device capabilities */
-static PaError ValidateParameters( const PaStreamParameters *parameters, PaUtilHostApiRepresentation *hostApi, StreamDirection mode )
-{
- PaError result = paNoError;
- int maxChans;
- const PaAlsaDeviceInfo *deviceInfo = NULL;
- assert( parameters );
-
- if( parameters->device != paUseHostApiSpecificDeviceSpecification )
- {
- assert( parameters->device < hostApi->info.deviceCount );
- PA_UNLESS( parameters->hostApiSpecificStreamInfo == NULL, paBadIODeviceCombination );
- deviceInfo = GetDeviceInfo( hostApi, parameters->device );
- }
- else
- {
- const PaAlsaStreamInfo *streamInfo = parameters->hostApiSpecificStreamInfo;
-
- PA_UNLESS( parameters->device == paUseHostApiSpecificDeviceSpecification, paInvalidDevice );
- PA_UNLESS( streamInfo->size == sizeof (PaAlsaStreamInfo) && streamInfo->version == 1,
- paIncompatibleHostApiSpecificStreamInfo );
-
- return paNoError; /* Skip further checking */
- }
-
- assert( deviceInfo );
- assert( parameters->hostApiSpecificStreamInfo == NULL );
- maxChans = (StreamDirection_In == mode ? deviceInfo->commonDeviceInfo.maxInputChannels :
- deviceInfo->commonDeviceInfo.maxOutputChannels);
- PA_UNLESS( parameters->channelCount <= maxChans, paInvalidChannelCount );
-
-error:
- return result;
-}
-
-/* Given an open stream, what sample formats are available? */
-
-static PaSampleFormat GetAvailableFormats( snd_pcm_t *pcm )
-{
- PaSampleFormat available = 0;
- snd_pcm_hw_params_t *hwParams;
- snd_pcm_hw_params_alloca( &hwParams );
-
- snd_pcm_hw_params_any( pcm, hwParams );
-
- if( snd_pcm_hw_params_test_format( pcm, hwParams, SND_PCM_FORMAT_FLOAT ) >= 0)
- available |= paFloat32;
-
- if( snd_pcm_hw_params_test_format( pcm, hwParams, SND_PCM_FORMAT_S32 ) >= 0)
- available |= paInt32;
-
- if( snd_pcm_hw_params_test_format( pcm, hwParams, SND_PCM_FORMAT_S24 ) >= 0)
- available |= paInt24;
-
- if( snd_pcm_hw_params_test_format( pcm, hwParams, SND_PCM_FORMAT_S16 ) >= 0)
- available |= paInt16;
-
- if( snd_pcm_hw_params_test_format( pcm, hwParams, SND_PCM_FORMAT_U8 ) >= 0)
- available |= paUInt8;
-
- if( snd_pcm_hw_params_test_format( pcm, hwParams, SND_PCM_FORMAT_S8 ) >= 0)
- available |= paInt8;
-
- return available;
-}
-
-static snd_pcm_format_t Pa2AlsaFormat( PaSampleFormat paFormat )
-{
- switch( paFormat )
- {
- case paFloat32:
- return SND_PCM_FORMAT_FLOAT;
-
- case paInt16:
- return SND_PCM_FORMAT_S16;
-
- case paInt24:
- return SND_PCM_FORMAT_S24;
-
- case paInt32:
- return SND_PCM_FORMAT_S32;
-
- case paInt8:
- return SND_PCM_FORMAT_S8;
-
- case paUInt8:
- return SND_PCM_FORMAT_U8;
-
- default:
- return SND_PCM_FORMAT_UNKNOWN;
- }
-}
-
-/** Open an ALSA pcm handle.
- *
- * The device to be open can be specified in a custom PaAlsaStreamInfo struct, or it will be a device number. In case of a
- * device number, it maybe specified through an env variable (PA_ALSA_PLUGHW) that we should open the corresponding plugin
- * device.
- */
-static PaError AlsaOpen( const PaUtilHostApiRepresentation *hostApi, const PaStreamParameters *params, StreamDirection
- streamDir, snd_pcm_t **pcm )
-{
- PaError result = paNoError;
- int ret;
- const char *deviceName = alloca( 50 );
- const PaAlsaDeviceInfo *deviceInfo = NULL;
- PaAlsaStreamInfo *streamInfo = (PaAlsaStreamInfo *)params->hostApiSpecificStreamInfo;
-
- if( !streamInfo )
- {
- int usePlug = 0;
- deviceInfo = GetDeviceInfo( hostApi, params->device );
-
- /* If device name starts with hw: and PA_ALSA_PLUGHW is 1, we open the plughw device instead */
- if( !strncmp( "hw:", deviceInfo->alsaName, 3 ) && getenv( "PA_ALSA_PLUGHW" ) )
- usePlug = atoi( getenv( "PA_ALSA_PLUGHW" ) );
- if( usePlug )
- snprintf( (char *) deviceName, 50, "plug%s", deviceInfo->alsaName );
- else
- deviceName = deviceInfo->alsaName;
- }
- else
- deviceName = streamInfo->deviceString;
-
- if( (ret = snd_pcm_open( pcm, deviceName, streamDir == StreamDirection_In ? SND_PCM_STREAM_CAPTURE : SND_PCM_STREAM_PLAYBACK,
- SND_PCM_NONBLOCK )) < 0 )
- {
- *pcm = NULL; /* Not to be closed */
- ENSURE_( ret, ret == -EBUSY ? paDeviceUnavailable : paBadIODeviceCombination );
- }
- ENSURE_( snd_pcm_nonblock( *pcm, 0 ), paUnanticipatedHostError );
-
-end:
- return result;
-
-error:
- goto end;
-}
-
-static PaError TestParameters( const PaUtilHostApiRepresentation *hostApi, const PaStreamParameters *parameters,
- double sampleRate, StreamDirection streamDir )
-{
- PaError result = paNoError;
- snd_pcm_t *pcm = NULL;
- PaSampleFormat availableFormats;
- /* We are able to adapt to a number of channels less than what the device supports */
- unsigned int numHostChannels;
- PaSampleFormat hostFormat;
- snd_pcm_hw_params_t *hwParams;
- snd_pcm_hw_params_alloca( &hwParams );
-
- if( !parameters->hostApiSpecificStreamInfo )
- {
- const PaAlsaDeviceInfo *devInfo = GetDeviceInfo( hostApi, parameters->device );
- numHostChannels = PA_MAX( parameters->channelCount, StreamDirection_In == streamDir ?
- devInfo->minInputChannels : devInfo->minOutputChannels );
- }
- else
- numHostChannels = parameters->channelCount;
-
- PA_ENSURE( AlsaOpen( hostApi, parameters, streamDir, &pcm ) );
-
- snd_pcm_hw_params_any( pcm, hwParams );
-
- if( SetApproximateSampleRate( pcm, hwParams, sampleRate ) < 0 )
- {
- result = paInvalidSampleRate;
- goto error;
- }
-
- if( snd_pcm_hw_params_set_channels( pcm, hwParams, numHostChannels ) < 0 )
- {
- result = paInvalidChannelCount;
- goto error;
- }
-
- /* See if we can find a best possible match */
- availableFormats = GetAvailableFormats( pcm );
- PA_ENSURE( hostFormat = PaUtil_SelectClosestAvailableFormat( availableFormats, parameters->sampleFormat ) );
- ENSURE_( snd_pcm_hw_params_set_format( pcm, hwParams, Pa2AlsaFormat( hostFormat ) ), paUnanticipatedHostError );
-
- ENSURE_( snd_pcm_hw_params( pcm, hwParams ), paUnanticipatedHostError );
-
-end:
- if( pcm )
- snd_pcm_close( pcm );
- return result;
-
-error:
- goto end;
-}
-
-static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
- const PaStreamParameters *inputParameters,
- const PaStreamParameters *outputParameters,
- double sampleRate )
-{
- int inputChannelCount = 0, outputChannelCount = 0;
- PaSampleFormat inputSampleFormat, outputSampleFormat;
- PaError result = paFormatIsSupported;
-
- if( inputParameters )
- {
- PA_ENSURE( ValidateParameters( inputParameters, hostApi, StreamDirection_In ) );
-
- inputChannelCount = inputParameters->channelCount;
- inputSampleFormat = inputParameters->sampleFormat;
- }
-
- if( outputParameters )
- {
- PA_ENSURE( ValidateParameters( outputParameters, hostApi, StreamDirection_Out ) );
-
- outputChannelCount = outputParameters->channelCount;
- outputSampleFormat = outputParameters->sampleFormat;
- }
-
- if( inputChannelCount )
- {
- if( (result = TestParameters( hostApi, inputParameters, sampleRate, StreamDirection_In ))
- != paNoError )
- goto error;
- }
- if ( outputChannelCount )
- {
- if( (result = TestParameters( hostApi, outputParameters, sampleRate, StreamDirection_Out ))
- != paNoError )
- goto error;
- }
-
- return paFormatIsSupported;
-
-error:
- return result;
-}
-
-static PaError PaAlsaStreamComponent_Initialize( PaAlsaStreamComponent *self, PaAlsaHostApiRepresentation *alsaApi,
- const PaStreamParameters *params, StreamDirection streamDir, int callbackMode )
-{
- PaError result = paNoError;
- PaSampleFormat userSampleFormat = params->sampleFormat, hostSampleFormat;
- assert( params->channelCount > 0 );
-
- /* Make sure things have an initial value */
- memset( self, 0, sizeof (PaAlsaStreamComponent) );
-
- if( NULL == params->hostApiSpecificStreamInfo )
- {
- const PaAlsaDeviceInfo *devInfo = GetDeviceInfo( &alsaApi->commonHostApiRep, params->device );
- self->numHostChannels = PA_MAX( params->channelCount, StreamDirection_In == streamDir ? devInfo->minInputChannels
- : devInfo->minOutputChannels );
- }
- else
- {
- /* We're blissfully unaware of the minimum channelCount */
- self->numHostChannels = params->channelCount;
- }
-
- PA_ENSURE( AlsaOpen( &alsaApi->commonHostApiRep, params, streamDir, &self->pcm ) );
- self->nfds = snd_pcm_poll_descriptors_count( self->pcm );
- hostSampleFormat = PaUtil_SelectClosestAvailableFormat( GetAvailableFormats( self->pcm ), userSampleFormat );
-
- self->hostSampleFormat = hostSampleFormat;
- self->nativeFormat = Pa2AlsaFormat( hostSampleFormat );
- self->hostInterleaved = self->userInterleaved = !(userSampleFormat & paNonInterleaved);
- self->numUserChannels = params->channelCount;
- self->streamDir = streamDir;
-
- if( !callbackMode && !self->userInterleaved )
- {
- /* Pre-allocate non-interleaved user provided buffers */
- PA_UNLESS( self->userBuffers = PaUtil_AllocateMemory( sizeof (void *) * self->numUserChannels ),
- paInsufficientMemory );
- }
-
-error:
- return result;
-}
-
-static void PaAlsaStreamComponent_Terminate( PaAlsaStreamComponent *self )
-{
- snd_pcm_close( self->pcm );
- if( self->userBuffers )
- PaUtil_FreeMemory( self->userBuffers );
-}
-
-/** Configure the associated ALSA pcm.
- *
- */
-static PaError PaAlsaStreamComponent_Configure( PaAlsaStreamComponent *self, const PaStreamParameters *params, unsigned long
- framesPerHostBuffer, int primeBuffers, int callbackMode, double *sampleRate, PaTime *returnedLatency )
-{
- /*
- int numPeriods;
-
- if( getenv("PA_NUMPERIODS") != NULL )
- numPeriods = atoi( getenv("PA_NUMPERIODS") );
- else
- numPeriods = ( (latency * sampleRate) / *framesPerBuffer ) + 1;
-
- PA_DEBUG(( "latency: %f, rate: %f, framesPerBuffer: %d\n", latency, sampleRate, *framesPerBuffer ));
- if( numPeriods <= 1 )
- numPeriods = 2;
- */
-
- /* Configuration consists of setting all of ALSA's parameters.
- * These parameters come in two flavors: hardware parameters
- * and software paramters. Hardware parameters will affect
- * the way the device is initialized, software parameters
- * affect the way ALSA interacts with me, the user-level client.
- */
-
- snd_pcm_hw_params_t *hwParams;
- snd_pcm_sw_params_t *swParams;
- PaError result = paNoError;
- snd_pcm_access_t accessMode, alternateAccessMode;
- unsigned int numPeriods, minPeriods = 2;
- int dir = 0;
- snd_pcm_t *pcm = self->pcm;
- PaTime latency = params->suggestedLatency;
- double sr = *sampleRate;
- *returnedLatency = -1.;
-
- snd_pcm_hw_params_alloca( &hwParams );
- snd_pcm_sw_params_alloca( &swParams );
-
- self->framesPerBuffer = framesPerHostBuffer;
-
- /* ... fill up the configuration space with all possibile
- * combinations of parameters this device will accept */
- ENSURE_( snd_pcm_hw_params_any( pcm, hwParams ), paUnanticipatedHostError );
-
- ENSURE_( snd_pcm_hw_params_set_periods_integer( pcm, hwParams ), paUnanticipatedHostError );
- ENSURE_( snd_pcm_hw_params_set_period_size_integer( pcm, hwParams ), paUnanticipatedHostError );
-
- if( self->userInterleaved )
- {
- accessMode = SND_PCM_ACCESS_MMAP_INTERLEAVED;
- alternateAccessMode = SND_PCM_ACCESS_MMAP_NONINTERLEAVED;
- }
- else
- {
- accessMode = SND_PCM_ACCESS_MMAP_NONINTERLEAVED;
- alternateAccessMode = SND_PCM_ACCESS_MMAP_INTERLEAVED;
- }
-
- /* If requested access mode fails, try alternate mode */
- if( snd_pcm_hw_params_set_access( pcm, hwParams, accessMode ) < 0 )
- {
- ENSURE_( snd_pcm_hw_params_set_access( pcm, hwParams, alternateAccessMode ), paUnanticipatedHostError );
- /* Flip mode */
- self->hostInterleaved = !self->userInterleaved;
- }
-
- ENSURE_( snd_pcm_hw_params_set_format( pcm, hwParams, self->nativeFormat ), paUnanticipatedHostError );
-
- ENSURE_( SetApproximateSampleRate( pcm, hwParams, sr ), paInvalidSampleRate );
- ENSURE_( GetExactSampleRate( hwParams, &sr ), paUnanticipatedHostError );
- /* reject if there's no sample rate within 1% of the one requested */
- if( (fabs( *sampleRate - sr ) / *sampleRate) > 0.01 )
- {
- PA_DEBUG(("%s: Wanted %f, closest sample rate was %d\n", __FUNCTION__, sampleRate, sr ));
- PA_ENSURE( paInvalidSampleRate );
- }
-
- ENSURE_( snd_pcm_hw_params_set_channels( pcm, hwParams, self->numHostChannels ), paInvalidChannelCount );
-
- /* I think there should be at least 2 periods (even though ALSA doesn't appear to enforce this) */
- dir = 0;
- ENSURE_( snd_pcm_hw_params_set_periods_min( pcm, hwParams, &minPeriods, &dir ), paUnanticipatedHostError );
- dir = 0;
- ENSURE_( snd_pcm_hw_params_set_period_size_near( pcm, hwParams, &self->framesPerBuffer, &dir ), paUnanticipatedHostError );
-
- /* Find an acceptable number of periods */
- numPeriods = (latency * sr) / self->framesPerBuffer + 1;
- dir = 0;
- ENSURE_( snd_pcm_hw_params_set_periods_near( pcm, hwParams, &numPeriods, &dir ), paUnanticipatedHostError );
- /* Minimum of periods should already be 2 */
- PA_UNLESS( numPeriods >= 2, paInternalError );
-
- /* Set the parameters! */
- ENSURE_( snd_pcm_hw_params( pcm, hwParams ), paUnanticipatedHostError );
- ENSURE_( snd_pcm_hw_params_get_buffer_size( hwParams, &self->bufferSize ), paUnanticipatedHostError );
-
- /* Latency in seconds, one period is not counted as latency */
- latency = (numPeriods - 1) * self->framesPerBuffer / sr;
-
- /* Now software parameters... */
- ENSURE_( snd_pcm_sw_params_current( pcm, swParams ), paUnanticipatedHostError );
-
- ENSURE_( snd_pcm_sw_params_set_start_threshold( pcm, swParams, self->framesPerBuffer ), paUnanticipatedHostError );
- ENSURE_( snd_pcm_sw_params_set_stop_threshold( pcm, swParams, self->bufferSize ), paUnanticipatedHostError );
-
- /* Silence buffer in the case of underrun */
- if( !primeBuffers ) /* XXX: Make sense? */
- {
- snd_pcm_uframes_t boundary;
- ENSURE_( snd_pcm_sw_params_get_boundary( swParams, &boundary ), paUnanticipatedHostError );
- ENSURE_( snd_pcm_sw_params_set_silence_threshold( pcm, swParams, 0 ), paUnanticipatedHostError );
- ENSURE_( snd_pcm_sw_params_set_silence_size( pcm, swParams, boundary ), paUnanticipatedHostError );
- }
-
- ENSURE_( snd_pcm_sw_params_set_avail_min( pcm, swParams, self->framesPerBuffer ), paUnanticipatedHostError );
- ENSURE_( snd_pcm_sw_params_set_xfer_align( pcm, swParams, 1 ), paUnanticipatedHostError );
- ENSURE_( snd_pcm_sw_params_set_tstamp_mode( pcm, swParams, SND_PCM_TSTAMP_MMAP ), paUnanticipatedHostError );
-
- /* Set the parameters! */
- ENSURE_( snd_pcm_sw_params( pcm, swParams ), paUnanticipatedHostError );
-
- *sampleRate = sr;
- *returnedLatency = latency;
-
-end:
- return result;
-
-error:
- goto end; /* No particular action */
-}
-
-static PaError PaAlsaStream_Initialize( PaAlsaStream *self, PaAlsaHostApiRepresentation *alsaApi, const PaStreamParameters *inParams,
- const PaStreamParameters *outParams, double sampleRate, unsigned long framesPerUserBuffer, PaStreamCallback callback,
- PaStreamFlags streamFlags, void *userData )
-{
- PaError result = paNoError;
- assert( self );
-
- memset( self, 0, sizeof (PaAlsaStream) );
-
- if( NULL != callback )
- {
- PaUtil_InitializeStreamRepresentation( &self->streamRepresentation,
- &alsaApi->callbackStreamInterface,
- callback, userData );
- self->callbackMode = 1;
- }
- else
- {
- PaUtil_InitializeStreamRepresentation( &self->streamRepresentation,
- &alsaApi->blockingStreamInterface,
- NULL, userData );
- }
-
- self->framesPerUserBuffer = framesPerUserBuffer;
- self->neverDropInput = streamFlags & paNeverDropInput;
- /* XXX: Ignore paPrimeOutputBuffersUsingStreamCallback untill buffer priming is fully supported in pa_process.c */
- /*
- if( outParams & streamFlags & paPrimeOutputBuffersUsingStreamCallback )
- self->primeBuffers = 1;
- */
- memset( &self->capture, 0, sizeof (PaAlsaStreamComponent) );
- memset( &self->playback, 0, sizeof (PaAlsaStreamComponent) );
- if( inParams )
- PA_ENSURE( PaAlsaStreamComponent_Initialize( &self->capture, alsaApi, inParams, StreamDirection_In, NULL != callback ) );
- if( outParams )
- PA_ENSURE( PaAlsaStreamComponent_Initialize( &self->playback, alsaApi, outParams, StreamDirection_Out, NULL != callback ) );
-
- assert( self->capture.nfds || self->playback.nfds );
-
- PA_UNLESS( self->pfds = (struct pollfd*)PaUtil_AllocateMemory( (self->capture.nfds +
- self->playback.nfds) * sizeof (struct pollfd) ), paInsufficientMemory );
-
- PaUtil_InitializeCpuLoadMeasurer( &self->cpuLoadMeasurer, sampleRate );
- InitializeThreading( &self->threading, &self->cpuLoadMeasurer );
- ASSERT_CALL_( pthread_mutex_init( &self->stateMtx, NULL ), 0 );
- ASSERT_CALL_( pthread_mutex_init( &self->startMtx, NULL ), 0 );
- ASSERT_CALL_( pthread_cond_init( &self->startCond, NULL ), 0 );
-
-error:
- return result;
-}
-
-/** Free resources associated with stream, and eventually stream itself.
- *
- * Frees allocated memory, and terminates individual StreamComponents.
- */
-static void PaAlsaStream_Terminate( PaAlsaStream *self )
-{
- assert( self );
-
- if( self->capture.pcm )
- {
- PaAlsaStreamComponent_Terminate( &self->capture );
- }
- if( self->playback.pcm )
- {
- PaAlsaStreamComponent_Terminate( &self->playback );
- }
-
- PaUtil_FreeMemory( self->pfds );
- ASSERT_CALL_( pthread_mutex_destroy( &self->stateMtx ), 0 );
- ASSERT_CALL_( pthread_mutex_destroy( &self->startMtx ), 0 );
- ASSERT_CALL_( pthread_cond_destroy( &self->startCond ), 0 );
-
- PaUtil_FreeMemory( self );
-}
-
-/** Calculate polling timeout
- *
- * @param frames Time to wait
- * @return Polling timeout in milliseconds
- */
-static int CalculatePollTimeout( const PaAlsaStream *stream, unsigned long frames )
-{
- assert( stream->streamRepresentation.streamInfo.sampleRate > 0.0 );
- /* Period in msecs, rounded up */
- return (int)ceil( 1000 * frames / stream->streamRepresentation.streamInfo.sampleRate );
-}
-
-/** Set up ALSA stream parameters.
- *
- */
-static PaError PaAlsaStream_Configure( PaAlsaStream *self, const PaStreamParameters *inParams, const PaStreamParameters
- *outParams, double sampleRate, unsigned long framesPerHostBuffer, double *inputLatency, double *outputLatency,
- unsigned long *maxHostBufferSize )
-{
- PaError result = paNoError;
- double realSr = sampleRate;
-
- if( self->capture.pcm )
- PA_ENSURE( PaAlsaStreamComponent_Configure( &self->capture, inParams, framesPerHostBuffer, self->primeBuffers,
- self->callbackMode, &realSr, inputLatency ) );
- if( self->playback.pcm )
- PA_ENSURE( PaAlsaStreamComponent_Configure( &self->playback, outParams, framesPerHostBuffer, self->primeBuffers,
- self->callbackMode, &realSr, outputLatency ) );
-
- /* Should be exact now */
- self->streamRepresentation.streamInfo.sampleRate = realSr;
-
- /* this will cause the two streams to automatically start/stop/prepare in sync.
- * We only need to execute these operations on one of the pair.
- * A: We don't want to do this on a blocking stream.
- */
- if( self->callbackMode && self->capture.pcm && self->playback.pcm )
- {
- int err = snd_pcm_link( self->capture.pcm, self->playback.pcm );
- if( err >= 0 )
- self->pcmsSynced = 1;
- else
- PA_DEBUG(( "%s: Unable to sync pcms: %s\n", __FUNCTION__, snd_strerror( err ) ));
- }
-
- /* Frames per host buffer for the stream is set as a compromise between the two directions */
- framesPerHostBuffer = PA_MIN( self->capture.pcm ? self->capture.framesPerBuffer : ULONG_MAX,
- self->playback.pcm ? self->playback.framesPerBuffer : ULONG_MAX );
- self->pollTimeout = CalculatePollTimeout( self, framesPerHostBuffer ); /* Period in msecs, rounded up */
-
- *maxHostBufferSize = PA_MAX( self->capture.pcm ? self->capture.bufferSize : 0,
- self->playback.pcm ? self->playback.bufferSize : 0 );
-
- /* Time before watchdog unthrottles realtime thread == 1/4 of period time in msecs */
- self->threading.throttledSleepTime = (unsigned long) (framesPerHostBuffer / sampleRate / 4 * 1000);
-
- if( self->callbackMode )
- {
- /* If the user expects a certain number of frames per callback we will either have to rely on block adaption
- * (framesPerHostBuffer is not an integer multiple of framesPerBuffer) or we can simply align the number
- * of host buffer frames with what the user specified */
- if( self->framesPerUserBuffer != paFramesPerBufferUnspecified )
- {
- /* self->alignFrames = 1; */
-
- /* Unless the ratio between number of host and user buffer frames is an integer we will have to rely
- * on block adaption */
- /*
- if( framesPerHostBuffer % framesPerBuffer != 0 || (self->capture.pcm && self->playback.pcm &&
- self->capture.framesPerBuffer != self->playback.framesPerBuffer) )
- self->useBlockAdaption = 1;
- else
- self->alignFrames = 1;
- */
- }
- }
-
-error:
- return result;
-}
-
-/* We need to determine how many frames per host buffer to use. Our
- * goals are to provide the best possible performance, but also to
- * most closely honor the requested latency settings. Therefore this
- * decision is based on:
- *
- * - the period sizes that playback and/or capture support. The
- * host buffer size has to be one of these.
- * - the number of periods that playback and/or capture support.
- *
- * We want to make period_size*(num_periods-1) to be as close as possible
- * to latency*rate for both playback and capture.
- *
- * This is one of those blocks of code that will just take a lot of
- * refinement to be any good.
- *
- * In the full-duplex case it is possible that the routine was unable
- * to find a number of frames per buffer acceptable to both devices
- * TODO: Implement an algorithm to find the value closest to acceptance
- * by both devices, to minimize difference between period sizes?
- */
-static PaError DetermineFramesPerBuffer( const PaAlsaStream *stream, double sampleRate, const PaStreamParameters *inputParameters,
- const PaStreamParameters *outputParameters, unsigned long *determinedFrames, const PaUtilHostApiRepresentation *hostApi )
-{
- PaError result = paNoError;
- unsigned long framesPerBuffer = 0;
- int numHostInputChannels = 0, numHostOutputChannels = 0;
-
- /* XXX: Clean this up */
- if( stream->capture.pcm )
- {
- const PaAlsaDeviceInfo *devInfo = GetDeviceInfo( hostApi, inputParameters->device );
- numHostInputChannels = PA_MAX( inputParameters->channelCount, devInfo->minInputChannels );
- }
- if( stream->playback.pcm )
- {
- const PaAlsaDeviceInfo *devInfo = GetDeviceInfo( hostApi, outputParameters->device );
- numHostOutputChannels = PA_MAX( outputParameters->channelCount, devInfo->minOutputChannels );
- }
-
- if( stream->capture.pcm && stream->playback.pcm )
- {
- snd_pcm_uframes_t desiredLatency, e;
- snd_pcm_uframes_t minPeriodSize, minPlayback, minCapture, maxPeriodSize, maxPlayback, maxCapture,
- optimalPeriodSize, periodSize;
- int dir = 0;
- unsigned int minPeriods = 2;
-
- snd_pcm_t *pcm;
- snd_pcm_hw_params_t *hwParamsPlayback, *hwParamsCapture;
-
- snd_pcm_hw_params_alloca( &hwParamsPlayback );
- snd_pcm_hw_params_alloca( &hwParamsCapture );
-
- /* Come up with a common desired latency */
- pcm = stream->playback.pcm;
- snd_pcm_hw_params_any( pcm, hwParamsPlayback );
- ENSURE_( SetApproximateSampleRate( pcm, hwParamsPlayback, sampleRate ), paInvalidSampleRate );
- ENSURE_( snd_pcm_hw_params_set_channels( pcm, hwParamsPlayback, numHostOutputChannels ),
- paBadIODeviceCombination );
-
- ENSURE_( snd_pcm_hw_params_set_period_size_integer( pcm, hwParamsPlayback ), paUnanticipatedHostError );
- ENSURE_( snd_pcm_hw_params_set_periods_integer( pcm, hwParamsPlayback ), paUnanticipatedHostError );
- ENSURE_( snd_pcm_hw_params_set_periods_min( pcm, hwParamsPlayback, &minPeriods, &dir ), paUnanticipatedHostError );
- ENSURE_( snd_pcm_hw_params_get_period_size_min( hwParamsPlayback, &minPlayback, &dir ), paUnanticipatedHostError );
- ENSURE_( snd_pcm_hw_params_get_period_size_max( hwParamsPlayback, &maxPlayback, &dir ), paUnanticipatedHostError );
-
- pcm = stream->capture.pcm;
- ENSURE_( snd_pcm_hw_params_any( pcm, hwParamsCapture ), paUnanticipatedHostError );
- ENSURE_( SetApproximateSampleRate( pcm, hwParamsCapture, sampleRate ), paBadIODeviceCombination );
- ENSURE_( snd_pcm_hw_params_set_channels( pcm, hwParamsCapture, numHostInputChannels ),
- paBadIODeviceCombination );
-
- ENSURE_( snd_pcm_hw_params_set_period_size_integer( pcm, hwParamsCapture ), paUnanticipatedHostError );
- ENSURE_( snd_pcm_hw_params_set_periods_integer( pcm, hwParamsCapture ), paUnanticipatedHostError );
- ENSURE_( snd_pcm_hw_params_set_periods_min( pcm, hwParamsCapture, &minPeriods, &dir ), paUnanticipatedHostError );
- ENSURE_( snd_pcm_hw_params_get_period_size_min( hwParamsCapture, &minCapture, &dir ), paUnanticipatedHostError );
- ENSURE_( snd_pcm_hw_params_get_period_size_max( hwParamsCapture, &maxCapture, &dir ), paUnanticipatedHostError );
-
- minPeriodSize = PA_MAX( minPlayback, minCapture );
- maxPeriodSize = PA_MIN( maxPlayback, maxCapture );
-
- desiredLatency = (snd_pcm_uframes_t) (PA_MIN( outputParameters->suggestedLatency, inputParameters->suggestedLatency )
- * sampleRate);
- /* Clamp desiredLatency */
- {
- snd_pcm_uframes_t tmp, maxBufferSize = ULONG_MAX;
- ENSURE_( snd_pcm_hw_params_get_buffer_size_max( hwParamsPlayback, &maxBufferSize ), paUnanticipatedHostError );
- ENSURE_( snd_pcm_hw_params_get_buffer_size_max( hwParamsCapture, &tmp ), paUnanticipatedHostError );
- maxBufferSize = PA_MIN( maxBufferSize, tmp );
-
- desiredLatency = PA_MIN( desiredLatency, maxBufferSize );
- }
-
- /* Find the closest power of 2 */
- e = ilogb( minPeriodSize );
- if( minPeriodSize & (minPeriodSize - 1) )
- e += 1;
- periodSize = (snd_pcm_uframes_t) pow( 2, e );
-
- while( periodSize <= maxPeriodSize )
- {
- if( snd_pcm_hw_params_test_period_size( stream->playback.pcm, hwParamsPlayback, periodSize, 0 ) >= 0 &&
- snd_pcm_hw_params_test_period_size( stream->capture.pcm, hwParamsCapture, periodSize, 0 ) >= 0 )
- break; /* Ok! */
-
- periodSize *= 2;
- }
-
- /* 4 periods considered optimal */
- optimalPeriodSize = PA_MAX( desiredLatency / 4, minPeriodSize );
- optimalPeriodSize = PA_MIN( optimalPeriodSize, maxPeriodSize );
-
- /* Find the closest power of 2 */
- e = ilogb( optimalPeriodSize );
- if( optimalPeriodSize & (optimalPeriodSize - 1) )
- e += 1;
- optimalPeriodSize = (snd_pcm_uframes_t) pow( 2, e );
-
- while( optimalPeriodSize >= periodSize )
- {
- pcm = stream->playback.pcm;
- if( snd_pcm_hw_params_test_period_size( pcm, hwParamsPlayback, optimalPeriodSize, 0 ) < 0 )
- continue;
-
- pcm = stream->capture.pcm;
- if( snd_pcm_hw_params_test_period_size( pcm, hwParamsCapture, optimalPeriodSize, 0 ) >= 0 )
- break;
-
- optimalPeriodSize /= 2;
- }
-
- if( optimalPeriodSize > periodSize )
- periodSize = optimalPeriodSize;
-
- if( periodSize <= maxPeriodSize )
- {
- /* Looks good */
- framesPerBuffer = periodSize;
- }
- else
- {
- /* Unable to find a common period size, oh well */
- optimalPeriodSize = PA_MAX( desiredLatency / 4, minPeriodSize );
- optimalPeriodSize = PA_MIN( optimalPeriodSize, maxPeriodSize );
-
- /* ConfigureStream should find individual period sizes acceptable for each device */
- framesPerBuffer = optimalPeriodSize;
- /* PA_ENSURE( paBadIODeviceCombination ); */
- }
- }
- else /* half-duplex is a slightly simpler case */
- {
- unsigned long bufferSize, channels;
- snd_pcm_t *pcm;
- snd_pcm_hw_params_t *hwParams;
-
- snd_pcm_hw_params_alloca( &hwParams );
-
- if( stream->capture.pcm )
- {
- pcm = stream->capture.pcm;
- bufferSize = inputParameters->suggestedLatency * sampleRate;
- channels = numHostInputChannels;
- }
- else
- {
- pcm = stream->playback.pcm;
- bufferSize = outputParameters->suggestedLatency * sampleRate;
- channels = numHostOutputChannels;
- }
-
- ENSURE_( snd_pcm_hw_params_any( pcm, hwParams ), paUnanticipatedHostError );
- ENSURE_( SetApproximateSampleRate( pcm, hwParams, sampleRate ), paInvalidSampleRate );
- ENSURE_( snd_pcm_hw_params_set_channels( pcm, hwParams, channels ), paBadIODeviceCombination );
-
- ENSURE_( snd_pcm_hw_params_set_period_size_integer( pcm, hwParams ), paUnanticipatedHostError );
- ENSURE_( snd_pcm_hw_params_set_periods_integer( pcm, hwParams ), paUnanticipatedHostError );
-
- /* Using 5 as a base number of periods, we try to approximate the suggested latency (+1 period),
- finding a combination of period/buffer size which best fits these constraints */
- framesPerBuffer = bufferSize / 4;
- bufferSize += framesPerBuffer; /* One period doesn't count as latency */
- ENSURE_( snd_pcm_hw_params_set_buffer_size_near( pcm, hwParams, &bufferSize ), paUnanticipatedHostError );
- ENSURE_( snd_pcm_hw_params_set_period_size_near( pcm, hwParams, &framesPerBuffer, NULL ), paUnanticipatedHostError );
- }
-
- PA_UNLESS( framesPerBuffer != 0, paInternalError );
- *determinedFrames = framesPerBuffer;
-
-error:
- return result;
-}
-
-static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
- PaStream** s,
- const PaStreamParameters *inputParameters,
- const PaStreamParameters *outputParameters,
- double sampleRate,
- unsigned long framesPerBuffer,
- PaStreamFlags streamFlags,
- PaStreamCallback *callback,
- void *userData )
-{
- PaError result = paNoError;
- PaAlsaHostApiRepresentation *alsaHostApi = (PaAlsaHostApiRepresentation*)hostApi;
- PaAlsaStream *stream = NULL;
- PaSampleFormat hostInputSampleFormat = 0, hostOutputSampleFormat = 0;
- PaSampleFormat inputSampleFormat = 0, outputSampleFormat = 0;
- int numInputChannels = 0, numOutputChannels = 0;
- PaTime inputLatency, outputLatency;
- unsigned long framesPerHostBuffer;
- PaUtilHostBufferSizeMode hostBufferSizeMode = paUtilBoundedHostBufferSize;
- unsigned long maxHostBufferSize; /* Upper bound of host buffer size */
-
- if( (streamFlags & paPlatformSpecificFlags) != 0 )
- return paInvalidFlag;
-
- if( inputParameters )
- {
- PA_ENSURE( ValidateParameters( inputParameters, hostApi, StreamDirection_In ) );
-
- numInputChannels = inputParameters->channelCount;
- inputSampleFormat = inputParameters->sampleFormat;
- }
- if( outputParameters )
- {
- PA_ENSURE( ValidateParameters( outputParameters, hostApi, StreamDirection_Out ) );
-
- numOutputChannels = outputParameters->channelCount;
- outputSampleFormat = outputParameters->sampleFormat;
- }
-
- /* XXX: Why do we support this anyway? */
- if( framesPerBuffer == paFramesPerBufferUnspecified && getenv( "PA_ALSA_PERIODSIZE" ) != NULL )
- {
- PA_DEBUG(( "%s: Getting framesPerBuffer from environment\n", __FUNCTION__ ));
- framesPerBuffer = atoi( getenv("PA_ALSA_PERIODSIZE") );
- }
- framesPerHostBuffer = framesPerBuffer;
-
- PA_UNLESS( stream = (PaAlsaStream*)PaUtil_AllocateMemory( sizeof(PaAlsaStream) ), paInsufficientMemory );
- PA_ENSURE( PaAlsaStream_Initialize( stream, alsaHostApi, inputParameters, outputParameters, sampleRate,
- framesPerBuffer, callback, streamFlags, userData ) );
-
- /* If the number of frames per buffer is unspecified, we have to come up with
- * one. This is both a blessing and a curse: a blessing because we can optimize
- * the number to best meet the requirements, but a curse because that's really
- * hard to do well. For this reason we also support an interface where the user
- * specifies these by setting environment variables. */
- if( framesPerBuffer == paFramesPerBufferUnspecified )
- {
- PA_ENSURE( DetermineFramesPerBuffer( stream, sampleRate, inputParameters, outputParameters, &framesPerHostBuffer,
- hostApi ) );
- }
-
- PA_ENSURE( PaAlsaStream_Configure( stream, inputParameters, outputParameters, sampleRate, framesPerHostBuffer,
- &inputLatency, &outputLatency, &maxHostBufferSize ) );
- hostInputSampleFormat = stream->capture.hostSampleFormat;
- hostOutputSampleFormat = stream->playback.hostSampleFormat;
-
- if( framesPerHostBuffer != framesPerBuffer )
- {
- PA_DEBUG(( "%s: Number of frames per user and host buffer differs\n", __FUNCTION__ ));
- }
-
- PA_ENSURE( PaUtil_InitializeBufferProcessor( &stream->bufferProcessor,
- numInputChannels, inputSampleFormat, hostInputSampleFormat,
- numOutputChannels, outputSampleFormat, hostOutputSampleFormat,
- sampleRate, streamFlags, framesPerBuffer, maxHostBufferSize,
- hostBufferSizeMode, callback, userData ) );
-
- /* Ok, buffer processor is initialized, now we can deduce it's latency */
- if( numInputChannels > 0 )
- stream->streamRepresentation.streamInfo.inputLatency = inputLatency + PaUtil_GetBufferProcessorInputLatency(
- &stream->bufferProcessor );
- if( numOutputChannels > 0 )
- stream->streamRepresentation.streamInfo.outputLatency = outputLatency + PaUtil_GetBufferProcessorOutputLatency(
- &stream->bufferProcessor );
-
- *s = (PaStream*)stream;
-
- return result;
-
-error:
- if( stream )
- PaAlsaStream_Terminate( stream );
-
- return result;
-}
-
-static PaError CloseStream( PaStream* s )
-{
- PaError result = paNoError;
- PaAlsaStream *stream = (PaAlsaStream*)s;
-
- PaUtil_TerminateBufferProcessor( &stream->bufferProcessor );
- PaUtil_TerminateStreamRepresentation( &stream->streamRepresentation );
-
- PaAlsaStream_Terminate( stream );
-
- return result;
-}
-
-static void SilenceBuffer( PaAlsaStream *stream )
-{
- const snd_pcm_channel_area_t *areas;
- snd_pcm_uframes_t frames = (snd_pcm_uframes_t)snd_pcm_avail_update( stream->playback.pcm ), offset;
-
- snd_pcm_mmap_begin( stream->playback.pcm, &areas, &offset, &frames );
- snd_pcm_areas_silence( areas, offset, stream->playback.numHostChannels, frames, stream->playback.nativeFormat );
- snd_pcm_mmap_commit( stream->playback.pcm, offset, frames );
-}
-
-/** Start/prepare pcm(s) for streaming.
- *
- * Depending on wether the stream is in callback or blocking mode, we will respectively start or simply
- * prepare the playback pcm. If the buffer has _not_ been primed, we will in callback mode prepare and
- * silence the buffer before starting playback. In blocking mode we simply prepare, as the playback will
- * be started automatically as the user writes to output.
- *
- * The capture pcm, however, will simply be prepared and started.
- *
- * PaAlsaStream::startMtx makes sure access is synchronized (useful in callback mode)
- */
-static PaError AlsaStart( PaAlsaStream *stream, int priming )
-{
- PaError result = paNoError;
-
- if( stream->playback.pcm )
- {
- if( stream->callbackMode )
- {
- if( !priming )
- {
- /* Buffer isn't primed, so prepare and silence */
- ENSURE_( snd_pcm_prepare( stream->playback.pcm ), paUnanticipatedHostError );
- SilenceBuffer( stream );
- }
- ENSURE_( snd_pcm_start( stream->playback.pcm ), paUnanticipatedHostError );
- }
- else
- ENSURE_( snd_pcm_prepare( stream->playback.pcm ), paUnanticipatedHostError );
- }
- if( stream->capture.pcm && !stream->pcmsSynced )
- {
- ENSURE_( snd_pcm_prepare( stream->capture.pcm ), paUnanticipatedHostError );
- /* For a blocking stream we want to start capture as well, since nothing will happen otherwise */
- ENSURE_( snd_pcm_start( stream->capture.pcm ), paUnanticipatedHostError );
- }
-
-end:
- return result;
-error:
- goto end;
-}
-
-/** Utility function for determining if pcms are in running state.
- *
- */
-static int IsRunning( PaAlsaStream *stream )
-{
- int result = 0;
-
- ASSERT_CALL_( pthread_mutex_lock( &stream->stateMtx ), 0 ); /* Synchronize access to pcm state */
- if( stream->capture.pcm )
- {
- snd_pcm_state_t capture_state = snd_pcm_state( stream->capture.pcm );
-
- if( capture_state == SND_PCM_STATE_RUNNING || capture_state == SND_PCM_STATE_XRUN
- || capture_state == SND_PCM_STATE_DRAINING )
- {
- result = 1;
- goto end;
- }
- }
-
- if( stream->playback.pcm )
- {
- snd_pcm_state_t playback_state = snd_pcm_state( stream->playback.pcm );
-
- if( playback_state == SND_PCM_STATE_RUNNING || playback_state == SND_PCM_STATE_XRUN
- || playback_state == SND_PCM_STATE_DRAINING )
- {
- result = 1;
- goto end;
- }
- }
-
-end:
- ASSERT_CALL_( pthread_mutex_unlock( &stream->stateMtx ), 0 );
-
- return result;
-}
-
-static PaError StartStream( PaStream *s )
-{
- PaError result = paNoError;
- PaAlsaStream *stream = (PaAlsaStream*)s;
- int streamStarted = 0; /* So we can know wether we need to take the stream down */
-
- /* Ready the processor */
- PaUtil_ResetBufferProcessor( &stream->bufferProcessor );
-
- /* Set now, so we can test for activity further down */
- stream->isActive = 1;
-
- if( stream->callbackMode )
- {
- int res = 0;
- PaTime pt = PaUtil_GetTime();
- struct timespec ts;
-
- PA_ENSURE( CreateCallbackThread( &stream->threading, &CallbackThreadFunc, stream ) );
- streamStarted = 1;
-
- /* Wait for stream to be started */
- ts.tv_sec = (time_t) floor( pt + 1 );
- ts.tv_nsec = (long) ((pt - floor( pt )) * 1000000000);
-
- /* Since we'll be holding a lock on the startMtx (when not waiting on the condition), IsRunning won't be checking
- * stream state at the same time as the callback thread affects it. We also check IsStreamActive, in the unlikely
- * case the callback thread exits in the meantime (the stream will be considered inactive after the thread exits) */
- ASSERT_CALL_( pthread_mutex_lock( &stream->startMtx ), 0 );
-
- /* Due to possible spurious wakeups, we enclose in a loop */
- while( !IsRunning( stream ) && IsStreamActive( s ) && !res )
- {
- res = pthread_cond_timedwait( &stream->startCond, &stream->startMtx, &ts );
- }
- ASSERT_CALL_( pthread_mutex_unlock( &stream->startMtx ), 0 );
-
- PA_UNLESS( !res || res == ETIMEDOUT, paInternalError );
- PA_DEBUG(( "%s: Waited for %g seconds for stream to start\n", __FUNCTION__, PaUtil_GetTime() - pt ));
-
- if( res == ETIMEDOUT )
- {
- PA_ENSURE( paTimedOut );
- }
- }
- else
- {
- PA_ENSURE( AlsaStart( stream, 0 ) );
- streamStarted = 1;
- }
-
-end:
- return result;
-error:
- if( streamStarted )
- AbortStream( stream );
- stream->isActive = 0;
-
- goto end;
-}
-
-static PaError AlsaStop( PaAlsaStream *stream, int abort )
-{
- PaError result = paNoError;
-
- if( abort )
- {
- if( stream->playback.pcm )
- ENSURE_( snd_pcm_drop( stream->playback.pcm ), paUnanticipatedHostError );
- if( stream->capture.pcm && !stream->pcmsSynced )
- ENSURE_( snd_pcm_drop( stream->capture.pcm ), paUnanticipatedHostError );
-
- PA_DEBUG(( "Dropped frames\n" ));
- }
- else
- {
- if( stream->playback.pcm )
- ENSURE_( snd_pcm_drain( stream->playback.pcm ), paUnanticipatedHostError );
- if( stream->capture.pcm && !stream->pcmsSynced )
- ENSURE_( snd_pcm_drain( stream->capture.pcm ), paUnanticipatedHostError );
- }
-
-end:
- return result;
-error:
- goto end;
-}
-
-/** Stop or abort stream.
- *
- * If a stream is in callback mode we will have to inspect wether the background thread has
- * finished, or we will have to take it out. In either case we join the thread before
- * returning. In blocking mode, we simply tell ALSA to stop abruptly (abort) or finish
- * buffers (drain)
- *
- * Stream will be considered inactive (!PaAlsaStream::isActive) after a call to this function
- */
-static PaError RealStop( PaAlsaStream *stream, int abort )
-{
- PaError result = paNoError;
-
- /* First deal with the callback thread, cancelling and/or joining
- * it if necessary
- */
- if( stream->callbackMode )
- {
- PaError threadRes, watchdogRes;
- stream->callbackAbort = abort;
-
- if( !abort )
- {
- PA_DEBUG(( "Stopping callback\n" ));
- stream->callbackStop = 1;
- }
- PA_ENSURE( KillCallbackThread( &stream->threading, !abort, &threadRes, &watchdogRes ) );
- if( threadRes != paNoError )
- PA_DEBUG(( "Callback thread returned: %d\n", threadRes ));
- if( watchdogRes != paNoError )
- PA_DEBUG(( "Watchdog thread returned: %d\n", watchdogRes ));
-
- stream->callbackStop = 0; /* The deed is done */
- stream->callback_finished = 0;
- }
- else
- {
- PA_ENSURE( AlsaStop( stream, abort ) );
- }
-
- stream->isActive = 0;
-
-end:
- return result;
-
-error:
- goto end;
-}
-
-static PaError StopStream( PaStream *s )
-{
- return RealStop( (PaAlsaStream *) s, 0 );
-}
-
-static PaError AbortStream( PaStream *s )
-{
- return RealStop( (PaAlsaStream * ) s, 1 );
-}
-
-/** The stream is considered stopped before StartStream, or AFTER a call to Abort/StopStream (callback
- * returning !paContinue is not considered)
- *
- */
-static PaError IsStreamStopped( PaStream *s )
-{
- PaAlsaStream *stream = (PaAlsaStream *)s;
-
- /* callback_finished indicates we need to join callback thread (ie. in Abort/StopStream) */
- return !IsStreamActive( s ) && !stream->callback_finished;
-}
-
-static PaError IsStreamActive( PaStream *s )
-{
- PaAlsaStream *stream = (PaAlsaStream*)s;
- return stream->isActive;
-}
-
-static PaTime GetStreamTime( PaStream *s )
-{
- PaAlsaStream *stream = (PaAlsaStream*)s;
-
- snd_timestamp_t timestamp;
- snd_pcm_status_t *status;
- snd_pcm_status_alloca( &status );
-
- /* TODO: what if we have both? does it really matter? */
-
- /* TODO: if running in callback mode, this will mean
- * libasound routines are being called from multiple threads.
- * need to verify that libasound is thread-safe. */
-
- if( stream->capture.pcm )
- {
- snd_pcm_status( stream->capture.pcm, status );
- }
- else if( stream->playback.pcm )
- {
- snd_pcm_status( stream->playback.pcm, status );
- }
-
- snd_pcm_status_get_tstamp( status, &timestamp );
- return timestamp.tv_sec + (PaTime)timestamp.tv_usec / 1000000.0;
-}
-
-static double GetStreamCpuLoad( PaStream* s )
-{
- PaAlsaStream *stream = (PaAlsaStream*)s;
-
- return PaUtil_GetCpuLoad( &stream->cpuLoadMeasurer );
-}
-
-static int SetApproximateSampleRate( snd_pcm_t *pcm, snd_pcm_hw_params_t *hwParams, double sampleRate )
-{
- unsigned long approx = (unsigned long) sampleRate;
- int dir = 0;
- double fraction = sampleRate - approx;
-
- assert( pcm && hwParams );
-
- if( fraction > 0.0 )
- {
- if( fraction > 0.5 )
- {
- ++approx;
- dir = -1;
- }
- else
- dir = 1;
- }
-
- return snd_pcm_hw_params_set_rate( pcm, hwParams, approx, dir );
-}
-
-/* Return exact sample rate in param sampleRate */
-static int GetExactSampleRate( snd_pcm_hw_params_t *hwParams, double *sampleRate )
-{
- unsigned int num, den;
- int err;
-
- assert( hwParams );
-
- err = snd_pcm_hw_params_get_rate_numden( hwParams, &num, &den );
- *sampleRate = (double) num / den;
-
- return err;
-}
-
-/* Utility functions for blocking/callback interfaces */
-
-/* Atomic restart of stream (we don't want the intermediate state visible) */
-static PaError AlsaRestart( PaAlsaStream *stream )
-{
- PaError result = paNoError;
-
- ASSERT_CALL_( pthread_mutex_lock( &stream->stateMtx ), 0 );
- PA_ENSURE( AlsaStop( stream, 0 ) );
- PA_ENSURE( AlsaStart( stream, 0 ) );
-
- PA_DEBUG(( "%s: Restarted audio\n", __FUNCTION__ ));
-
-error:
- ASSERT_CALL_( pthread_mutex_unlock( &stream->stateMtx ), 0 );
- return result;
-}
-
-/** Recover from xrun state.
- *
- */
-static PaError PaAlsaStream_HandleXrun( PaAlsaStream *self )
-{
- PaError result = paNoError;
- snd_pcm_status_t *st;
- PaTime now = PaUtil_GetTime();
- snd_timestamp_t t;
-
- snd_pcm_status_alloca( &st );
-
- if( self->playback.pcm )
- {
- snd_pcm_status( self->playback.pcm, st );
- if( snd_pcm_status_get_state( st ) == SND_PCM_STATE_XRUN )
- {
- snd_pcm_status_get_trigger_tstamp( st, &t );
- self->underrun = now * 1000 - ((PaTime) t.tv_sec * 1000 + (PaTime) t.tv_usec / 1000);
- }
- }
- if( self->capture.pcm )
- {
- snd_pcm_status( self->capture.pcm, st );
- if( snd_pcm_status_get_state( st ) == SND_PCM_STATE_XRUN )
- {
- snd_pcm_status_get_trigger_tstamp( st, &t );
- self->overrun = now * 1000 - ((PaTime) t.tv_sec * 1000 + (PaTime) t.tv_usec / 1000);
- }
- }
-
- PA_ENSURE( AlsaRestart( self ) );
-
-end:
- return result;
-error:
- goto end;
-}
-
-/** Decide if we should continue polling for specified direction, eventually adjust the poll timeout.
- *
- */
-static PaError ContinuePoll( const PaAlsaStream *stream, StreamDirection streamDir, int *pollTimeout, int *continuePoll )
-{
- PaError result = paNoError;
- snd_pcm_sframes_t delay, margin;
- int err;
- const PaAlsaStreamComponent *component = NULL, *otherComponent = NULL;
-
- *continuePoll = 1;
-
- if( StreamDirection_In == streamDir )
- {
- component = &stream->capture;
- otherComponent = &stream->playback;
- }
- else
- {
- component = &stream->playback;
- otherComponent = &stream->capture;
- }
-
- /* ALSA docs say that negative delay should indicate xrun, but in my experience snd_pcm_delay returns -EPIPE */
- if( (err = snd_pcm_delay( otherComponent->pcm, &delay )) < 0 )
- {
- if( err == -EPIPE )
- {
- /* Xrun */
- *continuePoll = 0;
- goto error;
- }
-
- ENSURE_( err, paUnanticipatedHostError );
- }
-
- if( StreamDirection_Out == streamDir )
- {
- /* Number of eligible frames before capture overrun */
- delay = otherComponent->bufferSize - delay;
- }
- margin = delay - otherComponent->framesPerBuffer / 2;
-
- if( margin < 0 )
- {
- PA_DEBUG(( "%s: Stopping poll for %s\n", __FUNCTION__, StreamDirection_In == streamDir ? "capture" : "playback" ));
- *continuePoll = 0;
- }
- else if( margin < otherComponent->framesPerBuffer )
- {
- *pollTimeout = CalculatePollTimeout( stream, margin );
- PA_DEBUG(( "%s: Trying to poll again for %s frames, pollTimeout: %d\n",
- __FUNCTION__, StreamDirection_In == streamDir ? "capture" : "playback", *pollTimeout ));
- }
-
-error:
- return result;
-}
-
-/* Callback interface */
-
-static void OnExit( void *data )
-{
- PaAlsaStream *stream = (PaAlsaStream *) data;
-
- assert( data );
-
- PaUtil_ResetCpuLoadMeasurer( &stream->cpuLoadMeasurer );
-
- stream->callback_finished = 1; /* Let the outside world know stream was stopped in callback */
- AlsaStop( stream, stream->callbackAbort );
- stream->callbackAbort = 0; /* Clear state */
-
- PA_DEBUG(( "OnExit: Stoppage\n" ));
-
- /* Eventually notify user all buffers have played */
- if( stream->streamRepresentation.streamFinishedCallback )
- stream->streamRepresentation.streamFinishedCallback( stream->streamRepresentation.userData );
- stream->isActive = 0;
-}
-
-static void CalculateTimeInfo( PaAlsaStream *stream, PaStreamCallbackTimeInfo *timeInfo )
-{
- snd_pcm_status_t *capture_status, *playback_status;
- snd_timestamp_t capture_timestamp, playback_timestamp;
- PaTime capture_time = 0., playback_time = 0.;
-
- snd_pcm_status_alloca( &capture_status );
- snd_pcm_status_alloca( &playback_status );
-
- if( stream->capture.pcm )
- {
- snd_pcm_sframes_t capture_delay;
-
- snd_pcm_status( stream->capture.pcm, capture_status );
- snd_pcm_status_get_tstamp( capture_status, &capture_timestamp );
-
- capture_time = capture_timestamp.tv_sec +
- ((PaTime)capture_timestamp.tv_usec / 1000000.0);
- timeInfo->currentTime = capture_time;
-
- capture_delay = snd_pcm_status_get_delay( capture_status );
- timeInfo->inputBufferAdcTime = timeInfo->currentTime -
- (PaTime)capture_delay / stream->streamRepresentation.streamInfo.sampleRate;
- }
- if( stream->playback.pcm )
- {
- snd_pcm_sframes_t playback_delay;
-
- snd_pcm_status( stream->playback.pcm, playback_status );
- snd_pcm_status_get_tstamp( playback_status, &playback_timestamp );
-
- playback_time = playback_timestamp.tv_sec +
- ((PaTime)playback_timestamp.tv_usec / 1000000.0);
-
- if( stream->capture.pcm ) /* Full duplex */
- {
- /* Hmm, we have both a playback and a capture timestamp.
- * Hopefully they are the same... */
- if( fabs( capture_time - playback_time ) > 0.01 )
- PA_DEBUG(("Capture time and playback time differ by %f\n", fabs(capture_time-playback_time)));
- }
- else
- timeInfo->currentTime = playback_time;
-
- playback_delay = snd_pcm_status_get_delay( playback_status );
- timeInfo->outputBufferDacTime = timeInfo->currentTime +
- (PaTime)playback_delay / stream->streamRepresentation.streamInfo.sampleRate;
- }
-}
-
-/** Called after buffer processing is finished.
- *
- * A number of mmapped frames is committed, it is possible that an xrun has occurred in the meantime.
- *
- * @param numFrames The number of frames that has been processed
- * @param xrun Return whether an xrun has occurred
- */
-static PaError PaAlsaStreamComponent_EndProcessing( PaAlsaStreamComponent *self, unsigned long numFrames, int *xrun )
-{
- PaError result = paNoError;
- int res;
-
- /* @concern FullDuplex It is possible that only one direction is marked ready after polling, and processed
- * afterwards
- */
- if( !self->ready )
- goto end;
-
- res = snd_pcm_mmap_commit( self->pcm, self->offset, numFrames );
- if( res == -EPIPE || res == -ESTRPIPE )
- {
- *xrun = 1;
- }
- else
- {
- ENSURE_( res, paUnanticipatedHostError );
- }
-
-end:
-error:
- return result;
-}
-
-/* Extract buffer from channel area */
-static unsigned char *ExtractAddress( const snd_pcm_channel_area_t *area, snd_pcm_uframes_t offset )
-{
- return (unsigned char *) area->addr + (area->first + offset * area->step) / 8;
-}
-
-/** Do necessary adaption between user and host channels.
- *
- @concern ChannelAdaption Adapting between user and host channels can involve silencing unused channels and
- duplicating mono information if host outputs come in pairs.
- */
-static PaError PaAlsaStreamComponent_DoChannelAdaption( PaAlsaStreamComponent *self, PaUtilBufferProcessor *bp, int numFrames )
-{
- PaError result = paNoError;
- unsigned char *p;
- int i;
- int unusedChans = self->numHostChannels - self->numUserChannels;
- unsigned char *src, *dst;
- int convertMono = (self->numHostChannels % 2) == 0 && (self->numUserChannels % 2) != 0;
-
- assert( StreamDirection_Out == self->streamDir );
-
- if( self->hostInterleaved )
- {
- int swidth = snd_pcm_format_size( self->nativeFormat, 1 );
- unsigned char *buffer = ExtractAddress( self->channelAreas, self->offset );
-
- /* Start after the last user channel */
- p = buffer + self->numUserChannels * swidth;
-
- if( convertMono )
- {
- /* Convert the last user channel into stereo pair */
- src = buffer + (self->numUserChannels - 1) * swidth;
- for( i = 0; i < numFrames; ++i )
- {
- dst = src + swidth;
- memcpy( dst, src, swidth );
- src += self->numHostChannels * swidth;
- }
-
- /* Don't touch the channel we just wrote to */
- p += swidth;
- --unusedChans;
- }
-
- if( unusedChans > 0 )
- {
- /* Silence unused output channels */
- for( i = 0; i < numFrames; ++i )
- {
- memset( p, 0, swidth * unusedChans );
- p += self->numHostChannels * swidth;
- }
- }
- }
- else
- {
- /* We extract the last user channel */
- if( convertMono )
- {
- ENSURE_( snd_pcm_area_copy( self->channelAreas + self->numUserChannels, self->offset, self->channelAreas +
- (self->numUserChannels - 1), self->offset, numFrames, self->nativeFormat ), paUnanticipatedHostError );
- --unusedChans;
- }
- if( unusedChans > 0 )
- {
- snd_pcm_areas_silence( self->channelAreas + (self->numHostChannels - unusedChans), self->offset, unusedChans, numFrames,
- self->nativeFormat );
- }
- }
-
-error:
- return result;
-}
-
-static PaError PaAlsaStream_EndProcessing( PaAlsaStream *self, unsigned long numFrames, int *xrunOccurred )
-{
- PaError result = paNoError;
- int xrun = 0;
-
- if( self->capture.pcm )
- {
- PA_ENSURE( PaAlsaStreamComponent_EndProcessing( &self->capture, numFrames, &xrun ) );
- }
- if( self->playback.pcm )
- {
- if( self->playback.numHostChannels > self->playback.numUserChannels )
- PA_ENSURE( PaAlsaStreamComponent_DoChannelAdaption( &self->playback, &self->bufferProcessor, numFrames ) );
- PA_ENSURE( PaAlsaStreamComponent_EndProcessing( &self->playback, numFrames, &xrun ) );
- }
-
-error:
- *xrunOccurred = xrun;
- return result;
-}
-
-/** Update the number of available frames.
- *
- */
-static PaError PaAlsaStreamComponent_GetAvailableFrames( PaAlsaStreamComponent *self, unsigned long *numFrames, int *xrunOccurred )
-{
- PaError result = paNoError;
- snd_pcm_sframes_t framesAvail = snd_pcm_avail_update( self->pcm );
- *xrunOccurred = 0;
-
- if( -EPIPE == framesAvail )
- {
- *xrunOccurred = 1;
- framesAvail = 0;
- }
- else
- ENSURE_( framesAvail, paUnanticipatedHostError );
-
- *numFrames = framesAvail;
-
-error:
- return result;
-}
-
-/** Fill in pollfd objects.
- */
-static PaError PaAlsaStreamComponent_BeginPolling( PaAlsaStreamComponent *self, struct pollfd *pfds )
-{
- PaError result = paNoError;
- int ret = snd_pcm_poll_descriptors( self->pcm, pfds, self->nfds );
- assert( ret == self->nfds );
-
- self->ready = 0;
-
- return result;
-}
-
-/** Examine results from poll().
- *
- * @param pfds pollfds to inspect
- * @param shouldPoll Should we continue to poll
- * @param xrun Has an xrun occurred
- */
-static PaError PaAlsaStreamComponent_EndPolling( PaAlsaStreamComponent *self, struct pollfd *pfds, int *shouldPoll, int *xrun )
-{
- PaError result = paNoError;
- unsigned short revents;
-
- ENSURE_( snd_pcm_poll_descriptors_revents( self->pcm, pfds, self->nfds, &revents ), paUnanticipatedHostError );
- if( revents != 0 )
- {
- if( revents & POLLERR )
- {
- *xrun = 1;
- }
- else
- self->ready = 1;
-
- *shouldPoll = 0;
- }
-
-error:
- return result;
-}
-
-/** Return the number of available frames for this stream.
- *
- * @concern FullDuplex The minimum available for the two directions is calculated, it might be desirable to ignore
- * one direction however (not marked ready from poll), so this is controlled by queryCapture and queryPlayback.
- *
- * @param queryCapture Check available for capture
- * @param queryPlayback Check available for playback
- * @param available The returned number of frames
- * @param xrunOccurred Return whether an xrun has occurred
- */
-static PaError PaAlsaStream_GetAvailableFrames( PaAlsaStream *self, int queryCapture, int queryPlayback, unsigned long
- *available, int *xrunOccurred )
-{
- PaError result = paNoError;
- unsigned long captureFrames, playbackFrames;
- *xrunOccurred = 0;
-
- assert( queryCapture || queryPlayback );
-
- if( queryCapture )
- {
- assert( self->capture.pcm );
- PA_ENSURE( PaAlsaStreamComponent_GetAvailableFrames( &self->capture, &captureFrames, xrunOccurred ) );
- if( *xrunOccurred )
- goto end;
- }
- if( queryPlayback )
- {
- assert( self->playback.pcm );
- PA_ENSURE( PaAlsaStreamComponent_GetAvailableFrames( &self->playback, &playbackFrames, xrunOccurred ) );
- if( *xrunOccurred )
- goto end;
- }
-
- if( queryCapture && queryPlayback )
- {
- *available = PA_MIN( captureFrames, playbackFrames );
- }
- else if( queryCapture )
- {
- *available = captureFrames;
- }
- else
- {
- *available = playbackFrames;
- }
-
-end:
-error:
- return result;
-}
-
-/** Wait for and report available buffer space from ALSA.
- *
- * Unless ALSA reports a minimum of frames available for I/O, we poll the ALSA filedescriptors for more.
- * Both of these operations can uncover xrun conditions.
- *
- * @concern Xruns Both polling and querying available frames can report an xrun condition.
- *
- * @param framesAvail Return the number of available frames
- * @param xrunOccurred Return whether an xrun has occurred
- */
-static PaError PaAlsaStream_WaitForFrames( PaAlsaStream *self, unsigned long *framesAvail, int *xrunOccurred )
-{
- PaError result = paNoError;
- int pollPlayback = self->playback.pcm != NULL, pollCapture = self->capture.pcm != NULL;
- int pollTimeout = self->pollTimeout;
- int xrun = 0;
-
- assert( self );
- assert( framesAvail );
-
- if( !self->callbackMode )
- {
- /* In blocking mode we will only wait if necessary */
- PA_ENSURE( PaAlsaStream_GetAvailableFrames( self, self->capture.pcm != NULL, self->playback.pcm != NULL,
- framesAvail, &xrun ) );
- if( xrun )
- {
- goto end;
- }
-
- if( *framesAvail > 0 )
- {
- /* Mark pcms ready from poll */
- if( self->capture.pcm )
- self->capture.ready = 1;
- if( self->playback.pcm )
- self->playback.ready = 1;
-
- goto end;
- }
- }
-
- while( pollPlayback || pollCapture )
- {
- int totalFds = 0;
- struct pollfd *capturePfds = NULL, *playbackPfds = NULL;
-
- pthread_testcancel();
-
- if( pollCapture )
- {
- capturePfds = self->pfds;
- PA_ENSURE( PaAlsaStreamComponent_BeginPolling( &self->capture, capturePfds ) );
- totalFds += self->capture.nfds;
- }
- if( pollPlayback )
- {
- playbackPfds = self->pfds + (self->capture.pcm ? self->capture.nfds : 0);
- PA_ENSURE( PaAlsaStreamComponent_BeginPolling( &self->playback, playbackPfds ) );
- totalFds += self->playback.nfds;
- }
-
- if( poll( self->pfds, totalFds, pollTimeout ) < 0 )
- {
- /* XXX: Depend on preprocessor condition? */
- if( errno == EINTR ) { /* gdb */
- continue;
- }
-
- /* TODO: Add macro for checking system calls */
- PA_ENSURE( paInternalError );
- }
-
- /* check the return status of our pfds */
- if( pollCapture )
- {
- PA_ENSURE( PaAlsaStreamComponent_EndPolling( &self->capture, capturePfds, &pollCapture, &xrun ) );
- }
- if( pollPlayback )
- {
- PA_ENSURE( PaAlsaStreamComponent_EndPolling( &self->playback, playbackPfds, &pollPlayback, &xrun ) );
- }
- if( xrun )
- {
- break;
- }
-
- /* @concern FullDuplex If only one of two pcms is ready we may want to compromise between the two.
- * If there is less than half a period's worth of samples left of frames in the other pcm's buffer we will
- * stop polling.
- */
- if( self->capture.pcm && self->playback.pcm )
- {
- if( pollCapture && !pollPlayback )
- {
- PA_ENSURE( ContinuePoll( self, StreamDirection_In, &pollTimeout, &pollCapture ) );
- }
- else if( pollPlayback && !pollCapture )
- {
- PA_ENSURE( ContinuePoll( self, StreamDirection_Out, &pollTimeout, &pollPlayback ) );
- }
- }
- }
-
- if( !xrun )
- {
- /* Get the number of available frames for the pcms that are marked ready.
- * @concern FullDuplex If only one direction is marked ready (from poll), the number of frames available for
- * the other direction is returned. This under the assumption that input is dropped earlier if paNeverDropInput
- * is not specified.
- */
- int captureReady = self->capture.pcm ? self->capture.ready : 0,
- playbackReady = self->playback.pcm ? self->playback.ready : 0;
- PA_ENSURE( PaAlsaStream_GetAvailableFrames( self, captureReady, playbackReady, framesAvail, &xrun ) );
-
- if( self->capture.pcm && self->playback.pcm )
- {
- if( !self->playback.ready && !self->neverDropInput )
- {
- /* TODO: Drop input */
- }
- }
- }
-
-end:
-error:
- if( xrun )
- {
- /* Recover from the xrun state */
- PA_ENSURE( PaAlsaStream_HandleXrun( self ) );
- *framesAvail = 0;
- }
- *xrunOccurred = xrun;
-
- return result;
-}
-
-/** Register per-channel ALSA buffer information with buffer processor.
- *
- * Mmapped buffer space is acquired from ALSA, and registered with the buffer processor. Differences between the
- * number of host and user channels is taken into account.
- *
- * @param numFrames On entrance the number of requested frames, on exit the number of contiguously accessible frames.
- */
-static PaError PaAlsaStreamComponent_RegisterChannels( PaAlsaStreamComponent *self, PaUtilBufferProcessor *bp,
- unsigned long *numFrames, int *xrun )
-{
- PaError result = paNoError;
- const snd_pcm_channel_area_t *areas, *area;
- void (*setChannel)(PaUtilBufferProcessor *, unsigned int, void *, unsigned int) =
- StreamDirection_In == self->streamDir ? PaUtil_SetInputChannel : PaUtil_SetOutputChannel;
- unsigned char *buffer, *p;
- int i;
- unsigned long framesAvail;
-
- /* This _must_ be called before mmap_begin */
- PA_ENSURE( PaAlsaStreamComponent_GetAvailableFrames( self, &framesAvail, xrun ) );
- if( *xrun )
- {
- *numFrames = 0;
- goto end;
- }
-
- ENSURE_( snd_pcm_mmap_begin( self->pcm, &areas, &self->offset, numFrames ), paUnanticipatedHostError );
-
- if( self->hostInterleaved )
- {
- int swidth = snd_pcm_format_size( self->nativeFormat, 1 );
-
- p = buffer = ExtractAddress( areas, self->offset );
- for( i = 0; i < self->numUserChannels; ++i )
- {
- /* We're setting the channels up to userChannels, but the stride will be hostChannels samples */
- setChannel( bp, i, p, self->numHostChannels );
- p += swidth;
- }
- }
- else
- {
- for( i = 0; i < self->numUserChannels; ++i )
- {
- area = areas + i;
- buffer = ExtractAddress( area, self->offset );
- setChannel( bp, i, buffer, 1 );
- }
- }
-
- /* @concern ChannelAdaption Buffer address is recorded so we can do some channel adaption later */
- self->channelAreas = (snd_pcm_channel_area_t *)areas;
-
-end:
-error:
- return result;
-}
-
-/** Initiate buffer processing.
- *
- * ALSA buffers are registered with the PA buffer processor and the buffer size (in frames) set.
- *
- * @concern FullDuplex If both directions are being processed, the minimum amount of frames for the two directions is
- * calculated.
- *
- * @param numFrames On entrance the number of available frames, on exit the number of received frames
- * @param xrunOccurred Return whether an xrun has occurred
- */
-static PaError PaAlsaStream_SetUpBuffers( PaAlsaStream *self, unsigned long *numFrames, int *xrunOccurred )
-{
- PaError result = paNoError;
- unsigned long captureFrames = ULONG_MAX, playbackFrames = ULONG_MAX, commonFrames = 0;
- int xrun = 0;
-
- /* Extract per-channel ALSA buffer pointers and register them with the buffer processor.
- * It is possible that a direction is not marked ready however, because it is out of sync with the other.
- */
- if( self->capture.pcm && self->capture.ready )
- {
- captureFrames = *numFrames;
- PA_ENSURE( PaAlsaStreamComponent_RegisterChannels( &self->capture, &self->bufferProcessor, &captureFrames,
- &xrun ) );
- }
- if( self->playback.pcm && self->playback.ready )
- {
- playbackFrames = *numFrames;
- PA_ENSURE( PaAlsaStreamComponent_RegisterChannels( &self->playback, &self->bufferProcessor, &playbackFrames,
- &xrun ) );
- }
- if( xrun )
- {
- /* Nothing more to do */
- assert( 0 == commonFrames );
- goto end;
- }
-
- commonFrames = PA_MIN( captureFrames, playbackFrames );
- assert( commonFrames <= *numFrames );
-
- /* Inform PortAudio of the number of frames we got.
- * @concern FullDuplex We might be experiencing underflow in either end; if its an input underflow, we go on
- * with output. If its output underflow however, depending on the paNeverDropInput flag, we may want to simply
- * discard the excess input or call the callback with paOutputOverflow flagged.
- */
- if( self->capture.pcm )
- {
- if( self->capture.ready )
- {
- PaUtil_SetInputFrameCount( &self->bufferProcessor, commonFrames );
- }
- else
- {
- /* We have input underflow */
- PaUtil_SetNoInput( &self->bufferProcessor );
- }
- }
- if( self->playback.pcm )
- {
- if( self->playback.ready )
- {
- PaUtil_SetOutputFrameCount( &self->bufferProcessor, commonFrames );
- }
- else
- {
- /* We have output underflow, but keeping input data (paNeverDropInput) */
- /* assert( self->neverDropInput ); */
- PaUtil_SetNoOutput( &self->bufferProcessor );
- }
- }
-
-end:
- *numFrames = commonFrames;
-error:
- if( xrun )
- {
- PA_ENSURE( PaAlsaStream_HandleXrun( self ) );
- *numFrames = 0;
- }
- *xrunOccurred = xrun;
-
- return result;
-}
-
-/** Callback thread's function.
- *
- * Roughly, the workflow can be described in the following way: The number of available frames that can be processed
- * directly is obtained from ALSA, we then request as much directly accessible memory as possible within this amount
- * from ALSA. The buffer memory is registered with the PA buffer processor and processing is carried out with
- * PaUtil_EndBufferProcessing. Finally, the number of processed frames is reported to ALSA. The processing can
- * happen in several iterations untill we have consumed the known number of available frames (or an xrun is detected).
- */
-static void *CallbackThreadFunc( void *userData )
-{
- PaError result = paNoError, *pres = NULL;
- PaAlsaStream *stream = (PaAlsaStream*) userData;
- PaStreamCallbackTimeInfo timeInfo = {0, 0, 0};
- snd_pcm_sframes_t startThreshold = 0;
- int callbackResult = paContinue;
- PaStreamCallbackFlags cbFlags = 0; /* We might want to keep state across iterations */
- int streamStarted = 0;
-
- assert( stream );
-
- callbackThread_ = pthread_self();
- /* Execute OnExit when exiting */
- pthread_cleanup_push( &OnExit, stream );
-
- /* Not implemented */
- assert( !stream->primeBuffers );
-
- /* @concern StreamStart If the output is being primed the output pcm needs to be prepared, otherwise the
- * stream is started immediately. The latter involves signaling the waiting main thread.
- */
- if( stream->primeBuffers )
- {
- snd_pcm_sframes_t avail;
-
- if( stream->playback.pcm )
- ENSURE_( snd_pcm_prepare( stream->playback.pcm ), paUnanticipatedHostError );
- if( stream->capture.pcm && !stream->pcmsSynced )
- ENSURE_( snd_pcm_prepare( stream->capture.pcm ), paUnanticipatedHostError );
-
- /* We can't be certain that the whole ring buffer is available for priming, but there should be
- * at least one period */
- avail = snd_pcm_avail_update( stream->playback.pcm );
- startThreshold = avail - (avail % stream->playback.framesPerBuffer);
- assert( startThreshold >= stream->playback.framesPerBuffer );
- }
- else
- {
- ASSERT_CALL_( pthread_mutex_lock( &stream->startMtx ), 0 );
- PA_ENSURE( AlsaStart( stream, 0 ) ); /* Buffer will be zeroed */
- ASSERT_CALL_( pthread_cond_signal( &stream->startCond ), 0 );
- ASSERT_CALL_( pthread_mutex_unlock( &stream->startMtx ), 0 );
-
- streamStarted = 1;
- }
-
- while( 1 )
- {
- unsigned long framesAvail, framesGot;
- int xrun = 0;
-
- pthread_testcancel();
-
- /* @concern StreamStop if the main thread has requested a stop and the stream has not been effectively
- * stopped we signal this condition by modifying callbackResult (we'll want to flush buffered output).
- */
- if( stream->callbackStop && paContinue == callbackResult )
- {
- PA_DEBUG(( "Setting callbackResult to paComplete\n" ));
- callbackResult = paComplete;
- }
-
- if( paContinue != callbackResult )
- {
- stream->callbackAbort = (paAbort == callbackResult);
- if( stream->callbackAbort ||
- /** @concern BlockAdaption Go on if adaption buffers are empty */
- PaUtil_IsBufferProcessorOutputEmpty( &stream->bufferProcessor ) )
- goto end;
-
- PA_DEBUG(( "%s: Flushing buffer processor\n", __FUNCTION__ ));
- /* There is still buffered output that needs to be processed */
- }
-
- /* Wait for data to become available, this comes down to polling the ALSA file descriptors untill we have
- * a number of available frames.
- */
- PA_ENSURE( PaAlsaStream_WaitForFrames( stream, &framesAvail, &xrun ) );
- if( xrun )
- {
- assert( 0 == framesAvail );
- continue;
-
- /* XXX: Report xruns to the user? A situation is conceivable where the callback is never invoked due
- * to constant xruns, it might be desirable to notify the user of this.
- */
- }
-
- /* Consume buffer space. Once we have a number of frames available for consumption we must retrieve the
- * mmapped buffers from ALSA, this is contiguously accessible memory however, so we may receive smaller
- * portions at a time than is available as a whole. Therefore we should be prepared to process several
- * chunks successively. The buffers are passed to the PA buffer processor.
- */
- while( framesAvail > 0 )
- {
- xrun = 0;
-
- pthread_testcancel();
-
- /** @concern Xruns Under/overflows are to be reported to the callback */
- if( stream->underrun > 0.0 )
- {
- cbFlags |= paOutputUnderflow;
- stream->underrun = 0.0;
- }
- if( stream->overrun > 0.0 )
- {
- cbFlags |= paInputOverflow;
- stream->overrun = 0.0;
- }
- if( stream->capture.pcm && stream->playback.pcm )
- {
- /** @concern FullDuplex It's possible that only one direction is being processed to avoid an
- * under- or overflow, this should be reported correspondingly */
- if( !stream->capture.ready )
- {
- cbFlags |= paInputUnderflow;
- PA_DEBUG(( "%s: Input underflow\n", __FUNCTION__ ));
- }
- else if( !stream->playback.ready )
- {
- cbFlags |= paOutputOverflow;
- PA_DEBUG(( "%s: Output overflow\n", __FUNCTION__ ));
- }
- }
-
- CallbackUpdate( &stream->threading );
- CalculateTimeInfo( stream, &timeInfo );
- PaUtil_BeginBufferProcessing( &stream->bufferProcessor, &timeInfo, cbFlags );
- cbFlags = 0;
-
- /* CPU load measurement should include processing activivity external to the stream callback */
- PaUtil_BeginCpuLoadMeasurement( &stream->cpuLoadMeasurer );
-
- framesGot = framesAvail;
- PA_ENSURE( PaAlsaStream_SetUpBuffers( stream, &framesGot, &xrun ) );
- framesAvail -= framesGot;
-
- if( framesGot > 0 )
- {
- assert( !xrun );
-
- PaUtil_EndBufferProcessing( &stream->bufferProcessor, &callbackResult );
- PA_ENSURE( PaAlsaStream_EndProcessing( stream, framesGot, &xrun ) );
- }
- PaUtil_EndCpuLoadMeasurement( &stream->cpuLoadMeasurer, framesGot );
-
- if( framesGot == 0 )
- {
- if( !xrun )
- PA_DEBUG(( "%s: Received less frames than reported from ALSA!\n", __FUNCTION__ ));
-
- /* Go back to polling for more frames */
- break;
-
- }
-
- if( paContinue != callbackResult )
- break;
- }
- }
-
- /* Match pthread_cleanup_push */
- pthread_cleanup_pop( 1 );
-
-end:
- pthread_exit( pres );
-
-error:
- /* Pass on error code */
- pres = malloc( sizeof (PaError) );
- *pres = result;
-
- goto end;
-}
-
-/* Blocking interface */
-
-static PaError ReadStream( PaStream* s, void *buffer, unsigned long frames )
-{
- PaError result = paNoError;
- PaAlsaStream *stream = (PaAlsaStream*)s;
- unsigned long framesGot, framesAvail;
- void *userBuffer;
- snd_pcm_t *save = stream->playback.pcm;
-
- assert( stream );
-
- PA_UNLESS( stream->capture.pcm, paCanNotReadFromAnOutputOnlyStream );
-
- /* Disregard playback */
- stream->playback.pcm = NULL;
-
- if( stream->overrun > 0. )
- {
- result = paInputOverflowed;
- stream->overrun = 0.0;
- }
-
- if( stream->capture.userInterleaved )
- userBuffer = buffer;
- else
- {
- /* Copy channels into local array */
- userBuffer = stream->capture.userBuffers;
- memcpy( userBuffer, buffer, sizeof (void *) * stream->capture.numUserChannels );
- }
-
- /* Start stream if in prepared state */
- if( snd_pcm_state( stream->capture.pcm ) == SND_PCM_STATE_PREPARED )
- {
- ENSURE_( snd_pcm_start( stream->capture.pcm ), paUnanticipatedHostError );
- }
-
- while( frames > 0 )
- {
- int xrun = 0;
- PA_ENSURE( PaAlsaStream_WaitForFrames( stream, &framesAvail, &xrun ) );
- framesGot = PA_MIN( framesAvail, frames );
-
- PA_ENSURE( PaAlsaStream_SetUpBuffers( stream, &framesGot, &xrun ) );
- if( framesGot > 0 )
- {
- framesGot = PaUtil_CopyInput( &stream->bufferProcessor, &userBuffer, framesGot );
- PA_ENSURE( PaAlsaStream_EndProcessing( stream, framesGot, &xrun ) );
- frames -= framesGot;
- }
- }
-
-end:
- stream->playback.pcm = save;
- return result;
-error:
- goto end;
-}
-
-static PaError WriteStream( PaStream* s, const void *buffer, unsigned long frames )
-{
- PaError result = paNoError;
- signed long err;
- PaAlsaStream *stream = (PaAlsaStream*)s;
- snd_pcm_uframes_t framesGot, framesAvail;
- const void *userBuffer;
- snd_pcm_t *save = stream->capture.pcm;
-
- assert( stream );
-
- PA_UNLESS( stream->playback.pcm, paCanNotWriteToAnInputOnlyStream );
-
- /* Disregard capture */
- stream->capture.pcm = NULL;
-
- if( stream->underrun > 0. )
- {
- result = paOutputUnderflowed;
- stream->underrun = 0.0;
- }
-
- if( stream->playback.userInterleaved )
- userBuffer = buffer;
- else /* Copy channels into local array */
- {
- userBuffer = stream->playback.userBuffers;
- memcpy( (void *)userBuffer, buffer, sizeof (void *) * stream->playback.numUserChannels );
- }
-
- while( frames > 0 )
- {
- int xrun = 0;
- snd_pcm_uframes_t hwAvail;
-
- PA_ENSURE( PaAlsaStream_WaitForFrames( stream, &framesAvail, &xrun ) );
- framesGot = PA_MIN( framesAvail, frames );
-
- PA_ENSURE( PaAlsaStream_SetUpBuffers( stream, &framesGot, &xrun ) );
- if( framesGot > 0 )
- {
- framesGot = PaUtil_CopyOutput( &stream->bufferProcessor, &userBuffer, framesGot );
- PA_ENSURE( PaAlsaStream_EndProcessing( stream, framesGot, &xrun ) );
- frames -= framesGot;
- }
-
- /* Frames residing in buffer */
- PA_ENSURE( err = GetStreamWriteAvailable( stream ) );
- framesAvail = err;
- hwAvail = stream->playback.bufferSize - framesAvail;
-
- /* Start stream after one period of samples worth */
- if( snd_pcm_state( stream->playback.pcm ) == SND_PCM_STATE_PREPARED &&
- hwAvail >= stream->playback.framesPerBuffer )
- {
- ENSURE_( snd_pcm_start( stream->playback.pcm ), paUnanticipatedHostError );
- }
- }
-
-end:
- stream->capture.pcm = save;
- return result;
-error:
- goto end;
-}
-
-/* Return frames available for reading. In the event of an overflow, the capture pcm will be restarted */
-static signed long GetStreamReadAvailable( PaStream* s )
-{
- PaError result = paNoError;
- PaAlsaStream *stream = (PaAlsaStream*)s;
- unsigned long avail;
- int xrun;
-
- PA_ENSURE( PaAlsaStreamComponent_GetAvailableFrames( &stream->capture, &avail, &xrun ) );
- if( xrun )
- {
- PA_ENSURE( PaAlsaStream_HandleXrun( stream ) );
- PA_ENSURE( PaAlsaStreamComponent_GetAvailableFrames( &stream->capture, &avail, &xrun ) );
- if( xrun )
- PA_ENSURE( paInputOverflowed );
- }
-
- return (signed long)avail;
-
-error:
- return result;
-}
-
-static signed long GetStreamWriteAvailable( PaStream* s )
-{
- PaError result = paNoError;
- PaAlsaStream *stream = (PaAlsaStream*)s;
- unsigned long avail;
- int xrun;
-
- PA_ENSURE( PaAlsaStreamComponent_GetAvailableFrames( &stream->playback, &avail, &xrun ) );
- if( xrun )
- {
- snd_pcm_sframes_t savail;
-
- PA_ENSURE( PaAlsaStream_HandleXrun( stream ) );
- savail = snd_pcm_avail_update( stream->playback.pcm );
-
- /* savail should not contain -EPIPE now, since PaAlsaStream_HandleXrun will only prepare the pcm */
- ENSURE_( savail, paUnanticipatedHostError );
-
- avail = (unsigned long) savail;
- }
-
- return (signed long)avail;
-
-error:
- return result;
-}
-
-/* Extensions */
-
-/* Initialize host api specific structure */
-void PaAlsa_InitializeStreamInfo( PaAlsaStreamInfo *info )
-{
- info->size = sizeof (PaAlsaStreamInfo);
- info->hostApiType = paALSA;
- info->version = 1;
- info->deviceString = NULL;
-}
-
-void PaAlsa_EnableRealtimeScheduling( PaStream *s, int enable )
-{
- PaAlsaStream *stream = (PaAlsaStream *) s;
- stream->threading.rtSched = enable;
-}
-
-void PaAlsa_EnableWatchdog( PaStream *s, int enable )
-{
- PaAlsaStream *stream = (PaAlsaStream *) s;
- stream->threading.useWatchdog = enable;
-}
+/*
+ * $Id: pa_linux_alsa.c,v 1.1.2.71 2005/04/15 18:20:18 aknudsen Exp $
+ * PortAudio Portable Real-Time Audio Library
+ * Latest Version at: http://www.portaudio.com
+ * ALSA implementation by Joshua Haberman and Arve Knudsen
+ *
+ * Copyright (c) 2002 Joshua Haberman <joshua@haberman.com>
+ * Copyright (c) 2005 Arve Knudsen <aknuds-1@broadpark.no>
+ *
+ * Based on the Open Source API proposed by Ross Bencina
+ * Copyright (c) 1999-2002 Ross Bencina, Phil Burk
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * Any person wishing to distribute modifications to the Software is
+ * requested to send the modifications to the original developer so that
+ * they can be incorporated into the canonical version.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+ * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+/*
+ * PJMEDIA - Multimedia over IP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#define ALSA_PCM_NEW_HW_PARAMS_API
+#define ALSA_PCM_NEW_SW_PARAMS_API
+#include <alsa/asoundlib.h>
+#undef ALSA_PCM_NEW_HW_PARAMS_API
+#undef ALSA_PCM_NEW_SW_PARAMS_API
+
+#include <sys/poll.h>
+#include <string.h> /* strlen() */
+#include <limits.h>
+#include <math.h>
+#include <pthread.h>
+#include <signal.h>
+#include <time.h>
+#include <sys/mman.h>
+#include <signal.h> /* For sig_atomic_t */
+
+#include "portaudio.h"
+#include "pa_util.h"
+#include "pa_unix_util.h"
+#include "pa_allocation.h"
+#include "pa_hostapi.h"
+#include "pa_stream.h"
+#include "pa_cpuload.h"
+#include "pa_process.h"
+
+#include "pa_linux_alsa.h"
+
+/* Check return value of ALSA function, and map it to PaError */
+#define ENSURE_(expr, code) \
+ do { \
+ if( UNLIKELY( (aErr_ = (expr)) < 0 ) ) \
+ { \
+ /* PaUtil_SetLastHostErrorInfo should only be used in the main thread */ \
+ if( (code) == paUnanticipatedHostError && pthread_self() != callbackThread_ ) \
+ { \
+ PaUtil_SetLastHostErrorInfo( paALSA, aErr_, snd_strerror( aErr_ ) ); \
+ } \
+ PaUtil_DebugPrint(( "Expression '" #expr "' failed in '" __FILE__ "', line: " STRINGIZE( __LINE__ ) "\n" )); \
+ result = (code); \
+ goto error; \
+ } \
+ } while( 0 );
+
+#define ASSERT_CALL_(expr, success) \
+ aErr_ = (expr); \
+ assert( aErr_ == success );
+
+static int aErr_; /* Used with ENSURE_ */
+static pthread_t callbackThread_;
+
+typedef enum
+{
+ StreamDirection_In,
+ StreamDirection_Out
+} StreamDirection;
+
+/* Threading utility struct */
+typedef struct PaAlsaThreading
+{
+ pthread_t watchdogThread;
+ pthread_t callbackThread;
+ int watchdogRunning;
+ int rtSched;
+ int rtPrio;
+ int useWatchdog;
+ unsigned long throttledSleepTime;
+ volatile PaTime callbackTime;
+ volatile PaTime callbackCpuTime;
+ PaUtilCpuLoadMeasurer *cpuLoadMeasurer;
+} PaAlsaThreading;
+
+typedef struct
+{
+ PaSampleFormat hostSampleFormat;
+ unsigned long framesPerBuffer;
+ int numUserChannels, numHostChannels;
+ int userInterleaved, hostInterleaved;
+
+ snd_pcm_t *pcm;
+ snd_pcm_uframes_t bufferSize;
+ snd_pcm_format_t nativeFormat;
+ unsigned int nfds;
+ int ready; /* Marked ready from poll */
+ void **userBuffers;
+ snd_pcm_uframes_t offset;
+ StreamDirection streamDir;
+
+ snd_pcm_channel_area_t *channelAreas; /* Needed for channel adaption */
+} PaAlsaStreamComponent;
+
+/* Implementation specific stream structure */
+typedef struct PaAlsaStream
+{
+ PaUtilStreamRepresentation streamRepresentation;
+ PaUtilCpuLoadMeasurer cpuLoadMeasurer;
+ PaUtilBufferProcessor bufferProcessor;
+ PaAlsaThreading threading;
+
+ unsigned long framesPerUserBuffer;
+
+ int primeBuffers;
+ int callbackMode; /* bool: are we running in callback mode? */
+ int pcmsSynced; /* Have we successfully synced pcms */
+
+ /* the callback thread uses these to poll the sound device(s), waiting
+ * for data to be ready/available */
+ struct pollfd *pfds;
+ int pollTimeout;
+
+ /* Used in communication between threads */
+ volatile sig_atomic_t callback_finished; /* bool: are we in the "callback finished" state? */
+ volatile sig_atomic_t callbackAbort; /* Drop frames? */
+ volatile sig_atomic_t callbackStop; /* Signal a stop */
+ volatile sig_atomic_t isActive; /* Is stream in active state? (Between StartStream and StopStream || !paContinue) */
+ pthread_mutex_t stateMtx; /* Used to synchronize access to stream state */
+ pthread_mutex_t startMtx; /* Used to synchronize stream start in callback mode */
+ pthread_cond_t startCond; /* Wait untill audio is started in callback thread */
+
+ int neverDropInput;
+
+ PaTime underrun;
+ PaTime overrun;
+
+ PaAlsaStreamComponent capture, playback;
+}
+PaAlsaStream;
+
+/* PaAlsaHostApiRepresentation - host api datastructure specific to this implementation */
+
+typedef struct PaAlsaHostApiRepresentation
+{
+ PaUtilHostApiRepresentation commonHostApiRep;
+ PaUtilStreamInterface callbackStreamInterface;
+ PaUtilStreamInterface blockingStreamInterface;
+
+ PaUtilAllocationGroup *allocations;
+
+ PaHostApiIndex hostApiIndex;
+}
+PaAlsaHostApiRepresentation;
+
+typedef struct PaAlsaDeviceInfo
+{
+ PaDeviceInfo commonDeviceInfo;
+ char *alsaName;
+ int isPlug;
+ int minInputChannels;
+ int minOutputChannels;
+}
+PaAlsaDeviceInfo;
+
+/* Threading utilities */
+
+static void InitializeThreading( PaAlsaThreading *th, PaUtilCpuLoadMeasurer *clm )
+{
+ th->watchdogRunning = 0;
+ th->rtSched = 0;
+ th->callbackTime = 0;
+ th->callbackCpuTime = 0;
+ th->useWatchdog = 1;
+ th->throttledSleepTime = 0;
+ th->cpuLoadMeasurer = clm;
+
+ th->rtPrio = (sched_get_priority_max( SCHED_FIFO ) - sched_get_priority_min( SCHED_FIFO )) / 2
+ + sched_get_priority_min( SCHED_FIFO );
+}
+
+static PaError KillCallbackThread( PaAlsaThreading *th, int wait, PaError *exitResult, PaError *watchdogExitResult )
+{
+ PaError result = paNoError;
+ void *pret;
+
+ if( exitResult )
+ *exitResult = paNoError;
+ if( watchdogExitResult )
+ *watchdogExitResult = paNoError;
+
+ if( th->watchdogRunning )
+ {
+ pthread_cancel( th->watchdogThread );
+ ASSERT_CALL_( pthread_join( th->watchdogThread, &pret ), 0 );
+
+ if( pret && pret != PTHREAD_CANCELED )
+ {
+ if( watchdogExitResult )
+ *watchdogExitResult = *(PaError *) pret;
+ free( pret );
+ }
+ }
+
+ /* Only kill the thread if it isn't in the process of stopping (flushing adaptation buffers) */
+ /* TODO: Make join time out */
+ if( !wait )
+ pthread_cancel( th->callbackThread ); /* XXX: Safe to call this if the thread has exited on its own? */
+ ASSERT_CALL_( pthread_join( th->callbackThread, &pret ), 0 );
+
+ if( pret && pret != PTHREAD_CANCELED )
+ {
+ if( exitResult )
+ *exitResult = *(PaError *) pret;
+ free( pret );
+ }
+
+ return result;
+}
+
+static void OnWatchdogExit( void *userData )
+{
+ PaAlsaThreading *th = (PaAlsaThreading *) userData;
+ struct sched_param spm = { 0 };
+ assert( th );
+
+ ASSERT_CALL_( pthread_setschedparam( th->callbackThread, SCHED_OTHER, &spm ), 0 ); /* Lower before exiting */
+ PA_DEBUG(( "Watchdog exiting\n" ));
+}
+
+static PaError BoostPriority( PaAlsaThreading *th )
+{
+ PaError result = paNoError;
+ struct sched_param spm = { 0 };
+ spm.sched_priority = th->rtPrio;
+
+ assert( th );
+
+ if( pthread_setschedparam( th->callbackThread, SCHED_FIFO, &spm ) != 0 )
+ {
+ PA_UNLESS( errno == EPERM, paInternalError ); /* Lack permission to raise priority */
+ PA_DEBUG(( "Failed bumping priority\n" ));
+ result = 0;
+ }
+ else
+ result = 1; /* Success */
+error:
+ return result;
+}
+
+static void *WatchdogFunc( void *userData )
+{
+ PaError result = paNoError, *pres = NULL;
+ int err;
+ PaAlsaThreading *th = (PaAlsaThreading *) userData;
+ unsigned intervalMsec = 500;
+ const PaTime maxSeconds = 3.; /* Max seconds between callbacks */
+ PaTime timeThen = PaUtil_GetTime(), timeNow, timeElapsed, cpuTimeThen, cpuTimeNow, cpuTimeElapsed;
+ double cpuLoad, avgCpuLoad = 0.;
+ int throttled = 0;
+
+ assert( th );
+
+ pthread_cleanup_push( &OnWatchdogExit, th ); /* Execute OnWatchdogExit when exiting */
+
+ /* Boost priority of callback thread */
+ PA_ENSURE( result = BoostPriority( th ) );
+ if( !result )
+ {
+ pthread_exit( NULL ); /* Boost failed, might as well exit */
+ }
+
+ cpuTimeThen = th->callbackCpuTime;
+ {
+ int policy;
+ struct sched_param spm = { 0 };
+ pthread_getschedparam( pthread_self(), &policy, &spm );
+ PA_DEBUG(( "%s: Watchdog priority is %d\n", __FUNCTION__, spm.sched_priority ));
+ }
+
+ while( 1 )
+ {
+ double lowpassCoeff = 0.9, lowpassCoeff1 = 0.99999 - lowpassCoeff;
+
+ /* Test before and after in case whatever underlying sleep call isn't interrupted by pthread_cancel */
+ pthread_testcancel();
+ Pa_Sleep( intervalMsec );
+ pthread_testcancel();
+
+ if( PaUtil_GetTime() - th->callbackTime > maxSeconds )
+ {
+ PA_DEBUG(( "Watchdog: Terminating callback thread\n" ));
+ /* Tell thread to terminate */
+ err = pthread_kill( th->callbackThread, SIGKILL );
+ pthread_exit( NULL );
+ }
+
+ PA_DEBUG(( "%s: PortAudio reports CPU load: %g\n", __FUNCTION__, PaUtil_GetCpuLoad( th->cpuLoadMeasurer ) ));
+
+ /* Check if we should throttle, or unthrottle :P */
+ cpuTimeNow = th->callbackCpuTime;
+ cpuTimeElapsed = cpuTimeNow - cpuTimeThen;
+ cpuTimeThen = cpuTimeNow;
+
+ timeNow = PaUtil_GetTime();
+ timeElapsed = timeNow - timeThen;
+ timeThen = timeNow;
+ cpuLoad = cpuTimeElapsed / timeElapsed;
+ avgCpuLoad = avgCpuLoad * lowpassCoeff + cpuLoad * lowpassCoeff1;
+ /*
+ if( throttled )
+ PA_DEBUG(( "Watchdog: CPU load: %g, %g\n", avgCpuLoad, cpuTimeElapsed ));
+ */
+ if( PaUtil_GetCpuLoad( th->cpuLoadMeasurer ) > .925 )
+ {
+ static int policy;
+ static struct sched_param spm = { 0 };
+ static const struct sched_param defaultSpm = { 0 };
+ PA_DEBUG(( "%s: Throttling audio thread, priority %d\n", __FUNCTION__, spm.sched_priority ));
+
+ pthread_getschedparam( th->callbackThread, &policy, &spm );
+ if( !pthread_setschedparam( th->callbackThread, SCHED_OTHER, &defaultSpm ) )
+ {
+ throttled = 1;
+ }
+ else
+ PA_DEBUG(( "Watchdog: Couldn't lower priority of audio thread: %s\n", strerror( errno ) ));
+
+ /* Give other processes a go, before raising priority again */
+ PA_DEBUG(( "%s: Watchdog sleeping for %lu msecs before unthrottling\n", __FUNCTION__, th->throttledSleepTime ));
+ Pa_Sleep( th->throttledSleepTime );
+
+ /* Reset callback priority */
+ if( pthread_setschedparam( th->callbackThread, SCHED_FIFO, &spm ) != 0 )
+ {
+ PA_DEBUG(( "%s: Couldn't raise priority of audio thread: %s\n", __FUNCTION__, strerror( errno ) ));
+ }
+
+ if( PaUtil_GetCpuLoad( th->cpuLoadMeasurer ) >= .99 )
+ intervalMsec = 50;
+ else
+ intervalMsec = 100;
+
+ /*
+ lowpassCoeff = .97;
+ lowpassCoeff1 = .99999 - lowpassCoeff;
+ */
+ }
+ else if( throttled && avgCpuLoad < .8 )
+ {
+ intervalMsec = 500;
+ throttled = 0;
+
+ /*
+ lowpassCoeff = .9;
+ lowpassCoeff1 = .99999 - lowpassCoeff;
+ */
+ }
+ }
+
+ pthread_cleanup_pop( 1 ); /* Execute cleanup on exit */
+
+error:
+ /* Shouldn't get here in the normal case */
+
+ /* Pass on error code */
+ pres = malloc( sizeof (PaError) );
+ *pres = result;
+
+ pthread_exit( pres );
+}
+
+static PaError CreateCallbackThread( PaAlsaThreading *th, void *(*callbackThreadFunc)( void * ), PaStream *s )
+{
+ PaError result = paNoError;
+ pthread_attr_t attr;
+ int started = 0;
+
+#if defined _POSIX_MEMLOCK && (_POSIX_MEMLOCK != -1)
+ if( th->rtSched )
+ {
+ if( mlockall( MCL_CURRENT | MCL_FUTURE ) < 0 )
+ {
+ int savedErrno = errno; /* In case errno gets overwritten */
+ assert( savedErrno != EINVAL ); /* Most likely a programmer error */
+ PA_UNLESS( (savedErrno == EPERM), paInternalError );
+ PA_DEBUG(( "%s: Failed locking memory\n", __FUNCTION__ ));
+ }
+ else
+ PA_DEBUG(( "%s: Successfully locked memory\n", __FUNCTION__ ));
+ }
+#endif
+
+ PA_UNLESS( !pthread_attr_init( &attr ), paInternalError );
+ /* Priority relative to other processes */
+ PA_UNLESS( !pthread_attr_setscope( &attr, PTHREAD_SCOPE_SYSTEM ), paInternalError );
+
+ PA_UNLESS( !pthread_create( &th->callbackThread, &attr, callbackThreadFunc, s ), paInternalError );
+ started = 1;
+
+ if( th->rtSched )
+ {
+ if( th->useWatchdog )
+ {
+ int err;
+ struct sched_param wdSpm = { 0 };
+ /* Launch watchdog, watchdog sets callback thread priority */
+ int prio = PA_MIN( th->rtPrio + 4, sched_get_priority_max( SCHED_FIFO ) );
+ wdSpm.sched_priority = prio;
+
+ PA_UNLESS( !pthread_attr_init( &attr ), paInternalError );
+ PA_UNLESS( !pthread_attr_setinheritsched( &attr, PTHREAD_EXPLICIT_SCHED ), paInternalError );
+ PA_UNLESS( !pthread_attr_setscope( &attr, PTHREAD_SCOPE_SYSTEM ), paInternalError );
+ PA_UNLESS( !pthread_attr_setschedpolicy( &attr, SCHED_FIFO ), paInternalError );
+ PA_UNLESS( !pthread_attr_setschedparam( &attr, &wdSpm ), paInternalError );
+ if( (err = pthread_create( &th->watchdogThread, &attr, &WatchdogFunc, th )) )
+ {
+ PA_UNLESS( err == EPERM, paInternalError );
+ /* Permission error, go on without realtime privileges */
+ PA_DEBUG(( "Failed bumping priority\n" ));
+ }
+ else
+ {
+ int policy;
+ th->watchdogRunning = 1;
+ ASSERT_CALL_( pthread_getschedparam( th->watchdogThread, &policy, &wdSpm ), 0 );
+ /* Check if priority is right, policy could potentially differ from SCHED_FIFO (but that's alright) */
+ if( wdSpm.sched_priority != prio )
+ {
+ PA_DEBUG(( "Watchdog priority not set correctly (%d)\n", wdSpm.sched_priority ));
+ PA_ENSURE( paInternalError );
+ }
+ }
+ }
+ else
+ PA_ENSURE( BoostPriority( th ) );
+ }
+
+end:
+ return result;
+error:
+ if( started )
+ KillCallbackThread( th, 0, NULL, NULL );
+
+ goto end;
+}
+
+static void CallbackUpdate( PaAlsaThreading *th )
+{
+ th->callbackTime = PaUtil_GetTime();
+ th->callbackCpuTime = PaUtil_GetCpuLoad( th->cpuLoadMeasurer );
+}
+
+/* prototypes for functions declared in this file */
+
+static void Terminate( struct PaUtilHostApiRepresentation *hostApi );
+static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
+ const PaStreamParameters *inputParameters,
+ const PaStreamParameters *outputParameters,
+ double sampleRate );
+static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
+ PaStream** s,
+ const PaStreamParameters *inputParameters,
+ const PaStreamParameters *outputParameters,
+ double sampleRate,
+ unsigned long framesPerBuffer,
+ PaStreamFlags streamFlags,
+ PaStreamCallback *callback,
+ void *userData );
+static PaError CloseStream( PaStream* stream );
+static PaError StartStream( PaStream *stream );
+static PaError StopStream( PaStream *stream );
+static PaError AbortStream( PaStream *stream );
+static PaError IsStreamStopped( PaStream *s );
+static PaError IsStreamActive( PaStream *stream );
+static PaTime GetStreamTime( PaStream *stream );
+static double GetStreamCpuLoad( PaStream* stream );
+static PaError BuildDeviceList( PaAlsaHostApiRepresentation *hostApi );
+static int SetApproximateSampleRate( snd_pcm_t *pcm, snd_pcm_hw_params_t *hwParams, double sampleRate );
+static int GetExactSampleRate( snd_pcm_hw_params_t *hwParams, double *sampleRate );
+
+/* Callback prototypes */
+static void *CallbackThreadFunc( void *userData );
+
+/* Blocking prototypes */
+static signed long GetStreamReadAvailable( PaStream* s );
+static signed long GetStreamWriteAvailable( PaStream* s );
+static PaError ReadStream( PaStream* stream, void *buffer, unsigned long frames );
+static PaError WriteStream( PaStream* stream, const void *buffer, unsigned long frames );
+
+
+static const PaAlsaDeviceInfo *GetDeviceInfo( const PaUtilHostApiRepresentation *hostApi, int device )
+{
+ return (const PaAlsaDeviceInfo *)hostApi->deviceInfos[device];
+}
+
+PaError PaAlsa_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex hostApiIndex )
+{
+ PaError result = paNoError;
+ PaAlsaHostApiRepresentation *alsaHostApi = NULL;
+
+ PA_UNLESS( alsaHostApi = (PaAlsaHostApiRepresentation*) PaUtil_AllocateMemory(
+ sizeof(PaAlsaHostApiRepresentation) ), paInsufficientMemory );
+ PA_UNLESS( alsaHostApi->allocations = PaUtil_CreateAllocationGroup(), paInsufficientMemory );
+ alsaHostApi->hostApiIndex = hostApiIndex;
+
+ *hostApi = (PaUtilHostApiRepresentation*)alsaHostApi;
+ (*hostApi)->info.structVersion = 1;
+ (*hostApi)->info.type = paALSA;
+ (*hostApi)->info.name = "ALSA";
+
+ (*hostApi)->Terminate = Terminate;
+ (*hostApi)->OpenStream = OpenStream;
+ (*hostApi)->IsFormatSupported = IsFormatSupported;
+
+ PA_ENSURE( BuildDeviceList( alsaHostApi ) );
+
+ PaUtil_InitializeStreamInterface( &alsaHostApi->callbackStreamInterface,
+ CloseStream, StartStream,
+ StopStream, AbortStream,
+ IsStreamStopped, IsStreamActive,
+ GetStreamTime, GetStreamCpuLoad,
+ PaUtil_DummyRead, PaUtil_DummyWrite,
+ PaUtil_DummyGetReadAvailable,
+ PaUtil_DummyGetWriteAvailable );
+
+ PaUtil_InitializeStreamInterface( &alsaHostApi->blockingStreamInterface,
+ CloseStream, StartStream,
+ StopStream, AbortStream,
+ IsStreamStopped, IsStreamActive,
+ GetStreamTime, PaUtil_DummyGetCpuLoad,
+ ReadStream, WriteStream,
+ GetStreamReadAvailable,
+ GetStreamWriteAvailable );
+
+ return result;
+
+error:
+ if( alsaHostApi )
+ {
+ if( alsaHostApi->allocations )
+ {
+ PaUtil_FreeAllAllocations( alsaHostApi->allocations );
+ PaUtil_DestroyAllocationGroup( alsaHostApi->allocations );
+ }
+
+ PaUtil_FreeMemory( alsaHostApi );
+ }
+
+ return result;
+}
+
+static void Terminate( struct PaUtilHostApiRepresentation *hostApi )
+{
+ PaAlsaHostApiRepresentation *alsaHostApi = (PaAlsaHostApiRepresentation*)hostApi;
+
+ assert( hostApi );
+
+ if( alsaHostApi->allocations )
+ {
+ PaUtil_FreeAllAllocations( alsaHostApi->allocations );
+ PaUtil_DestroyAllocationGroup( alsaHostApi->allocations );
+ }
+
+ PaUtil_FreeMemory( alsaHostApi );
+ snd_config_update_free_global();
+}
+
+/*! Determine max channels and default latencies.
+ *
+ * This function provides functionality to grope an opened (might be opened for capture or playback) pcm device for
+ * traits like max channels, suitable default latencies and default sample rate. Upon error, max channels is set to zero,
+ * and a suitable result returned. The device is closed before returning.
+ */
+static PaError GropeDevice( snd_pcm_t *pcm, int *minChannels, int *maxChannels, double *defaultLowLatency,
+ double *defaultHighLatency, double *defaultSampleRate, int isPlug )
+{
+ PaError result = paNoError;
+ snd_pcm_hw_params_t *hwParams;
+ snd_pcm_uframes_t lowLatency = 512, highLatency = 2048;
+ unsigned int minChans, maxChans;
+ double defaultSr = *defaultSampleRate;
+
+ assert( pcm );
+
+ ENSURE_( snd_pcm_nonblock( pcm, 0 ), paUnanticipatedHostError );
+
+ snd_pcm_hw_params_alloca( &hwParams );
+ snd_pcm_hw_params_any( pcm, hwParams );
+
+ if( defaultSr >= 0 )
+ {
+ /* Could be that the device opened in one mode supports samplerates that the other mode wont have,
+ * so try again .. */
+ if( SetApproximateSampleRate( pcm, hwParams, defaultSr ) < 0 )
+ {
+ defaultSr = -1.;
+ PA_DEBUG(( "%s: Original default samplerate failed, trying again ..\n", __FUNCTION__ ));
+ }
+ }
+
+ if( defaultSr < 0. ) /* Default sample rate not set */
+ {
+ unsigned int sampleRate = 44100; /* Will contain approximate rate returned by alsa-lib */
+ ENSURE_( snd_pcm_hw_params_set_rate_near( pcm, hwParams, &sampleRate, NULL ), paUnanticipatedHostError );
+ ENSURE_( GetExactSampleRate( hwParams, &defaultSr ), paUnanticipatedHostError );
+ }
+
+ ENSURE_( snd_pcm_hw_params_get_channels_min( hwParams, &minChans ), paUnanticipatedHostError );
+ ENSURE_( snd_pcm_hw_params_get_channels_max( hwParams, &maxChans ), paUnanticipatedHostError );
+ assert( maxChans <= INT_MAX );
+ assert( maxChans > 0 ); /* Weird linking issue could cause wrong version of ALSA symbols to be called,
+ resulting in zeroed values */
+
+ /* XXX: Limit to sensible number (ALSA plugins accept a crazy amount of channels)? */
+ if( isPlug && maxChans > 128 )
+ {
+ maxChans = 128;
+ PA_DEBUG(( "%s: Limiting number of plugin channels to %u\n", __FUNCTION__, maxChans ));
+ }
+
+ /* TWEAKME:
+ *
+ * Giving values for default min and max latency is not
+ * straightforward. Here are our objectives:
+ *
+ * * for low latency, we want to give the lowest value
+ * that will work reliably. This varies based on the
+ * sound card, kernel, CPU, etc. I think it is better
+ * to give sub-optimal latency than to give a number
+ * too low and cause dropouts. My conservative
+ * estimate at this point is to base it on 4096-sample
+ * latency at 44.1 kHz, which gives a latency of 23ms.
+ * * for high latency we want to give a large enough
+ * value that dropouts are basically impossible. This
+ * doesn't really require as much tweaking, since
+ * providing too large a number will just cause us to
+ * select the nearest setting that will work at stream
+ * config time.
+ */
+ ENSURE_( snd_pcm_hw_params_set_buffer_size_near( pcm, hwParams, &lowLatency ), paUnanticipatedHostError );
+
+ /* Have to reset hwParams, to set new buffer size */
+ ENSURE_( snd_pcm_hw_params_any( pcm, hwParams ), paUnanticipatedHostError );
+ ENSURE_( snd_pcm_hw_params_set_buffer_size_near( pcm, hwParams, &highLatency ), paUnanticipatedHostError );
+
+ *minChannels = (int)minChans;
+ *maxChannels = (int)maxChans;
+ *defaultSampleRate = defaultSr;
+ *defaultLowLatency = (double) lowLatency / *defaultSampleRate;
+ *defaultHighLatency = (double) highLatency / *defaultSampleRate;
+
+end:
+ snd_pcm_close( pcm );
+ return result;
+
+error:
+ goto end;
+}
+
+/* Initialize device info with invalid values (maxInputChannels and maxOutputChannels are set to zero since these indicate
+ * wether input/output is available) */
+static void InitializeDeviceInfo( PaDeviceInfo *deviceInfo )
+{
+ deviceInfo->structVersion = -1;
+ deviceInfo->name = NULL;
+ deviceInfo->hostApi = -1;
+ deviceInfo->maxInputChannels = 0;
+ deviceInfo->maxOutputChannels = 0;
+ deviceInfo->defaultLowInputLatency = -1.;
+ deviceInfo->defaultLowOutputLatency = -1.;
+ deviceInfo->defaultHighInputLatency = -1.;
+ deviceInfo->defaultHighOutputLatency = -1.;
+ deviceInfo->defaultSampleRate = -1.;
+}
+
+/* Helper struct */
+typedef struct
+{
+ char *alsaName;
+ char *name;
+ int isPlug;
+ int hasPlayback;
+ int hasCapture;
+} DeviceNames;
+
+static PaError PaAlsa_StrDup( PaAlsaHostApiRepresentation *alsaApi,
+ char **dst,
+ const char *src)
+{
+ PaError result = paNoError;
+ int len = strlen( src ) + 1;
+
+ /* PA_DEBUG(("PaStrDup %s %d\n", src, len)); */
+
+ PA_UNLESS( *dst = (char *)PaUtil_GroupAllocateMemory( alsaApi->allocations, len ),
+ paInsufficientMemory );
+ strncpy( *dst, src, len );
+
+error:
+ return result;
+}
+
+/* Disregard standard plugins
+ * XXX: Might want to make the "default" plugin available, if we can make it work
+ */
+static int IgnorePlugin( const char *pluginId )
+{
+#define numIgnored 10
+ static const char *ignoredPlugins[numIgnored] = {"hw", "plughw", "plug", "default", "dsnoop", "dmix", "tee",
+ "file", "null", "shm"};
+ int i;
+
+ for( i = 0; i < numIgnored; ++i )
+ {
+ if( !strcmp( pluginId, ignoredPlugins[i] ) )
+ {
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+/* Build PaDeviceInfo list, ignore devices for which we cannot determine capabilities (possibly busy, sigh) */
+static PaError BuildDeviceList( PaAlsaHostApiRepresentation *alsaApi )
+{
+ PaUtilHostApiRepresentation *commonApi = &alsaApi->commonHostApiRep;
+ PaAlsaDeviceInfo *deviceInfoArray;
+ int cardIdx = -1, devIdx = 0;
+ snd_ctl_card_info_t *cardInfo;
+ PaError result = paNoError;
+ size_t numDeviceNames = 0, maxDeviceNames = 1, i;
+ DeviceNames *deviceNames = NULL;
+ snd_config_t *topNode = NULL;
+ snd_pcm_info_t *pcmInfo;
+ int res;
+ int blocking = SND_PCM_NONBLOCK;
+ char alsaCardName[50];
+ if( getenv( "PA_ALSA_INITIALIZE_BLOCK" ) && atoi( getenv( "PA_ALSA_INITIALIZE_BLOCK" ) ) )
+ blocking = 0;
+
+ /* These two will be set to the first working input and output device, respectively */
+ commonApi->info.defaultInputDevice = paNoDevice;
+ commonApi->info.defaultOutputDevice = paNoDevice;
+
+ /* count the devices by enumerating all the card numbers */
+
+ /* snd_card_next() modifies the integer passed to it to be:
+ * the index of the first card if the parameter is -1
+ * the index of the next card if the parameter is the index of a card
+ * -1 if there are no more cards
+ *
+ * The function itself returns 0 if it succeeded. */
+ cardIdx = -1;
+ snd_ctl_card_info_alloca( &cardInfo );
+ snd_pcm_info_alloca( &pcmInfo );
+ while( snd_card_next( &cardIdx ) == 0 && cardIdx >= 0 )
+ {
+ char *cardName;
+ int devIdx = -1;
+ snd_ctl_t *ctl;
+ char buf[50];
+
+ snprintf( alsaCardName, sizeof (alsaCardName), "hw:%d", cardIdx );
+
+ /* Acquire name of card */
+ if( snd_ctl_open( &ctl, alsaCardName, 0 ) < 0 )
+ continue; /* Unable to open card :( */
+ snd_ctl_card_info( ctl, cardInfo );
+
+ PA_ENSURE( PaAlsa_StrDup( alsaApi, &cardName, snd_ctl_card_info_get_name( cardInfo )) );
+
+ while( snd_ctl_pcm_next_device( ctl, &devIdx ) == 0 && devIdx >= 0 )
+ {
+ char *alsaDeviceName, *deviceName;
+ size_t len;
+ int hasPlayback = 0, hasCapture = 0;
+ snprintf( buf, sizeof (buf), "%s:%d,%d", "hw", cardIdx, devIdx );
+
+ /* Obtain info about this particular device */
+ snd_pcm_info_set_device( pcmInfo, devIdx );
+ snd_pcm_info_set_subdevice( pcmInfo, 0 );
+ snd_pcm_info_set_stream( pcmInfo, SND_PCM_STREAM_CAPTURE );
+ if( snd_ctl_pcm_info( ctl, pcmInfo ) >= 0 )
+ hasCapture = 1;
+
+ snd_pcm_info_set_stream( pcmInfo, SND_PCM_STREAM_PLAYBACK );
+ if( snd_ctl_pcm_info( ctl, pcmInfo ) >= 0 )
+ hasPlayback = 1;
+
+ if( !hasPlayback && !hasCapture )
+ {
+ continue; /* Error */
+ }
+
+ /* The length of the string written by snprintf plus terminating 0 */
+ len = snprintf( NULL, 0, "%s: %s (%s)", cardName, snd_pcm_info_get_name( pcmInfo ), buf ) + 1;
+ PA_UNLESS( deviceName = (char *)PaUtil_GroupAllocateMemory( alsaApi->allocations, len ),
+ paInsufficientMemory );
+ snprintf( deviceName, len, "%s: %s (%s)", cardName,
+ snd_pcm_info_get_name( pcmInfo ), buf );
+
+ ++numDeviceNames;
+ if( !deviceNames || numDeviceNames > maxDeviceNames )
+ {
+ maxDeviceNames *= 2;
+ PA_UNLESS( deviceNames = (DeviceNames *) realloc( deviceNames, maxDeviceNames * sizeof (DeviceNames) ),
+ paInsufficientMemory );
+ }
+
+ PA_ENSURE( PaAlsa_StrDup( alsaApi, &alsaDeviceName, buf ) );
+
+ deviceNames[ numDeviceNames - 1 ].alsaName = alsaDeviceName;
+ deviceNames[ numDeviceNames - 1 ].name = deviceName;
+ deviceNames[ numDeviceNames - 1 ].isPlug = 0;
+ deviceNames[ numDeviceNames - 1 ].hasPlayback = hasPlayback;
+ deviceNames[ numDeviceNames - 1 ].hasCapture = hasCapture;
+ }
+ snd_ctl_close( ctl );
+ }
+
+ /* Iterate over plugin devices */
+ snd_config_update();
+ if( (res = snd_config_search( snd_config, "pcm", &topNode )) >= 0 )
+ {
+ snd_config_iterator_t i, next;
+
+ snd_config_for_each( i, next, topNode )
+ {
+ const char *tpStr = NULL, *idStr = NULL;
+ char *alsaDeviceName, *deviceName;
+ snd_config_t *n = snd_config_iterator_entry( i ), *tp = NULL;
+ if( snd_config_get_type( n ) != SND_CONFIG_TYPE_COMPOUND )
+ continue;
+
+ ENSURE_( snd_config_search( n, "type", &tp ), paUnanticipatedHostError );
+ ENSURE_( snd_config_get_string( tp, &tpStr ), paUnanticipatedHostError );
+
+ ENSURE_( snd_config_get_id( n, &idStr ), paUnanticipatedHostError );
+ if( IgnorePlugin( idStr ) )
+ {
+ PA_DEBUG(( "%s: Ignoring ALSA plugin device %s of type %s\n", __FUNCTION__, idStr, tpStr ));
+ continue;
+ }
+
+ PA_DEBUG(( "%s: Found plugin %s of type %s\n", __FUNCTION__, idStr, tpStr ));
+
+ PA_UNLESS( alsaDeviceName = (char*)PaUtil_GroupAllocateMemory( alsaApi->allocations,
+ strlen(idStr) + 6 ), paInsufficientMemory );
+ strcpy( alsaDeviceName, idStr );
+ PA_UNLESS( deviceName = (char*)PaUtil_GroupAllocateMemory( alsaApi->allocations,
+ strlen(idStr) + 1 ), paInsufficientMemory );
+ strcpy( deviceName, idStr );
+
+ ++numDeviceNames;
+ if( !deviceNames || numDeviceNames > maxDeviceNames )
+ {
+ maxDeviceNames *= 2;
+ PA_UNLESS( deviceNames = (DeviceNames *) realloc( deviceNames, maxDeviceNames * sizeof (DeviceNames) ),
+ paInsufficientMemory );
+ }
+
+ deviceNames[numDeviceNames - 1].alsaName = alsaDeviceName;
+ deviceNames[numDeviceNames - 1].name = deviceName;
+ deviceNames[numDeviceNames - 1].isPlug = 1;
+ deviceNames[numDeviceNames - 1].hasPlayback = 1;
+ deviceNames[numDeviceNames - 1].hasCapture = 1;
+ }
+ }
+ else
+ PA_DEBUG(( "%s: Iterating over ALSA plugins failed: %s\n", __FUNCTION__, snd_strerror( res ) ));
+
+ /* allocate deviceInfo memory based on the number of devices */
+ PA_UNLESS( commonApi->deviceInfos = (PaDeviceInfo**)PaUtil_GroupAllocateMemory(
+ alsaApi->allocations, sizeof(PaDeviceInfo*) * (numDeviceNames) ), paInsufficientMemory );
+
+ /* allocate all device info structs in a contiguous block */
+ PA_UNLESS( deviceInfoArray = (PaAlsaDeviceInfo*)PaUtil_GroupAllocateMemory(
+ alsaApi->allocations, sizeof(PaAlsaDeviceInfo) * numDeviceNames ), paInsufficientMemory );
+
+ /* Loop over list of cards, filling in info, if a device is deemed unavailable (can't get name),
+ * it's ignored.
+ */
+ /* while( snd_card_next( &cardIdx ) == 0 && cardIdx >= 0 ) */
+ for( i = 0, devIdx = 0; i < numDeviceNames; ++i )
+ {
+ snd_pcm_t *pcm;
+ PaAlsaDeviceInfo *deviceInfo = &deviceInfoArray[devIdx];
+ PaDeviceInfo *commonDeviceInfo = &deviceInfo->commonDeviceInfo;
+
+ /* Zero fields */
+ InitializeDeviceInfo( commonDeviceInfo );
+
+ /* to determine device capabilities, we must open the device and query the
+ * hardware parameter configuration space */
+
+ /* Query capture */
+ if( deviceNames[i].hasCapture &&
+ snd_pcm_open( &pcm, deviceNames[i].alsaName, SND_PCM_STREAM_CAPTURE, blocking ) >= 0 )
+ {
+ if( GropeDevice( pcm, &deviceInfo->minInputChannels, &commonDeviceInfo->maxInputChannels,
+ &commonDeviceInfo->defaultLowInputLatency, &commonDeviceInfo->defaultHighInputLatency,
+ &commonDeviceInfo->defaultSampleRate, deviceNames[i].isPlug ) != paNoError )
+ continue; /* Error */
+ }
+
+ /* Query playback */
+ if( deviceNames[i].hasPlayback &&
+ snd_pcm_open( &pcm, deviceNames[i].alsaName, SND_PCM_STREAM_PLAYBACK, blocking ) >= 0 )
+ {
+ if( GropeDevice( pcm, &deviceInfo->minOutputChannels, &commonDeviceInfo->maxOutputChannels,
+ &commonDeviceInfo->defaultLowOutputLatency, &commonDeviceInfo->defaultHighOutputLatency,
+ &commonDeviceInfo->defaultSampleRate, deviceNames[i].isPlug ) != paNoError )
+ continue; /* Error */
+ }
+
+ commonDeviceInfo->structVersion = 2;
+ commonDeviceInfo->hostApi = alsaApi->hostApiIndex;
+ commonDeviceInfo->name = deviceNames[i].name;
+ deviceInfo->alsaName = deviceNames[i].alsaName;
+ deviceInfo->isPlug = deviceNames[i].isPlug;
+
+ /* A: Storing pointer to PaAlsaDeviceInfo object as pointer to PaDeviceInfo object.
+ * Should now be safe to add device info, unless the device supports neither capture nor playback
+ */
+ if( commonDeviceInfo->maxInputChannels > 0 || commonDeviceInfo->maxOutputChannels > 0 )
+ {
+ if( commonApi->info.defaultInputDevice == paNoDevice && commonDeviceInfo->maxInputChannels > 0 )
+ commonApi->info.defaultInputDevice = devIdx;
+ if( commonApi->info.defaultOutputDevice == paNoDevice && commonDeviceInfo->maxOutputChannels > 0 )
+ commonApi->info.defaultOutputDevice = devIdx;
+
+ commonApi->deviceInfos[devIdx++] = (PaDeviceInfo *) deviceInfo;
+ }
+ }
+ free( deviceNames );
+
+ commonApi->info.deviceCount = devIdx; /* Number of successfully queried devices */
+
+end:
+ return result;
+
+error:
+ goto end; /* No particular action */
+}
+
+/* Check against known device capabilities */
+static PaError ValidateParameters( const PaStreamParameters *parameters, PaUtilHostApiRepresentation *hostApi, StreamDirection mode )
+{
+ PaError result = paNoError;
+ int maxChans;
+ const PaAlsaDeviceInfo *deviceInfo = NULL;
+ assert( parameters );
+
+ if( parameters->device != paUseHostApiSpecificDeviceSpecification )
+ {
+ assert( parameters->device < hostApi->info.deviceCount );
+ PA_UNLESS( parameters->hostApiSpecificStreamInfo == NULL, paBadIODeviceCombination );
+ deviceInfo = GetDeviceInfo( hostApi, parameters->device );
+ }
+ else
+ {
+ const PaAlsaStreamInfo *streamInfo = parameters->hostApiSpecificStreamInfo;
+
+ PA_UNLESS( parameters->device == paUseHostApiSpecificDeviceSpecification, paInvalidDevice );
+ PA_UNLESS( streamInfo->size == sizeof (PaAlsaStreamInfo) && streamInfo->version == 1,
+ paIncompatibleHostApiSpecificStreamInfo );
+
+ return paNoError; /* Skip further checking */
+ }
+
+ assert( deviceInfo );
+ assert( parameters->hostApiSpecificStreamInfo == NULL );
+ maxChans = (StreamDirection_In == mode ? deviceInfo->commonDeviceInfo.maxInputChannels :
+ deviceInfo->commonDeviceInfo.maxOutputChannels);
+ PA_UNLESS( parameters->channelCount <= maxChans, paInvalidChannelCount );
+
+error:
+ return result;
+}
+
+/* Given an open stream, what sample formats are available? */
+
+static PaSampleFormat GetAvailableFormats( snd_pcm_t *pcm )
+{
+ PaSampleFormat available = 0;
+ snd_pcm_hw_params_t *hwParams;
+ snd_pcm_hw_params_alloca( &hwParams );
+
+ snd_pcm_hw_params_any( pcm, hwParams );
+
+ if( snd_pcm_hw_params_test_format( pcm, hwParams, SND_PCM_FORMAT_FLOAT ) >= 0)
+ available |= paFloat32;
+
+ if( snd_pcm_hw_params_test_format( pcm, hwParams, SND_PCM_FORMAT_S32 ) >= 0)
+ available |= paInt32;
+
+ if( snd_pcm_hw_params_test_format( pcm, hwParams, SND_PCM_FORMAT_S24 ) >= 0)
+ available |= paInt24;
+
+ if( snd_pcm_hw_params_test_format( pcm, hwParams, SND_PCM_FORMAT_S16 ) >= 0)
+ available |= paInt16;
+
+ if( snd_pcm_hw_params_test_format( pcm, hwParams, SND_PCM_FORMAT_U8 ) >= 0)
+ available |= paUInt8;
+
+ if( snd_pcm_hw_params_test_format( pcm, hwParams, SND_PCM_FORMAT_S8 ) >= 0)
+ available |= paInt8;
+
+ return available;
+}
+
+static snd_pcm_format_t Pa2AlsaFormat( PaSampleFormat paFormat )
+{
+ switch( paFormat )
+ {
+ case paFloat32:
+ return SND_PCM_FORMAT_FLOAT;
+
+ case paInt16:
+ return SND_PCM_FORMAT_S16;
+
+ case paInt24:
+ return SND_PCM_FORMAT_S24;
+
+ case paInt32:
+ return SND_PCM_FORMAT_S32;
+
+ case paInt8:
+ return SND_PCM_FORMAT_S8;
+
+ case paUInt8:
+ return SND_PCM_FORMAT_U8;
+
+ default:
+ return SND_PCM_FORMAT_UNKNOWN;
+ }
+}
+
+/** Open an ALSA pcm handle.
+ *
+ * The device to be open can be specified in a custom PaAlsaStreamInfo struct, or it will be a device number. In case of a
+ * device number, it maybe specified through an env variable (PA_ALSA_PLUGHW) that we should open the corresponding plugin
+ * device.
+ */
+static PaError AlsaOpen( const PaUtilHostApiRepresentation *hostApi, const PaStreamParameters *params, StreamDirection
+ streamDir, snd_pcm_t **pcm )
+{
+ PaError result = paNoError;
+ int ret;
+ const char *deviceName = alloca( 50 );
+ const PaAlsaDeviceInfo *deviceInfo = NULL;
+ PaAlsaStreamInfo *streamInfo = (PaAlsaStreamInfo *)params->hostApiSpecificStreamInfo;
+
+ if( !streamInfo )
+ {
+ int usePlug = 0;
+ deviceInfo = GetDeviceInfo( hostApi, params->device );
+
+ /* If device name starts with hw: and PA_ALSA_PLUGHW is 1, we open the plughw device instead */
+ if( !strncmp( "hw:", deviceInfo->alsaName, 3 ) && getenv( "PA_ALSA_PLUGHW" ) )
+ usePlug = atoi( getenv( "PA_ALSA_PLUGHW" ) );
+ if( usePlug )
+ snprintf( (char *) deviceName, 50, "plug%s", deviceInfo->alsaName );
+ else
+ deviceName = deviceInfo->alsaName;
+ }
+ else
+ deviceName = streamInfo->deviceString;
+
+ if( (ret = snd_pcm_open( pcm, deviceName, streamDir == StreamDirection_In ? SND_PCM_STREAM_CAPTURE : SND_PCM_STREAM_PLAYBACK,
+ SND_PCM_NONBLOCK )) < 0 )
+ {
+ *pcm = NULL; /* Not to be closed */
+ ENSURE_( ret, ret == -EBUSY ? paDeviceUnavailable : paBadIODeviceCombination );
+ }
+ ENSURE_( snd_pcm_nonblock( *pcm, 0 ), paUnanticipatedHostError );
+
+end:
+ return result;
+
+error:
+ goto end;
+}
+
+static PaError TestParameters( const PaUtilHostApiRepresentation *hostApi, const PaStreamParameters *parameters,
+ double sampleRate, StreamDirection streamDir )
+{
+ PaError result = paNoError;
+ snd_pcm_t *pcm = NULL;
+ PaSampleFormat availableFormats;
+ /* We are able to adapt to a number of channels less than what the device supports */
+ unsigned int numHostChannels;
+ PaSampleFormat hostFormat;
+ snd_pcm_hw_params_t *hwParams;
+ snd_pcm_hw_params_alloca( &hwParams );
+
+ if( !parameters->hostApiSpecificStreamInfo )
+ {
+ const PaAlsaDeviceInfo *devInfo = GetDeviceInfo( hostApi, parameters->device );
+ numHostChannels = PA_MAX( parameters->channelCount, StreamDirection_In == streamDir ?
+ devInfo->minInputChannels : devInfo->minOutputChannels );
+ }
+ else
+ numHostChannels = parameters->channelCount;
+
+ PA_ENSURE( AlsaOpen( hostApi, parameters, streamDir, &pcm ) );
+
+ snd_pcm_hw_params_any( pcm, hwParams );
+
+ if( SetApproximateSampleRate( pcm, hwParams, sampleRate ) < 0 )
+ {
+ result = paInvalidSampleRate;
+ goto error;
+ }
+
+ if( snd_pcm_hw_params_set_channels( pcm, hwParams, numHostChannels ) < 0 )
+ {
+ result = paInvalidChannelCount;
+ goto error;
+ }
+
+ /* See if we can find a best possible match */
+ availableFormats = GetAvailableFormats( pcm );
+ PA_ENSURE( hostFormat = PaUtil_SelectClosestAvailableFormat( availableFormats, parameters->sampleFormat ) );
+ ENSURE_( snd_pcm_hw_params_set_format( pcm, hwParams, Pa2AlsaFormat( hostFormat ) ), paUnanticipatedHostError );
+
+ ENSURE_( snd_pcm_hw_params( pcm, hwParams ), paUnanticipatedHostError );
+
+end:
+ if( pcm )
+ snd_pcm_close( pcm );
+ return result;
+
+error:
+ goto end;
+}
+
+static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
+ const PaStreamParameters *inputParameters,
+ const PaStreamParameters *outputParameters,
+ double sampleRate )
+{
+ int inputChannelCount = 0, outputChannelCount = 0;
+ PaSampleFormat inputSampleFormat, outputSampleFormat;
+ PaError result = paFormatIsSupported;
+
+ if( inputParameters )
+ {
+ PA_ENSURE( ValidateParameters( inputParameters, hostApi, StreamDirection_In ) );
+
+ inputChannelCount = inputParameters->channelCount;
+ inputSampleFormat = inputParameters->sampleFormat;
+ }
+
+ if( outputParameters )
+ {
+ PA_ENSURE( ValidateParameters( outputParameters, hostApi, StreamDirection_Out ) );
+
+ outputChannelCount = outputParameters->channelCount;
+ outputSampleFormat = outputParameters->sampleFormat;
+ }
+
+ if( inputChannelCount )
+ {
+ if( (result = TestParameters( hostApi, inputParameters, sampleRate, StreamDirection_In ))
+ != paNoError )
+ goto error;
+ }
+ if ( outputChannelCount )
+ {
+ if( (result = TestParameters( hostApi, outputParameters, sampleRate, StreamDirection_Out ))
+ != paNoError )
+ goto error;
+ }
+
+ return paFormatIsSupported;
+
+error:
+ return result;
+}
+
+static PaError PaAlsaStreamComponent_Initialize( PaAlsaStreamComponent *self, PaAlsaHostApiRepresentation *alsaApi,
+ const PaStreamParameters *params, StreamDirection streamDir, int callbackMode )
+{
+ PaError result = paNoError;
+ PaSampleFormat userSampleFormat = params->sampleFormat, hostSampleFormat;
+ assert( params->channelCount > 0 );
+
+ /* Make sure things have an initial value */
+ memset( self, 0, sizeof (PaAlsaStreamComponent) );
+
+ if( NULL == params->hostApiSpecificStreamInfo )
+ {
+ const PaAlsaDeviceInfo *devInfo = GetDeviceInfo( &alsaApi->commonHostApiRep, params->device );
+ self->numHostChannels = PA_MAX( params->channelCount, StreamDirection_In == streamDir ? devInfo->minInputChannels
+ : devInfo->minOutputChannels );
+ }
+ else
+ {
+ /* We're blissfully unaware of the minimum channelCount */
+ self->numHostChannels = params->channelCount;
+ }
+
+ PA_ENSURE( AlsaOpen( &alsaApi->commonHostApiRep, params, streamDir, &self->pcm ) );
+ self->nfds = snd_pcm_poll_descriptors_count( self->pcm );
+ hostSampleFormat = PaUtil_SelectClosestAvailableFormat( GetAvailableFormats( self->pcm ), userSampleFormat );
+
+ self->hostSampleFormat = hostSampleFormat;
+ self->nativeFormat = Pa2AlsaFormat( hostSampleFormat );
+ self->hostInterleaved = self->userInterleaved = !(userSampleFormat & paNonInterleaved);
+ self->numUserChannels = params->channelCount;
+ self->streamDir = streamDir;
+
+ if( !callbackMode && !self->userInterleaved )
+ {
+ /* Pre-allocate non-interleaved user provided buffers */
+ PA_UNLESS( self->userBuffers = PaUtil_AllocateMemory( sizeof (void *) * self->numUserChannels ),
+ paInsufficientMemory );
+ }
+
+error:
+ return result;
+}
+
+static void PaAlsaStreamComponent_Terminate( PaAlsaStreamComponent *self )
+{
+ snd_pcm_close( self->pcm );
+ if( self->userBuffers )
+ PaUtil_FreeMemory( self->userBuffers );
+}
+
+/** Configure the associated ALSA pcm.
+ *
+ */
+static PaError PaAlsaStreamComponent_Configure( PaAlsaStreamComponent *self, const PaStreamParameters *params, unsigned long
+ framesPerHostBuffer, int primeBuffers, int callbackMode, double *sampleRate, PaTime *returnedLatency )
+{
+ /*
+ int numPeriods;
+
+ if( getenv("PA_NUMPERIODS") != NULL )
+ numPeriods = atoi( getenv("PA_NUMPERIODS") );
+ else
+ numPeriods = ( (latency * sampleRate) / *framesPerBuffer ) + 1;
+
+ PA_DEBUG(( "latency: %f, rate: %f, framesPerBuffer: %d\n", latency, sampleRate, *framesPerBuffer ));
+ if( numPeriods <= 1 )
+ numPeriods = 2;
+ */
+
+ /* Configuration consists of setting all of ALSA's parameters.
+ * These parameters come in two flavors: hardware parameters
+ * and software paramters. Hardware parameters will affect
+ * the way the device is initialized, software parameters
+ * affect the way ALSA interacts with me, the user-level client.
+ */
+
+ snd_pcm_hw_params_t *hwParams;
+ snd_pcm_sw_params_t *swParams;
+ PaError result = paNoError;
+ snd_pcm_access_t accessMode, alternateAccessMode;
+ unsigned int numPeriods, minPeriods = 2;
+ int dir = 0;
+ snd_pcm_t *pcm = self->pcm;
+ PaTime latency = params->suggestedLatency;
+ double sr = *sampleRate;
+ *returnedLatency = -1.;
+
+ snd_pcm_hw_params_alloca( &hwParams );
+ snd_pcm_sw_params_alloca( &swParams );
+
+ self->framesPerBuffer = framesPerHostBuffer;
+
+ /* ... fill up the configuration space with all possibile
+ * combinations of parameters this device will accept */
+ ENSURE_( snd_pcm_hw_params_any( pcm, hwParams ), paUnanticipatedHostError );
+
+ ENSURE_( snd_pcm_hw_params_set_periods_integer( pcm, hwParams ), paUnanticipatedHostError );
+ ENSURE_( snd_pcm_hw_params_set_period_size_integer( pcm, hwParams ), paUnanticipatedHostError );
+
+ if( self->userInterleaved )
+ {
+ accessMode = SND_PCM_ACCESS_MMAP_INTERLEAVED;
+ alternateAccessMode = SND_PCM_ACCESS_MMAP_NONINTERLEAVED;
+ }
+ else
+ {
+ accessMode = SND_PCM_ACCESS_MMAP_NONINTERLEAVED;
+ alternateAccessMode = SND_PCM_ACCESS_MMAP_INTERLEAVED;
+ }
+
+ /* If requested access mode fails, try alternate mode */
+ if( snd_pcm_hw_params_set_access( pcm, hwParams, accessMode ) < 0 )
+ {
+ ENSURE_( snd_pcm_hw_params_set_access( pcm, hwParams, alternateAccessMode ), paUnanticipatedHostError );
+ /* Flip mode */
+ self->hostInterleaved = !self->userInterleaved;
+ }
+
+ ENSURE_( snd_pcm_hw_params_set_format( pcm, hwParams, self->nativeFormat ), paUnanticipatedHostError );
+
+ ENSURE_( SetApproximateSampleRate( pcm, hwParams, sr ), paInvalidSampleRate );
+ ENSURE_( GetExactSampleRate( hwParams, &sr ), paUnanticipatedHostError );
+ /* reject if there's no sample rate within 1% of the one requested */
+ if( (fabs( *sampleRate - sr ) / *sampleRate) > 0.01 )
+ {
+ PA_DEBUG(("%s: Wanted %f, closest sample rate was %d\n", __FUNCTION__, sampleRate, sr ));
+ PA_ENSURE( paInvalidSampleRate );
+ }
+
+ ENSURE_( snd_pcm_hw_params_set_channels( pcm, hwParams, self->numHostChannels ), paInvalidChannelCount );
+
+ /* I think there should be at least 2 periods (even though ALSA doesn't appear to enforce this) */
+ dir = 0;
+ ENSURE_( snd_pcm_hw_params_set_periods_min( pcm, hwParams, &minPeriods, &dir ), paUnanticipatedHostError );
+ dir = 0;
+ ENSURE_( snd_pcm_hw_params_set_period_size_near( pcm, hwParams, &self->framesPerBuffer, &dir ), paUnanticipatedHostError );
+
+ /* Find an acceptable number of periods */
+ numPeriods = (latency * sr) / self->framesPerBuffer + 1;
+ dir = 0;
+ ENSURE_( snd_pcm_hw_params_set_periods_near( pcm, hwParams, &numPeriods, &dir ), paUnanticipatedHostError );
+ /* Minimum of periods should already be 2 */
+ PA_UNLESS( numPeriods >= 2, paInternalError );
+
+ /* Set the parameters! */
+ ENSURE_( snd_pcm_hw_params( pcm, hwParams ), paUnanticipatedHostError );
+ ENSURE_( snd_pcm_hw_params_get_buffer_size( hwParams, &self->bufferSize ), paUnanticipatedHostError );
+
+ /* Latency in seconds, one period is not counted as latency */
+ latency = (numPeriods - 1) * self->framesPerBuffer / sr;
+
+ /* Now software parameters... */
+ ENSURE_( snd_pcm_sw_params_current( pcm, swParams ), paUnanticipatedHostError );
+
+ ENSURE_( snd_pcm_sw_params_set_start_threshold( pcm, swParams, self->framesPerBuffer ), paUnanticipatedHostError );
+ ENSURE_( snd_pcm_sw_params_set_stop_threshold( pcm, swParams, self->bufferSize ), paUnanticipatedHostError );
+
+ /* Silence buffer in the case of underrun */
+ if( !primeBuffers ) /* XXX: Make sense? */
+ {
+ snd_pcm_uframes_t boundary;
+ ENSURE_( snd_pcm_sw_params_get_boundary( swParams, &boundary ), paUnanticipatedHostError );
+ ENSURE_( snd_pcm_sw_params_set_silence_threshold( pcm, swParams, 0 ), paUnanticipatedHostError );
+ ENSURE_( snd_pcm_sw_params_set_silence_size( pcm, swParams, boundary ), paUnanticipatedHostError );
+ }
+
+ ENSURE_( snd_pcm_sw_params_set_avail_min( pcm, swParams, self->framesPerBuffer ), paUnanticipatedHostError );
+ ENSURE_( snd_pcm_sw_params_set_xfer_align( pcm, swParams, 1 ), paUnanticipatedHostError );
+ ENSURE_( snd_pcm_sw_params_set_tstamp_mode( pcm, swParams, SND_PCM_TSTAMP_MMAP ), paUnanticipatedHostError );
+
+ /* Set the parameters! */
+ ENSURE_( snd_pcm_sw_params( pcm, swParams ), paUnanticipatedHostError );
+
+ *sampleRate = sr;
+ *returnedLatency = latency;
+
+end:
+ return result;
+
+error:
+ goto end; /* No particular action */
+}
+
+static PaError PaAlsaStream_Initialize( PaAlsaStream *self, PaAlsaHostApiRepresentation *alsaApi, const PaStreamParameters *inParams,
+ const PaStreamParameters *outParams, double sampleRate, unsigned long framesPerUserBuffer, PaStreamCallback callback,
+ PaStreamFlags streamFlags, void *userData )
+{
+ PaError result = paNoError;
+ assert( self );
+
+ memset( self, 0, sizeof (PaAlsaStream) );
+
+ if( NULL != callback )
+ {
+ PaUtil_InitializeStreamRepresentation( &self->streamRepresentation,
+ &alsaApi->callbackStreamInterface,
+ callback, userData );
+ self->callbackMode = 1;
+ }
+ else
+ {
+ PaUtil_InitializeStreamRepresentation( &self->streamRepresentation,
+ &alsaApi->blockingStreamInterface,
+ NULL, userData );
+ }
+
+ self->framesPerUserBuffer = framesPerUserBuffer;
+ self->neverDropInput = streamFlags & paNeverDropInput;
+ /* XXX: Ignore paPrimeOutputBuffersUsingStreamCallback untill buffer priming is fully supported in pa_process.c */
+ /*
+ if( outParams & streamFlags & paPrimeOutputBuffersUsingStreamCallback )
+ self->primeBuffers = 1;
+ */
+ memset( &self->capture, 0, sizeof (PaAlsaStreamComponent) );
+ memset( &self->playback, 0, sizeof (PaAlsaStreamComponent) );
+ if( inParams )
+ PA_ENSURE( PaAlsaStreamComponent_Initialize( &self->capture, alsaApi, inParams, StreamDirection_In, NULL != callback ) );
+ if( outParams )
+ PA_ENSURE( PaAlsaStreamComponent_Initialize( &self->playback, alsaApi, outParams, StreamDirection_Out, NULL != callback ) );
+
+ assert( self->capture.nfds || self->playback.nfds );
+
+ PA_UNLESS( self->pfds = (struct pollfd*)PaUtil_AllocateMemory( (self->capture.nfds +
+ self->playback.nfds) * sizeof (struct pollfd) ), paInsufficientMemory );
+
+ PaUtil_InitializeCpuLoadMeasurer( &self->cpuLoadMeasurer, sampleRate );
+ InitializeThreading( &self->threading, &self->cpuLoadMeasurer );
+ ASSERT_CALL_( pthread_mutex_init( &self->stateMtx, NULL ), 0 );
+ ASSERT_CALL_( pthread_mutex_init( &self->startMtx, NULL ), 0 );
+ ASSERT_CALL_( pthread_cond_init( &self->startCond, NULL ), 0 );
+
+error:
+ return result;
+}
+
+/** Free resources associated with stream, and eventually stream itself.
+ *
+ * Frees allocated memory, and terminates individual StreamComponents.
+ */
+static void PaAlsaStream_Terminate( PaAlsaStream *self )
+{
+ assert( self );
+
+ if( self->capture.pcm )
+ {
+ PaAlsaStreamComponent_Terminate( &self->capture );
+ }
+ if( self->playback.pcm )
+ {
+ PaAlsaStreamComponent_Terminate( &self->playback );
+ }
+
+ PaUtil_FreeMemory( self->pfds );
+ ASSERT_CALL_( pthread_mutex_destroy( &self->stateMtx ), 0 );
+ ASSERT_CALL_( pthread_mutex_destroy( &self->startMtx ), 0 );
+ ASSERT_CALL_( pthread_cond_destroy( &self->startCond ), 0 );
+
+ PaUtil_FreeMemory( self );
+}
+
+/** Calculate polling timeout
+ *
+ * @param frames Time to wait
+ * @return Polling timeout in milliseconds
+ */
+static int CalculatePollTimeout( const PaAlsaStream *stream, unsigned long frames )
+{
+ assert( stream->streamRepresentation.streamInfo.sampleRate > 0.0 );
+ /* Period in msecs, rounded up */
+ return (int)ceil( 1000 * frames / stream->streamRepresentation.streamInfo.sampleRate );
+}
+
+/** Set up ALSA stream parameters.
+ *
+ */
+static PaError PaAlsaStream_Configure( PaAlsaStream *self, const PaStreamParameters *inParams, const PaStreamParameters
+ *outParams, double sampleRate, unsigned long framesPerHostBuffer, double *inputLatency, double *outputLatency,
+ unsigned long *maxHostBufferSize )
+{
+ PaError result = paNoError;
+ double realSr = sampleRate;
+
+ if( self->capture.pcm )
+ PA_ENSURE( PaAlsaStreamComponent_Configure( &self->capture, inParams, framesPerHostBuffer, self->primeBuffers,
+ self->callbackMode, &realSr, inputLatency ) );
+ if( self->playback.pcm )
+ PA_ENSURE( PaAlsaStreamComponent_Configure( &self->playback, outParams, framesPerHostBuffer, self->primeBuffers,
+ self->callbackMode, &realSr, outputLatency ) );
+
+ /* Should be exact now */
+ self->streamRepresentation.streamInfo.sampleRate = realSr;
+
+ /* this will cause the two streams to automatically start/stop/prepare in sync.
+ * We only need to execute these operations on one of the pair.
+ * A: We don't want to do this on a blocking stream.
+ */
+ if( self->callbackMode && self->capture.pcm && self->playback.pcm )
+ {
+ int err = snd_pcm_link( self->capture.pcm, self->playback.pcm );
+ if( err >= 0 )
+ self->pcmsSynced = 1;
+ else
+ PA_DEBUG(( "%s: Unable to sync pcms: %s\n", __FUNCTION__, snd_strerror( err ) ));
+ }
+
+ /* Frames per host buffer for the stream is set as a compromise between the two directions */
+ framesPerHostBuffer = PA_MIN( self->capture.pcm ? self->capture.framesPerBuffer : ULONG_MAX,
+ self->playback.pcm ? self->playback.framesPerBuffer : ULONG_MAX );
+ self->pollTimeout = CalculatePollTimeout( self, framesPerHostBuffer ); /* Period in msecs, rounded up */
+
+ *maxHostBufferSize = PA_MAX( self->capture.pcm ? self->capture.bufferSize : 0,
+ self->playback.pcm ? self->playback.bufferSize : 0 );
+
+ /* Time before watchdog unthrottles realtime thread == 1/4 of period time in msecs */
+ self->threading.throttledSleepTime = (unsigned long) (framesPerHostBuffer / sampleRate / 4 * 1000);
+
+ if( self->callbackMode )
+ {
+ /* If the user expects a certain number of frames per callback we will either have to rely on block adaption
+ * (framesPerHostBuffer is not an integer multiple of framesPerBuffer) or we can simply align the number
+ * of host buffer frames with what the user specified */
+ if( self->framesPerUserBuffer != paFramesPerBufferUnspecified )
+ {
+ /* self->alignFrames = 1; */
+
+ /* Unless the ratio between number of host and user buffer frames is an integer we will have to rely
+ * on block adaption */
+ /*
+ if( framesPerHostBuffer % framesPerBuffer != 0 || (self->capture.pcm && self->playback.pcm &&
+ self->capture.framesPerBuffer != self->playback.framesPerBuffer) )
+ self->useBlockAdaption = 1;
+ else
+ self->alignFrames = 1;
+ */
+ }
+ }
+
+error:
+ return result;
+}
+
+/* We need to determine how many frames per host buffer to use. Our
+ * goals are to provide the best possible performance, but also to
+ * most closely honor the requested latency settings. Therefore this
+ * decision is based on:
+ *
+ * - the period sizes that playback and/or capture support. The
+ * host buffer size has to be one of these.
+ * - the number of periods that playback and/or capture support.
+ *
+ * We want to make period_size*(num_periods-1) to be as close as possible
+ * to latency*rate for both playback and capture.
+ *
+ * This is one of those blocks of code that will just take a lot of
+ * refinement to be any good.
+ *
+ * In the full-duplex case it is possible that the routine was unable
+ * to find a number of frames per buffer acceptable to both devices
+ * TODO: Implement an algorithm to find the value closest to acceptance
+ * by both devices, to minimize difference between period sizes?
+ */
+static PaError DetermineFramesPerBuffer( const PaAlsaStream *stream, double sampleRate, const PaStreamParameters *inputParameters,
+ const PaStreamParameters *outputParameters, unsigned long *determinedFrames, const PaUtilHostApiRepresentation *hostApi )
+{
+ PaError result = paNoError;
+ unsigned long framesPerBuffer = 0;
+ int numHostInputChannels = 0, numHostOutputChannels = 0;
+
+ /* XXX: Clean this up */
+ if( stream->capture.pcm )
+ {
+ const PaAlsaDeviceInfo *devInfo = GetDeviceInfo( hostApi, inputParameters->device );
+ numHostInputChannels = PA_MAX( inputParameters->channelCount, devInfo->minInputChannels );
+ }
+ if( stream->playback.pcm )
+ {
+ const PaAlsaDeviceInfo *devInfo = GetDeviceInfo( hostApi, outputParameters->device );
+ numHostOutputChannels = PA_MAX( outputParameters->channelCount, devInfo->minOutputChannels );
+ }
+
+ if( stream->capture.pcm && stream->playback.pcm )
+ {
+ snd_pcm_uframes_t desiredLatency, e;
+ snd_pcm_uframes_t minPeriodSize, minPlayback, minCapture, maxPeriodSize, maxPlayback, maxCapture,
+ optimalPeriodSize, periodSize;
+ int dir = 0;
+ unsigned int minPeriods = 2;
+
+ snd_pcm_t *pcm;
+ snd_pcm_hw_params_t *hwParamsPlayback, *hwParamsCapture;
+
+ snd_pcm_hw_params_alloca( &hwParamsPlayback );
+ snd_pcm_hw_params_alloca( &hwParamsCapture );
+
+ /* Come up with a common desired latency */
+ pcm = stream->playback.pcm;
+ snd_pcm_hw_params_any( pcm, hwParamsPlayback );
+ ENSURE_( SetApproximateSampleRate( pcm, hwParamsPlayback, sampleRate ), paInvalidSampleRate );
+ ENSURE_( snd_pcm_hw_params_set_channels( pcm, hwParamsPlayback, numHostOutputChannels ),
+ paBadIODeviceCombination );
+
+ ENSURE_( snd_pcm_hw_params_set_period_size_integer( pcm, hwParamsPlayback ), paUnanticipatedHostError );
+ ENSURE_( snd_pcm_hw_params_set_periods_integer( pcm, hwParamsPlayback ), paUnanticipatedHostError );
+ ENSURE_( snd_pcm_hw_params_set_periods_min( pcm, hwParamsPlayback, &minPeriods, &dir ), paUnanticipatedHostError );
+ ENSURE_( snd_pcm_hw_params_get_period_size_min( hwParamsPlayback, &minPlayback, &dir ), paUnanticipatedHostError );
+ ENSURE_( snd_pcm_hw_params_get_period_size_max( hwParamsPlayback, &maxPlayback, &dir ), paUnanticipatedHostError );
+
+ pcm = stream->capture.pcm;
+ ENSURE_( snd_pcm_hw_params_any( pcm, hwParamsCapture ), paUnanticipatedHostError );
+ ENSURE_( SetApproximateSampleRate( pcm, hwParamsCapture, sampleRate ), paBadIODeviceCombination );
+ ENSURE_( snd_pcm_hw_params_set_channels( pcm, hwParamsCapture, numHostInputChannels ),
+ paBadIODeviceCombination );
+
+ ENSURE_( snd_pcm_hw_params_set_period_size_integer( pcm, hwParamsCapture ), paUnanticipatedHostError );
+ ENSURE_( snd_pcm_hw_params_set_periods_integer( pcm, hwParamsCapture ), paUnanticipatedHostError );
+ ENSURE_( snd_pcm_hw_params_set_periods_min( pcm, hwParamsCapture, &minPeriods, &dir ), paUnanticipatedHostError );
+ ENSURE_( snd_pcm_hw_params_get_period_size_min( hwParamsCapture, &minCapture, &dir ), paUnanticipatedHostError );
+ ENSURE_( snd_pcm_hw_params_get_period_size_max( hwParamsCapture, &maxCapture, &dir ), paUnanticipatedHostError );
+
+ minPeriodSize = PA_MAX( minPlayback, minCapture );
+ maxPeriodSize = PA_MIN( maxPlayback, maxCapture );
+
+ desiredLatency = (snd_pcm_uframes_t) (PA_MIN( outputParameters->suggestedLatency, inputParameters->suggestedLatency )
+ * sampleRate);
+ /* Clamp desiredLatency */
+ {
+ snd_pcm_uframes_t tmp, maxBufferSize = ULONG_MAX;
+ ENSURE_( snd_pcm_hw_params_get_buffer_size_max( hwParamsPlayback, &maxBufferSize ), paUnanticipatedHostError );
+ ENSURE_( snd_pcm_hw_params_get_buffer_size_max( hwParamsCapture, &tmp ), paUnanticipatedHostError );
+ maxBufferSize = PA_MIN( maxBufferSize, tmp );
+
+ desiredLatency = PA_MIN( desiredLatency, maxBufferSize );
+ }
+
+ /* Find the closest power of 2 */
+ e = ilogb( minPeriodSize );
+ if( minPeriodSize & (minPeriodSize - 1) )
+ e += 1;
+ periodSize = (snd_pcm_uframes_t) pow( 2, e );
+
+ while( periodSize <= maxPeriodSize )
+ {
+ if( snd_pcm_hw_params_test_period_size( stream->playback.pcm, hwParamsPlayback, periodSize, 0 ) >= 0 &&
+ snd_pcm_hw_params_test_period_size( stream->capture.pcm, hwParamsCapture, periodSize, 0 ) >= 0 )
+ break; /* Ok! */
+
+ periodSize *= 2;
+ }
+
+ /* 4 periods considered optimal */
+ optimalPeriodSize = PA_MAX( desiredLatency / 4, minPeriodSize );
+ optimalPeriodSize = PA_MIN( optimalPeriodSize, maxPeriodSize );
+
+ /* Find the closest power of 2 */
+ e = ilogb( optimalPeriodSize );
+ if( optimalPeriodSize & (optimalPeriodSize - 1) )
+ e += 1;
+ optimalPeriodSize = (snd_pcm_uframes_t) pow( 2, e );
+
+ while( optimalPeriodSize >= periodSize )
+ {
+ pcm = stream->playback.pcm;
+ if( snd_pcm_hw_params_test_period_size( pcm, hwParamsPlayback, optimalPeriodSize, 0 ) < 0 )
+ continue;
+
+ pcm = stream->capture.pcm;
+ if( snd_pcm_hw_params_test_period_size( pcm, hwParamsCapture, optimalPeriodSize, 0 ) >= 0 )
+ break;
+
+ optimalPeriodSize /= 2;
+ }
+
+ if( optimalPeriodSize > periodSize )
+ periodSize = optimalPeriodSize;
+
+ if( periodSize <= maxPeriodSize )
+ {
+ /* Looks good */
+ framesPerBuffer = periodSize;
+ }
+ else
+ {
+ /* Unable to find a common period size, oh well */
+ optimalPeriodSize = PA_MAX( desiredLatency / 4, minPeriodSize );
+ optimalPeriodSize = PA_MIN( optimalPeriodSize, maxPeriodSize );
+
+ /* ConfigureStream should find individual period sizes acceptable for each device */
+ framesPerBuffer = optimalPeriodSize;
+ /* PA_ENSURE( paBadIODeviceCombination ); */
+ }
+ }
+ else /* half-duplex is a slightly simpler case */
+ {
+ unsigned long bufferSize, channels;
+ snd_pcm_t *pcm;
+ snd_pcm_hw_params_t *hwParams;
+
+ snd_pcm_hw_params_alloca( &hwParams );
+
+ if( stream->capture.pcm )
+ {
+ pcm = stream->capture.pcm;
+ bufferSize = inputParameters->suggestedLatency * sampleRate;
+ channels = numHostInputChannels;
+ }
+ else
+ {
+ pcm = stream->playback.pcm;
+ bufferSize = outputParameters->suggestedLatency * sampleRate;
+ channels = numHostOutputChannels;
+ }
+
+ ENSURE_( snd_pcm_hw_params_any( pcm, hwParams ), paUnanticipatedHostError );
+ ENSURE_( SetApproximateSampleRate( pcm, hwParams, sampleRate ), paInvalidSampleRate );
+ ENSURE_( snd_pcm_hw_params_set_channels( pcm, hwParams, channels ), paBadIODeviceCombination );
+
+ ENSURE_( snd_pcm_hw_params_set_period_size_integer( pcm, hwParams ), paUnanticipatedHostError );
+ ENSURE_( snd_pcm_hw_params_set_periods_integer( pcm, hwParams ), paUnanticipatedHostError );
+
+ /* Using 5 as a base number of periods, we try to approximate the suggested latency (+1 period),
+ finding a combination of period/buffer size which best fits these constraints */
+ framesPerBuffer = bufferSize / 4;
+ bufferSize += framesPerBuffer; /* One period doesn't count as latency */
+ ENSURE_( snd_pcm_hw_params_set_buffer_size_near( pcm, hwParams, &bufferSize ), paUnanticipatedHostError );
+ ENSURE_( snd_pcm_hw_params_set_period_size_near( pcm, hwParams, &framesPerBuffer, NULL ), paUnanticipatedHostError );
+ }
+
+ PA_UNLESS( framesPerBuffer != 0, paInternalError );
+ *determinedFrames = framesPerBuffer;
+
+error:
+ return result;
+}
+
+static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
+ PaStream** s,
+ const PaStreamParameters *inputParameters,
+ const PaStreamParameters *outputParameters,
+ double sampleRate,
+ unsigned long framesPerBuffer,
+ PaStreamFlags streamFlags,
+ PaStreamCallback *callback,
+ void *userData )
+{
+ PaError result = paNoError;
+ PaAlsaHostApiRepresentation *alsaHostApi = (PaAlsaHostApiRepresentation*)hostApi;
+ PaAlsaStream *stream = NULL;
+ PaSampleFormat hostInputSampleFormat = 0, hostOutputSampleFormat = 0;
+ PaSampleFormat inputSampleFormat = 0, outputSampleFormat = 0;
+ int numInputChannels = 0, numOutputChannels = 0;
+ PaTime inputLatency, outputLatency;
+ unsigned long framesPerHostBuffer;
+ PaUtilHostBufferSizeMode hostBufferSizeMode = paUtilBoundedHostBufferSize;
+ unsigned long maxHostBufferSize; /* Upper bound of host buffer size */
+
+ if( (streamFlags & paPlatformSpecificFlags) != 0 )
+ return paInvalidFlag;
+
+ if( inputParameters )
+ {
+ PA_ENSURE( ValidateParameters( inputParameters, hostApi, StreamDirection_In ) );
+
+ numInputChannels = inputParameters->channelCount;
+ inputSampleFormat = inputParameters->sampleFormat;
+ }
+ if( outputParameters )
+ {
+ PA_ENSURE( ValidateParameters( outputParameters, hostApi, StreamDirection_Out ) );
+
+ numOutputChannels = outputParameters->channelCount;
+ outputSampleFormat = outputParameters->sampleFormat;
+ }
+
+ /* XXX: Why do we support this anyway? */
+ if( framesPerBuffer == paFramesPerBufferUnspecified && getenv( "PA_ALSA_PERIODSIZE" ) != NULL )
+ {
+ PA_DEBUG(( "%s: Getting framesPerBuffer from environment\n", __FUNCTION__ ));
+ framesPerBuffer = atoi( getenv("PA_ALSA_PERIODSIZE") );
+ }
+ framesPerHostBuffer = framesPerBuffer;
+
+ PA_UNLESS( stream = (PaAlsaStream*)PaUtil_AllocateMemory( sizeof(PaAlsaStream) ), paInsufficientMemory );
+ PA_ENSURE( PaAlsaStream_Initialize( stream, alsaHostApi, inputParameters, outputParameters, sampleRate,
+ framesPerBuffer, callback, streamFlags, userData ) );
+
+ /* If the number of frames per buffer is unspecified, we have to come up with
+ * one. This is both a blessing and a curse: a blessing because we can optimize
+ * the number to best meet the requirements, but a curse because that's really
+ * hard to do well. For this reason we also support an interface where the user
+ * specifies these by setting environment variables. */
+ if( framesPerBuffer == paFramesPerBufferUnspecified )
+ {
+ PA_ENSURE( DetermineFramesPerBuffer( stream, sampleRate, inputParameters, outputParameters, &framesPerHostBuffer,
+ hostApi ) );
+ }
+
+ PA_ENSURE( PaAlsaStream_Configure( stream, inputParameters, outputParameters, sampleRate, framesPerHostBuffer,
+ &inputLatency, &outputLatency, &maxHostBufferSize ) );
+ hostInputSampleFormat = stream->capture.hostSampleFormat;
+ hostOutputSampleFormat = stream->playback.hostSampleFormat;
+
+ if( framesPerHostBuffer != framesPerBuffer )
+ {
+ PA_DEBUG(( "%s: Number of frames per user and host buffer differs\n", __FUNCTION__ ));
+ }
+
+ PA_ENSURE( PaUtil_InitializeBufferProcessor( &stream->bufferProcessor,
+ numInputChannels, inputSampleFormat, hostInputSampleFormat,
+ numOutputChannels, outputSampleFormat, hostOutputSampleFormat,
+ sampleRate, streamFlags, framesPerBuffer, maxHostBufferSize,
+ hostBufferSizeMode, callback, userData ) );
+
+ /* Ok, buffer processor is initialized, now we can deduce it's latency */
+ if( numInputChannels > 0 )
+ stream->streamRepresentation.streamInfo.inputLatency = inputLatency + PaUtil_GetBufferProcessorInputLatency(
+ &stream->bufferProcessor );
+ if( numOutputChannels > 0 )
+ stream->streamRepresentation.streamInfo.outputLatency = outputLatency + PaUtil_GetBufferProcessorOutputLatency(
+ &stream->bufferProcessor );
+
+ *s = (PaStream*)stream;
+
+ return result;
+
+error:
+ if( stream )
+ PaAlsaStream_Terminate( stream );
+
+ return result;
+}
+
+static PaError CloseStream( PaStream* s )
+{
+ PaError result = paNoError;
+ PaAlsaStream *stream = (PaAlsaStream*)s;
+
+ PaUtil_TerminateBufferProcessor( &stream->bufferProcessor );
+ PaUtil_TerminateStreamRepresentation( &stream->streamRepresentation );
+
+ PaAlsaStream_Terminate( stream );
+
+ return result;
+}
+
+static void SilenceBuffer( PaAlsaStream *stream )
+{
+ const snd_pcm_channel_area_t *areas;
+ snd_pcm_uframes_t frames = (snd_pcm_uframes_t)snd_pcm_avail_update( stream->playback.pcm ), offset;
+
+ snd_pcm_mmap_begin( stream->playback.pcm, &areas, &offset, &frames );
+ snd_pcm_areas_silence( areas, offset, stream->playback.numHostChannels, frames, stream->playback.nativeFormat );
+ snd_pcm_mmap_commit( stream->playback.pcm, offset, frames );
+}
+
+/** Start/prepare pcm(s) for streaming.
+ *
+ * Depending on wether the stream is in callback or blocking mode, we will respectively start or simply
+ * prepare the playback pcm. If the buffer has _not_ been primed, we will in callback mode prepare and
+ * silence the buffer before starting playback. In blocking mode we simply prepare, as the playback will
+ * be started automatically as the user writes to output.
+ *
+ * The capture pcm, however, will simply be prepared and started.
+ *
+ * PaAlsaStream::startMtx makes sure access is synchronized (useful in callback mode)
+ */
+static PaError AlsaStart( PaAlsaStream *stream, int priming )
+{
+ PaError result = paNoError;
+
+ if( stream->playback.pcm )
+ {
+ if( stream->callbackMode )
+ {
+ if( !priming )
+ {
+ /* Buffer isn't primed, so prepare and silence */
+ ENSURE_( snd_pcm_prepare( stream->playback.pcm ), paUnanticipatedHostError );
+ SilenceBuffer( stream );
+ }
+ ENSURE_( snd_pcm_start( stream->playback.pcm ), paUnanticipatedHostError );
+ }
+ else
+ ENSURE_( snd_pcm_prepare( stream->playback.pcm ), paUnanticipatedHostError );
+ }
+ if( stream->capture.pcm && !stream->pcmsSynced )
+ {
+ ENSURE_( snd_pcm_prepare( stream->capture.pcm ), paUnanticipatedHostError );
+ /* For a blocking stream we want to start capture as well, since nothing will happen otherwise */
+ ENSURE_( snd_pcm_start( stream->capture.pcm ), paUnanticipatedHostError );
+ }
+
+end:
+ return result;
+error:
+ goto end;
+}
+
+/** Utility function for determining if pcms are in running state.
+ *
+ */
+static int IsRunning( PaAlsaStream *stream )
+{
+ int result = 0;
+
+ ASSERT_CALL_( pthread_mutex_lock( &stream->stateMtx ), 0 ); /* Synchronize access to pcm state */
+ if( stream->capture.pcm )
+ {
+ snd_pcm_state_t capture_state = snd_pcm_state( stream->capture.pcm );
+
+ if( capture_state == SND_PCM_STATE_RUNNING || capture_state == SND_PCM_STATE_XRUN
+ || capture_state == SND_PCM_STATE_DRAINING )
+ {
+ result = 1;
+ goto end;
+ }
+ }
+
+ if( stream->playback.pcm )
+ {
+ snd_pcm_state_t playback_state = snd_pcm_state( stream->playback.pcm );
+
+ if( playback_state == SND_PCM_STATE_RUNNING || playback_state == SND_PCM_STATE_XRUN
+ || playback_state == SND_PCM_STATE_DRAINING )
+ {
+ result = 1;
+ goto end;
+ }
+ }
+
+end:
+ ASSERT_CALL_( pthread_mutex_unlock( &stream->stateMtx ), 0 );
+
+ return result;
+}
+
+static PaError StartStream( PaStream *s )
+{
+ PaError result = paNoError;
+ PaAlsaStream *stream = (PaAlsaStream*)s;
+ int streamStarted = 0; /* So we can know wether we need to take the stream down */
+
+ /* Ready the processor */
+ PaUtil_ResetBufferProcessor( &stream->bufferProcessor );
+
+ /* Set now, so we can test for activity further down */
+ stream->isActive = 1;
+
+ if( stream->callbackMode )
+ {
+ int res = 0;
+ PaTime pt = PaUtil_GetTime();
+ struct timespec ts;
+
+ PA_ENSURE( CreateCallbackThread( &stream->threading, &CallbackThreadFunc, stream ) );
+ streamStarted = 1;
+
+ /* Wait for stream to be started */
+ ts.tv_sec = (time_t) floor( pt + 1 );
+ ts.tv_nsec = (long) ((pt - floor( pt )) * 1000000000);
+
+ /* Since we'll be holding a lock on the startMtx (when not waiting on the condition), IsRunning won't be checking
+ * stream state at the same time as the callback thread affects it. We also check IsStreamActive, in the unlikely
+ * case the callback thread exits in the meantime (the stream will be considered inactive after the thread exits) */
+ ASSERT_CALL_( pthread_mutex_lock( &stream->startMtx ), 0 );
+
+ /* Due to possible spurious wakeups, we enclose in a loop */
+ while( !IsRunning( stream ) && IsStreamActive( s ) && !res )
+ {
+ res = pthread_cond_timedwait( &stream->startCond, &stream->startMtx, &ts );
+ }
+ ASSERT_CALL_( pthread_mutex_unlock( &stream->startMtx ), 0 );
+
+ PA_UNLESS( !res || res == ETIMEDOUT, paInternalError );
+ PA_DEBUG(( "%s: Waited for %g seconds for stream to start\n", __FUNCTION__, PaUtil_GetTime() - pt ));
+
+ if( res == ETIMEDOUT )
+ {
+ PA_ENSURE( paTimedOut );
+ }
+ }
+ else
+ {
+ PA_ENSURE( AlsaStart( stream, 0 ) );
+ streamStarted = 1;
+ }
+
+end:
+ return result;
+error:
+ if( streamStarted )
+ AbortStream( stream );
+ stream->isActive = 0;
+
+ goto end;
+}
+
+static PaError AlsaStop( PaAlsaStream *stream, int abort )
+{
+ PaError result = paNoError;
+
+ if( abort )
+ {
+ if( stream->playback.pcm )
+ ENSURE_( snd_pcm_drop( stream->playback.pcm ), paUnanticipatedHostError );
+ if( stream->capture.pcm && !stream->pcmsSynced )
+ ENSURE_( snd_pcm_drop( stream->capture.pcm ), paUnanticipatedHostError );
+
+ PA_DEBUG(( "Dropped frames\n" ));
+ }
+ else
+ {
+ if( stream->playback.pcm )
+ ENSURE_( snd_pcm_drain( stream->playback.pcm ), paUnanticipatedHostError );
+ if( stream->capture.pcm && !stream->pcmsSynced )
+ ENSURE_( snd_pcm_drain( stream->capture.pcm ), paUnanticipatedHostError );
+ }
+
+end:
+ return result;
+error:
+ goto end;
+}
+
+/** Stop or abort stream.
+ *
+ * If a stream is in callback mode we will have to inspect wether the background thread has
+ * finished, or we will have to take it out. In either case we join the thread before
+ * returning. In blocking mode, we simply tell ALSA to stop abruptly (abort) or finish
+ * buffers (drain)
+ *
+ * Stream will be considered inactive (!PaAlsaStream::isActive) after a call to this function
+ */
+static PaError RealStop( PaAlsaStream *stream, int abort )
+{
+ PaError result = paNoError;
+
+ /* First deal with the callback thread, cancelling and/or joining
+ * it if necessary
+ */
+ if( stream->callbackMode )
+ {
+ PaError threadRes, watchdogRes;
+ stream->callbackAbort = abort;
+
+ if( !abort )
+ {
+ PA_DEBUG(( "Stopping callback\n" ));
+ stream->callbackStop = 1;
+ }
+ PA_ENSURE( KillCallbackThread( &stream->threading, !abort, &threadRes, &watchdogRes ) );
+ if( threadRes != paNoError )
+ PA_DEBUG(( "Callback thread returned: %d\n", threadRes ));
+ if( watchdogRes != paNoError )
+ PA_DEBUG(( "Watchdog thread returned: %d\n", watchdogRes ));
+
+ stream->callbackStop = 0; /* The deed is done */
+ stream->callback_finished = 0;
+ }
+ else
+ {
+ PA_ENSURE( AlsaStop( stream, abort ) );
+ }
+
+ stream->isActive = 0;
+
+end:
+ return result;
+
+error:
+ goto end;
+}
+
+static PaError StopStream( PaStream *s )
+{
+ return RealStop( (PaAlsaStream *) s, 0 );
+}
+
+static PaError AbortStream( PaStream *s )
+{
+ return RealStop( (PaAlsaStream * ) s, 1 );
+}
+
+/** The stream is considered stopped before StartStream, or AFTER a call to Abort/StopStream (callback
+ * returning !paContinue is not considered)
+ *
+ */
+static PaError IsStreamStopped( PaStream *s )
+{
+ PaAlsaStream *stream = (PaAlsaStream *)s;
+
+ /* callback_finished indicates we need to join callback thread (ie. in Abort/StopStream) */
+ return !IsStreamActive( s ) && !stream->callback_finished;
+}
+
+static PaError IsStreamActive( PaStream *s )
+{
+ PaAlsaStream *stream = (PaAlsaStream*)s;
+ return stream->isActive;
+}
+
+static PaTime GetStreamTime( PaStream *s )
+{
+ PaAlsaStream *stream = (PaAlsaStream*)s;
+
+ snd_timestamp_t timestamp;
+ snd_pcm_status_t *status;
+ snd_pcm_status_alloca( &status );
+
+ /* TODO: what if we have both? does it really matter? */
+
+ /* TODO: if running in callback mode, this will mean
+ * libasound routines are being called from multiple threads.
+ * need to verify that libasound is thread-safe. */
+
+ if( stream->capture.pcm )
+ {
+ snd_pcm_status( stream->capture.pcm, status );
+ }
+ else if( stream->playback.pcm )
+ {
+ snd_pcm_status( stream->playback.pcm, status );
+ }
+
+ snd_pcm_status_get_tstamp( status, &timestamp );
+ return timestamp.tv_sec + (PaTime)timestamp.tv_usec / 1000000.0;
+}
+
+static double GetStreamCpuLoad( PaStream* s )
+{
+ PaAlsaStream *stream = (PaAlsaStream*)s;
+
+ return PaUtil_GetCpuLoad( &stream->cpuLoadMeasurer );
+}
+
+static int SetApproximateSampleRate( snd_pcm_t *pcm, snd_pcm_hw_params_t *hwParams, double sampleRate )
+{
+ unsigned long approx = (unsigned long) sampleRate;
+ int dir = 0;
+ double fraction = sampleRate - approx;
+
+ assert( pcm && hwParams );
+
+ if( fraction > 0.0 )
+ {
+ if( fraction > 0.5 )
+ {
+ ++approx;
+ dir = -1;
+ }
+ else
+ dir = 1;
+ }
+
+ return snd_pcm_hw_params_set_rate( pcm, hwParams, approx, dir );
+}
+
+/* Return exact sample rate in param sampleRate */
+static int GetExactSampleRate( snd_pcm_hw_params_t *hwParams, double *sampleRate )
+{
+ unsigned int num, den;
+ int err;
+
+ assert( hwParams );
+
+ err = snd_pcm_hw_params_get_rate_numden( hwParams, &num, &den );
+ *sampleRate = (double) num / den;
+
+ return err;
+}
+
+/* Utility functions for blocking/callback interfaces */
+
+/* Atomic restart of stream (we don't want the intermediate state visible) */
+static PaError AlsaRestart( PaAlsaStream *stream )
+{
+ PaError result = paNoError;
+
+ ASSERT_CALL_( pthread_mutex_lock( &stream->stateMtx ), 0 );
+ PA_ENSURE( AlsaStop( stream, 0 ) );
+ PA_ENSURE( AlsaStart( stream, 0 ) );
+
+ PA_DEBUG(( "%s: Restarted audio\n", __FUNCTION__ ));
+
+error:
+ ASSERT_CALL_( pthread_mutex_unlock( &stream->stateMtx ), 0 );
+ return result;
+}
+
+/** Recover from xrun state.
+ *
+ */
+static PaError PaAlsaStream_HandleXrun( PaAlsaStream *self )
+{
+ PaError result = paNoError;
+ snd_pcm_status_t *st;
+ PaTime now = PaUtil_GetTime();
+ snd_timestamp_t t;
+
+ snd_pcm_status_alloca( &st );
+
+ if( self->playback.pcm )
+ {
+ snd_pcm_status( self->playback.pcm, st );
+ if( snd_pcm_status_get_state( st ) == SND_PCM_STATE_XRUN )
+ {
+ snd_pcm_status_get_trigger_tstamp( st, &t );
+ self->underrun = now * 1000 - ((PaTime) t.tv_sec * 1000 + (PaTime) t.tv_usec / 1000);
+ }
+ }
+ if( self->capture.pcm )
+ {
+ snd_pcm_status( self->capture.pcm, st );
+ if( snd_pcm_status_get_state( st ) == SND_PCM_STATE_XRUN )
+ {
+ snd_pcm_status_get_trigger_tstamp( st, &t );
+ self->overrun = now * 1000 - ((PaTime) t.tv_sec * 1000 + (PaTime) t.tv_usec / 1000);
+ }
+ }
+
+ PA_ENSURE( AlsaRestart( self ) );
+
+end:
+ return result;
+error:
+ goto end;
+}
+
+/** Decide if we should continue polling for specified direction, eventually adjust the poll timeout.
+ *
+ */
+static PaError ContinuePoll( const PaAlsaStream *stream, StreamDirection streamDir, int *pollTimeout, int *continuePoll )
+{
+ PaError result = paNoError;
+ snd_pcm_sframes_t delay, margin;
+ int err;
+ const PaAlsaStreamComponent *component = NULL, *otherComponent = NULL;
+
+ *continuePoll = 1;
+
+ if( StreamDirection_In == streamDir )
+ {
+ component = &stream->capture;
+ otherComponent = &stream->playback;
+ }
+ else
+ {
+ component = &stream->playback;
+ otherComponent = &stream->capture;
+ }
+
+ /* ALSA docs say that negative delay should indicate xrun, but in my experience snd_pcm_delay returns -EPIPE */
+ if( (err = snd_pcm_delay( otherComponent->pcm, &delay )) < 0 )
+ {
+ if( err == -EPIPE )
+ {
+ /* Xrun */
+ *continuePoll = 0;
+ goto error;
+ }
+
+ ENSURE_( err, paUnanticipatedHostError );
+ }
+
+ if( StreamDirection_Out == streamDir )
+ {
+ /* Number of eligible frames before capture overrun */
+ delay = otherComponent->bufferSize - delay;
+ }
+ margin = delay - otherComponent->framesPerBuffer / 2;
+
+ if( margin < 0 )
+ {
+ PA_DEBUG(( "%s: Stopping poll for %s\n", __FUNCTION__, StreamDirection_In == streamDir ? "capture" : "playback" ));
+ *continuePoll = 0;
+ }
+ else if( margin < otherComponent->framesPerBuffer )
+ {
+ *pollTimeout = CalculatePollTimeout( stream, margin );
+ PA_DEBUG(( "%s: Trying to poll again for %s frames, pollTimeout: %d\n",
+ __FUNCTION__, StreamDirection_In == streamDir ? "capture" : "playback", *pollTimeout ));
+ }
+
+error:
+ return result;
+}
+
+/* Callback interface */
+
+static void OnExit( void *data )
+{
+ PaAlsaStream *stream = (PaAlsaStream *) data;
+
+ assert( data );
+
+ PaUtil_ResetCpuLoadMeasurer( &stream->cpuLoadMeasurer );
+
+ stream->callback_finished = 1; /* Let the outside world know stream was stopped in callback */
+ AlsaStop( stream, stream->callbackAbort );
+ stream->callbackAbort = 0; /* Clear state */
+
+ PA_DEBUG(( "OnExit: Stoppage\n" ));
+
+ /* Eventually notify user all buffers have played */
+ if( stream->streamRepresentation.streamFinishedCallback )
+ stream->streamRepresentation.streamFinishedCallback( stream->streamRepresentation.userData );
+ stream->isActive = 0;
+}
+
+static void CalculateTimeInfo( PaAlsaStream *stream, PaStreamCallbackTimeInfo *timeInfo )
+{
+ snd_pcm_status_t *capture_status, *playback_status;
+ snd_timestamp_t capture_timestamp, playback_timestamp;
+ PaTime capture_time = 0., playback_time = 0.;
+
+ snd_pcm_status_alloca( &capture_status );
+ snd_pcm_status_alloca( &playback_status );
+
+ if( stream->capture.pcm )
+ {
+ snd_pcm_sframes_t capture_delay;
+
+ snd_pcm_status( stream->capture.pcm, capture_status );
+ snd_pcm_status_get_tstamp( capture_status, &capture_timestamp );
+
+ capture_time = capture_timestamp.tv_sec +
+ ((PaTime)capture_timestamp.tv_usec / 1000000.0);
+ timeInfo->currentTime = capture_time;
+
+ capture_delay = snd_pcm_status_get_delay( capture_status );
+ timeInfo->inputBufferAdcTime = timeInfo->currentTime -
+ (PaTime)capture_delay / stream->streamRepresentation.streamInfo.sampleRate;
+ }
+ if( stream->playback.pcm )
+ {
+ snd_pcm_sframes_t playback_delay;
+
+ snd_pcm_status( stream->playback.pcm, playback_status );
+ snd_pcm_status_get_tstamp( playback_status, &playback_timestamp );
+
+ playback_time = playback_timestamp.tv_sec +
+ ((PaTime)playback_timestamp.tv_usec / 1000000.0);
+
+ if( stream->capture.pcm ) /* Full duplex */
+ {
+ /* Hmm, we have both a playback and a capture timestamp.
+ * Hopefully they are the same... */
+ if( fabs( capture_time - playback_time ) > 0.01 )
+ PA_DEBUG(("Capture time and playback time differ by %f\n", fabs(capture_time-playback_time)));
+ }
+ else
+ timeInfo->currentTime = playback_time;
+
+ playback_delay = snd_pcm_status_get_delay( playback_status );
+ timeInfo->outputBufferDacTime = timeInfo->currentTime +
+ (PaTime)playback_delay / stream->streamRepresentation.streamInfo.sampleRate;
+ }
+}
+
+/** Called after buffer processing is finished.
+ *
+ * A number of mmapped frames is committed, it is possible that an xrun has occurred in the meantime.
+ *
+ * @param numFrames The number of frames that has been processed
+ * @param xrun Return whether an xrun has occurred
+ */
+static PaError PaAlsaStreamComponent_EndProcessing( PaAlsaStreamComponent *self, unsigned long numFrames, int *xrun )
+{
+ PaError result = paNoError;
+ int res;
+
+ /* @concern FullDuplex It is possible that only one direction is marked ready after polling, and processed
+ * afterwards
+ */
+ if( !self->ready )
+ goto end;
+
+ res = snd_pcm_mmap_commit( self->pcm, self->offset, numFrames );
+ if( res == -EPIPE || res == -ESTRPIPE )
+ {
+ *xrun = 1;
+ }
+ else
+ {
+ ENSURE_( res, paUnanticipatedHostError );
+ }
+
+end:
+error:
+ return result;
+}
+
+/* Extract buffer from channel area */
+static unsigned char *ExtractAddress( const snd_pcm_channel_area_t *area, snd_pcm_uframes_t offset )
+{
+ return (unsigned char *) area->addr + (area->first + offset * area->step) / 8;
+}
+
+/** Do necessary adaption between user and host channels.
+ *
+ @concern ChannelAdaption Adapting between user and host channels can involve silencing unused channels and
+ duplicating mono information if host outputs come in pairs.
+ */
+static PaError PaAlsaStreamComponent_DoChannelAdaption( PaAlsaStreamComponent *self, PaUtilBufferProcessor *bp, int numFrames )
+{
+ PaError result = paNoError;
+ unsigned char *p;
+ int i;
+ int unusedChans = self->numHostChannels - self->numUserChannels;
+ unsigned char *src, *dst;
+ int convertMono = (self->numHostChannels % 2) == 0 && (self->numUserChannels % 2) != 0;
+
+ assert( StreamDirection_Out == self->streamDir );
+
+ if( self->hostInterleaved )
+ {
+ int swidth = snd_pcm_format_size( self->nativeFormat, 1 );
+ unsigned char *buffer = ExtractAddress( self->channelAreas, self->offset );
+
+ /* Start after the last user channel */
+ p = buffer + self->numUserChannels * swidth;
+
+ if( convertMono )
+ {
+ /* Convert the last user channel into stereo pair */
+ src = buffer + (self->numUserChannels - 1) * swidth;
+ for( i = 0; i < numFrames; ++i )
+ {
+ dst = src + swidth;
+ memcpy( dst, src, swidth );
+ src += self->numHostChannels * swidth;
+ }
+
+ /* Don't touch the channel we just wrote to */
+ p += swidth;
+ --unusedChans;
+ }
+
+ if( unusedChans > 0 )
+ {
+ /* Silence unused output channels */
+ for( i = 0; i < numFrames; ++i )
+ {
+ memset( p, 0, swidth * unusedChans );
+ p += self->numHostChannels * swidth;
+ }
+ }
+ }
+ else
+ {
+ /* We extract the last user channel */
+ if( convertMono )
+ {
+ ENSURE_( snd_pcm_area_copy( self->channelAreas + self->numUserChannels, self->offset, self->channelAreas +
+ (self->numUserChannels - 1), self->offset, numFrames, self->nativeFormat ), paUnanticipatedHostError );
+ --unusedChans;
+ }
+ if( unusedChans > 0 )
+ {
+ snd_pcm_areas_silence( self->channelAreas + (self->numHostChannels - unusedChans), self->offset, unusedChans, numFrames,
+ self->nativeFormat );
+ }
+ }
+
+error:
+ return result;
+}
+
+static PaError PaAlsaStream_EndProcessing( PaAlsaStream *self, unsigned long numFrames, int *xrunOccurred )
+{
+ PaError result = paNoError;
+ int xrun = 0;
+
+ if( self->capture.pcm )
+ {
+ PA_ENSURE( PaAlsaStreamComponent_EndProcessing( &self->capture, numFrames, &xrun ) );
+ }
+ if( self->playback.pcm )
+ {
+ if( self->playback.numHostChannels > self->playback.numUserChannels )
+ PA_ENSURE( PaAlsaStreamComponent_DoChannelAdaption( &self->playback, &self->bufferProcessor, numFrames ) );
+ PA_ENSURE( PaAlsaStreamComponent_EndProcessing( &self->playback, numFrames, &xrun ) );
+ }
+
+error:
+ *xrunOccurred = xrun;
+ return result;
+}
+
+/** Update the number of available frames.
+ *
+ */
+static PaError PaAlsaStreamComponent_GetAvailableFrames( PaAlsaStreamComponent *self, unsigned long *numFrames, int *xrunOccurred )
+{
+ PaError result = paNoError;
+ snd_pcm_sframes_t framesAvail = snd_pcm_avail_update( self->pcm );
+ *xrunOccurred = 0;
+
+ if( -EPIPE == framesAvail )
+ {
+ *xrunOccurred = 1;
+ framesAvail = 0;
+ }
+ else
+ ENSURE_( framesAvail, paUnanticipatedHostError );
+
+ *numFrames = framesAvail;
+
+error:
+ return result;
+}
+
+/** Fill in pollfd objects.
+ */
+static PaError PaAlsaStreamComponent_BeginPolling( PaAlsaStreamComponent *self, struct pollfd *pfds )
+{
+ PaError result = paNoError;
+ int ret = snd_pcm_poll_descriptors( self->pcm, pfds, self->nfds );
+ assert( ret == self->nfds );
+
+ self->ready = 0;
+
+ return result;
+}
+
+/** Examine results from poll().
+ *
+ * @param pfds pollfds to inspect
+ * @param shouldPoll Should we continue to poll
+ * @param xrun Has an xrun occurred
+ */
+static PaError PaAlsaStreamComponent_EndPolling( PaAlsaStreamComponent *self, struct pollfd *pfds, int *shouldPoll, int *xrun )
+{
+ PaError result = paNoError;
+ unsigned short revents;
+
+ ENSURE_( snd_pcm_poll_descriptors_revents( self->pcm, pfds, self->nfds, &revents ), paUnanticipatedHostError );
+ if( revents != 0 )
+ {
+ if( revents & POLLERR )
+ {
+ *xrun = 1;
+ }
+ else
+ self->ready = 1;
+
+ *shouldPoll = 0;
+ }
+
+error:
+ return result;
+}
+
+/** Return the number of available frames for this stream.
+ *
+ * @concern FullDuplex The minimum available for the two directions is calculated, it might be desirable to ignore
+ * one direction however (not marked ready from poll), so this is controlled by queryCapture and queryPlayback.
+ *
+ * @param queryCapture Check available for capture
+ * @param queryPlayback Check available for playback
+ * @param available The returned number of frames
+ * @param xrunOccurred Return whether an xrun has occurred
+ */
+static PaError PaAlsaStream_GetAvailableFrames( PaAlsaStream *self, int queryCapture, int queryPlayback, unsigned long
+ *available, int *xrunOccurred )
+{
+ PaError result = paNoError;
+ unsigned long captureFrames, playbackFrames;
+ *xrunOccurred = 0;
+
+ assert( queryCapture || queryPlayback );
+
+ if( queryCapture )
+ {
+ assert( self->capture.pcm );
+ PA_ENSURE( PaAlsaStreamComponent_GetAvailableFrames( &self->capture, &captureFrames, xrunOccurred ) );
+ if( *xrunOccurred )
+ goto end;
+ }
+ if( queryPlayback )
+ {
+ assert( self->playback.pcm );
+ PA_ENSURE( PaAlsaStreamComponent_GetAvailableFrames( &self->playback, &playbackFrames, xrunOccurred ) );
+ if( *xrunOccurred )
+ goto end;
+ }
+
+ if( queryCapture && queryPlayback )
+ {
+ *available = PA_MIN( captureFrames, playbackFrames );
+ }
+ else if( queryCapture )
+ {
+ *available = captureFrames;
+ }
+ else
+ {
+ *available = playbackFrames;
+ }
+
+end:
+error:
+ return result;
+}
+
+/** Wait for and report available buffer space from ALSA.
+ *
+ * Unless ALSA reports a minimum of frames available for I/O, we poll the ALSA filedescriptors for more.
+ * Both of these operations can uncover xrun conditions.
+ *
+ * @concern Xruns Both polling and querying available frames can report an xrun condition.
+ *
+ * @param framesAvail Return the number of available frames
+ * @param xrunOccurred Return whether an xrun has occurred
+ */
+static PaError PaAlsaStream_WaitForFrames( PaAlsaStream *self, unsigned long *framesAvail, int *xrunOccurred )
+{
+ PaError result = paNoError;
+ int pollPlayback = self->playback.pcm != NULL, pollCapture = self->capture.pcm != NULL;
+ int pollTimeout = self->pollTimeout;
+ int xrun = 0;
+
+ assert( self );
+ assert( framesAvail );
+
+ if( !self->callbackMode )
+ {
+ /* In blocking mode we will only wait if necessary */
+ PA_ENSURE( PaAlsaStream_GetAvailableFrames( self, self->capture.pcm != NULL, self->playback.pcm != NULL,
+ framesAvail, &xrun ) );
+ if( xrun )
+ {
+ goto end;
+ }
+
+ if( *framesAvail > 0 )
+ {
+ /* Mark pcms ready from poll */
+ if( self->capture.pcm )
+ self->capture.ready = 1;
+ if( self->playback.pcm )
+ self->playback.ready = 1;
+
+ goto end;
+ }
+ }
+
+ while( pollPlayback || pollCapture )
+ {
+ int totalFds = 0;
+ struct pollfd *capturePfds = NULL, *playbackPfds = NULL;
+
+ pthread_testcancel();
+
+ if( pollCapture )
+ {
+ capturePfds = self->pfds;
+ PA_ENSURE( PaAlsaStreamComponent_BeginPolling( &self->capture, capturePfds ) );
+ totalFds += self->capture.nfds;
+ }
+ if( pollPlayback )
+ {
+ playbackPfds = self->pfds + (self->capture.pcm ? self->capture.nfds : 0);
+ PA_ENSURE( PaAlsaStreamComponent_BeginPolling( &self->playback, playbackPfds ) );
+ totalFds += self->playback.nfds;
+ }
+
+ if( poll( self->pfds, totalFds, pollTimeout ) < 0 )
+ {
+ /* XXX: Depend on preprocessor condition? */
+ if( errno == EINTR ) { /* gdb */
+ continue;
+ }
+
+ /* TODO: Add macro for checking system calls */
+ PA_ENSURE( paInternalError );
+ }
+
+ /* check the return status of our pfds */
+ if( pollCapture )
+ {
+ PA_ENSURE( PaAlsaStreamComponent_EndPolling( &self->capture, capturePfds, &pollCapture, &xrun ) );
+ }
+ if( pollPlayback )
+ {
+ PA_ENSURE( PaAlsaStreamComponent_EndPolling( &self->playback, playbackPfds, &pollPlayback, &xrun ) );
+ }
+ if( xrun )
+ {
+ break;
+ }
+
+ /* @concern FullDuplex If only one of two pcms is ready we may want to compromise between the two.
+ * If there is less than half a period's worth of samples left of frames in the other pcm's buffer we will
+ * stop polling.
+ */
+ if( self->capture.pcm && self->playback.pcm )
+ {
+ if( pollCapture && !pollPlayback )
+ {
+ PA_ENSURE( ContinuePoll( self, StreamDirection_In, &pollTimeout, &pollCapture ) );
+ }
+ else if( pollPlayback && !pollCapture )
+ {
+ PA_ENSURE( ContinuePoll( self, StreamDirection_Out, &pollTimeout, &pollPlayback ) );
+ }
+ }
+ }
+
+ if( !xrun )
+ {
+ /* Get the number of available frames for the pcms that are marked ready.
+ * @concern FullDuplex If only one direction is marked ready (from poll), the number of frames available for
+ * the other direction is returned. This under the assumption that input is dropped earlier if paNeverDropInput
+ * is not specified.
+ */
+ int captureReady = self->capture.pcm ? self->capture.ready : 0,
+ playbackReady = self->playback.pcm ? self->playback.ready : 0;
+ PA_ENSURE( PaAlsaStream_GetAvailableFrames( self, captureReady, playbackReady, framesAvail, &xrun ) );
+
+ if( self->capture.pcm && self->playback.pcm )
+ {
+ if( !self->playback.ready && !self->neverDropInput )
+ {
+ /* TODO: Drop input */
+ }
+ }
+ }
+
+end:
+error:
+ if( xrun )
+ {
+ /* Recover from the xrun state */
+ PA_ENSURE( PaAlsaStream_HandleXrun( self ) );
+ *framesAvail = 0;
+ }
+ *xrunOccurred = xrun;
+
+ return result;
+}
+
+/** Register per-channel ALSA buffer information with buffer processor.
+ *
+ * Mmapped buffer space is acquired from ALSA, and registered with the buffer processor. Differences between the
+ * number of host and user channels is taken into account.
+ *
+ * @param numFrames On entrance the number of requested frames, on exit the number of contiguously accessible frames.
+ */
+static PaError PaAlsaStreamComponent_RegisterChannels( PaAlsaStreamComponent *self, PaUtilBufferProcessor *bp,
+ unsigned long *numFrames, int *xrun )
+{
+ PaError result = paNoError;
+ const snd_pcm_channel_area_t *areas, *area;
+ void (*setChannel)(PaUtilBufferProcessor *, unsigned int, void *, unsigned int) =
+ StreamDirection_In == self->streamDir ? PaUtil_SetInputChannel : PaUtil_SetOutputChannel;
+ unsigned char *buffer, *p;
+ int i;
+ unsigned long framesAvail;
+
+ /* This _must_ be called before mmap_begin */
+ PA_ENSURE( PaAlsaStreamComponent_GetAvailableFrames( self, &framesAvail, xrun ) );
+ if( *xrun )
+ {
+ *numFrames = 0;
+ goto end;
+ }
+
+ ENSURE_( snd_pcm_mmap_begin( self->pcm, &areas, &self->offset, numFrames ), paUnanticipatedHostError );
+
+ if( self->hostInterleaved )
+ {
+ int swidth = snd_pcm_format_size( self->nativeFormat, 1 );
+
+ p = buffer = ExtractAddress( areas, self->offset );
+ for( i = 0; i < self->numUserChannels; ++i )
+ {
+ /* We're setting the channels up to userChannels, but the stride will be hostChannels samples */
+ setChannel( bp, i, p, self->numHostChannels );
+ p += swidth;
+ }
+ }
+ else
+ {
+ for( i = 0; i < self->numUserChannels; ++i )
+ {
+ area = areas + i;
+ buffer = ExtractAddress( area, self->offset );
+ setChannel( bp, i, buffer, 1 );
+ }
+ }
+
+ /* @concern ChannelAdaption Buffer address is recorded so we can do some channel adaption later */
+ self->channelAreas = (snd_pcm_channel_area_t *)areas;
+
+end:
+error:
+ return result;
+}
+
+/** Initiate buffer processing.
+ *
+ * ALSA buffers are registered with the PA buffer processor and the buffer size (in frames) set.
+ *
+ * @concern FullDuplex If both directions are being processed, the minimum amount of frames for the two directions is
+ * calculated.
+ *
+ * @param numFrames On entrance the number of available frames, on exit the number of received frames
+ * @param xrunOccurred Return whether an xrun has occurred
+ */
+static PaError PaAlsaStream_SetUpBuffers( PaAlsaStream *self, unsigned long *numFrames, int *xrunOccurred )
+{
+ PaError result = paNoError;
+ unsigned long captureFrames = ULONG_MAX, playbackFrames = ULONG_MAX, commonFrames = 0;
+ int xrun = 0;
+
+ /* Extract per-channel ALSA buffer pointers and register them with the buffer processor.
+ * It is possible that a direction is not marked ready however, because it is out of sync with the other.
+ */
+ if( self->capture.pcm && self->capture.ready )
+ {
+ captureFrames = *numFrames;
+ PA_ENSURE( PaAlsaStreamComponent_RegisterChannels( &self->capture, &self->bufferProcessor, &captureFrames,
+ &xrun ) );
+ }
+ if( self->playback.pcm && self->playback.ready )
+ {
+ playbackFrames = *numFrames;
+ PA_ENSURE( PaAlsaStreamComponent_RegisterChannels( &self->playback, &self->bufferProcessor, &playbackFrames,
+ &xrun ) );
+ }
+ if( xrun )
+ {
+ /* Nothing more to do */
+ assert( 0 == commonFrames );
+ goto end;
+ }
+
+ commonFrames = PA_MIN( captureFrames, playbackFrames );
+ assert( commonFrames <= *numFrames );
+
+ /* Inform PortAudio of the number of frames we got.
+ * @concern FullDuplex We might be experiencing underflow in either end; if its an input underflow, we go on
+ * with output. If its output underflow however, depending on the paNeverDropInput flag, we may want to simply
+ * discard the excess input or call the callback with paOutputOverflow flagged.
+ */
+ if( self->capture.pcm )
+ {
+ if( self->capture.ready )
+ {
+ PaUtil_SetInputFrameCount( &self->bufferProcessor, commonFrames );
+ }
+ else
+ {
+ /* We have input underflow */
+ PaUtil_SetNoInput( &self->bufferProcessor );
+ }
+ }
+ if( self->playback.pcm )
+ {
+ if( self->playback.ready )
+ {
+ PaUtil_SetOutputFrameCount( &self->bufferProcessor, commonFrames );
+ }
+ else
+ {
+ /* We have output underflow, but keeping input data (paNeverDropInput) */
+ /* assert( self->neverDropInput ); */
+ PaUtil_SetNoOutput( &self->bufferProcessor );
+ }
+ }
+
+end:
+ *numFrames = commonFrames;
+error:
+ if( xrun )
+ {
+ PA_ENSURE( PaAlsaStream_HandleXrun( self ) );
+ *numFrames = 0;
+ }
+ *xrunOccurred = xrun;
+
+ return result;
+}
+
+/** Callback thread's function.
+ *
+ * Roughly, the workflow can be described in the following way: The number of available frames that can be processed
+ * directly is obtained from ALSA, we then request as much directly accessible memory as possible within this amount
+ * from ALSA. The buffer memory is registered with the PA buffer processor and processing is carried out with
+ * PaUtil_EndBufferProcessing. Finally, the number of processed frames is reported to ALSA. The processing can
+ * happen in several iterations untill we have consumed the known number of available frames (or an xrun is detected).
+ */
+static void *CallbackThreadFunc( void *userData )
+{
+ PaError result = paNoError, *pres = NULL;
+ PaAlsaStream *stream = (PaAlsaStream*) userData;
+ PaStreamCallbackTimeInfo timeInfo = {0, 0, 0};
+ snd_pcm_sframes_t startThreshold = 0;
+ int callbackResult = paContinue;
+ PaStreamCallbackFlags cbFlags = 0; /* We might want to keep state across iterations */
+ int streamStarted = 0;
+
+ assert( stream );
+
+ callbackThread_ = pthread_self();
+ /* Execute OnExit when exiting */
+ pthread_cleanup_push( &OnExit, stream );
+
+ /* Not implemented */
+ assert( !stream->primeBuffers );
+
+ /* @concern StreamStart If the output is being primed the output pcm needs to be prepared, otherwise the
+ * stream is started immediately. The latter involves signaling the waiting main thread.
+ */
+ if( stream->primeBuffers )
+ {
+ snd_pcm_sframes_t avail;
+
+ if( stream->playback.pcm )
+ ENSURE_( snd_pcm_prepare( stream->playback.pcm ), paUnanticipatedHostError );
+ if( stream->capture.pcm && !stream->pcmsSynced )
+ ENSURE_( snd_pcm_prepare( stream->capture.pcm ), paUnanticipatedHostError );
+
+ /* We can't be certain that the whole ring buffer is available for priming, but there should be
+ * at least one period */
+ avail = snd_pcm_avail_update( stream->playback.pcm );
+ startThreshold = avail - (avail % stream->playback.framesPerBuffer);
+ assert( startThreshold >= stream->playback.framesPerBuffer );
+ }
+ else
+ {
+ ASSERT_CALL_( pthread_mutex_lock( &stream->startMtx ), 0 );
+ PA_ENSURE( AlsaStart( stream, 0 ) ); /* Buffer will be zeroed */
+ ASSERT_CALL_( pthread_cond_signal( &stream->startCond ), 0 );
+ ASSERT_CALL_( pthread_mutex_unlock( &stream->startMtx ), 0 );
+
+ streamStarted = 1;
+ }
+
+ while( 1 )
+ {
+ unsigned long framesAvail, framesGot;
+ int xrun = 0;
+
+ pthread_testcancel();
+
+ /* @concern StreamStop if the main thread has requested a stop and the stream has not been effectively
+ * stopped we signal this condition by modifying callbackResult (we'll want to flush buffered output).
+ */
+ if( stream->callbackStop && paContinue == callbackResult )
+ {
+ PA_DEBUG(( "Setting callbackResult to paComplete\n" ));
+ callbackResult = paComplete;
+ }
+
+ if( paContinue != callbackResult )
+ {
+ stream->callbackAbort = (paAbort == callbackResult);
+ if( stream->callbackAbort ||
+ /** @concern BlockAdaption Go on if adaption buffers are empty */
+ PaUtil_IsBufferProcessorOutputEmpty( &stream->bufferProcessor ) )
+ goto end;
+
+ PA_DEBUG(( "%s: Flushing buffer processor\n", __FUNCTION__ ));
+ /* There is still buffered output that needs to be processed */
+ }
+
+ /* Wait for data to become available, this comes down to polling the ALSA file descriptors untill we have
+ * a number of available frames.
+ */
+ PA_ENSURE( PaAlsaStream_WaitForFrames( stream, &framesAvail, &xrun ) );
+ if( xrun )
+ {
+ assert( 0 == framesAvail );
+ continue;
+
+ /* XXX: Report xruns to the user? A situation is conceivable where the callback is never invoked due
+ * to constant xruns, it might be desirable to notify the user of this.
+ */
+ }
+
+ /* Consume buffer space. Once we have a number of frames available for consumption we must retrieve the
+ * mmapped buffers from ALSA, this is contiguously accessible memory however, so we may receive smaller
+ * portions at a time than is available as a whole. Therefore we should be prepared to process several
+ * chunks successively. The buffers are passed to the PA buffer processor.
+ */
+ while( framesAvail > 0 )
+ {
+ xrun = 0;
+
+ pthread_testcancel();
+
+ /** @concern Xruns Under/overflows are to be reported to the callback */
+ if( stream->underrun > 0.0 )
+ {
+ cbFlags |= paOutputUnderflow;
+ stream->underrun = 0.0;
+ }
+ if( stream->overrun > 0.0 )
+ {
+ cbFlags |= paInputOverflow;
+ stream->overrun = 0.0;
+ }
+ if( stream->capture.pcm && stream->playback.pcm )
+ {
+ /** @concern FullDuplex It's possible that only one direction is being processed to avoid an
+ * under- or overflow, this should be reported correspondingly */
+ if( !stream->capture.ready )
+ {
+ cbFlags |= paInputUnderflow;
+ PA_DEBUG(( "%s: Input underflow\n", __FUNCTION__ ));
+ }
+ else if( !stream->playback.ready )
+ {
+ cbFlags |= paOutputOverflow;
+ PA_DEBUG(( "%s: Output overflow\n", __FUNCTION__ ));
+ }
+ }
+
+ CallbackUpdate( &stream->threading );
+ CalculateTimeInfo( stream, &timeInfo );
+ PaUtil_BeginBufferProcessing( &stream->bufferProcessor, &timeInfo, cbFlags );
+ cbFlags = 0;
+
+ /* CPU load measurement should include processing activivity external to the stream callback */
+ PaUtil_BeginCpuLoadMeasurement( &stream->cpuLoadMeasurer );
+
+ framesGot = framesAvail;
+ PA_ENSURE( PaAlsaStream_SetUpBuffers( stream, &framesGot, &xrun ) );
+ framesAvail -= framesGot;
+
+ if( framesGot > 0 )
+ {
+ assert( !xrun );
+
+ PaUtil_EndBufferProcessing( &stream->bufferProcessor, &callbackResult );
+ PA_ENSURE( PaAlsaStream_EndProcessing( stream, framesGot, &xrun ) );
+ }
+ PaUtil_EndCpuLoadMeasurement( &stream->cpuLoadMeasurer, framesGot );
+
+ if( framesGot == 0 )
+ {
+ if( !xrun )
+ PA_DEBUG(( "%s: Received less frames than reported from ALSA!\n", __FUNCTION__ ));
+
+ /* Go back to polling for more frames */
+ break;
+
+ }
+
+ if( paContinue != callbackResult )
+ break;
+ }
+ }
+
+ /* Match pthread_cleanup_push */
+ pthread_cleanup_pop( 1 );
+
+end:
+ pthread_exit( pres );
+
+error:
+ /* Pass on error code */
+ pres = malloc( sizeof (PaError) );
+ *pres = result;
+
+ goto end;
+}
+
+/* Blocking interface */
+
+static PaError ReadStream( PaStream* s, void *buffer, unsigned long frames )
+{
+ PaError result = paNoError;
+ PaAlsaStream *stream = (PaAlsaStream*)s;
+ unsigned long framesGot, framesAvail;
+ void *userBuffer;
+ snd_pcm_t *save = stream->playback.pcm;
+
+ assert( stream );
+
+ PA_UNLESS( stream->capture.pcm, paCanNotReadFromAnOutputOnlyStream );
+
+ /* Disregard playback */
+ stream->playback.pcm = NULL;
+
+ if( stream->overrun > 0. )
+ {
+ result = paInputOverflowed;
+ stream->overrun = 0.0;
+ }
+
+ if( stream->capture.userInterleaved )
+ userBuffer = buffer;
+ else
+ {
+ /* Copy channels into local array */
+ userBuffer = stream->capture.userBuffers;
+ memcpy( userBuffer, buffer, sizeof (void *) * stream->capture.numUserChannels );
+ }
+
+ /* Start stream if in prepared state */
+ if( snd_pcm_state( stream->capture.pcm ) == SND_PCM_STATE_PREPARED )
+ {
+ ENSURE_( snd_pcm_start( stream->capture.pcm ), paUnanticipatedHostError );
+ }
+
+ while( frames > 0 )
+ {
+ int xrun = 0;
+ PA_ENSURE( PaAlsaStream_WaitForFrames( stream, &framesAvail, &xrun ) );
+ framesGot = PA_MIN( framesAvail, frames );
+
+ PA_ENSURE( PaAlsaStream_SetUpBuffers( stream, &framesGot, &xrun ) );
+ if( framesGot > 0 )
+ {
+ framesGot = PaUtil_CopyInput( &stream->bufferProcessor, &userBuffer, framesGot );
+ PA_ENSURE( PaAlsaStream_EndProcessing( stream, framesGot, &xrun ) );
+ frames -= framesGot;
+ }
+ }
+
+end:
+ stream->playback.pcm = save;
+ return result;
+error:
+ goto end;
+}
+
+static PaError WriteStream( PaStream* s, const void *buffer, unsigned long frames )
+{
+ PaError result = paNoError;
+ signed long err;
+ PaAlsaStream *stream = (PaAlsaStream*)s;
+ snd_pcm_uframes_t framesGot, framesAvail;
+ const void *userBuffer;
+ snd_pcm_t *save = stream->capture.pcm;
+
+ assert( stream );
+
+ PA_UNLESS( stream->playback.pcm, paCanNotWriteToAnInputOnlyStream );
+
+ /* Disregard capture */
+ stream->capture.pcm = NULL;
+
+ if( stream->underrun > 0. )
+ {
+ result = paOutputUnderflowed;
+ stream->underrun = 0.0;
+ }
+
+ if( stream->playback.userInterleaved )
+ userBuffer = buffer;
+ else /* Copy channels into local array */
+ {
+ userBuffer = stream->playback.userBuffers;
+ memcpy( (void *)userBuffer, buffer, sizeof (void *) * stream->playback.numUserChannels );
+ }
+
+ while( frames > 0 )
+ {
+ int xrun = 0;
+ snd_pcm_uframes_t hwAvail;
+
+ PA_ENSURE( PaAlsaStream_WaitForFrames( stream, &framesAvail, &xrun ) );
+ framesGot = PA_MIN( framesAvail, frames );
+
+ PA_ENSURE( PaAlsaStream_SetUpBuffers( stream, &framesGot, &xrun ) );
+ if( framesGot > 0 )
+ {
+ framesGot = PaUtil_CopyOutput( &stream->bufferProcessor, &userBuffer, framesGot );
+ PA_ENSURE( PaAlsaStream_EndProcessing( stream, framesGot, &xrun ) );
+ frames -= framesGot;
+ }
+
+ /* Frames residing in buffer */
+ PA_ENSURE( err = GetStreamWriteAvailable( stream ) );
+ framesAvail = err;
+ hwAvail = stream->playback.bufferSize - framesAvail;
+
+ /* Start stream after one period of samples worth */
+ if( snd_pcm_state( stream->playback.pcm ) == SND_PCM_STATE_PREPARED &&
+ hwAvail >= stream->playback.framesPerBuffer )
+ {
+ ENSURE_( snd_pcm_start( stream->playback.pcm ), paUnanticipatedHostError );
+ }
+ }
+
+end:
+ stream->capture.pcm = save;
+ return result;
+error:
+ goto end;
+}
+
+/* Return frames available for reading. In the event of an overflow, the capture pcm will be restarted */
+static signed long GetStreamReadAvailable( PaStream* s )
+{
+ PaError result = paNoError;
+ PaAlsaStream *stream = (PaAlsaStream*)s;
+ unsigned long avail;
+ int xrun;
+
+ PA_ENSURE( PaAlsaStreamComponent_GetAvailableFrames( &stream->capture, &avail, &xrun ) );
+ if( xrun )
+ {
+ PA_ENSURE( PaAlsaStream_HandleXrun( stream ) );
+ PA_ENSURE( PaAlsaStreamComponent_GetAvailableFrames( &stream->capture, &avail, &xrun ) );
+ if( xrun )
+ PA_ENSURE( paInputOverflowed );
+ }
+
+ return (signed long)avail;
+
+error:
+ return result;
+}
+
+static signed long GetStreamWriteAvailable( PaStream* s )
+{
+ PaError result = paNoError;
+ PaAlsaStream *stream = (PaAlsaStream*)s;
+ unsigned long avail;
+ int xrun;
+
+ PA_ENSURE( PaAlsaStreamComponent_GetAvailableFrames( &stream->playback, &avail, &xrun ) );
+ if( xrun )
+ {
+ snd_pcm_sframes_t savail;
+
+ PA_ENSURE( PaAlsaStream_HandleXrun( stream ) );
+ savail = snd_pcm_avail_update( stream->playback.pcm );
+
+ /* savail should not contain -EPIPE now, since PaAlsaStream_HandleXrun will only prepare the pcm */
+ ENSURE_( savail, paUnanticipatedHostError );
+
+ avail = (unsigned long) savail;
+ }
+
+ return (signed long)avail;
+
+error:
+ return result;
+}
+
+/* Extensions */
+
+/* Initialize host api specific structure */
+void PaAlsa_InitializeStreamInfo( PaAlsaStreamInfo *info )
+{
+ info->size = sizeof (PaAlsaStreamInfo);
+ info->hostApiType = paALSA;
+ info->version = 1;
+ info->deviceString = NULL;
+}
+
+void PaAlsa_EnableRealtimeScheduling( PaStream *s, int enable )
+{
+ PaAlsaStream *stream = (PaAlsaStream *) s;
+ stream->threading.rtSched = enable;
+}
+
+void PaAlsa_EnableWatchdog( PaStream *s, int enable )
+{
+ PaAlsaStream *stream = (PaAlsaStream *) s;
+ stream->threading.useWatchdog = enable;
+}
diff --git a/pjmedia/src/pjmedia/portaudio/pa_linux_alsa.h b/pjmedia/src/pjmedia/portaudio/pa_linux_alsa.h
index e6f44b16..a8d811bb 100644
--- a/pjmedia/src/pjmedia/portaudio/pa_linux_alsa.h
+++ b/pjmedia/src/pjmedia/portaudio/pa_linux_alsa.h
@@ -1,64 +1,85 @@
-#ifndef PA_LINUX_ALSA_H
-#define PA_LINUX_ALSA_H
-
-/*
- * $Id: pa_linux_alsa.h,v 1.1.2.12 2004/09/25 14:15:25 aknudsen Exp $
- * PortAudio Portable Real-Time Audio Library
- * ALSA-specific extensions
- *
- * Copyright (c) 1999-2000 Ross Bencina and Phil Burk
- *
- * Permission is hereby granted, free of charge, to any person obtaining
- * a copy of this software and associated documentation files
- * (the "Software"), to deal in the Software without restriction,
- * including without limitation the rights to use, copy, modify, merge,
- * publish, distribute, sublicense, and/or sell copies of the Software,
- * and to permit persons to whom the Software is furnished to do so,
- * subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be
- * included in all copies or substantial portions of the Software.
- *
- * Any person wishing to distribute modifications to the Software is
- * requested to send the modifications to the original developer so that
- * they can be incorporated into the canonical version.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
- * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
- * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
- * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
- * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
- *
- */
-
-/** @file
- * ALSA-specific PortAudio API extension header file.
- */
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-typedef struct PaAlsaStreamInfo
-{
- unsigned long size;
- PaHostApiTypeId hostApiType;
- unsigned long version;
-
- const char *deviceString;
-}
-PaAlsaStreamInfo;
-
-void PaAlsa_InitializeStreamInfo( PaAlsaStreamInfo *info );
-
-void PaAlsa_EnableRealtimeScheduling( PaStream *s, int enable );
-
-void PaAlsa_EnableWatchdog( PaStream *s, int enable );
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif
+/*
+ * PJMEDIA - Multimedia over IP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef PA_LINUX_ALSA_H
+#define PA_LINUX_ALSA_H
+
+/*
+ * $Id: pa_linux_alsa.h,v 1.1.2.12 2004/09/25 14:15:25 aknudsen Exp $
+ * PortAudio Portable Real-Time Audio Library
+ * ALSA-specific extensions
+ *
+ * Copyright (c) 1999-2000 Ross Bencina and Phil Burk
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * Any person wishing to distribute modifications to the Software is
+ * requested to send the modifications to the original developer so that
+ * they can be incorporated into the canonical version.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+ * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+/** @file
+ * ALSA-specific PortAudio API extension header file.
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct PaAlsaStreamInfo
+{
+ unsigned long size;
+ PaHostApiTypeId hostApiType;
+ unsigned long version;
+
+ const char *deviceString;
+}
+PaAlsaStreamInfo;
+
+void PaAlsa_InitializeStreamInfo( PaAlsaStreamInfo *info );
+
+void PaAlsa_EnableRealtimeScheduling( PaStream *s, int enable );
+
+void PaAlsa_EnableWatchdog( PaStream *s, int enable );
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/pjmedia/src/pjmedia/portaudio/pa_process.c b/pjmedia/src/pjmedia/portaudio/pa_process.c
index cf711d41..041593b9 100644
--- a/pjmedia/src/pjmedia/portaudio/pa_process.c
+++ b/pjmedia/src/pjmedia/portaudio/pa_process.c
@@ -1,1756 +1,1777 @@
-/*
- * $Id: pa_process.c,v 1.1.2.48 2004/12/13 09:48:43 rossbencina Exp $
- * Portable Audio I/O Library
- * streamCallback <-> host buffer processing adapter
- *
- * Based on the Open Source API proposed by Ross Bencina
- * Copyright (c) 1999-2002 Ross Bencina, Phil Burk
- *
- * Permission is hereby granted, free of charge, to any person obtaining
- * a copy of this software and associated documentation files
- * (the "Software"), to deal in the Software without restriction,
- * including without limitation the rights to use, copy, modify, merge,
- * publish, distribute, sublicense, and/or sell copies of the Software,
- * and to permit persons to whom the Software is furnished to do so,
- * subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be
- * included in all copies or substantial portions of the Software.
- *
- * Any person wishing to distribute modifications to the Software is
- * requested to send the modifications to the original developer so that
- * they can be incorporated into the canonical version.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
- * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
- * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
- * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
- * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
- */
-
-/** @file
- @brief Buffer Processor implementation.
-
- The code in this file is not optimised yet - although it's not clear that
- it needs to be. there may appear to be redundancies
- that could be factored into common functions, but the redundanceis are left
- intentionally as each appearance may have different optimisation possibilities.
-
- The optimisations which are planned involve only converting data in-place
- where possible, rather than copying to the temp buffer(s).
-
- Note that in the extreme case of being able to convert in-place, and there
- being no conversion necessary there should be some code which short-circuits
- the operation.
-
- @todo Consider cache tilings for intereave<->deinterleave.
-
- @todo implement timeInfo->currentTime int PaUtil_BeginBufferProcessing()
-
- @todo specify and implement some kind of logical policy for handling the
- underflow and overflow stream flags when the underflow/overflow overlaps
- multiple user buffers/callbacks.
-
- @todo provide support for priming the buffers with data from the callback.
- The client interface is now implemented through PaUtil_SetNoInput()
- which sets bp->hostInputChannels[0][0].data to zero. However this is
- currently only implemented in NonAdaptingProcess(). It shouldn't be
- needed for AdaptingInputOnlyProcess() (no priming should ever be
- requested for AdaptingInputOnlyProcess()).
- Not sure if additional work should be required to make it work with
- AdaptingOutputOnlyProcess, but it definitely is required for
- AdaptingProcess.
-
- @todo implement PaUtil_SetNoOutput for AdaptingProcess
-
- @todo don't allocate temp buffers for blocking streams unless they are
- needed. At the moment they are needed, but perhaps for host APIs
- where the implementation passes a buffer to the host they could be
- used.
-*/
-
-
-#include <assert.h>
-#include <string.h> /* memset() */
-
-#include "pa_process.h"
-#include "pa_util.h"
-
-
-#define PA_FRAMES_PER_TEMP_BUFFER_WHEN_HOST_BUFFER_SIZE_IS_UNKNOWN_ 1024
-
-#define PA_MIN_( a, b ) ( ((a)<(b)) ? (a) : (b) )
-
-
-/* greatest common divisor - PGCD in French */
-static unsigned long GCD( unsigned long a, unsigned long b )
-{
- return (b==0) ? a : GCD( b, a%b);
-}
-
-/* least common multiple - PPCM in French */
-static unsigned long LCM( unsigned long a, unsigned long b )
-{
- return (a*b) / GCD(a,b);
-}
-
-#define PA_MAX_( a, b ) (((a) > (b)) ? (a) : (b))
-
-static unsigned long CalculateFrameShift( unsigned long M, unsigned long N )
-{
- unsigned long result = 0;
- unsigned long i;
- unsigned long lcm;
-
- assert( M > 0 );
- assert( N > 0 );
-
- lcm = LCM( M, N );
- for( i = M; i < lcm; i += M )
- result = PA_MAX_( result, i % N );
-
- return result;
-}
-
-
-PaError PaUtil_InitializeBufferProcessor( PaUtilBufferProcessor* bp,
- int inputChannelCount, PaSampleFormat userInputSampleFormat,
- PaSampleFormat hostInputSampleFormat,
- int outputChannelCount, PaSampleFormat userOutputSampleFormat,
- PaSampleFormat hostOutputSampleFormat,
- double sampleRate,
- PaStreamFlags streamFlags,
- unsigned long framesPerUserBuffer,
- unsigned long framesPerHostBuffer,
- PaUtilHostBufferSizeMode hostBufferSizeMode,
- PaStreamCallback *streamCallback, void *userData )
-{
- PaError result = paNoError;
- PaError bytesPerSample;
- unsigned long tempInputBufferSize, tempOutputBufferSize;
-
- /* initialize buffer ptrs to zero so they can be freed if necessary in error */
- bp->tempInputBuffer = 0;
- bp->tempInputBufferPtrs = 0;
- bp->tempOutputBuffer = 0;
- bp->tempOutputBufferPtrs = 0;
-
- bp->framesPerUserBuffer = framesPerUserBuffer;
- bp->framesPerHostBuffer = framesPerHostBuffer;
-
- bp->inputChannelCount = inputChannelCount;
- bp->outputChannelCount = outputChannelCount;
-
- bp->hostBufferSizeMode = hostBufferSizeMode;
-
- bp->hostInputChannels[0] = bp->hostInputChannels[1] = 0;
- bp->hostOutputChannels[0] = bp->hostOutputChannels[1] = 0;
-
- if( framesPerUserBuffer == 0 ) /* streamCallback will accept any buffer size */
- {
- bp->useNonAdaptingProcess = 1;
- bp->initialFramesInTempInputBuffer = 0;
- bp->initialFramesInTempOutputBuffer = 0;
-
- if( hostBufferSizeMode == paUtilFixedHostBufferSize
- || hostBufferSizeMode == paUtilBoundedHostBufferSize )
- {
- bp->framesPerTempBuffer = framesPerHostBuffer;
- }
- else /* unknown host buffer size */
- {
- bp->framesPerTempBuffer = PA_FRAMES_PER_TEMP_BUFFER_WHEN_HOST_BUFFER_SIZE_IS_UNKNOWN_;
- }
- }
- else
- {
- bp->framesPerTempBuffer = framesPerUserBuffer;
-
- if( hostBufferSizeMode == paUtilFixedHostBufferSize
- && framesPerHostBuffer % framesPerUserBuffer == 0 )
- {
- bp->useNonAdaptingProcess = 1;
- bp->initialFramesInTempInputBuffer = 0;
- bp->initialFramesInTempOutputBuffer = 0;
- }
- else
- {
- bp->useNonAdaptingProcess = 0;
-
- if( inputChannelCount > 0 && outputChannelCount > 0 )
- {
- /* full duplex */
- if( hostBufferSizeMode == paUtilFixedHostBufferSize )
- {
- unsigned long frameShift =
- CalculateFrameShift( framesPerHostBuffer, framesPerUserBuffer );
-
- if( framesPerUserBuffer > framesPerHostBuffer )
- {
- bp->initialFramesInTempInputBuffer = frameShift;
- bp->initialFramesInTempOutputBuffer = 0;
- }
- else
- {
- bp->initialFramesInTempInputBuffer = 0;
- bp->initialFramesInTempOutputBuffer = frameShift;
- }
- }
- else /* variable host buffer size, add framesPerUserBuffer latency */
- {
- bp->initialFramesInTempInputBuffer = 0;
- bp->initialFramesInTempOutputBuffer = framesPerUserBuffer;
- }
- }
- else
- {
- /* half duplex */
- bp->initialFramesInTempInputBuffer = 0;
- bp->initialFramesInTempOutputBuffer = 0;
- }
- }
- }
-
-
- bp->framesInTempInputBuffer = bp->initialFramesInTempInputBuffer;
- bp->framesInTempOutputBuffer = bp->initialFramesInTempOutputBuffer;
-
-
- if( inputChannelCount > 0 )
- {
- bytesPerSample = Pa_GetSampleSize( hostInputSampleFormat );
- if( bytesPerSample > 0 )
- {
- bp->bytesPerHostInputSample = bytesPerSample;
- }
- else
- {
- result = bytesPerSample;
- goto error;
- }
-
- bytesPerSample = Pa_GetSampleSize( userInputSampleFormat );
- if( bytesPerSample > 0 )
- {
- bp->bytesPerUserInputSample = bytesPerSample;
- }
- else
- {
- result = bytesPerSample;
- goto error;
- }
-
- bp->inputConverter =
- PaUtil_SelectConverter( hostInputSampleFormat, userInputSampleFormat, streamFlags );
-
- bp->inputZeroer = PaUtil_SelectZeroer( hostInputSampleFormat );
-
- bp->userInputIsInterleaved = (userInputSampleFormat & paNonInterleaved)?0:1;
-
-
- tempInputBufferSize =
- bp->framesPerTempBuffer * bp->bytesPerUserInputSample * inputChannelCount;
-
- bp->tempInputBuffer = PaUtil_AllocateMemory( tempInputBufferSize );
- if( bp->tempInputBuffer == 0 )
- {
- result = paInsufficientMemory;
- goto error;
- }
-
- if( bp->framesInTempInputBuffer > 0 )
- memset( bp->tempInputBuffer, 0, tempInputBufferSize );
-
- if( userInputSampleFormat & paNonInterleaved )
- {
- bp->tempInputBufferPtrs =
- (void **)PaUtil_AllocateMemory( sizeof(void*)*inputChannelCount );
- if( bp->tempInputBufferPtrs == 0 )
- {
- result = paInsufficientMemory;
- goto error;
- }
- }
-
- bp->hostInputChannels[0] = (PaUtilChannelDescriptor*)
- PaUtil_AllocateMemory( sizeof(PaUtilChannelDescriptor) * inputChannelCount * 2);
- if( bp->hostInputChannels[0] == 0 )
- {
- result = paInsufficientMemory;
- goto error;
- }
-
- bp->hostInputChannels[1] = &bp->hostInputChannels[0][inputChannelCount];
- }
-
- if( outputChannelCount > 0 )
- {
- bytesPerSample = Pa_GetSampleSize( hostOutputSampleFormat );
- if( bytesPerSample > 0 )
- {
- bp->bytesPerHostOutputSample = bytesPerSample;
- }
- else
- {
- result = bytesPerSample;
- goto error;
- }
-
- bytesPerSample = Pa_GetSampleSize( userOutputSampleFormat );
- if( bytesPerSample > 0 )
- {
- bp->bytesPerUserOutputSample = bytesPerSample;
- }
- else
- {
- result = bytesPerSample;
- goto error;
- }
-
- bp->outputConverter =
- PaUtil_SelectConverter( userOutputSampleFormat, hostOutputSampleFormat, streamFlags );
-
- bp->outputZeroer = PaUtil_SelectZeroer( hostOutputSampleFormat );
-
- bp->userOutputIsInterleaved = (userOutputSampleFormat & paNonInterleaved)?0:1;
-
- tempOutputBufferSize =
- bp->framesPerTempBuffer * bp->bytesPerUserOutputSample * outputChannelCount;
-
- bp->tempOutputBuffer = PaUtil_AllocateMemory( tempOutputBufferSize );
- if( bp->tempOutputBuffer == 0 )
- {
- result = paInsufficientMemory;
- goto error;
- }
-
- if( bp->framesInTempOutputBuffer > 0 )
- memset( bp->tempOutputBuffer, 0, tempOutputBufferSize );
-
- if( userOutputSampleFormat & paNonInterleaved )
- {
- bp->tempOutputBufferPtrs =
- (void **)PaUtil_AllocateMemory( sizeof(void*)*outputChannelCount );
- if( bp->tempOutputBufferPtrs == 0 )
- {
- result = paInsufficientMemory;
- goto error;
- }
- }
-
- bp->hostOutputChannels[0] = (PaUtilChannelDescriptor*)
- PaUtil_AllocateMemory( sizeof(PaUtilChannelDescriptor)*outputChannelCount * 2 );
- if( bp->hostOutputChannels[0] == 0 )
- {
- result = paInsufficientMemory;
- goto error;
- }
-
- bp->hostOutputChannels[1] = &bp->hostOutputChannels[0][outputChannelCount];
- }
-
- PaUtil_InitializeTriangularDitherState( &bp->ditherGenerator );
-
- bp->samplePeriod = 1. / sampleRate;
-
- bp->streamCallback = streamCallback;
- bp->userData = userData;
-
- return result;
-
-error:
- if( bp->tempInputBuffer )
- PaUtil_FreeMemory( bp->tempInputBuffer );
-
- if( bp->tempInputBufferPtrs )
- PaUtil_FreeMemory( bp->tempInputBufferPtrs );
-
- if( bp->hostInputChannels[0] )
- PaUtil_FreeMemory( bp->hostInputChannels[0] );
-
- if( bp->tempOutputBuffer )
- PaUtil_FreeMemory( bp->tempOutputBuffer );
-
- if( bp->tempOutputBufferPtrs )
- PaUtil_FreeMemory( bp->tempOutputBufferPtrs );
-
- if( bp->hostOutputChannels[0] )
- PaUtil_FreeMemory( bp->hostOutputChannels[0] );
-
- return result;
-}
-
-
-void PaUtil_TerminateBufferProcessor( PaUtilBufferProcessor* bp )
-{
- if( bp->tempInputBuffer )
- PaUtil_FreeMemory( bp->tempInputBuffer );
-
- if( bp->tempInputBufferPtrs )
- PaUtil_FreeMemory( bp->tempInputBufferPtrs );
-
- if( bp->hostInputChannels[0] )
- PaUtil_FreeMemory( bp->hostInputChannels[0] );
-
- if( bp->tempOutputBuffer )
- PaUtil_FreeMemory( bp->tempOutputBuffer );
-
- if( bp->tempOutputBufferPtrs )
- PaUtil_FreeMemory( bp->tempOutputBufferPtrs );
-
- if( bp->hostOutputChannels[0] )
- PaUtil_FreeMemory( bp->hostOutputChannels[0] );
-}
-
-
-void PaUtil_ResetBufferProcessor( PaUtilBufferProcessor* bp )
-{
- unsigned long tempInputBufferSize, tempOutputBufferSize;
-
- bp->framesInTempInputBuffer = bp->initialFramesInTempInputBuffer;
- bp->framesInTempOutputBuffer = bp->initialFramesInTempOutputBuffer;
-
- if( bp->framesInTempInputBuffer > 0 )
- {
- tempInputBufferSize =
- bp->framesPerTempBuffer * bp->bytesPerUserInputSample * bp->inputChannelCount;
- memset( bp->tempInputBuffer, 0, tempInputBufferSize );
- }
-
- if( bp->framesInTempOutputBuffer > 0 )
- {
- tempOutputBufferSize =
- bp->framesPerTempBuffer * bp->bytesPerUserOutputSample * bp->outputChannelCount;
- memset( bp->tempOutputBuffer, 0, tempOutputBufferSize );
- }
-}
-
-
-unsigned long PaUtil_GetBufferProcessorInputLatency( PaUtilBufferProcessor* bp )
-{
- return bp->initialFramesInTempInputBuffer;
-}
-
-
-unsigned long PaUtil_GetBufferProcessorOutputLatency( PaUtilBufferProcessor* bp )
-{
- return bp->initialFramesInTempOutputBuffer;
-}
-
-
-void PaUtil_SetInputFrameCount( PaUtilBufferProcessor* bp,
- unsigned long frameCount )
-{
- if( frameCount == 0 )
- bp->hostInputFrameCount[0] = bp->framesPerHostBuffer;
- else
- bp->hostInputFrameCount[0] = frameCount;
-}
-
-
-void PaUtil_SetNoInput( PaUtilBufferProcessor* bp )
-{
- assert( bp->inputChannelCount > 0 );
-
- bp->hostInputChannels[0][0].data = 0;
-}
-
-
-void PaUtil_SetInputChannel( PaUtilBufferProcessor* bp,
- unsigned int channel, void *data, unsigned int stride )
-{
- assert( channel < bp->inputChannelCount );
-
- bp->hostInputChannels[0][channel].data = data;
- bp->hostInputChannels[0][channel].stride = stride;
-}
-
-
-void PaUtil_SetInterleavedInputChannels( PaUtilBufferProcessor* bp,
- unsigned int firstChannel, void *data, unsigned int channelCount )
-{
- unsigned int i;
- unsigned int channel = firstChannel;
- unsigned char *p = (unsigned char*)data;
-
- if( channelCount == 0 )
- channelCount = bp->inputChannelCount;
-
- assert( firstChannel < bp->inputChannelCount );
- assert( firstChannel + channelCount <= bp->inputChannelCount );
-
- for( i=0; i< channelCount; ++i )
- {
- bp->hostInputChannels[0][channel+i].data = p;
- p += bp->bytesPerHostInputSample;
- bp->hostInputChannels[0][channel+i].stride = channelCount;
- }
-}
-
-
-void PaUtil_SetNonInterleavedInputChannel( PaUtilBufferProcessor* bp,
- unsigned int channel, void *data )
-{
- assert( channel < bp->inputChannelCount );
-
- bp->hostInputChannels[0][channel].data = data;
- bp->hostInputChannels[0][channel].stride = 1;
-}
-
-
-void PaUtil_Set2ndInputFrameCount( PaUtilBufferProcessor* bp,
- unsigned long frameCount )
-{
- bp->hostInputFrameCount[1] = frameCount;
-}
-
-
-void PaUtil_Set2ndInputChannel( PaUtilBufferProcessor* bp,
- unsigned int channel, void *data, unsigned int stride )
-{
- assert( channel < bp->inputChannelCount );
-
- bp->hostInputChannels[1][channel].data = data;
- bp->hostInputChannels[1][channel].stride = stride;
-}
-
-
-void PaUtil_Set2ndInterleavedInputChannels( PaUtilBufferProcessor* bp,
- unsigned int firstChannel, void *data, unsigned int channelCount )
-{
- unsigned int i;
- unsigned int channel = firstChannel;
- unsigned char *p = (unsigned char*)data;
-
- if( channelCount == 0 )
- channelCount = bp->inputChannelCount;
-
- assert( firstChannel < bp->inputChannelCount );
- assert( firstChannel + channelCount <= bp->inputChannelCount );
-
- for( i=0; i< channelCount; ++i )
- {
- bp->hostInputChannels[1][channel+i].data = p;
- p += bp->bytesPerHostInputSample;
- bp->hostInputChannels[1][channel+i].stride = channelCount;
- }
-}
-
-
-void PaUtil_Set2ndNonInterleavedInputChannel( PaUtilBufferProcessor* bp,
- unsigned int channel, void *data )
-{
- assert( channel < bp->inputChannelCount );
-
- bp->hostInputChannels[1][channel].data = data;
- bp->hostInputChannels[1][channel].stride = 1;
-}
-
-
-void PaUtil_SetOutputFrameCount( PaUtilBufferProcessor* bp,
- unsigned long frameCount )
-{
- if( frameCount == 0 )
- bp->hostOutputFrameCount[0] = bp->framesPerHostBuffer;
- else
- bp->hostOutputFrameCount[0] = frameCount;
-}
-
-
-void PaUtil_SetNoOutput( PaUtilBufferProcessor* bp )
-{
- assert( bp->outputChannelCount > 0 );
-
- bp->hostOutputChannels[0][0].data = 0;
-}
-
-
-void PaUtil_SetOutputChannel( PaUtilBufferProcessor* bp,
- unsigned int channel, void *data, unsigned int stride )
-{
- assert( channel < bp->outputChannelCount );
-
- bp->hostOutputChannels[0][channel].data = data;
- bp->hostOutputChannels[0][channel].stride = stride;
-}
-
-
-void PaUtil_SetInterleavedOutputChannels( PaUtilBufferProcessor* bp,
- unsigned int firstChannel, void *data, unsigned int channelCount )
-{
- unsigned int i;
- unsigned int channel = firstChannel;
- unsigned char *p = (unsigned char*)data;
-
- if( channelCount == 0 )
- channelCount = bp->outputChannelCount;
-
- assert( firstChannel < bp->outputChannelCount );
- assert( firstChannel + channelCount <= bp->outputChannelCount );
-
- for( i=0; i< channelCount; ++i )
- {
- bp->hostOutputChannels[0][channel+i].data = p;
- p += bp->bytesPerHostOutputSample;
- bp->hostOutputChannels[0][channel+i].stride = channelCount;
- }
-}
-
-
-void PaUtil_SetNonInterleavedOutputChannel( PaUtilBufferProcessor* bp,
- unsigned int channel, void *data )
-{
- assert( channel < bp->outputChannelCount );
-
- bp->hostOutputChannels[0][channel].data = data;
- bp->hostOutputChannels[0][channel].stride = 1;
-}
-
-
-void PaUtil_Set2ndOutputFrameCount( PaUtilBufferProcessor* bp,
- unsigned long frameCount )
-{
- bp->hostOutputFrameCount[1] = frameCount;
-}
-
-
-void PaUtil_Set2ndOutputChannel( PaUtilBufferProcessor* bp,
- unsigned int channel, void *data, unsigned int stride )
-{
- assert( channel < bp->outputChannelCount );
-
- bp->hostOutputChannels[1][channel].data = data;
- bp->hostOutputChannels[1][channel].stride = stride;
-}
-
-
-void PaUtil_Set2ndInterleavedOutputChannels( PaUtilBufferProcessor* bp,
- unsigned int firstChannel, void *data, unsigned int channelCount )
-{
- unsigned int i;
- unsigned int channel = firstChannel;
- unsigned char *p = (unsigned char*)data;
-
- if( channelCount == 0 )
- channelCount = bp->outputChannelCount;
-
- assert( firstChannel < bp->outputChannelCount );
- assert( firstChannel + channelCount <= bp->outputChannelCount );
-
- for( i=0; i< channelCount; ++i )
- {
- bp->hostOutputChannels[1][channel+i].data = p;
- p += bp->bytesPerHostOutputSample;
- bp->hostOutputChannels[1][channel+i].stride = channelCount;
- }
-}
-
-
-void PaUtil_Set2ndNonInterleavedOutputChannel( PaUtilBufferProcessor* bp,
- unsigned int channel, void *data )
-{
- assert( channel < bp->outputChannelCount );
-
- bp->hostOutputChannels[1][channel].data = data;
- bp->hostOutputChannels[1][channel].stride = 1;
-}
-
-
-void PaUtil_BeginBufferProcessing( PaUtilBufferProcessor* bp,
- PaStreamCallbackTimeInfo* timeInfo, PaStreamCallbackFlags callbackStatusFlags )
-{
- bp->timeInfo = timeInfo;
-
- /* the first streamCallback will be called to process samples which are
- currently in the input buffer before the ones starting at the timeInfo time */
-
- bp->timeInfo->inputBufferAdcTime -= bp->framesInTempInputBuffer * bp->samplePeriod;
-
- bp->timeInfo->currentTime = 0; /** FIXME: @todo time info currentTime not implemented */
-
- /* the first streamCallback will be called to generate samples which will be
- outputted after the frames currently in the output buffer have been
- outputted. */
- bp->timeInfo->outputBufferDacTime += bp->framesInTempOutputBuffer * bp->samplePeriod;
-
- bp->callbackStatusFlags = callbackStatusFlags;
-
- bp->hostInputFrameCount[1] = 0;
- bp->hostOutputFrameCount[1] = 0;
-}
-
-
-/*
- NonAdaptingProcess() is a simple buffer copying adaptor that can handle
- both full and half duplex copies. It processes framesToProcess frames,
- broken into blocks bp->framesPerTempBuffer long.
- This routine can be used when the streamCallback doesn't care what length
- the buffers are, or when framesToProcess is an integer multiple of
- bp->framesPerTempBuffer, in which case streamCallback will always be called
- with bp->framesPerTempBuffer samples.
-*/
-static unsigned long NonAdaptingProcess( PaUtilBufferProcessor *bp,
- int *streamCallbackResult,
- PaUtilChannelDescriptor *hostInputChannels,
- PaUtilChannelDescriptor *hostOutputChannels,
- unsigned long framesToProcess )
-{
- void *userInput, *userOutput;
- unsigned char *srcBytePtr, *destBytePtr;
- unsigned int srcSampleStrideSamples; /* stride from one sample to the next within a channel, in samples */
- unsigned int srcChannelStrideBytes; /* stride from one channel to the next, in bytes */
- unsigned int destSampleStrideSamples; /* stride from one sample to the next within a channel, in samples */
- unsigned int destChannelStrideBytes; /* stride from one channel to the next, in bytes */
- unsigned int i;
- unsigned long frameCount;
- unsigned long framesToGo = framesToProcess;
- unsigned long framesProcessed = 0;
-
-
- if( *streamCallbackResult == paContinue )
- {
- do
- {
- frameCount = PA_MIN_( bp->framesPerTempBuffer, framesToGo );
-
- /* configure user input buffer and convert input data (host -> user) */
- if( bp->inputChannelCount == 0 )
- {
- /* no input */
- userInput = 0;
- }
- else /* there are input channels */
- {
- /*
- could use more elaborate logic here and sometimes process
- buffers in-place.
- */
-
- destBytePtr = (unsigned char *)bp->tempInputBuffer;
-
- if( bp->userInputIsInterleaved )
- {
- destSampleStrideSamples = bp->inputChannelCount;
- destChannelStrideBytes = bp->bytesPerUserInputSample;
- userInput = bp->tempInputBuffer;
- }
- else /* user input is not interleaved */
- {
- destSampleStrideSamples = 1;
- destChannelStrideBytes = frameCount * bp->bytesPerUserInputSample;
-
- /* setup non-interleaved ptrs */
- for( i=0; i<bp->inputChannelCount; ++i )
- {
- bp->tempInputBufferPtrs[i] = ((unsigned char*)bp->tempInputBuffer) +
- i * bp->bytesPerUserInputSample * frameCount;
- }
-
- userInput = bp->tempInputBufferPtrs;
- }
-
- if( !bp->hostInputChannels[0][0].data )
- {
- /* no input was supplied (see PaUtil_SetNoInput), so
- zero the input buffer */
-
- for( i=0; i<bp->inputChannelCount; ++i )
- {
- bp->inputZeroer( destBytePtr, destSampleStrideSamples, frameCount );
- destBytePtr += destChannelStrideBytes; /* skip to next destination channel */
- }
- }
- else
- {
- for( i=0; i<bp->inputChannelCount; ++i )
- {
- bp->inputConverter( destBytePtr, destSampleStrideSamples,
- hostInputChannels[i].data,
- hostInputChannels[i].stride,
- frameCount, &bp->ditherGenerator );
-
- destBytePtr += destChannelStrideBytes; /* skip to next destination channel */
-
- /* advance src ptr for next iteration */
- hostInputChannels[i].data = ((unsigned char*)hostInputChannels[i].data) +
- frameCount * hostInputChannels[i].stride * bp->bytesPerHostInputSample;
- }
- }
- }
-
- /* configure user output buffer */
- if( bp->outputChannelCount == 0 )
- {
- /* no output */
- userOutput = 0;
- }
- else /* there are output channels */
- {
- if( bp->userOutputIsInterleaved )
- {
- userOutput = bp->tempOutputBuffer;
- }
- else /* user output is not interleaved */
- {
- for( i = 0; i < bp->outputChannelCount; ++i )
- {
- bp->tempOutputBufferPtrs[i] = ((unsigned char*)bp->tempOutputBuffer) +
- i * bp->bytesPerUserOutputSample * frameCount;
- }
-
- userOutput = bp->tempOutputBufferPtrs;
- }
- }
-
- *streamCallbackResult = bp->streamCallback( userInput, userOutput,
- frameCount, bp->timeInfo, bp->callbackStatusFlags, bp->userData );
-
- if( *streamCallbackResult == paAbort )
- {
- /* callback returned paAbort, don't advance framesProcessed
- and framesToGo, they will be handled below */
- }
- else
- {
- bp->timeInfo->inputBufferAdcTime += frameCount * bp->samplePeriod;
- bp->timeInfo->outputBufferDacTime += frameCount * bp->samplePeriod;
-
- /* convert output data (user -> host) */
-
- if( bp->outputChannelCount != 0 && bp->hostOutputChannels[0][0].data )
- {
- /*
- could use more elaborate logic here and sometimes process
- buffers in-place.
- */
-
- srcBytePtr = (unsigned char *)bp->tempOutputBuffer;
-
- if( bp->userOutputIsInterleaved )
- {
- srcSampleStrideSamples = bp->outputChannelCount;
- srcChannelStrideBytes = bp->bytesPerUserOutputSample;
- }
- else /* user output is not interleaved */
- {
- srcSampleStrideSamples = 1;
- srcChannelStrideBytes = frameCount * bp->bytesPerUserOutputSample;
- }
-
- for( i=0; i<bp->outputChannelCount; ++i )
- {
- bp->outputConverter( hostOutputChannels[i].data,
- hostOutputChannels[i].stride,
- srcBytePtr, srcSampleStrideSamples,
- frameCount, &bp->ditherGenerator );
-
- srcBytePtr += srcChannelStrideBytes; /* skip to next source channel */
-
- /* advance dest ptr for next iteration */
- hostOutputChannels[i].data = ((unsigned char*)hostOutputChannels[i].data) +
- frameCount * hostOutputChannels[i].stride * bp->bytesPerHostOutputSample;
- }
- }
-
- framesProcessed += frameCount;
-
- framesToGo -= frameCount;
- }
- }
- while( framesToGo > 0 && *streamCallbackResult == paContinue );
- }
-
- if( framesToGo > 0 )
- {
- /* zero any remaining frames output. There will only be remaining frames
- if the callback has returned paComplete or paAbort */
-
- frameCount = framesToGo;
-
- if( bp->outputChannelCount != 0 && bp->hostOutputChannels[0][0].data )
- {
- for( i=0; i<bp->outputChannelCount; ++i )
- {
- bp->outputZeroer( hostOutputChannels[i].data,
- hostOutputChannels[i].stride,
- frameCount );
-
- /* advance dest ptr for next iteration */
- hostOutputChannels[i].data = ((unsigned char*)hostOutputChannels[i].data) +
- frameCount * hostOutputChannels[i].stride * bp->bytesPerHostOutputSample;
- }
- }
-
- framesProcessed += frameCount;
- }
-
- return framesProcessed;
-}
-
-
-/*
- AdaptingInputOnlyProcess() is a half duplex input buffer processor. It
- converts data from the input buffers into the temporary input buffer,
- when the temporary input buffer is full, it calls the streamCallback.
-*/
-static unsigned long AdaptingInputOnlyProcess( PaUtilBufferProcessor *bp,
- int *streamCallbackResult,
- PaUtilChannelDescriptor *hostInputChannels,
- unsigned long framesToProcess )
-{
- void *userInput, *userOutput;
- unsigned char *destBytePtr;
- unsigned int destSampleStrideSamples; /* stride from one sample to the next within a channel, in samples */
- unsigned int destChannelStrideBytes; /* stride from one channel to the next, in bytes */
- unsigned int i;
- unsigned long frameCount;
- unsigned long framesToGo = framesToProcess;
- unsigned long framesProcessed = 0;
-
- userOutput = 0;
-
- do
- {
- frameCount = ( bp->framesInTempInputBuffer + framesToGo > bp->framesPerUserBuffer )
- ? ( bp->framesPerUserBuffer - bp->framesInTempInputBuffer )
- : framesToGo;
-
- /* convert frameCount samples into temp buffer */
-
- if( bp->userInputIsInterleaved )
- {
- destBytePtr = ((unsigned char*)bp->tempInputBuffer) +
- bp->bytesPerUserInputSample * bp->inputChannelCount *
- bp->framesInTempInputBuffer;
-
- destSampleStrideSamples = bp->inputChannelCount;
- destChannelStrideBytes = bp->bytesPerUserInputSample;
-
- userInput = bp->tempInputBuffer;
- }
- else /* user input is not interleaved */
- {
- destBytePtr = ((unsigned char*)bp->tempInputBuffer) +
- bp->bytesPerUserInputSample * bp->framesInTempInputBuffer;
-
- destSampleStrideSamples = 1;
- destChannelStrideBytes = bp->framesPerUserBuffer * bp->bytesPerUserInputSample;
-
- /* setup non-interleaved ptrs */
- for( i=0; i<bp->inputChannelCount; ++i )
- {
- bp->tempInputBufferPtrs[i] = ((unsigned char*)bp->tempInputBuffer) +
- i * bp->bytesPerUserInputSample * bp->framesPerUserBuffer;
- }
-
- userInput = bp->tempInputBufferPtrs;
- }
-
- for( i=0; i<bp->inputChannelCount; ++i )
- {
- bp->inputConverter( destBytePtr, destSampleStrideSamples,
- hostInputChannels[i].data,
- hostInputChannels[i].stride,
- frameCount, &bp->ditherGenerator );
-
- destBytePtr += destChannelStrideBytes; /* skip to next destination channel */
-
- /* advance src ptr for next iteration */
- hostInputChannels[i].data = ((unsigned char*)hostInputChannels[i].data) +
- frameCount * hostInputChannels[i].stride * bp->bytesPerHostInputSample;
- }
-
- bp->framesInTempInputBuffer += frameCount;
-
- if( bp->framesInTempInputBuffer == bp->framesPerUserBuffer )
- {
- /**
- @todo (non-critical optimisation)
- The conditional below implements the continue/complete/abort mechanism
- simply by continuing on iterating through the input buffer, but not
- passing the data to the callback. With care, the outer loop could be
- terminated earlier, thus some unneeded conversion cycles would be
- saved.
- */
- if( *streamCallbackResult == paContinue )
- {
- bp->timeInfo->outputBufferDacTime = 0;
-
- *streamCallbackResult = bp->streamCallback( userInput, userOutput,
- bp->framesPerUserBuffer, bp->timeInfo,
- bp->callbackStatusFlags, bp->userData );
-
- bp->timeInfo->inputBufferAdcTime += frameCount * bp->samplePeriod;
- }
-
- bp->framesInTempInputBuffer = 0;
- }
-
- framesProcessed += frameCount;
-
- framesToGo -= frameCount;
- }while( framesToGo > 0 );
-
- return framesProcessed;
-}
-
-
-/*
- AdaptingOutputOnlyProcess() is a half duplex output buffer processor.
- It converts data from the temporary output buffer, to the output buffers,
- when the temporary output buffer is empty, it calls the streamCallback.
-*/
-static unsigned long AdaptingOutputOnlyProcess( PaUtilBufferProcessor *bp,
- int *streamCallbackResult,
- PaUtilChannelDescriptor *hostOutputChannels,
- unsigned long framesToProcess )
-{
- void *userInput, *userOutput;
- unsigned char *srcBytePtr;
- unsigned int srcSampleStrideSamples; /* stride from one sample to the next within a channel, in samples */
- unsigned int srcChannelStrideBytes; /* stride from one channel to the next, in bytes */
- unsigned int i;
- unsigned long frameCount;
- unsigned long framesToGo = framesToProcess;
- unsigned long framesProcessed = 0;
-
- do
- {
- if( bp->framesInTempOutputBuffer == 0 && *streamCallbackResult == paContinue )
- {
- userInput = 0;
-
- /* setup userOutput */
- if( bp->userOutputIsInterleaved )
- {
- userOutput = bp->tempOutputBuffer;
- }
- else /* user output is not interleaved */
- {
- for( i = 0; i < bp->outputChannelCount; ++i )
- {
- bp->tempOutputBufferPtrs[i] = ((unsigned char*)bp->tempOutputBuffer) +
- i * bp->framesPerUserBuffer * bp->bytesPerUserOutputSample;
- }
-
- userOutput = bp->tempOutputBufferPtrs;
- }
-
- bp->timeInfo->inputBufferAdcTime = 0;
-
- *streamCallbackResult = bp->streamCallback( userInput, userOutput,
- bp->framesPerUserBuffer, bp->timeInfo,
- bp->callbackStatusFlags, bp->userData );
-
- if( *streamCallbackResult == paAbort )
- {
- /* if the callback returned paAbort, we disregard its output */
- }
- else
- {
- bp->timeInfo->outputBufferDacTime += bp->framesPerUserBuffer * bp->samplePeriod;
-
- bp->framesInTempOutputBuffer = bp->framesPerUserBuffer;
- }
- }
-
- if( bp->framesInTempOutputBuffer > 0 )
- {
- /* convert frameCount frames from user buffer to host buffer */
-
- frameCount = PA_MIN_( bp->framesInTempOutputBuffer, framesToGo );
-
- if( bp->userOutputIsInterleaved )
- {
- srcBytePtr = ((unsigned char*)bp->tempOutputBuffer) +
- bp->bytesPerUserOutputSample * bp->outputChannelCount *
- (bp->framesPerUserBuffer - bp->framesInTempOutputBuffer);
-
- srcSampleStrideSamples = bp->outputChannelCount;
- srcChannelStrideBytes = bp->bytesPerUserOutputSample;
- }
- else /* user output is not interleaved */
- {
- srcBytePtr = ((unsigned char*)bp->tempOutputBuffer) +
- bp->bytesPerUserOutputSample *
- (bp->framesPerUserBuffer - bp->framesInTempOutputBuffer);
-
- srcSampleStrideSamples = 1;
- srcChannelStrideBytes = bp->framesPerUserBuffer * bp->bytesPerUserOutputSample;
- }
-
- for( i=0; i<bp->outputChannelCount; ++i )
- {
- bp->outputConverter( hostOutputChannels[i].data,
- hostOutputChannels[i].stride,
- srcBytePtr, srcSampleStrideSamples,
- frameCount, &bp->ditherGenerator );
-
- srcBytePtr += srcChannelStrideBytes; /* skip to next source channel */
-
- /* advance dest ptr for next iteration */
- hostOutputChannels[i].data = ((unsigned char*)hostOutputChannels[i].data) +
- frameCount * hostOutputChannels[i].stride * bp->bytesPerHostOutputSample;
- }
-
- bp->framesInTempOutputBuffer -= frameCount;
- }
- else
- {
- /* no more user data is available because the callback has returned
- paComplete or paAbort. Fill the remainder of the host buffer
- with zeros.
- */
-
- frameCount = framesToGo;
-
- for( i=0; i<bp->outputChannelCount; ++i )
- {
- bp->outputZeroer( hostOutputChannels[i].data,
- hostOutputChannels[i].stride,
- frameCount );
-
- /* advance dest ptr for next iteration */
- hostOutputChannels[i].data = ((unsigned char*)hostOutputChannels[i].data) +
- frameCount * hostOutputChannels[i].stride * bp->bytesPerHostOutputSample;
- }
- }
-
- framesProcessed += frameCount;
-
- framesToGo -= frameCount;
-
- }while( framesToGo > 0 );
-
- return framesProcessed;
-}
-
-/* CopyTempOutputBuffersToHostOutputBuffers is called from AdaptingProcess to copy frames from
- tempOutputBuffer to hostOutputChannels. This includes data conversion
- and interleaving.
-*/
-static void CopyTempOutputBuffersToHostOutputBuffers( PaUtilBufferProcessor *bp)
-{
- unsigned long maxFramesToCopy;
- PaUtilChannelDescriptor *hostOutputChannels;
- unsigned int frameCount;
- unsigned char *srcBytePtr;
- unsigned int srcSampleStrideSamples; /* stride from one sample to the next within a channel, in samples */
- unsigned int srcChannelStrideBytes; /* stride from one channel to the next, in bytes */
- unsigned int i;
-
- /* copy frames from user to host output buffers */
- while( bp->framesInTempOutputBuffer > 0 &&
- ((bp->hostOutputFrameCount[0] + bp->hostOutputFrameCount[1]) > 0) )
- {
- maxFramesToCopy = bp->framesInTempOutputBuffer;
-
- /* select the output buffer set (1st or 2nd) */
- if( bp->hostOutputFrameCount[0] > 0 )
- {
- hostOutputChannels = bp->hostOutputChannels[0];
- frameCount = PA_MIN_( bp->hostOutputFrameCount[0], maxFramesToCopy );
- }
- else
- {
- hostOutputChannels = bp->hostOutputChannels[1];
- frameCount = PA_MIN_( bp->hostOutputFrameCount[1], maxFramesToCopy );
- }
-
- if( bp->userOutputIsInterleaved )
- {
- srcBytePtr = ((unsigned char*)bp->tempOutputBuffer) +
- bp->bytesPerUserOutputSample * bp->outputChannelCount *
- (bp->framesPerUserBuffer - bp->framesInTempOutputBuffer);
-
- srcSampleStrideSamples = bp->outputChannelCount;
- srcChannelStrideBytes = bp->bytesPerUserOutputSample;
- }
- else /* user output is not interleaved */
- {
- srcBytePtr = ((unsigned char*)bp->tempOutputBuffer) +
- bp->bytesPerUserOutputSample *
- (bp->framesPerUserBuffer - bp->framesInTempOutputBuffer);
-
- srcSampleStrideSamples = 1;
- srcChannelStrideBytes = bp->framesPerUserBuffer * bp->bytesPerUserOutputSample;
- }
-
- for( i=0; i<bp->outputChannelCount; ++i )
- {
- bp->outputConverter( hostOutputChannels[i].data,
- hostOutputChannels[i].stride,
- srcBytePtr, srcSampleStrideSamples,
- frameCount, &bp->ditherGenerator );
-
- srcBytePtr += srcChannelStrideBytes; /* skip to next source channel */
-
- /* advance dest ptr for next iteration */
- hostOutputChannels[i].data = ((unsigned char*)hostOutputChannels[i].data) +
- frameCount * hostOutputChannels[i].stride * bp->bytesPerHostOutputSample;
- }
-
- if( bp->hostOutputFrameCount[0] > 0 )
- bp->hostOutputFrameCount[0] -= frameCount;
- else
- bp->hostOutputFrameCount[1] -= frameCount;
-
- bp->framesInTempOutputBuffer -= frameCount;
- }
-}
-
-/*
- AdaptingProcess is a full duplex adapting buffer processor. It converts
- data from the temporary output buffer into the host output buffers, then
- from the host input buffers into the temporary input buffers. Calling the
- streamCallback when necessary.
- When processPartialUserBuffers is 0, all available input data will be
- consumed and all available output space will be filled. When
- processPartialUserBuffers is non-zero, as many full user buffers
- as possible will be processed, but partial buffers will not be consumed.
-*/
-static unsigned long AdaptingProcess( PaUtilBufferProcessor *bp,
- int *streamCallbackResult, int processPartialUserBuffers )
-{
- void *userInput, *userOutput;
- unsigned long framesProcessed = 0;
- unsigned long framesAvailable;
- unsigned long endProcessingMinFrameCount;
- unsigned long maxFramesToCopy;
- PaUtilChannelDescriptor *hostInputChannels, *hostOutputChannels;
- unsigned int frameCount;
- unsigned char *destBytePtr;
- unsigned int destSampleStrideSamples; /* stride from one sample to the next within a channel, in samples */
- unsigned int destChannelStrideBytes; /* stride from one channel to the next, in bytes */
- unsigned int i, j;
-
-
- framesAvailable = bp->hostInputFrameCount[0] + bp->hostInputFrameCount[1];/* this is assumed to be the same as the output buffer's frame count */
-
- if( processPartialUserBuffers )
- endProcessingMinFrameCount = 0;
- else
- endProcessingMinFrameCount = (bp->framesPerUserBuffer - 1);
-
- /* Fill host output with remaining frames in user output (tempOutputBuffer) */
- CopyTempOutputBuffersToHostOutputBuffers( bp );
-
- while( framesAvailable > endProcessingMinFrameCount )
- {
-
- if( bp->framesInTempOutputBuffer == 0 && *streamCallbackResult != paContinue )
- {
- /* the callback will not be called any more, so zero what remains
- of the host output buffers */
-
- for( i=0; i<2; ++i )
- {
- frameCount = bp->hostOutputFrameCount[i];
- if( frameCount > 0 )
- {
- hostOutputChannels = bp->hostOutputChannels[i];
-
- for( j=0; j<bp->outputChannelCount; ++j )
- {
- bp->outputZeroer( hostOutputChannels[j].data,
- hostOutputChannels[j].stride,
- frameCount );
-
- /* advance dest ptr for next iteration */
- hostOutputChannels[j].data = ((unsigned char*)hostOutputChannels[j].data) +
- frameCount * hostOutputChannels[j].stride * bp->bytesPerHostOutputSample;
- }
- bp->hostOutputFrameCount[i] = 0;
- }
- }
- }
-
-
- /* copy frames from host to user input buffers */
- while( bp->framesInTempInputBuffer < bp->framesPerUserBuffer &&
- ((bp->hostInputFrameCount[0] + bp->hostInputFrameCount[1]) > 0) )
- {
- maxFramesToCopy = bp->framesPerUserBuffer - bp->framesInTempInputBuffer;
-
- /* select the input buffer set (1st or 2nd) */
- if( bp->hostInputFrameCount[0] > 0 )
- {
- hostInputChannels = bp->hostInputChannels[0];
- frameCount = PA_MIN_( bp->hostInputFrameCount[0], maxFramesToCopy );
- }
- else
- {
- hostInputChannels = bp->hostInputChannels[1];
- frameCount = PA_MIN_( bp->hostInputFrameCount[1], maxFramesToCopy );
- }
-
- /* configure conversion destination pointers */
- if( bp->userInputIsInterleaved )
- {
- destBytePtr = ((unsigned char*)bp->tempInputBuffer) +
- bp->bytesPerUserInputSample * bp->inputChannelCount *
- bp->framesInTempInputBuffer;
-
- destSampleStrideSamples = bp->inputChannelCount;
- destChannelStrideBytes = bp->bytesPerUserInputSample;
- }
- else /* user input is not interleaved */
- {
- destBytePtr = ((unsigned char*)bp->tempInputBuffer) +
- bp->bytesPerUserInputSample * bp->framesInTempInputBuffer;
-
- destSampleStrideSamples = 1;
- destChannelStrideBytes = bp->framesPerUserBuffer * bp->bytesPerUserInputSample;
- }
-
- for( i=0; i<bp->inputChannelCount; ++i )
- {
- bp->inputConverter( destBytePtr, destSampleStrideSamples,
- hostInputChannels[i].data,
- hostInputChannels[i].stride,
- frameCount, &bp->ditherGenerator );
-
- destBytePtr += destChannelStrideBytes; /* skip to next destination channel */
-
- /* advance src ptr for next iteration */
- hostInputChannels[i].data = ((unsigned char*)hostInputChannels[i].data) +
- frameCount * hostInputChannels[i].stride * bp->bytesPerHostInputSample;
- }
-
- if( bp->hostInputFrameCount[0] > 0 )
- bp->hostInputFrameCount[0] -= frameCount;
- else
- bp->hostInputFrameCount[1] -= frameCount;
-
- bp->framesInTempInputBuffer += frameCount;
-
- /* update framesAvailable and framesProcessed based on input consumed
- unless something is very wrong this will also correspond to the
- amount of output generated */
- framesAvailable -= frameCount;
- framesProcessed += frameCount;
- }
-
- /* call streamCallback */
- if( bp->framesInTempInputBuffer == bp->framesPerUserBuffer &&
- bp->framesInTempOutputBuffer == 0 )
- {
- if( *streamCallbackResult == paContinue )
- {
- /* setup userInput */
- if( bp->userInputIsInterleaved )
- {
- userInput = bp->tempInputBuffer;
- }
- else /* user input is not interleaved */
- {
- for( i = 0; i < bp->inputChannelCount; ++i )
- {
- bp->tempInputBufferPtrs[i] = ((unsigned char*)bp->tempInputBuffer) +
- i * bp->framesPerUserBuffer * bp->bytesPerUserInputSample;
- }
-
- userInput = bp->tempInputBufferPtrs;
- }
-
- /* setup userOutput */
- if( bp->userOutputIsInterleaved )
- {
- userOutput = bp->tempOutputBuffer;
- }
- else /* user output is not interleaved */
- {
- for( i = 0; i < bp->outputChannelCount; ++i )
- {
- bp->tempOutputBufferPtrs[i] = ((unsigned char*)bp->tempOutputBuffer) +
- i * bp->framesPerUserBuffer * bp->bytesPerUserOutputSample;
- }
-
- userOutput = bp->tempOutputBufferPtrs;
- }
-
- /* call streamCallback */
-
- *streamCallbackResult = bp->streamCallback( userInput, userOutput,
- bp->framesPerUserBuffer, bp->timeInfo,
- bp->callbackStatusFlags, bp->userData );
-
- bp->timeInfo->inputBufferAdcTime += bp->framesPerUserBuffer * bp->samplePeriod;
- bp->timeInfo->outputBufferDacTime += bp->framesPerUserBuffer * bp->samplePeriod;
-
- bp->framesInTempInputBuffer = 0;
-
- if( *streamCallbackResult == paAbort )
- bp->framesInTempOutputBuffer = 0;
- else
- bp->framesInTempOutputBuffer = bp->framesPerUserBuffer;
- }
- else
- {
- /* paComplete or paAbort has already been called. */
-
- bp->framesInTempInputBuffer = 0;
- }
- }
-
- /* copy frames from user (tempOutputBuffer) to host output buffers (hostOutputChannels)
- Means to process the user output provided by the callback. Has to be called after
- each callback. */
- CopyTempOutputBuffersToHostOutputBuffers( bp );
-
- }
-
- return framesProcessed;
-}
-
-
-unsigned long PaUtil_EndBufferProcessing( PaUtilBufferProcessor* bp, int *streamCallbackResult )
-{
- unsigned long framesToProcess, framesToGo;
- unsigned long framesProcessed = 0;
-
- if( bp->inputChannelCount != 0 && bp->outputChannelCount != 0
- && bp->hostInputChannels[0][0].data /* input was supplied (see PaUtil_SetNoInput) */
- && bp->hostOutputChannels[0][0].data /* output was supplied (see PaUtil_SetNoOutput) */ )
- {
- assert( (bp->hostInputFrameCount[0] + bp->hostInputFrameCount[1]) ==
- (bp->hostOutputFrameCount[0] + bp->hostOutputFrameCount[1]) );
- }
-
- assert( *streamCallbackResult == paContinue
- || *streamCallbackResult == paComplete
- || *streamCallbackResult == paAbort ); /* don't forget to pass in a valid callback result value */
-
- if( bp->useNonAdaptingProcess )
- {
- if( bp->inputChannelCount != 0 && bp->outputChannelCount != 0 )
- {
- /* full duplex non-adapting process, splice buffers if they are
- different lengths */
-
- framesToGo = bp->hostOutputFrameCount[0] + bp->hostOutputFrameCount[1]; /* relies on assert above for input/output equivalence */
-
- do{
- unsigned long noInputInputFrameCount;
- unsigned long *hostInputFrameCount;
- PaUtilChannelDescriptor *hostInputChannels;
- unsigned long noOutputOutputFrameCount;
- unsigned long *hostOutputFrameCount;
- PaUtilChannelDescriptor *hostOutputChannels;
- unsigned long framesProcessedThisIteration;
-
- if( !bp->hostInputChannels[0][0].data )
- {
- /* no input was supplied (see PaUtil_SetNoInput)
- NonAdaptingProcess knows how to deal with this
- */
- noInputInputFrameCount = framesToGo;
- hostInputFrameCount = &noInputInputFrameCount;
- hostInputChannels = 0;
- }
- else if( bp->hostInputFrameCount[0] != 0 )
- {
- hostInputFrameCount = &bp->hostInputFrameCount[0];
- hostInputChannels = bp->hostInputChannels[0];
- }
- else
- {
- hostInputFrameCount = &bp->hostInputFrameCount[1];
- hostInputChannels = bp->hostInputChannels[1];
- }
-
- if( !bp->hostOutputChannels[0][0].data )
- {
- /* no output was supplied (see PaUtil_SetNoOutput)
- NonAdaptingProcess knows how to deal with this
- */
- noOutputOutputFrameCount = framesToGo;
- hostOutputFrameCount = &noOutputOutputFrameCount;
- hostOutputChannels = 0;
- }
- if( bp->hostOutputFrameCount[0] != 0 )
- {
- hostOutputFrameCount = &bp->hostOutputFrameCount[0];
- hostOutputChannels = bp->hostOutputChannels[0];
- }
- else
- {
- hostOutputFrameCount = &bp->hostOutputFrameCount[1];
- hostOutputChannels = bp->hostOutputChannels[1];
- }
-
- framesToProcess = PA_MIN_( *hostInputFrameCount,
- *hostOutputFrameCount );
-
- assert( framesToProcess != 0 );
-
- framesProcessedThisIteration = NonAdaptingProcess( bp, streamCallbackResult,
- hostInputChannels, hostOutputChannels,
- framesToProcess );
-
- *hostInputFrameCount -= framesProcessedThisIteration;
- *hostOutputFrameCount -= framesProcessedThisIteration;
-
- framesProcessed += framesProcessedThisIteration;
- framesToGo -= framesProcessedThisIteration;
-
- }while( framesToGo > 0 );
- }
- else
- {
- /* half duplex non-adapting process, just process 1st and 2nd buffer */
- /* process first buffer */
-
- framesToProcess = (bp->inputChannelCount != 0)
- ? bp->hostInputFrameCount[0]
- : bp->hostOutputFrameCount[0];
-
- framesProcessed = NonAdaptingProcess( bp, streamCallbackResult,
- bp->hostInputChannels[0], bp->hostOutputChannels[0],
- framesToProcess );
-
- /* process second buffer if provided */
-
- framesToProcess = (bp->inputChannelCount != 0)
- ? bp->hostInputFrameCount[1]
- : bp->hostOutputFrameCount[1];
- if( framesToProcess > 0 )
- {
- framesProcessed += NonAdaptingProcess( bp, streamCallbackResult,
- bp->hostInputChannels[1], bp->hostOutputChannels[1],
- framesToProcess );
- }
- }
- }
- else /* block adaption necessary*/
- {
-
- if( bp->inputChannelCount != 0 && bp->outputChannelCount != 0 )
- {
- /* full duplex */
-
- if( bp->hostBufferSizeMode == paUtilVariableHostBufferSizePartialUsageAllowed )
- {
- framesProcessed = AdaptingProcess( bp, streamCallbackResult,
- 0 /* dont process partial user buffers */ );
- }
- else
- {
- framesProcessed = AdaptingProcess( bp, streamCallbackResult,
- 1 /* process partial user buffers */ );
- }
- }
- else if( bp->inputChannelCount != 0 )
- {
- /* input only */
- framesToProcess = bp->hostInputFrameCount[0];
-
- framesProcessed = AdaptingInputOnlyProcess( bp, streamCallbackResult,
- bp->hostInputChannels[0], framesToProcess );
-
- framesToProcess = bp->hostInputFrameCount[1];
- if( framesToProcess > 0 )
- {
- framesProcessed += AdaptingInputOnlyProcess( bp, streamCallbackResult,
- bp->hostInputChannels[1], framesToProcess );
- }
- }
- else
- {
- /* output only */
- framesToProcess = bp->hostOutputFrameCount[0];
-
- framesProcessed = AdaptingOutputOnlyProcess( bp, streamCallbackResult,
- bp->hostOutputChannels[0], framesToProcess );
-
- framesToProcess = bp->hostOutputFrameCount[1];
- if( framesToProcess > 0 )
- {
- framesProcessed += AdaptingOutputOnlyProcess( bp, streamCallbackResult,
- bp->hostOutputChannels[1], framesToProcess );
- }
- }
- }
-
- return framesProcessed;
-}
-
-
-int PaUtil_IsBufferProcessorOutputEmpty( PaUtilBufferProcessor* bp )
-{
- return (bp->framesInTempOutputBuffer) ? 0 : 1;
-}
-
-
-unsigned long PaUtil_CopyInput( PaUtilBufferProcessor* bp,
- void **buffer, unsigned long frameCount )
-{
- PaUtilChannelDescriptor *hostInputChannels;
- unsigned int framesToCopy;
- unsigned char *destBytePtr;
- void **nonInterleavedDestPtrs;
- unsigned int destSampleStrideSamples; /* stride from one sample to the next within a channel, in samples */
- unsigned int destChannelStrideBytes; /* stride from one channel to the next, in bytes */
- unsigned int i;
-
- hostInputChannels = bp->hostInputChannels[0];
- framesToCopy = PA_MIN_( bp->hostInputFrameCount[0], frameCount );
-
- if( bp->userInputIsInterleaved )
- {
- destBytePtr = (unsigned char*)*buffer;
-
- destSampleStrideSamples = bp->inputChannelCount;
- destChannelStrideBytes = bp->bytesPerUserInputSample;
-
- for( i=0; i<bp->inputChannelCount; ++i )
- {
- bp->inputConverter( destBytePtr, destSampleStrideSamples,
- hostInputChannels[i].data,
- hostInputChannels[i].stride,
- framesToCopy, &bp->ditherGenerator );
-
- destBytePtr += destChannelStrideBytes; /* skip to next source channel */
-
- /* advance dest ptr for next iteration */
- hostInputChannels[i].data = ((unsigned char*)hostInputChannels[i].data) +
- framesToCopy * hostInputChannels[i].stride * bp->bytesPerHostInputSample;
- }
-
- /* advance callers dest pointer (buffer) */
- *buffer = ((unsigned char *)*buffer) +
- framesToCopy * bp->inputChannelCount * bp->bytesPerUserInputSample;
- }
- else
- {
- /* user input is not interleaved */
-
- nonInterleavedDestPtrs = (void**)*buffer;
-
- destSampleStrideSamples = 1;
-
- for( i=0; i<bp->inputChannelCount; ++i )
- {
- destBytePtr = (unsigned char*)nonInterleavedDestPtrs[i];
-
- bp->inputConverter( destBytePtr, destSampleStrideSamples,
- hostInputChannels[i].data,
- hostInputChannels[i].stride,
- framesToCopy, &bp->ditherGenerator );
-
- /* advance callers dest pointer (nonInterleavedDestPtrs[i]) */
- destBytePtr += bp->bytesPerUserInputSample * framesToCopy;
- nonInterleavedDestPtrs[i] = destBytePtr;
-
- /* advance dest ptr for next iteration */
- hostInputChannels[i].data = ((unsigned char*)hostInputChannels[i].data) +
- framesToCopy * hostInputChannels[i].stride * bp->bytesPerHostInputSample;
- }
- }
-
- bp->hostInputFrameCount[0] -= framesToCopy;
-
- return framesToCopy;
-}
-
-unsigned long PaUtil_CopyOutput( PaUtilBufferProcessor* bp,
- const void ** buffer, unsigned long frameCount )
-{
- PaUtilChannelDescriptor *hostOutputChannels;
- unsigned int framesToCopy;
- unsigned char *srcBytePtr;
- void **nonInterleavedSrcPtrs;
- unsigned int srcSampleStrideSamples; /* stride from one sample to the next within a channel, in samples */
- unsigned int srcChannelStrideBytes; /* stride from one channel to the next, in bytes */
- unsigned int i;
-
- hostOutputChannels = bp->hostOutputChannels[0];
- framesToCopy = PA_MIN_( bp->hostOutputFrameCount[0], frameCount );
-
- if( bp->userOutputIsInterleaved )
- {
- srcBytePtr = (unsigned char*)*buffer;
-
- srcSampleStrideSamples = bp->outputChannelCount;
- srcChannelStrideBytes = bp->bytesPerUserOutputSample;
-
- for( i=0; i<bp->outputChannelCount; ++i )
- {
- bp->outputConverter( hostOutputChannels[i].data,
- hostOutputChannels[i].stride,
- srcBytePtr, srcSampleStrideSamples,
- framesToCopy, &bp->ditherGenerator );
-
- srcBytePtr += srcChannelStrideBytes; /* skip to next source channel */
-
- /* advance dest ptr for next iteration */
- hostOutputChannels[i].data = ((unsigned char*)hostOutputChannels[i].data) +
- framesToCopy * hostOutputChannels[i].stride * bp->bytesPerHostOutputSample;
- }
-
- /* advance callers source pointer (buffer) */
- *buffer = ((unsigned char *)*buffer) +
- framesToCopy * bp->outputChannelCount * bp->bytesPerUserOutputSample;
-
- }
- else
- {
- /* user output is not interleaved */
-
- nonInterleavedSrcPtrs = (void**)*buffer;
-
- srcSampleStrideSamples = 1;
-
- for( i=0; i<bp->outputChannelCount; ++i )
- {
- srcBytePtr = (unsigned char*)nonInterleavedSrcPtrs[i];
-
- bp->outputConverter( hostOutputChannels[i].data,
- hostOutputChannels[i].stride,
- srcBytePtr, srcSampleStrideSamples,
- framesToCopy, &bp->ditherGenerator );
-
-
- /* advance callers source pointer (nonInterleavedSrcPtrs[i]) */
- srcBytePtr += bp->bytesPerUserOutputSample * framesToCopy;
- nonInterleavedSrcPtrs[i] = srcBytePtr;
-
- /* advance dest ptr for next iteration */
- hostOutputChannels[i].data = ((unsigned char*)hostOutputChannels[i].data) +
- framesToCopy * hostOutputChannels[i].stride * bp->bytesPerHostOutputSample;
- }
- }
-
- bp->hostOutputFrameCount[0] += framesToCopy;
-
- return framesToCopy;
-}
-
-
-unsigned long PaUtil_ZeroOutput( PaUtilBufferProcessor* bp, unsigned long frameCount )
-{
- PaUtilChannelDescriptor *hostOutputChannels;
- unsigned int framesToZero;
- unsigned int i;
-
- hostOutputChannels = bp->hostOutputChannels[0];
- framesToZero = PA_MIN_( bp->hostOutputFrameCount[0], frameCount );
-
- for( i=0; i<bp->outputChannelCount; ++i )
- {
- bp->outputZeroer( hostOutputChannels[i].data,
- hostOutputChannels[i].stride,
- framesToZero );
-
-
- /* advance dest ptr for next iteration */
- hostOutputChannels[i].data = ((unsigned char*)hostOutputChannels[i].data) +
- framesToZero * hostOutputChannels[i].stride * bp->bytesPerHostOutputSample;
- }
-
- bp->hostOutputFrameCount[0] += framesToZero;
-
- return framesToZero;
-}
+/*
+ * $Id: pa_process.c,v 1.1.2.48 2004/12/13 09:48:43 rossbencina Exp $
+ * Portable Audio I/O Library
+ * streamCallback <-> host buffer processing adapter
+ *
+ * Based on the Open Source API proposed by Ross Bencina
+ * Copyright (c) 1999-2002 Ross Bencina, Phil Burk
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * Any person wishing to distribute modifications to the Software is
+ * requested to send the modifications to the original developer so that
+ * they can be incorporated into the canonical version.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+ * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+/*
+ * PJMEDIA - Multimedia over IP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/** @file
+ @brief Buffer Processor implementation.
+
+ The code in this file is not optimised yet - although it's not clear that
+ it needs to be. there may appear to be redundancies
+ that could be factored into common functions, but the redundanceis are left
+ intentionally as each appearance may have different optimisation possibilities.
+
+ The optimisations which are planned involve only converting data in-place
+ where possible, rather than copying to the temp buffer(s).
+
+ Note that in the extreme case of being able to convert in-place, and there
+ being no conversion necessary there should be some code which short-circuits
+ the operation.
+
+ @todo Consider cache tilings for intereave<->deinterleave.
+
+ @todo implement timeInfo->currentTime int PaUtil_BeginBufferProcessing()
+
+ @todo specify and implement some kind of logical policy for handling the
+ underflow and overflow stream flags when the underflow/overflow overlaps
+ multiple user buffers/callbacks.
+
+ @todo provide support for priming the buffers with data from the callback.
+ The client interface is now implemented through PaUtil_SetNoInput()
+ which sets bp->hostInputChannels[0][0].data to zero. However this is
+ currently only implemented in NonAdaptingProcess(). It shouldn't be
+ needed for AdaptingInputOnlyProcess() (no priming should ever be
+ requested for AdaptingInputOnlyProcess()).
+ Not sure if additional work should be required to make it work with
+ AdaptingOutputOnlyProcess, but it definitely is required for
+ AdaptingProcess.
+
+ @todo implement PaUtil_SetNoOutput for AdaptingProcess
+
+ @todo don't allocate temp buffers for blocking streams unless they are
+ needed. At the moment they are needed, but perhaps for host APIs
+ where the implementation passes a buffer to the host they could be
+ used.
+*/
+
+
+#include <assert.h>
+#include <string.h> /* memset() */
+
+#include "pa_process.h"
+#include "pa_util.h"
+
+
+#define PA_FRAMES_PER_TEMP_BUFFER_WHEN_HOST_BUFFER_SIZE_IS_UNKNOWN_ 1024
+
+#define PA_MIN_( a, b ) ( ((a)<(b)) ? (a) : (b) )
+
+
+/* greatest common divisor - PGCD in French */
+static unsigned long GCD( unsigned long a, unsigned long b )
+{
+ return (b==0) ? a : GCD( b, a%b);
+}
+
+/* least common multiple - PPCM in French */
+static unsigned long LCM( unsigned long a, unsigned long b )
+{
+ return (a*b) / GCD(a,b);
+}
+
+#define PA_MAX_( a, b ) (((a) > (b)) ? (a) : (b))
+
+static unsigned long CalculateFrameShift( unsigned long M, unsigned long N )
+{
+ unsigned long result = 0;
+ unsigned long i;
+ unsigned long lcm;
+
+ assert( M > 0 );
+ assert( N > 0 );
+
+ lcm = LCM( M, N );
+ for( i = M; i < lcm; i += M )
+ result = PA_MAX_( result, i % N );
+
+ return result;
+}
+
+
+PaError PaUtil_InitializeBufferProcessor( PaUtilBufferProcessor* bp,
+ int inputChannelCount, PaSampleFormat userInputSampleFormat,
+ PaSampleFormat hostInputSampleFormat,
+ int outputChannelCount, PaSampleFormat userOutputSampleFormat,
+ PaSampleFormat hostOutputSampleFormat,
+ double sampleRate,
+ PaStreamFlags streamFlags,
+ unsigned long framesPerUserBuffer,
+ unsigned long framesPerHostBuffer,
+ PaUtilHostBufferSizeMode hostBufferSizeMode,
+ PaStreamCallback *streamCallback, void *userData )
+{
+ PaError result = paNoError;
+ PaError bytesPerSample;
+ unsigned long tempInputBufferSize, tempOutputBufferSize;
+
+ /* initialize buffer ptrs to zero so they can be freed if necessary in error */
+ bp->tempInputBuffer = 0;
+ bp->tempInputBufferPtrs = 0;
+ bp->tempOutputBuffer = 0;
+ bp->tempOutputBufferPtrs = 0;
+
+ bp->framesPerUserBuffer = framesPerUserBuffer;
+ bp->framesPerHostBuffer = framesPerHostBuffer;
+
+ bp->inputChannelCount = inputChannelCount;
+ bp->outputChannelCount = outputChannelCount;
+
+ bp->hostBufferSizeMode = hostBufferSizeMode;
+
+ bp->hostInputChannels[0] = bp->hostInputChannels[1] = 0;
+ bp->hostOutputChannels[0] = bp->hostOutputChannels[1] = 0;
+
+ if( framesPerUserBuffer == 0 ) /* streamCallback will accept any buffer size */
+ {
+ bp->useNonAdaptingProcess = 1;
+ bp->initialFramesInTempInputBuffer = 0;
+ bp->initialFramesInTempOutputBuffer = 0;
+
+ if( hostBufferSizeMode == paUtilFixedHostBufferSize
+ || hostBufferSizeMode == paUtilBoundedHostBufferSize )
+ {
+ bp->framesPerTempBuffer = framesPerHostBuffer;
+ }
+ else /* unknown host buffer size */
+ {
+ bp->framesPerTempBuffer = PA_FRAMES_PER_TEMP_BUFFER_WHEN_HOST_BUFFER_SIZE_IS_UNKNOWN_;
+ }
+ }
+ else
+ {
+ bp->framesPerTempBuffer = framesPerUserBuffer;
+
+ if( hostBufferSizeMode == paUtilFixedHostBufferSize
+ && framesPerHostBuffer % framesPerUserBuffer == 0 )
+ {
+ bp->useNonAdaptingProcess = 1;
+ bp->initialFramesInTempInputBuffer = 0;
+ bp->initialFramesInTempOutputBuffer = 0;
+ }
+ else
+ {
+ bp->useNonAdaptingProcess = 0;
+
+ if( inputChannelCount > 0 && outputChannelCount > 0 )
+ {
+ /* full duplex */
+ if( hostBufferSizeMode == paUtilFixedHostBufferSize )
+ {
+ unsigned long frameShift =
+ CalculateFrameShift( framesPerHostBuffer, framesPerUserBuffer );
+
+ if( framesPerUserBuffer > framesPerHostBuffer )
+ {
+ bp->initialFramesInTempInputBuffer = frameShift;
+ bp->initialFramesInTempOutputBuffer = 0;
+ }
+ else
+ {
+ bp->initialFramesInTempInputBuffer = 0;
+ bp->initialFramesInTempOutputBuffer = frameShift;
+ }
+ }
+ else /* variable host buffer size, add framesPerUserBuffer latency */
+ {
+ bp->initialFramesInTempInputBuffer = 0;
+ bp->initialFramesInTempOutputBuffer = framesPerUserBuffer;
+ }
+ }
+ else
+ {
+ /* half duplex */
+ bp->initialFramesInTempInputBuffer = 0;
+ bp->initialFramesInTempOutputBuffer = 0;
+ }
+ }
+ }
+
+
+ bp->framesInTempInputBuffer = bp->initialFramesInTempInputBuffer;
+ bp->framesInTempOutputBuffer = bp->initialFramesInTempOutputBuffer;
+
+
+ if( inputChannelCount > 0 )
+ {
+ bytesPerSample = Pa_GetSampleSize( hostInputSampleFormat );
+ if( bytesPerSample > 0 )
+ {
+ bp->bytesPerHostInputSample = bytesPerSample;
+ }
+ else
+ {
+ result = bytesPerSample;
+ goto error;
+ }
+
+ bytesPerSample = Pa_GetSampleSize( userInputSampleFormat );
+ if( bytesPerSample > 0 )
+ {
+ bp->bytesPerUserInputSample = bytesPerSample;
+ }
+ else
+ {
+ result = bytesPerSample;
+ goto error;
+ }
+
+ bp->inputConverter =
+ PaUtil_SelectConverter( hostInputSampleFormat, userInputSampleFormat, streamFlags );
+
+ bp->inputZeroer = PaUtil_SelectZeroer( hostInputSampleFormat );
+
+ bp->userInputIsInterleaved = (userInputSampleFormat & paNonInterleaved)?0:1;
+
+
+ tempInputBufferSize =
+ bp->framesPerTempBuffer * bp->bytesPerUserInputSample * inputChannelCount;
+
+ bp->tempInputBuffer = PaUtil_AllocateMemory( tempInputBufferSize );
+ if( bp->tempInputBuffer == 0 )
+ {
+ result = paInsufficientMemory;
+ goto error;
+ }
+
+ if( bp->framesInTempInputBuffer > 0 )
+ memset( bp->tempInputBuffer, 0, tempInputBufferSize );
+
+ if( userInputSampleFormat & paNonInterleaved )
+ {
+ bp->tempInputBufferPtrs =
+ (void **)PaUtil_AllocateMemory( sizeof(void*)*inputChannelCount );
+ if( bp->tempInputBufferPtrs == 0 )
+ {
+ result = paInsufficientMemory;
+ goto error;
+ }
+ }
+
+ bp->hostInputChannels[0] = (PaUtilChannelDescriptor*)
+ PaUtil_AllocateMemory( sizeof(PaUtilChannelDescriptor) * inputChannelCount * 2);
+ if( bp->hostInputChannels[0] == 0 )
+ {
+ result = paInsufficientMemory;
+ goto error;
+ }
+
+ bp->hostInputChannels[1] = &bp->hostInputChannels[0][inputChannelCount];
+ }
+
+ if( outputChannelCount > 0 )
+ {
+ bytesPerSample = Pa_GetSampleSize( hostOutputSampleFormat );
+ if( bytesPerSample > 0 )
+ {
+ bp->bytesPerHostOutputSample = bytesPerSample;
+ }
+ else
+ {
+ result = bytesPerSample;
+ goto error;
+ }
+
+ bytesPerSample = Pa_GetSampleSize( userOutputSampleFormat );
+ if( bytesPerSample > 0 )
+ {
+ bp->bytesPerUserOutputSample = bytesPerSample;
+ }
+ else
+ {
+ result = bytesPerSample;
+ goto error;
+ }
+
+ bp->outputConverter =
+ PaUtil_SelectConverter( userOutputSampleFormat, hostOutputSampleFormat, streamFlags );
+
+ bp->outputZeroer = PaUtil_SelectZeroer( hostOutputSampleFormat );
+
+ bp->userOutputIsInterleaved = (userOutputSampleFormat & paNonInterleaved)?0:1;
+
+ tempOutputBufferSize =
+ bp->framesPerTempBuffer * bp->bytesPerUserOutputSample * outputChannelCount;
+
+ bp->tempOutputBuffer = PaUtil_AllocateMemory( tempOutputBufferSize );
+ if( bp->tempOutputBuffer == 0 )
+ {
+ result = paInsufficientMemory;
+ goto error;
+ }
+
+ if( bp->framesInTempOutputBuffer > 0 )
+ memset( bp->tempOutputBuffer, 0, tempOutputBufferSize );
+
+ if( userOutputSampleFormat & paNonInterleaved )
+ {
+ bp->tempOutputBufferPtrs =
+ (void **)PaUtil_AllocateMemory( sizeof(void*)*outputChannelCount );
+ if( bp->tempOutputBufferPtrs == 0 )
+ {
+ result = paInsufficientMemory;
+ goto error;
+ }
+ }
+
+ bp->hostOutputChannels[0] = (PaUtilChannelDescriptor*)
+ PaUtil_AllocateMemory( sizeof(PaUtilChannelDescriptor)*outputChannelCount * 2 );
+ if( bp->hostOutputChannels[0] == 0 )
+ {
+ result = paInsufficientMemory;
+ goto error;
+ }
+
+ bp->hostOutputChannels[1] = &bp->hostOutputChannels[0][outputChannelCount];
+ }
+
+ PaUtil_InitializeTriangularDitherState( &bp->ditherGenerator );
+
+ bp->samplePeriod = 1. / sampleRate;
+
+ bp->streamCallback = streamCallback;
+ bp->userData = userData;
+
+ return result;
+
+error:
+ if( bp->tempInputBuffer )
+ PaUtil_FreeMemory( bp->tempInputBuffer );
+
+ if( bp->tempInputBufferPtrs )
+ PaUtil_FreeMemory( bp->tempInputBufferPtrs );
+
+ if( bp->hostInputChannels[0] )
+ PaUtil_FreeMemory( bp->hostInputChannels[0] );
+
+ if( bp->tempOutputBuffer )
+ PaUtil_FreeMemory( bp->tempOutputBuffer );
+
+ if( bp->tempOutputBufferPtrs )
+ PaUtil_FreeMemory( bp->tempOutputBufferPtrs );
+
+ if( bp->hostOutputChannels[0] )
+ PaUtil_FreeMemory( bp->hostOutputChannels[0] );
+
+ return result;
+}
+
+
+void PaUtil_TerminateBufferProcessor( PaUtilBufferProcessor* bp )
+{
+ if( bp->tempInputBuffer )
+ PaUtil_FreeMemory( bp->tempInputBuffer );
+
+ if( bp->tempInputBufferPtrs )
+ PaUtil_FreeMemory( bp->tempInputBufferPtrs );
+
+ if( bp->hostInputChannels[0] )
+ PaUtil_FreeMemory( bp->hostInputChannels[0] );
+
+ if( bp->tempOutputBuffer )
+ PaUtil_FreeMemory( bp->tempOutputBuffer );
+
+ if( bp->tempOutputBufferPtrs )
+ PaUtil_FreeMemory( bp->tempOutputBufferPtrs );
+
+ if( bp->hostOutputChannels[0] )
+ PaUtil_FreeMemory( bp->hostOutputChannels[0] );
+}
+
+
+void PaUtil_ResetBufferProcessor( PaUtilBufferProcessor* bp )
+{
+ unsigned long tempInputBufferSize, tempOutputBufferSize;
+
+ bp->framesInTempInputBuffer = bp->initialFramesInTempInputBuffer;
+ bp->framesInTempOutputBuffer = bp->initialFramesInTempOutputBuffer;
+
+ if( bp->framesInTempInputBuffer > 0 )
+ {
+ tempInputBufferSize =
+ bp->framesPerTempBuffer * bp->bytesPerUserInputSample * bp->inputChannelCount;
+ memset( bp->tempInputBuffer, 0, tempInputBufferSize );
+ }
+
+ if( bp->framesInTempOutputBuffer > 0 )
+ {
+ tempOutputBufferSize =
+ bp->framesPerTempBuffer * bp->bytesPerUserOutputSample * bp->outputChannelCount;
+ memset( bp->tempOutputBuffer, 0, tempOutputBufferSize );
+ }
+}
+
+
+unsigned long PaUtil_GetBufferProcessorInputLatency( PaUtilBufferProcessor* bp )
+{
+ return bp->initialFramesInTempInputBuffer;
+}
+
+
+unsigned long PaUtil_GetBufferProcessorOutputLatency( PaUtilBufferProcessor* bp )
+{
+ return bp->initialFramesInTempOutputBuffer;
+}
+
+
+void PaUtil_SetInputFrameCount( PaUtilBufferProcessor* bp,
+ unsigned long frameCount )
+{
+ if( frameCount == 0 )
+ bp->hostInputFrameCount[0] = bp->framesPerHostBuffer;
+ else
+ bp->hostInputFrameCount[0] = frameCount;
+}
+
+
+void PaUtil_SetNoInput( PaUtilBufferProcessor* bp )
+{
+ assert( bp->inputChannelCount > 0 );
+
+ bp->hostInputChannels[0][0].data = 0;
+}
+
+
+void PaUtil_SetInputChannel( PaUtilBufferProcessor* bp,
+ unsigned int channel, void *data, unsigned int stride )
+{
+ assert( channel < bp->inputChannelCount );
+
+ bp->hostInputChannels[0][channel].data = data;
+ bp->hostInputChannels[0][channel].stride = stride;
+}
+
+
+void PaUtil_SetInterleavedInputChannels( PaUtilBufferProcessor* bp,
+ unsigned int firstChannel, void *data, unsigned int channelCount )
+{
+ unsigned int i;
+ unsigned int channel = firstChannel;
+ unsigned char *p = (unsigned char*)data;
+
+ if( channelCount == 0 )
+ channelCount = bp->inputChannelCount;
+
+ assert( firstChannel < bp->inputChannelCount );
+ assert( firstChannel + channelCount <= bp->inputChannelCount );
+
+ for( i=0; i< channelCount; ++i )
+ {
+ bp->hostInputChannels[0][channel+i].data = p;
+ p += bp->bytesPerHostInputSample;
+ bp->hostInputChannels[0][channel+i].stride = channelCount;
+ }
+}
+
+
+void PaUtil_SetNonInterleavedInputChannel( PaUtilBufferProcessor* bp,
+ unsigned int channel, void *data )
+{
+ assert( channel < bp->inputChannelCount );
+
+ bp->hostInputChannels[0][channel].data = data;
+ bp->hostInputChannels[0][channel].stride = 1;
+}
+
+
+void PaUtil_Set2ndInputFrameCount( PaUtilBufferProcessor* bp,
+ unsigned long frameCount )
+{
+ bp->hostInputFrameCount[1] = frameCount;
+}
+
+
+void PaUtil_Set2ndInputChannel( PaUtilBufferProcessor* bp,
+ unsigned int channel, void *data, unsigned int stride )
+{
+ assert( channel < bp->inputChannelCount );
+
+ bp->hostInputChannels[1][channel].data = data;
+ bp->hostInputChannels[1][channel].stride = stride;
+}
+
+
+void PaUtil_Set2ndInterleavedInputChannels( PaUtilBufferProcessor* bp,
+ unsigned int firstChannel, void *data, unsigned int channelCount )
+{
+ unsigned int i;
+ unsigned int channel = firstChannel;
+ unsigned char *p = (unsigned char*)data;
+
+ if( channelCount == 0 )
+ channelCount = bp->inputChannelCount;
+
+ assert( firstChannel < bp->inputChannelCount );
+ assert( firstChannel + channelCount <= bp->inputChannelCount );
+
+ for( i=0; i< channelCount; ++i )
+ {
+ bp->hostInputChannels[1][channel+i].data = p;
+ p += bp->bytesPerHostInputSample;
+ bp->hostInputChannels[1][channel+i].stride = channelCount;
+ }
+}
+
+
+void PaUtil_Set2ndNonInterleavedInputChannel( PaUtilBufferProcessor* bp,
+ unsigned int channel, void *data )
+{
+ assert( channel < bp->inputChannelCount );
+
+ bp->hostInputChannels[1][channel].data = data;
+ bp->hostInputChannels[1][channel].stride = 1;
+}
+
+
+void PaUtil_SetOutputFrameCount( PaUtilBufferProcessor* bp,
+ unsigned long frameCount )
+{
+ if( frameCount == 0 )
+ bp->hostOutputFrameCount[0] = bp->framesPerHostBuffer;
+ else
+ bp->hostOutputFrameCount[0] = frameCount;
+}
+
+
+void PaUtil_SetNoOutput( PaUtilBufferProcessor* bp )
+{
+ assert( bp->outputChannelCount > 0 );
+
+ bp->hostOutputChannels[0][0].data = 0;
+}
+
+
+void PaUtil_SetOutputChannel( PaUtilBufferProcessor* bp,
+ unsigned int channel, void *data, unsigned int stride )
+{
+ assert( channel < bp->outputChannelCount );
+
+ bp->hostOutputChannels[0][channel].data = data;
+ bp->hostOutputChannels[0][channel].stride = stride;
+}
+
+
+void PaUtil_SetInterleavedOutputChannels( PaUtilBufferProcessor* bp,
+ unsigned int firstChannel, void *data, unsigned int channelCount )
+{
+ unsigned int i;
+ unsigned int channel = firstChannel;
+ unsigned char *p = (unsigned char*)data;
+
+ if( channelCount == 0 )
+ channelCount = bp->outputChannelCount;
+
+ assert( firstChannel < bp->outputChannelCount );
+ assert( firstChannel + channelCount <= bp->outputChannelCount );
+
+ for( i=0; i< channelCount; ++i )
+ {
+ bp->hostOutputChannels[0][channel+i].data = p;
+ p += bp->bytesPerHostOutputSample;
+ bp->hostOutputChannels[0][channel+i].stride = channelCount;
+ }
+}
+
+
+void PaUtil_SetNonInterleavedOutputChannel( PaUtilBufferProcessor* bp,
+ unsigned int channel, void *data )
+{
+ assert( channel < bp->outputChannelCount );
+
+ bp->hostOutputChannels[0][channel].data = data;
+ bp->hostOutputChannels[0][channel].stride = 1;
+}
+
+
+void PaUtil_Set2ndOutputFrameCount( PaUtilBufferProcessor* bp,
+ unsigned long frameCount )
+{
+ bp->hostOutputFrameCount[1] = frameCount;
+}
+
+
+void PaUtil_Set2ndOutputChannel( PaUtilBufferProcessor* bp,
+ unsigned int channel, void *data, unsigned int stride )
+{
+ assert( channel < bp->outputChannelCount );
+
+ bp->hostOutputChannels[1][channel].data = data;
+ bp->hostOutputChannels[1][channel].stride = stride;
+}
+
+
+void PaUtil_Set2ndInterleavedOutputChannels( PaUtilBufferProcessor* bp,
+ unsigned int firstChannel, void *data, unsigned int channelCount )
+{
+ unsigned int i;
+ unsigned int channel = firstChannel;
+ unsigned char *p = (unsigned char*)data;
+
+ if( channelCount == 0 )
+ channelCount = bp->outputChannelCount;
+
+ assert( firstChannel < bp->outputChannelCount );
+ assert( firstChannel + channelCount <= bp->outputChannelCount );
+
+ for( i=0; i< channelCount; ++i )
+ {
+ bp->hostOutputChannels[1][channel+i].data = p;
+ p += bp->bytesPerHostOutputSample;
+ bp->hostOutputChannels[1][channel+i].stride = channelCount;
+ }
+}
+
+
+void PaUtil_Set2ndNonInterleavedOutputChannel( PaUtilBufferProcessor* bp,
+ unsigned int channel, void *data )
+{
+ assert( channel < bp->outputChannelCount );
+
+ bp->hostOutputChannels[1][channel].data = data;
+ bp->hostOutputChannels[1][channel].stride = 1;
+}
+
+
+void PaUtil_BeginBufferProcessing( PaUtilBufferProcessor* bp,
+ PaStreamCallbackTimeInfo* timeInfo, PaStreamCallbackFlags callbackStatusFlags )
+{
+ bp->timeInfo = timeInfo;
+
+ /* the first streamCallback will be called to process samples which are
+ currently in the input buffer before the ones starting at the timeInfo time */
+
+ bp->timeInfo->inputBufferAdcTime -= bp->framesInTempInputBuffer * bp->samplePeriod;
+
+ bp->timeInfo->currentTime = 0; /** FIXME: @todo time info currentTime not implemented */
+
+ /* the first streamCallback will be called to generate samples which will be
+ outputted after the frames currently in the output buffer have been
+ outputted. */
+ bp->timeInfo->outputBufferDacTime += bp->framesInTempOutputBuffer * bp->samplePeriod;
+
+ bp->callbackStatusFlags = callbackStatusFlags;
+
+ bp->hostInputFrameCount[1] = 0;
+ bp->hostOutputFrameCount[1] = 0;
+}
+
+
+/*
+ NonAdaptingProcess() is a simple buffer copying adaptor that can handle
+ both full and half duplex copies. It processes framesToProcess frames,
+ broken into blocks bp->framesPerTempBuffer long.
+ This routine can be used when the streamCallback doesn't care what length
+ the buffers are, or when framesToProcess is an integer multiple of
+ bp->framesPerTempBuffer, in which case streamCallback will always be called
+ with bp->framesPerTempBuffer samples.
+*/
+static unsigned long NonAdaptingProcess( PaUtilBufferProcessor *bp,
+ int *streamCallbackResult,
+ PaUtilChannelDescriptor *hostInputChannels,
+ PaUtilChannelDescriptor *hostOutputChannels,
+ unsigned long framesToProcess )
+{
+ void *userInput, *userOutput;
+ unsigned char *srcBytePtr, *destBytePtr;
+ unsigned int srcSampleStrideSamples; /* stride from one sample to the next within a channel, in samples */
+ unsigned int srcChannelStrideBytes; /* stride from one channel to the next, in bytes */
+ unsigned int destSampleStrideSamples; /* stride from one sample to the next within a channel, in samples */
+ unsigned int destChannelStrideBytes; /* stride from one channel to the next, in bytes */
+ unsigned int i;
+ unsigned long frameCount;
+ unsigned long framesToGo = framesToProcess;
+ unsigned long framesProcessed = 0;
+
+
+ if( *streamCallbackResult == paContinue )
+ {
+ do
+ {
+ frameCount = PA_MIN_( bp->framesPerTempBuffer, framesToGo );
+
+ /* configure user input buffer and convert input data (host -> user) */
+ if( bp->inputChannelCount == 0 )
+ {
+ /* no input */
+ userInput = 0;
+ }
+ else /* there are input channels */
+ {
+ /*
+ could use more elaborate logic here and sometimes process
+ buffers in-place.
+ */
+
+ destBytePtr = (unsigned char *)bp->tempInputBuffer;
+
+ if( bp->userInputIsInterleaved )
+ {
+ destSampleStrideSamples = bp->inputChannelCount;
+ destChannelStrideBytes = bp->bytesPerUserInputSample;
+ userInput = bp->tempInputBuffer;
+ }
+ else /* user input is not interleaved */
+ {
+ destSampleStrideSamples = 1;
+ destChannelStrideBytes = frameCount * bp->bytesPerUserInputSample;
+
+ /* setup non-interleaved ptrs */
+ for( i=0; i<bp->inputChannelCount; ++i )
+ {
+ bp->tempInputBufferPtrs[i] = ((unsigned char*)bp->tempInputBuffer) +
+ i * bp->bytesPerUserInputSample * frameCount;
+ }
+
+ userInput = bp->tempInputBufferPtrs;
+ }
+
+ if( !bp->hostInputChannels[0][0].data )
+ {
+ /* no input was supplied (see PaUtil_SetNoInput), so
+ zero the input buffer */
+
+ for( i=0; i<bp->inputChannelCount; ++i )
+ {
+ bp->inputZeroer( destBytePtr, destSampleStrideSamples, frameCount );
+ destBytePtr += destChannelStrideBytes; /* skip to next destination channel */
+ }
+ }
+ else
+ {
+ for( i=0; i<bp->inputChannelCount; ++i )
+ {
+ bp->inputConverter( destBytePtr, destSampleStrideSamples,
+ hostInputChannels[i].data,
+ hostInputChannels[i].stride,
+ frameCount, &bp->ditherGenerator );
+
+ destBytePtr += destChannelStrideBytes; /* skip to next destination channel */
+
+ /* advance src ptr for next iteration */
+ hostInputChannels[i].data = ((unsigned char*)hostInputChannels[i].data) +
+ frameCount * hostInputChannels[i].stride * bp->bytesPerHostInputSample;
+ }
+ }
+ }
+
+ /* configure user output buffer */
+ if( bp->outputChannelCount == 0 )
+ {
+ /* no output */
+ userOutput = 0;
+ }
+ else /* there are output channels */
+ {
+ if( bp->userOutputIsInterleaved )
+ {
+ userOutput = bp->tempOutputBuffer;
+ }
+ else /* user output is not interleaved */
+ {
+ for( i = 0; i < bp->outputChannelCount; ++i )
+ {
+ bp->tempOutputBufferPtrs[i] = ((unsigned char*)bp->tempOutputBuffer) +
+ i * bp->bytesPerUserOutputSample * frameCount;
+ }
+
+ userOutput = bp->tempOutputBufferPtrs;
+ }
+ }
+
+ *streamCallbackResult = bp->streamCallback( userInput, userOutput,
+ frameCount, bp->timeInfo, bp->callbackStatusFlags, bp->userData );
+
+ if( *streamCallbackResult == paAbort )
+ {
+ /* callback returned paAbort, don't advance framesProcessed
+ and framesToGo, they will be handled below */
+ }
+ else
+ {
+ bp->timeInfo->inputBufferAdcTime += frameCount * bp->samplePeriod;
+ bp->timeInfo->outputBufferDacTime += frameCount * bp->samplePeriod;
+
+ /* convert output data (user -> host) */
+
+ if( bp->outputChannelCount != 0 && bp->hostOutputChannels[0][0].data )
+ {
+ /*
+ could use more elaborate logic here and sometimes process
+ buffers in-place.
+ */
+
+ srcBytePtr = (unsigned char *)bp->tempOutputBuffer;
+
+ if( bp->userOutputIsInterleaved )
+ {
+ srcSampleStrideSamples = bp->outputChannelCount;
+ srcChannelStrideBytes = bp->bytesPerUserOutputSample;
+ }
+ else /* user output is not interleaved */
+ {
+ srcSampleStrideSamples = 1;
+ srcChannelStrideBytes = frameCount * bp->bytesPerUserOutputSample;
+ }
+
+ for( i=0; i<bp->outputChannelCount; ++i )
+ {
+ bp->outputConverter( hostOutputChannels[i].data,
+ hostOutputChannels[i].stride,
+ srcBytePtr, srcSampleStrideSamples,
+ frameCount, &bp->ditherGenerator );
+
+ srcBytePtr += srcChannelStrideBytes; /* skip to next source channel */
+
+ /* advance dest ptr for next iteration */
+ hostOutputChannels[i].data = ((unsigned char*)hostOutputChannels[i].data) +
+ frameCount * hostOutputChannels[i].stride * bp->bytesPerHostOutputSample;
+ }
+ }
+
+ framesProcessed += frameCount;
+
+ framesToGo -= frameCount;
+ }
+ }
+ while( framesToGo > 0 && *streamCallbackResult == paContinue );
+ }
+
+ if( framesToGo > 0 )
+ {
+ /* zero any remaining frames output. There will only be remaining frames
+ if the callback has returned paComplete or paAbort */
+
+ frameCount = framesToGo;
+
+ if( bp->outputChannelCount != 0 && bp->hostOutputChannels[0][0].data )
+ {
+ for( i=0; i<bp->outputChannelCount; ++i )
+ {
+ bp->outputZeroer( hostOutputChannels[i].data,
+ hostOutputChannels[i].stride,
+ frameCount );
+
+ /* advance dest ptr for next iteration */
+ hostOutputChannels[i].data = ((unsigned char*)hostOutputChannels[i].data) +
+ frameCount * hostOutputChannels[i].stride * bp->bytesPerHostOutputSample;
+ }
+ }
+
+ framesProcessed += frameCount;
+ }
+
+ return framesProcessed;
+}
+
+
+/*
+ AdaptingInputOnlyProcess() is a half duplex input buffer processor. It
+ converts data from the input buffers into the temporary input buffer,
+ when the temporary input buffer is full, it calls the streamCallback.
+*/
+static unsigned long AdaptingInputOnlyProcess( PaUtilBufferProcessor *bp,
+ int *streamCallbackResult,
+ PaUtilChannelDescriptor *hostInputChannels,
+ unsigned long framesToProcess )
+{
+ void *userInput, *userOutput;
+ unsigned char *destBytePtr;
+ unsigned int destSampleStrideSamples; /* stride from one sample to the next within a channel, in samples */
+ unsigned int destChannelStrideBytes; /* stride from one channel to the next, in bytes */
+ unsigned int i;
+ unsigned long frameCount;
+ unsigned long framesToGo = framesToProcess;
+ unsigned long framesProcessed = 0;
+
+ userOutput = 0;
+
+ do
+ {
+ frameCount = ( bp->framesInTempInputBuffer + framesToGo > bp->framesPerUserBuffer )
+ ? ( bp->framesPerUserBuffer - bp->framesInTempInputBuffer )
+ : framesToGo;
+
+ /* convert frameCount samples into temp buffer */
+
+ if( bp->userInputIsInterleaved )
+ {
+ destBytePtr = ((unsigned char*)bp->tempInputBuffer) +
+ bp->bytesPerUserInputSample * bp->inputChannelCount *
+ bp->framesInTempInputBuffer;
+
+ destSampleStrideSamples = bp->inputChannelCount;
+ destChannelStrideBytes = bp->bytesPerUserInputSample;
+
+ userInput = bp->tempInputBuffer;
+ }
+ else /* user input is not interleaved */
+ {
+ destBytePtr = ((unsigned char*)bp->tempInputBuffer) +
+ bp->bytesPerUserInputSample * bp->framesInTempInputBuffer;
+
+ destSampleStrideSamples = 1;
+ destChannelStrideBytes = bp->framesPerUserBuffer * bp->bytesPerUserInputSample;
+
+ /* setup non-interleaved ptrs */
+ for( i=0; i<bp->inputChannelCount; ++i )
+ {
+ bp->tempInputBufferPtrs[i] = ((unsigned char*)bp->tempInputBuffer) +
+ i * bp->bytesPerUserInputSample * bp->framesPerUserBuffer;
+ }
+
+ userInput = bp->tempInputBufferPtrs;
+ }
+
+ for( i=0; i<bp->inputChannelCount; ++i )
+ {
+ bp->inputConverter( destBytePtr, destSampleStrideSamples,
+ hostInputChannels[i].data,
+ hostInputChannels[i].stride,
+ frameCount, &bp->ditherGenerator );
+
+ destBytePtr += destChannelStrideBytes; /* skip to next destination channel */
+
+ /* advance src ptr for next iteration */
+ hostInputChannels[i].data = ((unsigned char*)hostInputChannels[i].data) +
+ frameCount * hostInputChannels[i].stride * bp->bytesPerHostInputSample;
+ }
+
+ bp->framesInTempInputBuffer += frameCount;
+
+ if( bp->framesInTempInputBuffer == bp->framesPerUserBuffer )
+ {
+ /**
+ @todo (non-critical optimisation)
+ The conditional below implements the continue/complete/abort mechanism
+ simply by continuing on iterating through the input buffer, but not
+ passing the data to the callback. With care, the outer loop could be
+ terminated earlier, thus some unneeded conversion cycles would be
+ saved.
+ */
+ if( *streamCallbackResult == paContinue )
+ {
+ bp->timeInfo->outputBufferDacTime = 0;
+
+ *streamCallbackResult = bp->streamCallback( userInput, userOutput,
+ bp->framesPerUserBuffer, bp->timeInfo,
+ bp->callbackStatusFlags, bp->userData );
+
+ bp->timeInfo->inputBufferAdcTime += frameCount * bp->samplePeriod;
+ }
+
+ bp->framesInTempInputBuffer = 0;
+ }
+
+ framesProcessed += frameCount;
+
+ framesToGo -= frameCount;
+ }while( framesToGo > 0 );
+
+ return framesProcessed;
+}
+
+
+/*
+ AdaptingOutputOnlyProcess() is a half duplex output buffer processor.
+ It converts data from the temporary output buffer, to the output buffers,
+ when the temporary output buffer is empty, it calls the streamCallback.
+*/
+static unsigned long AdaptingOutputOnlyProcess( PaUtilBufferProcessor *bp,
+ int *streamCallbackResult,
+ PaUtilChannelDescriptor *hostOutputChannels,
+ unsigned long framesToProcess )
+{
+ void *userInput, *userOutput;
+ unsigned char *srcBytePtr;
+ unsigned int srcSampleStrideSamples; /* stride from one sample to the next within a channel, in samples */
+ unsigned int srcChannelStrideBytes; /* stride from one channel to the next, in bytes */
+ unsigned int i;
+ unsigned long frameCount;
+ unsigned long framesToGo = framesToProcess;
+ unsigned long framesProcessed = 0;
+
+ do
+ {
+ if( bp->framesInTempOutputBuffer == 0 && *streamCallbackResult == paContinue )
+ {
+ userInput = 0;
+
+ /* setup userOutput */
+ if( bp->userOutputIsInterleaved )
+ {
+ userOutput = bp->tempOutputBuffer;
+ }
+ else /* user output is not interleaved */
+ {
+ for( i = 0; i < bp->outputChannelCount; ++i )
+ {
+ bp->tempOutputBufferPtrs[i] = ((unsigned char*)bp->tempOutputBuffer) +
+ i * bp->framesPerUserBuffer * bp->bytesPerUserOutputSample;
+ }
+
+ userOutput = bp->tempOutputBufferPtrs;
+ }
+
+ bp->timeInfo->inputBufferAdcTime = 0;
+
+ *streamCallbackResult = bp->streamCallback( userInput, userOutput,
+ bp->framesPerUserBuffer, bp->timeInfo,
+ bp->callbackStatusFlags, bp->userData );
+
+ if( *streamCallbackResult == paAbort )
+ {
+ /* if the callback returned paAbort, we disregard its output */
+ }
+ else
+ {
+ bp->timeInfo->outputBufferDacTime += bp->framesPerUserBuffer * bp->samplePeriod;
+
+ bp->framesInTempOutputBuffer = bp->framesPerUserBuffer;
+ }
+ }
+
+ if( bp->framesInTempOutputBuffer > 0 )
+ {
+ /* convert frameCount frames from user buffer to host buffer */
+
+ frameCount = PA_MIN_( bp->framesInTempOutputBuffer, framesToGo );
+
+ if( bp->userOutputIsInterleaved )
+ {
+ srcBytePtr = ((unsigned char*)bp->tempOutputBuffer) +
+ bp->bytesPerUserOutputSample * bp->outputChannelCount *
+ (bp->framesPerUserBuffer - bp->framesInTempOutputBuffer);
+
+ srcSampleStrideSamples = bp->outputChannelCount;
+ srcChannelStrideBytes = bp->bytesPerUserOutputSample;
+ }
+ else /* user output is not interleaved */
+ {
+ srcBytePtr = ((unsigned char*)bp->tempOutputBuffer) +
+ bp->bytesPerUserOutputSample *
+ (bp->framesPerUserBuffer - bp->framesInTempOutputBuffer);
+
+ srcSampleStrideSamples = 1;
+ srcChannelStrideBytes = bp->framesPerUserBuffer * bp->bytesPerUserOutputSample;
+ }
+
+ for( i=0; i<bp->outputChannelCount; ++i )
+ {
+ bp->outputConverter( hostOutputChannels[i].data,
+ hostOutputChannels[i].stride,
+ srcBytePtr, srcSampleStrideSamples,
+ frameCount, &bp->ditherGenerator );
+
+ srcBytePtr += srcChannelStrideBytes; /* skip to next source channel */
+
+ /* advance dest ptr for next iteration */
+ hostOutputChannels[i].data = ((unsigned char*)hostOutputChannels[i].data) +
+ frameCount * hostOutputChannels[i].stride * bp->bytesPerHostOutputSample;
+ }
+
+ bp->framesInTempOutputBuffer -= frameCount;
+ }
+ else
+ {
+ /* no more user data is available because the callback has returned
+ paComplete or paAbort. Fill the remainder of the host buffer
+ with zeros.
+ */
+
+ frameCount = framesToGo;
+
+ for( i=0; i<bp->outputChannelCount; ++i )
+ {
+ bp->outputZeroer( hostOutputChannels[i].data,
+ hostOutputChannels[i].stride,
+ frameCount );
+
+ /* advance dest ptr for next iteration */
+ hostOutputChannels[i].data = ((unsigned char*)hostOutputChannels[i].data) +
+ frameCount * hostOutputChannels[i].stride * bp->bytesPerHostOutputSample;
+ }
+ }
+
+ framesProcessed += frameCount;
+
+ framesToGo -= frameCount;
+
+ }while( framesToGo > 0 );
+
+ return framesProcessed;
+}
+
+/* CopyTempOutputBuffersToHostOutputBuffers is called from AdaptingProcess to copy frames from
+ tempOutputBuffer to hostOutputChannels. This includes data conversion
+ and interleaving.
+*/
+static void CopyTempOutputBuffersToHostOutputBuffers( PaUtilBufferProcessor *bp)
+{
+ unsigned long maxFramesToCopy;
+ PaUtilChannelDescriptor *hostOutputChannels;
+ unsigned int frameCount;
+ unsigned char *srcBytePtr;
+ unsigned int srcSampleStrideSamples; /* stride from one sample to the next within a channel, in samples */
+ unsigned int srcChannelStrideBytes; /* stride from one channel to the next, in bytes */
+ unsigned int i;
+
+ /* copy frames from user to host output buffers */
+ while( bp->framesInTempOutputBuffer > 0 &&
+ ((bp->hostOutputFrameCount[0] + bp->hostOutputFrameCount[1]) > 0) )
+ {
+ maxFramesToCopy = bp->framesInTempOutputBuffer;
+
+ /* select the output buffer set (1st or 2nd) */
+ if( bp->hostOutputFrameCount[0] > 0 )
+ {
+ hostOutputChannels = bp->hostOutputChannels[0];
+ frameCount = PA_MIN_( bp->hostOutputFrameCount[0], maxFramesToCopy );
+ }
+ else
+ {
+ hostOutputChannels = bp->hostOutputChannels[1];
+ frameCount = PA_MIN_( bp->hostOutputFrameCount[1], maxFramesToCopy );
+ }
+
+ if( bp->userOutputIsInterleaved )
+ {
+ srcBytePtr = ((unsigned char*)bp->tempOutputBuffer) +
+ bp->bytesPerUserOutputSample * bp->outputChannelCount *
+ (bp->framesPerUserBuffer - bp->framesInTempOutputBuffer);
+
+ srcSampleStrideSamples = bp->outputChannelCount;
+ srcChannelStrideBytes = bp->bytesPerUserOutputSample;
+ }
+ else /* user output is not interleaved */
+ {
+ srcBytePtr = ((unsigned char*)bp->tempOutputBuffer) +
+ bp->bytesPerUserOutputSample *
+ (bp->framesPerUserBuffer - bp->framesInTempOutputBuffer);
+
+ srcSampleStrideSamples = 1;
+ srcChannelStrideBytes = bp->framesPerUserBuffer * bp->bytesPerUserOutputSample;
+ }
+
+ for( i=0; i<bp->outputChannelCount; ++i )
+ {
+ bp->outputConverter( hostOutputChannels[i].data,
+ hostOutputChannels[i].stride,
+ srcBytePtr, srcSampleStrideSamples,
+ frameCount, &bp->ditherGenerator );
+
+ srcBytePtr += srcChannelStrideBytes; /* skip to next source channel */
+
+ /* advance dest ptr for next iteration */
+ hostOutputChannels[i].data = ((unsigned char*)hostOutputChannels[i].data) +
+ frameCount * hostOutputChannels[i].stride * bp->bytesPerHostOutputSample;
+ }
+
+ if( bp->hostOutputFrameCount[0] > 0 )
+ bp->hostOutputFrameCount[0] -= frameCount;
+ else
+ bp->hostOutputFrameCount[1] -= frameCount;
+
+ bp->framesInTempOutputBuffer -= frameCount;
+ }
+}
+
+/*
+ AdaptingProcess is a full duplex adapting buffer processor. It converts
+ data from the temporary output buffer into the host output buffers, then
+ from the host input buffers into the temporary input buffers. Calling the
+ streamCallback when necessary.
+ When processPartialUserBuffers is 0, all available input data will be
+ consumed and all available output space will be filled. When
+ processPartialUserBuffers is non-zero, as many full user buffers
+ as possible will be processed, but partial buffers will not be consumed.
+*/
+static unsigned long AdaptingProcess( PaUtilBufferProcessor *bp,
+ int *streamCallbackResult, int processPartialUserBuffers )
+{
+ void *userInput, *userOutput;
+ unsigned long framesProcessed = 0;
+ unsigned long framesAvailable;
+ unsigned long endProcessingMinFrameCount;
+ unsigned long maxFramesToCopy;
+ PaUtilChannelDescriptor *hostInputChannels, *hostOutputChannels;
+ unsigned int frameCount;
+ unsigned char *destBytePtr;
+ unsigned int destSampleStrideSamples; /* stride from one sample to the next within a channel, in samples */
+ unsigned int destChannelStrideBytes; /* stride from one channel to the next, in bytes */
+ unsigned int i, j;
+
+
+ framesAvailable = bp->hostInputFrameCount[0] + bp->hostInputFrameCount[1];/* this is assumed to be the same as the output buffer's frame count */
+
+ if( processPartialUserBuffers )
+ endProcessingMinFrameCount = 0;
+ else
+ endProcessingMinFrameCount = (bp->framesPerUserBuffer - 1);
+
+ /* Fill host output with remaining frames in user output (tempOutputBuffer) */
+ CopyTempOutputBuffersToHostOutputBuffers( bp );
+
+ while( framesAvailable > endProcessingMinFrameCount )
+ {
+
+ if( bp->framesInTempOutputBuffer == 0 && *streamCallbackResult != paContinue )
+ {
+ /* the callback will not be called any more, so zero what remains
+ of the host output buffers */
+
+ for( i=0; i<2; ++i )
+ {
+ frameCount = bp->hostOutputFrameCount[i];
+ if( frameCount > 0 )
+ {
+ hostOutputChannels = bp->hostOutputChannels[i];
+
+ for( j=0; j<bp->outputChannelCount; ++j )
+ {
+ bp->outputZeroer( hostOutputChannels[j].data,
+ hostOutputChannels[j].stride,
+ frameCount );
+
+ /* advance dest ptr for next iteration */
+ hostOutputChannels[j].data = ((unsigned char*)hostOutputChannels[j].data) +
+ frameCount * hostOutputChannels[j].stride * bp->bytesPerHostOutputSample;
+ }
+ bp->hostOutputFrameCount[i] = 0;
+ }
+ }
+ }
+
+
+ /* copy frames from host to user input buffers */
+ while( bp->framesInTempInputBuffer < bp->framesPerUserBuffer &&
+ ((bp->hostInputFrameCount[0] + bp->hostInputFrameCount[1]) > 0) )
+ {
+ maxFramesToCopy = bp->framesPerUserBuffer - bp->framesInTempInputBuffer;
+
+ /* select the input buffer set (1st or 2nd) */
+ if( bp->hostInputFrameCount[0] > 0 )
+ {
+ hostInputChannels = bp->hostInputChannels[0];
+ frameCount = PA_MIN_( bp->hostInputFrameCount[0], maxFramesToCopy );
+ }
+ else
+ {
+ hostInputChannels = bp->hostInputChannels[1];
+ frameCount = PA_MIN_( bp->hostInputFrameCount[1], maxFramesToCopy );
+ }
+
+ /* configure conversion destination pointers */
+ if( bp->userInputIsInterleaved )
+ {
+ destBytePtr = ((unsigned char*)bp->tempInputBuffer) +
+ bp->bytesPerUserInputSample * bp->inputChannelCount *
+ bp->framesInTempInputBuffer;
+
+ destSampleStrideSamples = bp->inputChannelCount;
+ destChannelStrideBytes = bp->bytesPerUserInputSample;
+ }
+ else /* user input is not interleaved */
+ {
+ destBytePtr = ((unsigned char*)bp->tempInputBuffer) +
+ bp->bytesPerUserInputSample * bp->framesInTempInputBuffer;
+
+ destSampleStrideSamples = 1;
+ destChannelStrideBytes = bp->framesPerUserBuffer * bp->bytesPerUserInputSample;
+ }
+
+ for( i=0; i<bp->inputChannelCount; ++i )
+ {
+ bp->inputConverter( destBytePtr, destSampleStrideSamples,
+ hostInputChannels[i].data,
+ hostInputChannels[i].stride,
+ frameCount, &bp->ditherGenerator );
+
+ destBytePtr += destChannelStrideBytes; /* skip to next destination channel */
+
+ /* advance src ptr for next iteration */
+ hostInputChannels[i].data = ((unsigned char*)hostInputChannels[i].data) +
+ frameCount * hostInputChannels[i].stride * bp->bytesPerHostInputSample;
+ }
+
+ if( bp->hostInputFrameCount[0] > 0 )
+ bp->hostInputFrameCount[0] -= frameCount;
+ else
+ bp->hostInputFrameCount[1] -= frameCount;
+
+ bp->framesInTempInputBuffer += frameCount;
+
+ /* update framesAvailable and framesProcessed based on input consumed
+ unless something is very wrong this will also correspond to the
+ amount of output generated */
+ framesAvailable -= frameCount;
+ framesProcessed += frameCount;
+ }
+
+ /* call streamCallback */
+ if( bp->framesInTempInputBuffer == bp->framesPerUserBuffer &&
+ bp->framesInTempOutputBuffer == 0 )
+ {
+ if( *streamCallbackResult == paContinue )
+ {
+ /* setup userInput */
+ if( bp->userInputIsInterleaved )
+ {
+ userInput = bp->tempInputBuffer;
+ }
+ else /* user input is not interleaved */
+ {
+ for( i = 0; i < bp->inputChannelCount; ++i )
+ {
+ bp->tempInputBufferPtrs[i] = ((unsigned char*)bp->tempInputBuffer) +
+ i * bp->framesPerUserBuffer * bp->bytesPerUserInputSample;
+ }
+
+ userInput = bp->tempInputBufferPtrs;
+ }
+
+ /* setup userOutput */
+ if( bp->userOutputIsInterleaved )
+ {
+ userOutput = bp->tempOutputBuffer;
+ }
+ else /* user output is not interleaved */
+ {
+ for( i = 0; i < bp->outputChannelCount; ++i )
+ {
+ bp->tempOutputBufferPtrs[i] = ((unsigned char*)bp->tempOutputBuffer) +
+ i * bp->framesPerUserBuffer * bp->bytesPerUserOutputSample;
+ }
+
+ userOutput = bp->tempOutputBufferPtrs;
+ }
+
+ /* call streamCallback */
+
+ *streamCallbackResult = bp->streamCallback( userInput, userOutput,
+ bp->framesPerUserBuffer, bp->timeInfo,
+ bp->callbackStatusFlags, bp->userData );
+
+ bp->timeInfo->inputBufferAdcTime += bp->framesPerUserBuffer * bp->samplePeriod;
+ bp->timeInfo->outputBufferDacTime += bp->framesPerUserBuffer * bp->samplePeriod;
+
+ bp->framesInTempInputBuffer = 0;
+
+ if( *streamCallbackResult == paAbort )
+ bp->framesInTempOutputBuffer = 0;
+ else
+ bp->framesInTempOutputBuffer = bp->framesPerUserBuffer;
+ }
+ else
+ {
+ /* paComplete or paAbort has already been called. */
+
+ bp->framesInTempInputBuffer = 0;
+ }
+ }
+
+ /* copy frames from user (tempOutputBuffer) to host output buffers (hostOutputChannels)
+ Means to process the user output provided by the callback. Has to be called after
+ each callback. */
+ CopyTempOutputBuffersToHostOutputBuffers( bp );
+
+ }
+
+ return framesProcessed;
+}
+
+
+unsigned long PaUtil_EndBufferProcessing( PaUtilBufferProcessor* bp, int *streamCallbackResult )
+{
+ unsigned long framesToProcess, framesToGo;
+ unsigned long framesProcessed = 0;
+
+ if( bp->inputChannelCount != 0 && bp->outputChannelCount != 0
+ && bp->hostInputChannels[0][0].data /* input was supplied (see PaUtil_SetNoInput) */
+ && bp->hostOutputChannels[0][0].data /* output was supplied (see PaUtil_SetNoOutput) */ )
+ {
+ assert( (bp->hostInputFrameCount[0] + bp->hostInputFrameCount[1]) ==
+ (bp->hostOutputFrameCount[0] + bp->hostOutputFrameCount[1]) );
+ }
+
+ assert( *streamCallbackResult == paContinue
+ || *streamCallbackResult == paComplete
+ || *streamCallbackResult == paAbort ); /* don't forget to pass in a valid callback result value */
+
+ if( bp->useNonAdaptingProcess )
+ {
+ if( bp->inputChannelCount != 0 && bp->outputChannelCount != 0 )
+ {
+ /* full duplex non-adapting process, splice buffers if they are
+ different lengths */
+
+ framesToGo = bp->hostOutputFrameCount[0] + bp->hostOutputFrameCount[1]; /* relies on assert above for input/output equivalence */
+
+ do{
+ unsigned long noInputInputFrameCount;
+ unsigned long *hostInputFrameCount;
+ PaUtilChannelDescriptor *hostInputChannels;
+ unsigned long noOutputOutputFrameCount;
+ unsigned long *hostOutputFrameCount;
+ PaUtilChannelDescriptor *hostOutputChannels;
+ unsigned long framesProcessedThisIteration;
+
+ if( !bp->hostInputChannels[0][0].data )
+ {
+ /* no input was supplied (see PaUtil_SetNoInput)
+ NonAdaptingProcess knows how to deal with this
+ */
+ noInputInputFrameCount = framesToGo;
+ hostInputFrameCount = &noInputInputFrameCount;
+ hostInputChannels = 0;
+ }
+ else if( bp->hostInputFrameCount[0] != 0 )
+ {
+ hostInputFrameCount = &bp->hostInputFrameCount[0];
+ hostInputChannels = bp->hostInputChannels[0];
+ }
+ else
+ {
+ hostInputFrameCount = &bp->hostInputFrameCount[1];
+ hostInputChannels = bp->hostInputChannels[1];
+ }
+
+ if( !bp->hostOutputChannels[0][0].data )
+ {
+ /* no output was supplied (see PaUtil_SetNoOutput)
+ NonAdaptingProcess knows how to deal with this
+ */
+ noOutputOutputFrameCount = framesToGo;
+ hostOutputFrameCount = &noOutputOutputFrameCount;
+ hostOutputChannels = 0;
+ }
+ if( bp->hostOutputFrameCount[0] != 0 )
+ {
+ hostOutputFrameCount = &bp->hostOutputFrameCount[0];
+ hostOutputChannels = bp->hostOutputChannels[0];
+ }
+ else
+ {
+ hostOutputFrameCount = &bp->hostOutputFrameCount[1];
+ hostOutputChannels = bp->hostOutputChannels[1];
+ }
+
+ framesToProcess = PA_MIN_( *hostInputFrameCount,
+ *hostOutputFrameCount );
+
+ assert( framesToProcess != 0 );
+
+ framesProcessedThisIteration = NonAdaptingProcess( bp, streamCallbackResult,
+ hostInputChannels, hostOutputChannels,
+ framesToProcess );
+
+ *hostInputFrameCount -= framesProcessedThisIteration;
+ *hostOutputFrameCount -= framesProcessedThisIteration;
+
+ framesProcessed += framesProcessedThisIteration;
+ framesToGo -= framesProcessedThisIteration;
+
+ }while( framesToGo > 0 );
+ }
+ else
+ {
+ /* half duplex non-adapting process, just process 1st and 2nd buffer */
+ /* process first buffer */
+
+ framesToProcess = (bp->inputChannelCount != 0)
+ ? bp->hostInputFrameCount[0]
+ : bp->hostOutputFrameCount[0];
+
+ framesProcessed = NonAdaptingProcess( bp, streamCallbackResult,
+ bp->hostInputChannels[0], bp->hostOutputChannels[0],
+ framesToProcess );
+
+ /* process second buffer if provided */
+
+ framesToProcess = (bp->inputChannelCount != 0)
+ ? bp->hostInputFrameCount[1]
+ : bp->hostOutputFrameCount[1];
+ if( framesToProcess > 0 )
+ {
+ framesProcessed += NonAdaptingProcess( bp, streamCallbackResult,
+ bp->hostInputChannels[1], bp->hostOutputChannels[1],
+ framesToProcess );
+ }
+ }
+ }
+ else /* block adaption necessary*/
+ {
+
+ if( bp->inputChannelCount != 0 && bp->outputChannelCount != 0 )
+ {
+ /* full duplex */
+
+ if( bp->hostBufferSizeMode == paUtilVariableHostBufferSizePartialUsageAllowed )
+ {
+ framesProcessed = AdaptingProcess( bp, streamCallbackResult,
+ 0 /* dont process partial user buffers */ );
+ }
+ else
+ {
+ framesProcessed = AdaptingProcess( bp, streamCallbackResult,
+ 1 /* process partial user buffers */ );
+ }
+ }
+ else if( bp->inputChannelCount != 0 )
+ {
+ /* input only */
+ framesToProcess = bp->hostInputFrameCount[0];
+
+ framesProcessed = AdaptingInputOnlyProcess( bp, streamCallbackResult,
+ bp->hostInputChannels[0], framesToProcess );
+
+ framesToProcess = bp->hostInputFrameCount[1];
+ if( framesToProcess > 0 )
+ {
+ framesProcessed += AdaptingInputOnlyProcess( bp, streamCallbackResult,
+ bp->hostInputChannels[1], framesToProcess );
+ }
+ }
+ else
+ {
+ /* output only */
+ framesToProcess = bp->hostOutputFrameCount[0];
+
+ framesProcessed = AdaptingOutputOnlyProcess( bp, streamCallbackResult,
+ bp->hostOutputChannels[0], framesToProcess );
+
+ framesToProcess = bp->hostOutputFrameCount[1];
+ if( framesToProcess > 0 )
+ {
+ framesProcessed += AdaptingOutputOnlyProcess( bp, streamCallbackResult,
+ bp->hostOutputChannels[1], framesToProcess );
+ }
+ }
+ }
+
+ return framesProcessed;
+}
+
+
+int PaUtil_IsBufferProcessorOutputEmpty( PaUtilBufferProcessor* bp )
+{
+ return (bp->framesInTempOutputBuffer) ? 0 : 1;
+}
+
+
+unsigned long PaUtil_CopyInput( PaUtilBufferProcessor* bp,
+ void **buffer, unsigned long frameCount )
+{
+ PaUtilChannelDescriptor *hostInputChannels;
+ unsigned int framesToCopy;
+ unsigned char *destBytePtr;
+ void **nonInterleavedDestPtrs;
+ unsigned int destSampleStrideSamples; /* stride from one sample to the next within a channel, in samples */
+ unsigned int destChannelStrideBytes; /* stride from one channel to the next, in bytes */
+ unsigned int i;
+
+ hostInputChannels = bp->hostInputChannels[0];
+ framesToCopy = PA_MIN_( bp->hostInputFrameCount[0], frameCount );
+
+ if( bp->userInputIsInterleaved )
+ {
+ destBytePtr = (unsigned char*)*buffer;
+
+ destSampleStrideSamples = bp->inputChannelCount;
+ destChannelStrideBytes = bp->bytesPerUserInputSample;
+
+ for( i=0; i<bp->inputChannelCount; ++i )
+ {
+ bp->inputConverter( destBytePtr, destSampleStrideSamples,
+ hostInputChannels[i].data,
+ hostInputChannels[i].stride,
+ framesToCopy, &bp->ditherGenerator );
+
+ destBytePtr += destChannelStrideBytes; /* skip to next source channel */
+
+ /* advance dest ptr for next iteration */
+ hostInputChannels[i].data = ((unsigned char*)hostInputChannels[i].data) +
+ framesToCopy * hostInputChannels[i].stride * bp->bytesPerHostInputSample;
+ }
+
+ /* advance callers dest pointer (buffer) */
+ *buffer = ((unsigned char *)*buffer) +
+ framesToCopy * bp->inputChannelCount * bp->bytesPerUserInputSample;
+ }
+ else
+ {
+ /* user input is not interleaved */
+
+ nonInterleavedDestPtrs = (void**)*buffer;
+
+ destSampleStrideSamples = 1;
+
+ for( i=0; i<bp->inputChannelCount; ++i )
+ {
+ destBytePtr = (unsigned char*)nonInterleavedDestPtrs[i];
+
+ bp->inputConverter( destBytePtr, destSampleStrideSamples,
+ hostInputChannels[i].data,
+ hostInputChannels[i].stride,
+ framesToCopy, &bp->ditherGenerator );
+
+ /* advance callers dest pointer (nonInterleavedDestPtrs[i]) */
+ destBytePtr += bp->bytesPerUserInputSample * framesToCopy;
+ nonInterleavedDestPtrs[i] = destBytePtr;
+
+ /* advance dest ptr for next iteration */
+ hostInputChannels[i].data = ((unsigned char*)hostInputChannels[i].data) +
+ framesToCopy * hostInputChannels[i].stride * bp->bytesPerHostInputSample;
+ }
+ }
+
+ bp->hostInputFrameCount[0] -= framesToCopy;
+
+ return framesToCopy;
+}
+
+unsigned long PaUtil_CopyOutput( PaUtilBufferProcessor* bp,
+ const void ** buffer, unsigned long frameCount )
+{
+ PaUtilChannelDescriptor *hostOutputChannels;
+ unsigned int framesToCopy;
+ unsigned char *srcBytePtr;
+ void **nonInterleavedSrcPtrs;
+ unsigned int srcSampleStrideSamples; /* stride from one sample to the next within a channel, in samples */
+ unsigned int srcChannelStrideBytes; /* stride from one channel to the next, in bytes */
+ unsigned int i;
+
+ hostOutputChannels = bp->hostOutputChannels[0];
+ framesToCopy = PA_MIN_( bp->hostOutputFrameCount[0], frameCount );
+
+ if( bp->userOutputIsInterleaved )
+ {
+ srcBytePtr = (unsigned char*)*buffer;
+
+ srcSampleStrideSamples = bp->outputChannelCount;
+ srcChannelStrideBytes = bp->bytesPerUserOutputSample;
+
+ for( i=0; i<bp->outputChannelCount; ++i )
+ {
+ bp->outputConverter( hostOutputChannels[i].data,
+ hostOutputChannels[i].stride,
+ srcBytePtr, srcSampleStrideSamples,
+ framesToCopy, &bp->ditherGenerator );
+
+ srcBytePtr += srcChannelStrideBytes; /* skip to next source channel */
+
+ /* advance dest ptr for next iteration */
+ hostOutputChannels[i].data = ((unsigned char*)hostOutputChannels[i].data) +
+ framesToCopy * hostOutputChannels[i].stride * bp->bytesPerHostOutputSample;
+ }
+
+ /* advance callers source pointer (buffer) */
+ *buffer = ((unsigned char *)*buffer) +
+ framesToCopy * bp->outputChannelCount * bp->bytesPerUserOutputSample;
+
+ }
+ else
+ {
+ /* user output is not interleaved */
+
+ nonInterleavedSrcPtrs = (void**)*buffer;
+
+ srcSampleStrideSamples = 1;
+
+ for( i=0; i<bp->outputChannelCount; ++i )
+ {
+ srcBytePtr = (unsigned char*)nonInterleavedSrcPtrs[i];
+
+ bp->outputConverter( hostOutputChannels[i].data,
+ hostOutputChannels[i].stride,
+ srcBytePtr, srcSampleStrideSamples,
+ framesToCopy, &bp->ditherGenerator );
+
+
+ /* advance callers source pointer (nonInterleavedSrcPtrs[i]) */
+ srcBytePtr += bp->bytesPerUserOutputSample * framesToCopy;
+ nonInterleavedSrcPtrs[i] = srcBytePtr;
+
+ /* advance dest ptr for next iteration */
+ hostOutputChannels[i].data = ((unsigned char*)hostOutputChannels[i].data) +
+ framesToCopy * hostOutputChannels[i].stride * bp->bytesPerHostOutputSample;
+ }
+ }
+
+ bp->hostOutputFrameCount[0] += framesToCopy;
+
+ return framesToCopy;
+}
+
+
+unsigned long PaUtil_ZeroOutput( PaUtilBufferProcessor* bp, unsigned long frameCount )
+{
+ PaUtilChannelDescriptor *hostOutputChannels;
+ unsigned int framesToZero;
+ unsigned int i;
+
+ hostOutputChannels = bp->hostOutputChannels[0];
+ framesToZero = PA_MIN_( bp->hostOutputFrameCount[0], frameCount );
+
+ for( i=0; i<bp->outputChannelCount; ++i )
+ {
+ bp->outputZeroer( hostOutputChannels[i].data,
+ hostOutputChannels[i].stride,
+ framesToZero );
+
+
+ /* advance dest ptr for next iteration */
+ hostOutputChannels[i].data = ((unsigned char*)hostOutputChannels[i].data) +
+ framesToZero * hostOutputChannels[i].stride * bp->bytesPerHostOutputSample;
+ }
+
+ bp->hostOutputFrameCount[0] += framesToZero;
+
+ return framesToZero;
+}
diff --git a/pjmedia/src/pjmedia/portaudio/pa_process.h b/pjmedia/src/pjmedia/portaudio/pa_process.h
index c52e9ea0..21ebecec 100644
--- a/pjmedia/src/pjmedia/portaudio/pa_process.h
+++ b/pjmedia/src/pjmedia/portaudio/pa_process.h
@@ -1,741 +1,762 @@
-#ifndef PA_PROCESS_H
-#define PA_PROCESS_H
-/*
- * $Id: pa_process.h,v 1.1.2.30 2004/12/13 09:48:44 rossbencina Exp $
- * Portable Audio I/O Library callback buffer processing adapters
- *
- * Based on the Open Source API proposed by Ross Bencina
- * Copyright (c) 1999-2002 Phil Burk, Ross Bencina
- *
- * Permission is hereby granted, free of charge, to any person obtaining
- * a copy of this software and associated documentation files
- * (the "Software"), to deal in the Software without restriction,
- * including without limitation the rights to use, copy, modify, merge,
- * publish, distribute, sublicense, and/or sell copies of the Software,
- * and to permit persons to whom the Software is furnished to do so,
- * subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be
- * included in all copies or substantial portions of the Software.
- *
- * Any person wishing to distribute modifications to the Software is
- * requested to send the modifications to the original developer so that
- * they can be incorporated into the canonical version.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
- * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
- * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
- * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
- * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
- */
-
-/** @file
- @brief Buffer Processor prototypes. A Buffer Processor performs buffer length
- adaption, coordinates sample format conversion, and interleaves/deinterleaves
- channels.
-
- <h3>Overview</h3>
-
- The "Buffer Processor" (PaUtilBufferProcessor) manages conversion of audio
- data from host buffers to user buffers and back again. Where required, the
- buffer processor takes care of converting between host and user sample formats,
- interleaving and deinterleaving multichannel buffers, and adapting between host
- and user buffers with different lengths. The buffer processor may be used with
- full and half duplex streams, for both callback streams and blocking read/write
- streams.
-
- One of the important capabilities provided by the buffer processor is
- the ability to adapt between user and host buffer sizes of different lengths
- with minimum latency. Although this task is relatively easy to perform when
- the host buffer size is an integer multiple of the user buffer size, the
- problem is more complicated when this is not the case - especially for
- full-duplex callback streams. Where necessary the adaption is implemented by
- internally buffering some input and/or output data. The buffer adation
- algorithm used by the buffer processor was originally implemented by
- Stephan Letz for the ASIO version of PortAudio, and is described in his
- Callback_adaption_.pdf which is included in the distribution.
-
- The buffer processor performs sample conversion using the functions provided
- by pa_converters.c.
-
- The following sections provide an overview of how to use the buffer processor.
- Interested readers are advised to consult the host API implementations for
- examples of buffer processor usage.
-
-
- <h4>Initialization, resetting and termination</h4>
-
- When a stream is opened, the buffer processor should be initialized using
- PaUtil_InitializeBufferProcessor. This function initializes internal state
- and allocates temporary buffers as neccesary according to the supplied
- configuration parameters. Some of the parameters correspond to those requested
- by the user in their call to Pa_OpenStream(), others reflect the requirements
- of the host API implementation - they indicate host buffer sizes, formats,
- and the type of buffering which the Host API uses. The buffer processor should
- be initialized for callback streams and blocking read/write streams.
-
- Call PaUtil_ResetBufferProcessor to clear any sample data which is present
- in the buffer processor before starting to use it (for example when
- Pa_StartStream is called).
-
- When the buffer processor is no longer used call
- PaUtil_TerminateBufferProcessor.
-
-
- <h4>Using the buffer processor for a callback stream</h4>
-
- The buffer processor's role in a callback stream is to take host input buffers
- process them with the stream callback, and fill host output buffers. For a
- full duplex stream, the buffer processor handles input and output simultaneously
- due to the requirements of the minimum-latency buffer adation algorithm.
-
- When a host buffer becomes available, the implementation should call
- the buffer processor to process the buffer. The buffer processor calls the
- stream callback to consume and/or produce audio data as necessary. The buffer
- processor will convert sample formats, interleave/deinterleave channels,
- and slice or chunk the data to the appropriate buffer lengths according to
- the requirements of the stream callback and the host API.
-
- To process a host buffer (or a pair of host buffers for a full-duplex stream)
- use the following calling sequence:
-
- -# Call PaUtil_BeginBufferProcessing
- -# For a stream which takes input:
- - Call PaUtil_SetInputFrameCount with the number of frames in the host input
- buffer.
- - Call one of the following functions one or more times to tell the
- buffer processor about the host input buffer(s): PaUtil_SetInputChannel,
- PaUtil_SetInterleavedInputChannels, PaUtil_SetNonInterleavedInputChannel.
- Which function you call will depend on whether the host buffer(s) are
- interleaved or not.
- - If the available host data is split accross two buffers (for example a
- data range at the end of a circular buffer and another range at the
- beginning of the circular buffer), also call
- PaUtil_Set2ndInputFrameCount, PaUtil_Set2ndInputChannel,
- PaUtil_Set2ndInterleavedInputChannels,
- PaUtil_Set2ndNonInterleavedInputChannel as necessary to tell the buffer
- processor about the second buffer.
- -# For a stream which generates output:
- - Call PaUtil_SetOutputFrameCount with the number of frames in the host
- output buffer.
- - Call one of the following functions one or more times to tell the
- buffer processor about the host output buffer(s): PaUtil_SetOutputChannel,
- PaUtil_SetInterleavedOutputChannels, PaUtil_SetNonInterleavedOutputChannel.
- Which function you call will depend on whether the host buffer(s) are
- interleaved or not.
- - If the available host output buffer space is split accross two buffers
- (for example a data range at the end of a circular buffer and another
- range at the beginning of the circular buffer), call
- PaUtil_Set2ndOutputFrameCount, PaUtil_Set2ndOutputChannel,
- PaUtil_Set2ndInterleavedOutputChannels,
- PaUtil_Set2ndNonInterleavedOutputChannel as necessary to tell the buffer
- processor about the second buffer.
- -# Call PaUtil_EndBufferProcessing, this function performs the actual data
- conversion and processing.
-
-
- <h4>Using the buffer processor for a blocking read/write stream</h4>
-
- Blocking read/write streams use the buffer processor to convert and copy user
- output data to a host buffer, and to convert and copy host input data to
- the user's buffer. The buffer processor does not perform any buffer adaption.
- When using the buffer processor in a blocking read/write stream the input and
- output conversion are performed separately by the PaUtil_CopyInput and
- PaUtil_CopyOutput functions.
-
- To copy data from a host input buffer to the buffer(s) which the user supplies
- to Pa_ReadStream, use the following calling sequence.
-
- - Repeat the following three steps until the user buffer(s) have been filled
- with samples from the host input buffers:
- -# Call PaUtil_SetInputFrameCount with the number of frames in the host
- input buffer.
- -# Call one of the following functions one or more times to tell the
- buffer processor about the host input buffer(s): PaUtil_SetInputChannel,
- PaUtil_SetInterleavedInputChannels, PaUtil_SetNonInterleavedInputChannel.
- Which function you call will depend on whether the host buffer(s) are
- interleaved or not.
- -# Call PaUtil_CopyInput with the user buffer pointer (or a copy of the
- array of buffer pointers for a non-interleaved stream) passed to
- Pa_ReadStream, along with the number of frames in the user buffer(s).
- Be careful to pass a <i>copy</i> of the user buffer pointers to
- PaUtil_CopyInput because PaUtil_CopyInput advances the pointers to
- the start of the next region to copy.
- - PaUtil_CopyInput will not copy more data than is available in the
- host buffer(s), so the above steps need to be repeated until the user
- buffer(s) are full.
-
-
- To copy data to the host output buffer from the user buffers(s) supplied
- to Pa_WriteStream use the following calling sequence.
-
- - Repeat the following three steps until all frames from the user buffer(s)
- have been copied to the host API:
- -# Call PaUtil_SetOutputFrameCount with the number of frames in the host
- output buffer.
- -# Call one of the following functions one or more times to tell the
- buffer processor about the host output buffer(s): PaUtil_SetOutputChannel,
- PaUtil_SetInterleavedOutputChannels, PaUtil_SetNonInterleavedOutputChannel.
- Which function you call will depend on whether the host buffer(s) are
- interleaved or not.
- -# Call PaUtil_CopyOutput with the user buffer pointer (or a copy of the
- array of buffer pointers for a non-interleaved stream) passed to
- Pa_WriteStream, along with the number of frames in the user buffer(s).
- Be careful to pass a <i>copy</i> of the user buffer pointers to
- PaUtil_CopyOutput because PaUtil_CopyOutput advances the pointers to
- the start of the next region to copy.
- - PaUtil_CopyOutput will not copy more data than fits in the host buffer(s),
- so the above steps need to be repeated until all user data is copied.
-*/
-
-
-#include "portaudio.h"
-#include "pa_converters.h"
-#include "pa_dither.h"
-
-#ifdef __cplusplus
-extern "C"
-{
-#endif /* __cplusplus */
-
-
-/** @brief Mode flag passed to PaUtil_InitializeBufferProcessor indicating the type
- of buffering that the host API uses.
-
- The mode used depends on whether the host API or the implementation manages
- the buffers, and how these buffers are used (scatter gather, circular buffer).
-*/
-typedef enum {
-/** The host buffer size is a fixed known size. */
- paUtilFixedHostBufferSize,
-
-/** The host buffer size may vary, but has a known maximum size. */
- paUtilBoundedHostBufferSize,
-
-/** Nothing is known about the host buffer size. */
- paUtilUnknownHostBufferSize,
-
-/** The host buffer size varies, and the client does not require the buffer
- processor to consume all of the input and fill all of the output buffer. This
- is useful when the implementation has access to the host API's circular buffer
- and only needs to consume/fill some of it, not necessarily all of it, with each
- call to the buffer processor. This is the only mode where
- PaUtil_EndBufferProcessing() may not consume the whole buffer.
-*/
- paUtilVariableHostBufferSizePartialUsageAllowed
-}PaUtilHostBufferSizeMode;
-
-
-/** @brief An auxilliary data structure used internally by the buffer processor
- to represent host input and output buffers. */
-typedef struct PaUtilChannelDescriptor{
- void *data;
- unsigned int stride; /**< stride in samples, not bytes */
-}PaUtilChannelDescriptor;
-
-
-/** @brief The main buffer processor data structure.
-
- Allocate one of these, initialize it with PaUtil_InitializeBufferProcessor
- and terminate it with PaUtil_TerminateBufferProcessor.
-*/
-typedef struct {
- unsigned long framesPerUserBuffer;
- unsigned long framesPerHostBuffer;
-
- PaUtilHostBufferSizeMode hostBufferSizeMode;
- int useNonAdaptingProcess;
- unsigned long framesPerTempBuffer;
-
- unsigned int inputChannelCount;
- unsigned int bytesPerHostInputSample;
- unsigned int bytesPerUserInputSample;
- int userInputIsInterleaved;
- PaUtilConverter *inputConverter;
- PaUtilZeroer *inputZeroer;
-
- unsigned int outputChannelCount;
- unsigned int bytesPerHostOutputSample;
- unsigned int bytesPerUserOutputSample;
- int userOutputIsInterleaved;
- PaUtilConverter *outputConverter;
- PaUtilZeroer *outputZeroer;
-
- unsigned long initialFramesInTempInputBuffer;
- unsigned long initialFramesInTempOutputBuffer;
-
- void *tempInputBuffer; /**< used for slips, block adaption, and conversion. */
- void **tempInputBufferPtrs; /**< storage for non-interleaved buffer pointers, NULL for interleaved user input */
- unsigned long framesInTempInputBuffer; /**< frames remaining in input buffer from previous adaption iteration */
-
- void *tempOutputBuffer; /**< used for slips, block adaption, and conversion. */
- void **tempOutputBufferPtrs; /**< storage for non-interleaved buffer pointers, NULL for interleaved user output */
- unsigned long framesInTempOutputBuffer; /**< frames remaining in input buffer from previous adaption iteration */
-
- PaStreamCallbackTimeInfo *timeInfo;
-
- PaStreamCallbackFlags callbackStatusFlags;
-
- unsigned long hostInputFrameCount[2];
- PaUtilChannelDescriptor *hostInputChannels[2]; /**< pointers to arrays of channel descriptors.
- pointers are NULL for half-duplex output processing.
- hostInputChannels[i].data is NULL when the caller
- calls PaUtil_SetNoInput()
- */
- unsigned long hostOutputFrameCount[2];
- PaUtilChannelDescriptor *hostOutputChannels[2]; /**< pointers to arrays of channel descriptors.
- pointers are NULL for half-duplex input processing.
- hostOutputChannels[i].data is NULL when the caller
- calls PaUtil_SetNoOutput()
- */
-
- PaUtilTriangularDitherGenerator ditherGenerator;
-
- double samplePeriod;
-
- PaStreamCallback *streamCallback;
- void *userData;
-} PaUtilBufferProcessor;
-
-
-/** @name Initialization, termination, resetting and info */
-/*@{*/
-
-/** Initialize a buffer processor's representation stored in a
- PaUtilBufferProcessor structure. Be sure to call
- PaUtil_TerminateBufferProcessor after finishing with a buffer processor.
-
- @param bufferProcessor The buffer processor structure to initialize.
-
- @param inputChannelCount The number of input channels as passed to
- Pa_OpenStream or 0 for an output-only stream.
-
- @param userInputSampleFormat Format of user input samples, as passed to
- Pa_OpenStream. This parameter is ignored for ouput-only streams.
-
- @param hostInputSampleFormat Format of host input samples. This parameter is
- ignored for output-only streams. See note about host buffer interleave below.
-
- @param outputChannelCount The number of output channels as passed to
- Pa_OpenStream or 0 for an input-only stream.
-
- @param userOutputSampleFormat Format of user output samples, as passed to
- Pa_OpenStream. This parameter is ignored for input-only streams.
-
- @param hostOutputSampleFormat Format of host output samples. This parameter is
- ignored for input-only streams. See note about host buffer interleave below.
-
- @param sampleRate Sample rate of the stream. The more accurate this is the
- better - it is used for updating time stamps when adapting buffers.
-
- @param streamFlags Stream flags as passed to Pa_OpenStream, this parameter is
- used for selecting special sample conversion options such as clipping and
- dithering.
-
- @param framesPerUserBuffer Number of frames per user buffer, as requested
- by the framesPerBuffer parameter to Pa_OpenStream. This parameter may be
- zero to indicate that the user will accept any (and varying) buffer sizes.
-
- @param framesPerHostBuffer Specifies the number of frames per host buffer
- for the fixed buffer size mode, and the maximum number of frames
- per host buffer for the bounded host buffer size mode. It is ignored for
- the other modes.
-
- @param hostBufferSizeMode A mode flag indicating the size variability of
- host buffers that will be passed to the buffer processor. See
- PaUtilHostBufferSizeMode for further details.
-
- @param streamCallback The user stream callback passed to Pa_OpenStream.
-
- @param userData The user data field passed to Pa_OpenStream.
-
- @note The interleave flag is ignored for host buffer formats. Host
- interleave is determined by the use of different SetInput and SetOutput
- functions.
-
- @return An error code indicating whether the initialization was successful.
- If the error code is not PaNoError, the buffer processor was not initialized
- and should not be used.
-
- @see Pa_OpenStream, PaUtilHostBufferSizeMode, PaUtil_TerminateBufferProcessor
-*/
-PaError PaUtil_InitializeBufferProcessor( PaUtilBufferProcessor* bufferProcessor,
- int inputChannelCount, PaSampleFormat userInputSampleFormat,
- PaSampleFormat hostInputSampleFormat,
- int outputChannelCount, PaSampleFormat userOutputSampleFormat,
- PaSampleFormat hostOutputSampleFormat,
- double sampleRate,
- PaStreamFlags streamFlags,
- unsigned long framesPerUserBuffer, /* 0 indicates don't care */
- unsigned long framesPerHostBuffer,
- PaUtilHostBufferSizeMode hostBufferSizeMode,
- PaStreamCallback *streamCallback, void *userData );
-
-
-/** Terminate a buffer processor's representation. Deallocates any temporary
- buffers allocated by PaUtil_InitializeBufferProcessor.
-
- @param bufferProcessor The buffer processor structure to terminate.
-
- @see PaUtil_InitializeBufferProcessor.
-*/
-void PaUtil_TerminateBufferProcessor( PaUtilBufferProcessor* bufferProcessor );
-
-
-/** Clear any internally buffered data. If you call
- PaUtil_InitializeBufferProcessor in your OpenStream routine, make sure you
- call PaUtil_ResetBufferProcessor in your StartStream call.
-
- @param bufferProcessor The buffer processor to reset.
-*/
-void PaUtil_ResetBufferProcessor( PaUtilBufferProcessor* bufferProcessor );
-
-
-/** Retrieve the input latency of a buffer processor.
-
- @param bufferProcessor The buffer processor examine.
-
- @return The input latency introduced by the buffer processor, in frames.
-
- @see PaUtil_GetBufferProcessorOutputLatency
-*/
-unsigned long PaUtil_GetBufferProcessorInputLatency( PaUtilBufferProcessor* bufferProcessor );
-
-/** Retrieve the output latency of a buffer processor.
-
- @param bufferProcessor The buffer processor examine.
-
- @return The output latency introduced by the buffer processor, in frames.
-
- @see PaUtil_GetBufferProcessorInputLatency
-*/
-unsigned long PaUtil_GetBufferProcessorOutputLatency( PaUtilBufferProcessor* bufferProcessor );
-
-/*@}*/
-
-
-/** @name Host buffer pointer configuration
-
- Functions to set host input and output buffers, used by both callback streams
- and blocking read/write streams.
-*/
-/*@{*/
-
-
-/** Set the number of frames in the input host buffer(s) specified by the
- PaUtil_Set*InputChannel functions.
-
- @param bufferProcessor The buffer processor.
-
- @param frameCount The number of host input frames. A 0 frameCount indicates to
- use the framesPerHostBuffer value passed to PaUtil_InitializeBufferProcessor.
-
- @see PaUtil_SetNoInput, PaUtil_SetInputChannel,
- PaUtil_SetInterleavedInputChannels, PaUtil_SetNonInterleavedInputChannel
-*/
-void PaUtil_SetInputFrameCount( PaUtilBufferProcessor* bufferProcessor,
- unsigned long frameCount );
-
-
-/** Indicate that no input is avalable. This function should be used when
- priming the output of a full-duplex stream opened with the
- paPrimeOutputBuffersUsingStreamCallback flag. Note that it is not necessary
- to call this or any othe PaUtil_Set*Input* functions for ouput-only streams.
-
- @param bufferProcessor The buffer processor.
-*/
-void PaUtil_SetNoInput( PaUtilBufferProcessor* bufferProcessor );
-
-
-/** Provide the buffer processor with a pointer to a host input channel.
-
- @param bufferProcessor The buffer processor.
- @param channel The channel number.
- @param data The buffer.
- @param stride The stride from one sample to the next, in samples. For
- interleaved host buffers, the stride will usually be the same as the number of
- channels in the buffer.
-*/
-void PaUtil_SetInputChannel( PaUtilBufferProcessor* bufferProcessor,
- unsigned int channel, void *data, unsigned int stride );
-
-
-/** Provide the buffer processor with a pointer to an number of interleaved
- host input channels.
-
- @param bufferProcessor The buffer processor.
- @param firstChannel The first channel number.
- @param data The buffer.
- @param channelCount The number of interleaved channels in the buffer. If
- channelCount is zero, the number of channels specified to
- PaUtil_InitializeBufferProcessor will be used.
-*/
-void PaUtil_SetInterleavedInputChannels( PaUtilBufferProcessor* bufferProcessor,
- unsigned int firstChannel, void *data, unsigned int channelCount );
-
-
-/** Provide the buffer processor with a pointer to one non-interleaved host
- output channel.
-
- @param bufferProcessor The buffer processor.
- @param channel The channel number.
- @param data The buffer.
-*/
-void PaUtil_SetNonInterleavedInputChannel( PaUtilBufferProcessor* bufferProcessor,
- unsigned int channel, void *data );
-
-
-/** Use for the second buffer half when the input buffer is split in two halves.
- @see PaUtil_SetInputFrameCount
-*/
-void PaUtil_Set2ndInputFrameCount( PaUtilBufferProcessor* bufferProcessor,
- unsigned long frameCount );
-
-/** Use for the second buffer half when the input buffer is split in two halves.
- @see PaUtil_SetInputChannel
-*/
-void PaUtil_Set2ndInputChannel( PaUtilBufferProcessor* bufferProcessor,
- unsigned int channel, void *data, unsigned int stride );
-
-/** Use for the second buffer half when the input buffer is split in two halves.
- @see PaUtil_SetInterleavedInputChannels
-*/
-void PaUtil_Set2ndInterleavedInputChannels( PaUtilBufferProcessor* bufferProcessor,
- unsigned int firstChannel, void *data, unsigned int channelCount );
-
-/** Use for the second buffer half when the input buffer is split in two halves.
- @see PaUtil_SetNonInterleavedInputChannel
-*/
-void PaUtil_Set2ndNonInterleavedInputChannel( PaUtilBufferProcessor* bufferProcessor,
- unsigned int channel, void *data );
-
-
-/** Set the number of frames in the output host buffer(s) specified by the
- PaUtil_Set*OutputChannel functions.
-
- @param bufferProcessor The buffer processor.
-
- @param frameCount The number of host output frames. A 0 frameCount indicates to
- use the framesPerHostBuffer value passed to PaUtil_InitializeBufferProcessor.
-
- @see PaUtil_SetOutputChannel, PaUtil_SetInterleavedOutputChannels,
- PaUtil_SetNonInterleavedOutputChannel
-*/
-void PaUtil_SetOutputFrameCount( PaUtilBufferProcessor* bufferProcessor,
- unsigned long frameCount );
-
-
-/** Indicate that the output will be discarded. This function should be used
- when implementing the paNeverDropInput mode for full duplex streams.
-
- @param bufferProcessor The buffer processor.
-*/
-void PaUtil_SetNoOutput( PaUtilBufferProcessor* bufferProcessor );
-
-
-/** Provide the buffer processor with a pointer to a host output channel.
-
- @param bufferProcessor The buffer processor.
- @param channel The channel number.
- @param data The buffer.
- @param stride The stride from one sample to the next, in samples. For
- interleaved host buffers, the stride will usually be the same as the number of
- channels in the buffer.
-*/
-void PaUtil_SetOutputChannel( PaUtilBufferProcessor* bufferProcessor,
- unsigned int channel, void *data, unsigned int stride );
-
-
-/** Provide the buffer processor with a pointer to a number of interleaved
- host output channels.
-
- @param bufferProcessor The buffer processor.
- @param firstChannel The first channel number.
- @param data The buffer.
- @param channelCount The number of interleaved channels in the buffer. If
- channelCount is zero, the number of channels specified to
- PaUtil_InitializeBufferProcessor will be used.
-*/
-void PaUtil_SetInterleavedOutputChannels( PaUtilBufferProcessor* bufferProcessor,
- unsigned int firstChannel, void *data, unsigned int channelCount );
-
-
-/** Provide the buffer processor with a pointer to one non-interleaved host
- output channel.
-
- @param bufferProcessor The buffer processor.
- @param channel The channel number.
- @param data The buffer.
-*/
-void PaUtil_SetNonInterleavedOutputChannel( PaUtilBufferProcessor* bufferProcessor,
- unsigned int channel, void *data );
-
-
-/** Use for the second buffer half when the output buffer is split in two halves.
- @see PaUtil_SetOutputFrameCount
-*/
-void PaUtil_Set2ndOutputFrameCount( PaUtilBufferProcessor* bufferProcessor,
- unsigned long frameCount );
-
-/** Use for the second buffer half when the output buffer is split in two halves.
- @see PaUtil_SetOutputChannel
-*/
-void PaUtil_Set2ndOutputChannel( PaUtilBufferProcessor* bufferProcessor,
- unsigned int channel, void *data, unsigned int stride );
-
-/** Use for the second buffer half when the output buffer is split in two halves.
- @see PaUtil_SetInterleavedOutputChannels
-*/
-void PaUtil_Set2ndInterleavedOutputChannels( PaUtilBufferProcessor* bufferProcessor,
- unsigned int firstChannel, void *data, unsigned int channelCount );
-
-/** Use for the second buffer half when the output buffer is split in two halves.
- @see PaUtil_SetNonInterleavedOutputChannel
-*/
-void PaUtil_Set2ndNonInterleavedOutputChannel( PaUtilBufferProcessor* bufferProcessor,
- unsigned int channel, void *data );
-
-/*@}*/
-
-
-/** @name Buffer processing functions for callback streams
-*/
-/*@{*/
-
-/** Commence processing a host buffer (or a pair of host buffers in the
- full-duplex case) for a callback stream.
-
- @param bufferProcessor The buffer processor.
-
- @param timeInfo Timing information for the first sample of the host
- buffer(s). This information may be adjusted when buffer adaption is being
- performed.
-
- @param callbackStatusFlags Flags indicating whether underruns and overruns
- have occurred since the last time the buffer processor was called.
-*/
-void PaUtil_BeginBufferProcessing( PaUtilBufferProcessor* bufferProcessor,
- PaStreamCallbackTimeInfo* timeInfo, PaStreamCallbackFlags callbackStatusFlags );
-
-
-/** Finish processing a host buffer (or a pair of host buffers in the
- full-duplex case) for a callback stream.
-
- @param bufferProcessor The buffer processor.
-
- @param callbackResult On input, indicates a previous callback result, and on
- exit, the result of the user stream callback, if it is called.
- On entry callbackResult should contain one of { paContinue, paComplete, or
- paAbort}. If paComplete is passed, the stream callback will not be called
- but any audio that was generated by previous stream callbacks will be copied
- to the output buffer(s). You can check whether the buffer processor's internal
- buffer is empty by calling PaUtil_IsBufferProcessorOutputEmpty.
-
- If the stream callback is called its result is stored in *callbackResult. If
- the stream callback returns paComplete or paAbort, all output buffers will be
- full of valid data - some of which may be zeros to acount for data that
- wasn't generated by the terminating callback.
-
- @return The number of frames processed. This usually corresponds to the
- number of frames specified by the PaUtil_Set*FrameCount functions, exept in
- the paUtilVariableHostBufferSizePartialUsageAllowed buffer size mode when a
- smaller value may be returned.
-*/
-unsigned long PaUtil_EndBufferProcessing( PaUtilBufferProcessor* bufferProcessor,
- int *callbackResult );
-
-
-/** Determine whether any callback generated output remains in the bufffer
- processor's internal buffers. This method may be used to determine when to
- continue calling PaUtil_EndBufferProcessing() after the callback has returned
- a callbackResult of paComplete.
-
- @param bufferProcessor The buffer processor.
-
- @return Returns non-zero when callback generated output remains in the internal
- buffer and zero (0) when there internal buffer contains no callback generated
- data.
-*/
-int PaUtil_IsBufferProcessorOutputEmpty( PaUtilBufferProcessor* bufferProcessor );
-
-/*@}*/
-
-
-/** @name Buffer processing functions for blocking read/write streams
-*/
-/*@{*/
-
-/** Copy samples from host input channels set up by the PaUtil_Set*InputChannels
- functions to a user supplied buffer. This function is intended for use with
- blocking read/write streams. Copies the minimum of the number of
- user frames (specified by the frameCount parameter) and the number of available
- host frames (specified in a previous call to SetInputFrameCount()).
-
- @param bufferProcessor The buffer processor.
-
- @param buffer A pointer to the user buffer pointer, or a pointer to a pointer
- to an array of user buffer pointers for a non-interleaved stream. It is
- important that this parameter points to a copy of the user buffer pointers,
- not to the actual user buffer pointers, because this function updates the
- pointers before returning.
-
- @param frameCount The number of frames of data in the buffer(s) pointed to by
- the buffer parameter.
-
- @return The number of frames copied. The buffer pointer(s) pointed to by the
- buffer parameter are advanced to point to the frame(s) following the last one
- filled.
-*/
-unsigned long PaUtil_CopyInput( PaUtilBufferProcessor* bufferProcessor,
- void **buffer, unsigned long frameCount );
-
-
-/* Copy samples from a user supplied buffer to host output channels set up by
- the PaUtil_Set*OutputChannels functions. This function is intended for use with
- blocking read/write streams. Copies the minimum of the number of
- user frames (specified by the frameCount parameter) and the number of
- host frames (specified in a previous call to SetOutputFrameCount()).
-
- @param bufferProcessor The buffer processor.
-
- @param buffer A pointer to the user buffer pointer, or a pointer to a pointer
- to an array of user buffer pointers for a non-interleaved stream. It is
- important that this parameter points to a copy of the user buffer pointers,
- not to the actual user buffer pointers, because this function updates the
- pointers before returning.
-
- @param frameCount The number of frames of data in the buffer(s) pointed to by
- the buffer parameter.
-
- @return The number of frames copied. The buffer pointer(s) pointed to by the
- buffer parameter are advanced to point to the frame(s) following the last one
- copied.
-*/
-unsigned long PaUtil_CopyOutput( PaUtilBufferProcessor* bufferProcessor,
- const void ** buffer, unsigned long frameCount );
-
-
-/* Zero samples in host output channels set up by the PaUtil_Set*OutputChannels
- functions. This function is useful for flushing streams.
- Zeros the minimum of frameCount and the number of host frames specified in a
- previous call to SetOutputFrameCount().
-
- @param bufferProcessor The buffer processor.
-
- @param frameCount The maximum number of frames to zero.
-
- @return The number of frames zeroed.
-*/
-unsigned long PaUtil_ZeroOutput( PaUtilBufferProcessor* bufferProcessor,
- unsigned long frameCount );
-
-
-/*@}*/
-
-
-#ifdef __cplusplus
-}
-#endif /* __cplusplus */
-#endif /* PA_PROCESS_H */
+/*
+ * PJMEDIA - Multimedia over IP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef PA_PROCESS_H
+#define PA_PROCESS_H
+/*
+ * $Id: pa_process.h,v 1.1.2.30 2004/12/13 09:48:44 rossbencina Exp $
+ * Portable Audio I/O Library callback buffer processing adapters
+ *
+ * Based on the Open Source API proposed by Ross Bencina
+ * Copyright (c) 1999-2002 Phil Burk, Ross Bencina
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * Any person wishing to distribute modifications to the Software is
+ * requested to send the modifications to the original developer so that
+ * they can be incorporated into the canonical version.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+ * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+/** @file
+ @brief Buffer Processor prototypes. A Buffer Processor performs buffer length
+ adaption, coordinates sample format conversion, and interleaves/deinterleaves
+ channels.
+
+ <h3>Overview</h3>
+
+ The "Buffer Processor" (PaUtilBufferProcessor) manages conversion of audio
+ data from host buffers to user buffers and back again. Where required, the
+ buffer processor takes care of converting between host and user sample formats,
+ interleaving and deinterleaving multichannel buffers, and adapting between host
+ and user buffers with different lengths. The buffer processor may be used with
+ full and half duplex streams, for both callback streams and blocking read/write
+ streams.
+
+ One of the important capabilities provided by the buffer processor is
+ the ability to adapt between user and host buffer sizes of different lengths
+ with minimum latency. Although this task is relatively easy to perform when
+ the host buffer size is an integer multiple of the user buffer size, the
+ problem is more complicated when this is not the case - especially for
+ full-duplex callback streams. Where necessary the adaption is implemented by
+ internally buffering some input and/or output data. The buffer adation
+ algorithm used by the buffer processor was originally implemented by
+ Stephan Letz for the ASIO version of PortAudio, and is described in his
+ Callback_adaption_.pdf which is included in the distribution.
+
+ The buffer processor performs sample conversion using the functions provided
+ by pa_converters.c.
+
+ The following sections provide an overview of how to use the buffer processor.
+ Interested readers are advised to consult the host API implementations for
+ examples of buffer processor usage.
+
+
+ <h4>Initialization, resetting and termination</h4>
+
+ When a stream is opened, the buffer processor should be initialized using
+ PaUtil_InitializeBufferProcessor. This function initializes internal state
+ and allocates temporary buffers as neccesary according to the supplied
+ configuration parameters. Some of the parameters correspond to those requested
+ by the user in their call to Pa_OpenStream(), others reflect the requirements
+ of the host API implementation - they indicate host buffer sizes, formats,
+ and the type of buffering which the Host API uses. The buffer processor should
+ be initialized for callback streams and blocking read/write streams.
+
+ Call PaUtil_ResetBufferProcessor to clear any sample data which is present
+ in the buffer processor before starting to use it (for example when
+ Pa_StartStream is called).
+
+ When the buffer processor is no longer used call
+ PaUtil_TerminateBufferProcessor.
+
+
+ <h4>Using the buffer processor for a callback stream</h4>
+
+ The buffer processor's role in a callback stream is to take host input buffers
+ process them with the stream callback, and fill host output buffers. For a
+ full duplex stream, the buffer processor handles input and output simultaneously
+ due to the requirements of the minimum-latency buffer adation algorithm.
+
+ When a host buffer becomes available, the implementation should call
+ the buffer processor to process the buffer. The buffer processor calls the
+ stream callback to consume and/or produce audio data as necessary. The buffer
+ processor will convert sample formats, interleave/deinterleave channels,
+ and slice or chunk the data to the appropriate buffer lengths according to
+ the requirements of the stream callback and the host API.
+
+ To process a host buffer (or a pair of host buffers for a full-duplex stream)
+ use the following calling sequence:
+
+ -# Call PaUtil_BeginBufferProcessing
+ -# For a stream which takes input:
+ - Call PaUtil_SetInputFrameCount with the number of frames in the host input
+ buffer.
+ - Call one of the following functions one or more times to tell the
+ buffer processor about the host input buffer(s): PaUtil_SetInputChannel,
+ PaUtil_SetInterleavedInputChannels, PaUtil_SetNonInterleavedInputChannel.
+ Which function you call will depend on whether the host buffer(s) are
+ interleaved or not.
+ - If the available host data is split accross two buffers (for example a
+ data range at the end of a circular buffer and another range at the
+ beginning of the circular buffer), also call
+ PaUtil_Set2ndInputFrameCount, PaUtil_Set2ndInputChannel,
+ PaUtil_Set2ndInterleavedInputChannels,
+ PaUtil_Set2ndNonInterleavedInputChannel as necessary to tell the buffer
+ processor about the second buffer.
+ -# For a stream which generates output:
+ - Call PaUtil_SetOutputFrameCount with the number of frames in the host
+ output buffer.
+ - Call one of the following functions one or more times to tell the
+ buffer processor about the host output buffer(s): PaUtil_SetOutputChannel,
+ PaUtil_SetInterleavedOutputChannels, PaUtil_SetNonInterleavedOutputChannel.
+ Which function you call will depend on whether the host buffer(s) are
+ interleaved or not.
+ - If the available host output buffer space is split accross two buffers
+ (for example a data range at the end of a circular buffer and another
+ range at the beginning of the circular buffer), call
+ PaUtil_Set2ndOutputFrameCount, PaUtil_Set2ndOutputChannel,
+ PaUtil_Set2ndInterleavedOutputChannels,
+ PaUtil_Set2ndNonInterleavedOutputChannel as necessary to tell the buffer
+ processor about the second buffer.
+ -# Call PaUtil_EndBufferProcessing, this function performs the actual data
+ conversion and processing.
+
+
+ <h4>Using the buffer processor for a blocking read/write stream</h4>
+
+ Blocking read/write streams use the buffer processor to convert and copy user
+ output data to a host buffer, and to convert and copy host input data to
+ the user's buffer. The buffer processor does not perform any buffer adaption.
+ When using the buffer processor in a blocking read/write stream the input and
+ output conversion are performed separately by the PaUtil_CopyInput and
+ PaUtil_CopyOutput functions.
+
+ To copy data from a host input buffer to the buffer(s) which the user supplies
+ to Pa_ReadStream, use the following calling sequence.
+
+ - Repeat the following three steps until the user buffer(s) have been filled
+ with samples from the host input buffers:
+ -# Call PaUtil_SetInputFrameCount with the number of frames in the host
+ input buffer.
+ -# Call one of the following functions one or more times to tell the
+ buffer processor about the host input buffer(s): PaUtil_SetInputChannel,
+ PaUtil_SetInterleavedInputChannels, PaUtil_SetNonInterleavedInputChannel.
+ Which function you call will depend on whether the host buffer(s) are
+ interleaved or not.
+ -# Call PaUtil_CopyInput with the user buffer pointer (or a copy of the
+ array of buffer pointers for a non-interleaved stream) passed to
+ Pa_ReadStream, along with the number of frames in the user buffer(s).
+ Be careful to pass a <i>copy</i> of the user buffer pointers to
+ PaUtil_CopyInput because PaUtil_CopyInput advances the pointers to
+ the start of the next region to copy.
+ - PaUtil_CopyInput will not copy more data than is available in the
+ host buffer(s), so the above steps need to be repeated until the user
+ buffer(s) are full.
+
+
+ To copy data to the host output buffer from the user buffers(s) supplied
+ to Pa_WriteStream use the following calling sequence.
+
+ - Repeat the following three steps until all frames from the user buffer(s)
+ have been copied to the host API:
+ -# Call PaUtil_SetOutputFrameCount with the number of frames in the host
+ output buffer.
+ -# Call one of the following functions one or more times to tell the
+ buffer processor about the host output buffer(s): PaUtil_SetOutputChannel,
+ PaUtil_SetInterleavedOutputChannels, PaUtil_SetNonInterleavedOutputChannel.
+ Which function you call will depend on whether the host buffer(s) are
+ interleaved or not.
+ -# Call PaUtil_CopyOutput with the user buffer pointer (or a copy of the
+ array of buffer pointers for a non-interleaved stream) passed to
+ Pa_WriteStream, along with the number of frames in the user buffer(s).
+ Be careful to pass a <i>copy</i> of the user buffer pointers to
+ PaUtil_CopyOutput because PaUtil_CopyOutput advances the pointers to
+ the start of the next region to copy.
+ - PaUtil_CopyOutput will not copy more data than fits in the host buffer(s),
+ so the above steps need to be repeated until all user data is copied.
+*/
+
+
+#include "portaudio.h"
+#include "pa_converters.h"
+#include "pa_dither.h"
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif /* __cplusplus */
+
+
+/** @brief Mode flag passed to PaUtil_InitializeBufferProcessor indicating the type
+ of buffering that the host API uses.
+
+ The mode used depends on whether the host API or the implementation manages
+ the buffers, and how these buffers are used (scatter gather, circular buffer).
+*/
+typedef enum {
+/** The host buffer size is a fixed known size. */
+ paUtilFixedHostBufferSize,
+
+/** The host buffer size may vary, but has a known maximum size. */
+ paUtilBoundedHostBufferSize,
+
+/** Nothing is known about the host buffer size. */
+ paUtilUnknownHostBufferSize,
+
+/** The host buffer size varies, and the client does not require the buffer
+ processor to consume all of the input and fill all of the output buffer. This
+ is useful when the implementation has access to the host API's circular buffer
+ and only needs to consume/fill some of it, not necessarily all of it, with each
+ call to the buffer processor. This is the only mode where
+ PaUtil_EndBufferProcessing() may not consume the whole buffer.
+*/
+ paUtilVariableHostBufferSizePartialUsageAllowed
+}PaUtilHostBufferSizeMode;
+
+
+/** @brief An auxilliary data structure used internally by the buffer processor
+ to represent host input and output buffers. */
+typedef struct PaUtilChannelDescriptor{
+ void *data;
+ unsigned int stride; /**< stride in samples, not bytes */
+}PaUtilChannelDescriptor;
+
+
+/** @brief The main buffer processor data structure.
+
+ Allocate one of these, initialize it with PaUtil_InitializeBufferProcessor
+ and terminate it with PaUtil_TerminateBufferProcessor.
+*/
+typedef struct {
+ unsigned long framesPerUserBuffer;
+ unsigned long framesPerHostBuffer;
+
+ PaUtilHostBufferSizeMode hostBufferSizeMode;
+ int useNonAdaptingProcess;
+ unsigned long framesPerTempBuffer;
+
+ unsigned int inputChannelCount;
+ unsigned int bytesPerHostInputSample;
+ unsigned int bytesPerUserInputSample;
+ int userInputIsInterleaved;
+ PaUtilConverter *inputConverter;
+ PaUtilZeroer *inputZeroer;
+
+ unsigned int outputChannelCount;
+ unsigned int bytesPerHostOutputSample;
+ unsigned int bytesPerUserOutputSample;
+ int userOutputIsInterleaved;
+ PaUtilConverter *outputConverter;
+ PaUtilZeroer *outputZeroer;
+
+ unsigned long initialFramesInTempInputBuffer;
+ unsigned long initialFramesInTempOutputBuffer;
+
+ void *tempInputBuffer; /**< used for slips, block adaption, and conversion. */
+ void **tempInputBufferPtrs; /**< storage for non-interleaved buffer pointers, NULL for interleaved user input */
+ unsigned long framesInTempInputBuffer; /**< frames remaining in input buffer from previous adaption iteration */
+
+ void *tempOutputBuffer; /**< used for slips, block adaption, and conversion. */
+ void **tempOutputBufferPtrs; /**< storage for non-interleaved buffer pointers, NULL for interleaved user output */
+ unsigned long framesInTempOutputBuffer; /**< frames remaining in input buffer from previous adaption iteration */
+
+ PaStreamCallbackTimeInfo *timeInfo;
+
+ PaStreamCallbackFlags callbackStatusFlags;
+
+ unsigned long hostInputFrameCount[2];
+ PaUtilChannelDescriptor *hostInputChannels[2]; /**< pointers to arrays of channel descriptors.
+ pointers are NULL for half-duplex output processing.
+ hostInputChannels[i].data is NULL when the caller
+ calls PaUtil_SetNoInput()
+ */
+ unsigned long hostOutputFrameCount[2];
+ PaUtilChannelDescriptor *hostOutputChannels[2]; /**< pointers to arrays of channel descriptors.
+ pointers are NULL for half-duplex input processing.
+ hostOutputChannels[i].data is NULL when the caller
+ calls PaUtil_SetNoOutput()
+ */
+
+ PaUtilTriangularDitherGenerator ditherGenerator;
+
+ double samplePeriod;
+
+ PaStreamCallback *streamCallback;
+ void *userData;
+} PaUtilBufferProcessor;
+
+
+/** @name Initialization, termination, resetting and info */
+/*@{*/
+
+/** Initialize a buffer processor's representation stored in a
+ PaUtilBufferProcessor structure. Be sure to call
+ PaUtil_TerminateBufferProcessor after finishing with a buffer processor.
+
+ @param bufferProcessor The buffer processor structure to initialize.
+
+ @param inputChannelCount The number of input channels as passed to
+ Pa_OpenStream or 0 for an output-only stream.
+
+ @param userInputSampleFormat Format of user input samples, as passed to
+ Pa_OpenStream. This parameter is ignored for ouput-only streams.
+
+ @param hostInputSampleFormat Format of host input samples. This parameter is
+ ignored for output-only streams. See note about host buffer interleave below.
+
+ @param outputChannelCount The number of output channels as passed to
+ Pa_OpenStream or 0 for an input-only stream.
+
+ @param userOutputSampleFormat Format of user output samples, as passed to
+ Pa_OpenStream. This parameter is ignored for input-only streams.
+
+ @param hostOutputSampleFormat Format of host output samples. This parameter is
+ ignored for input-only streams. See note about host buffer interleave below.
+
+ @param sampleRate Sample rate of the stream. The more accurate this is the
+ better - it is used for updating time stamps when adapting buffers.
+
+ @param streamFlags Stream flags as passed to Pa_OpenStream, this parameter is
+ used for selecting special sample conversion options such as clipping and
+ dithering.
+
+ @param framesPerUserBuffer Number of frames per user buffer, as requested
+ by the framesPerBuffer parameter to Pa_OpenStream. This parameter may be
+ zero to indicate that the user will accept any (and varying) buffer sizes.
+
+ @param framesPerHostBuffer Specifies the number of frames per host buffer
+ for the fixed buffer size mode, and the maximum number of frames
+ per host buffer for the bounded host buffer size mode. It is ignored for
+ the other modes.
+
+ @param hostBufferSizeMode A mode flag indicating the size variability of
+ host buffers that will be passed to the buffer processor. See
+ PaUtilHostBufferSizeMode for further details.
+
+ @param streamCallback The user stream callback passed to Pa_OpenStream.
+
+ @param userData The user data field passed to Pa_OpenStream.
+
+ @note The interleave flag is ignored for host buffer formats. Host
+ interleave is determined by the use of different SetInput and SetOutput
+ functions.
+
+ @return An error code indicating whether the initialization was successful.
+ If the error code is not PaNoError, the buffer processor was not initialized
+ and should not be used.
+
+ @see Pa_OpenStream, PaUtilHostBufferSizeMode, PaUtil_TerminateBufferProcessor
+*/
+PaError PaUtil_InitializeBufferProcessor( PaUtilBufferProcessor* bufferProcessor,
+ int inputChannelCount, PaSampleFormat userInputSampleFormat,
+ PaSampleFormat hostInputSampleFormat,
+ int outputChannelCount, PaSampleFormat userOutputSampleFormat,
+ PaSampleFormat hostOutputSampleFormat,
+ double sampleRate,
+ PaStreamFlags streamFlags,
+ unsigned long framesPerUserBuffer, /* 0 indicates don't care */
+ unsigned long framesPerHostBuffer,
+ PaUtilHostBufferSizeMode hostBufferSizeMode,
+ PaStreamCallback *streamCallback, void *userData );
+
+
+/** Terminate a buffer processor's representation. Deallocates any temporary
+ buffers allocated by PaUtil_InitializeBufferProcessor.
+
+ @param bufferProcessor The buffer processor structure to terminate.
+
+ @see PaUtil_InitializeBufferProcessor.
+*/
+void PaUtil_TerminateBufferProcessor( PaUtilBufferProcessor* bufferProcessor );
+
+
+/** Clear any internally buffered data. If you call
+ PaUtil_InitializeBufferProcessor in your OpenStream routine, make sure you
+ call PaUtil_ResetBufferProcessor in your StartStream call.
+
+ @param bufferProcessor The buffer processor to reset.
+*/
+void PaUtil_ResetBufferProcessor( PaUtilBufferProcessor* bufferProcessor );
+
+
+/** Retrieve the input latency of a buffer processor.
+
+ @param bufferProcessor The buffer processor examine.
+
+ @return The input latency introduced by the buffer processor, in frames.
+
+ @see PaUtil_GetBufferProcessorOutputLatency
+*/
+unsigned long PaUtil_GetBufferProcessorInputLatency( PaUtilBufferProcessor* bufferProcessor );
+
+/** Retrieve the output latency of a buffer processor.
+
+ @param bufferProcessor The buffer processor examine.
+
+ @return The output latency introduced by the buffer processor, in frames.
+
+ @see PaUtil_GetBufferProcessorInputLatency
+*/
+unsigned long PaUtil_GetBufferProcessorOutputLatency( PaUtilBufferProcessor* bufferProcessor );
+
+/*@}*/
+
+
+/** @name Host buffer pointer configuration
+
+ Functions to set host input and output buffers, used by both callback streams
+ and blocking read/write streams.
+*/
+/*@{*/
+
+
+/** Set the number of frames in the input host buffer(s) specified by the
+ PaUtil_Set*InputChannel functions.
+
+ @param bufferProcessor The buffer processor.
+
+ @param frameCount The number of host input frames. A 0 frameCount indicates to
+ use the framesPerHostBuffer value passed to PaUtil_InitializeBufferProcessor.
+
+ @see PaUtil_SetNoInput, PaUtil_SetInputChannel,
+ PaUtil_SetInterleavedInputChannels, PaUtil_SetNonInterleavedInputChannel
+*/
+void PaUtil_SetInputFrameCount( PaUtilBufferProcessor* bufferProcessor,
+ unsigned long frameCount );
+
+
+/** Indicate that no input is avalable. This function should be used when
+ priming the output of a full-duplex stream opened with the
+ paPrimeOutputBuffersUsingStreamCallback flag. Note that it is not necessary
+ to call this or any othe PaUtil_Set*Input* functions for ouput-only streams.
+
+ @param bufferProcessor The buffer processor.
+*/
+void PaUtil_SetNoInput( PaUtilBufferProcessor* bufferProcessor );
+
+
+/** Provide the buffer processor with a pointer to a host input channel.
+
+ @param bufferProcessor The buffer processor.
+ @param channel The channel number.
+ @param data The buffer.
+ @param stride The stride from one sample to the next, in samples. For
+ interleaved host buffers, the stride will usually be the same as the number of
+ channels in the buffer.
+*/
+void PaUtil_SetInputChannel( PaUtilBufferProcessor* bufferProcessor,
+ unsigned int channel, void *data, unsigned int stride );
+
+
+/** Provide the buffer processor with a pointer to an number of interleaved
+ host input channels.
+
+ @param bufferProcessor The buffer processor.
+ @param firstChannel The first channel number.
+ @param data The buffer.
+ @param channelCount The number of interleaved channels in the buffer. If
+ channelCount is zero, the number of channels specified to
+ PaUtil_InitializeBufferProcessor will be used.
+*/
+void PaUtil_SetInterleavedInputChannels( PaUtilBufferProcessor* bufferProcessor,
+ unsigned int firstChannel, void *data, unsigned int channelCount );
+
+
+/** Provide the buffer processor with a pointer to one non-interleaved host
+ output channel.
+
+ @param bufferProcessor The buffer processor.
+ @param channel The channel number.
+ @param data The buffer.
+*/
+void PaUtil_SetNonInterleavedInputChannel( PaUtilBufferProcessor* bufferProcessor,
+ unsigned int channel, void *data );
+
+
+/** Use for the second buffer half when the input buffer is split in two halves.
+ @see PaUtil_SetInputFrameCount
+*/
+void PaUtil_Set2ndInputFrameCount( PaUtilBufferProcessor* bufferProcessor,
+ unsigned long frameCount );
+
+/** Use for the second buffer half when the input buffer is split in two halves.
+ @see PaUtil_SetInputChannel
+*/
+void PaUtil_Set2ndInputChannel( PaUtilBufferProcessor* bufferProcessor,
+ unsigned int channel, void *data, unsigned int stride );
+
+/** Use for the second buffer half when the input buffer is split in two halves.
+ @see PaUtil_SetInterleavedInputChannels
+*/
+void PaUtil_Set2ndInterleavedInputChannels( PaUtilBufferProcessor* bufferProcessor,
+ unsigned int firstChannel, void *data, unsigned int channelCount );
+
+/** Use for the second buffer half when the input buffer is split in two halves.
+ @see PaUtil_SetNonInterleavedInputChannel
+*/
+void PaUtil_Set2ndNonInterleavedInputChannel( PaUtilBufferProcessor* bufferProcessor,
+ unsigned int channel, void *data );
+
+
+/** Set the number of frames in the output host buffer(s) specified by the
+ PaUtil_Set*OutputChannel functions.
+
+ @param bufferProcessor The buffer processor.
+
+ @param frameCount The number of host output frames. A 0 frameCount indicates to
+ use the framesPerHostBuffer value passed to PaUtil_InitializeBufferProcessor.
+
+ @see PaUtil_SetOutputChannel, PaUtil_SetInterleavedOutputChannels,
+ PaUtil_SetNonInterleavedOutputChannel
+*/
+void PaUtil_SetOutputFrameCount( PaUtilBufferProcessor* bufferProcessor,
+ unsigned long frameCount );
+
+
+/** Indicate that the output will be discarded. This function should be used
+ when implementing the paNeverDropInput mode for full duplex streams.
+
+ @param bufferProcessor The buffer processor.
+*/
+void PaUtil_SetNoOutput( PaUtilBufferProcessor* bufferProcessor );
+
+
+/** Provide the buffer processor with a pointer to a host output channel.
+
+ @param bufferProcessor The buffer processor.
+ @param channel The channel number.
+ @param data The buffer.
+ @param stride The stride from one sample to the next, in samples. For
+ interleaved host buffers, the stride will usually be the same as the number of
+ channels in the buffer.
+*/
+void PaUtil_SetOutputChannel( PaUtilBufferProcessor* bufferProcessor,
+ unsigned int channel, void *data, unsigned int stride );
+
+
+/** Provide the buffer processor with a pointer to a number of interleaved
+ host output channels.
+
+ @param bufferProcessor The buffer processor.
+ @param firstChannel The first channel number.
+ @param data The buffer.
+ @param channelCount The number of interleaved channels in the buffer. If
+ channelCount is zero, the number of channels specified to
+ PaUtil_InitializeBufferProcessor will be used.
+*/
+void PaUtil_SetInterleavedOutputChannels( PaUtilBufferProcessor* bufferProcessor,
+ unsigned int firstChannel, void *data, unsigned int channelCount );
+
+
+/** Provide the buffer processor with a pointer to one non-interleaved host
+ output channel.
+
+ @param bufferProcessor The buffer processor.
+ @param channel The channel number.
+ @param data The buffer.
+*/
+void PaUtil_SetNonInterleavedOutputChannel( PaUtilBufferProcessor* bufferProcessor,
+ unsigned int channel, void *data );
+
+
+/** Use for the second buffer half when the output buffer is split in two halves.
+ @see PaUtil_SetOutputFrameCount
+*/
+void PaUtil_Set2ndOutputFrameCount( PaUtilBufferProcessor* bufferProcessor,
+ unsigned long frameCount );
+
+/** Use for the second buffer half when the output buffer is split in two halves.
+ @see PaUtil_SetOutputChannel
+*/
+void PaUtil_Set2ndOutputChannel( PaUtilBufferProcessor* bufferProcessor,
+ unsigned int channel, void *data, unsigned int stride );
+
+/** Use for the second buffer half when the output buffer is split in two halves.
+ @see PaUtil_SetInterleavedOutputChannels
+*/
+void PaUtil_Set2ndInterleavedOutputChannels( PaUtilBufferProcessor* bufferProcessor,
+ unsigned int firstChannel, void *data, unsigned int channelCount );
+
+/** Use for the second buffer half when the output buffer is split in two halves.
+ @see PaUtil_SetNonInterleavedOutputChannel
+*/
+void PaUtil_Set2ndNonInterleavedOutputChannel( PaUtilBufferProcessor* bufferProcessor,
+ unsigned int channel, void *data );
+
+/*@}*/
+
+
+/** @name Buffer processing functions for callback streams
+*/
+/*@{*/
+
+/** Commence processing a host buffer (or a pair of host buffers in the
+ full-duplex case) for a callback stream.
+
+ @param bufferProcessor The buffer processor.
+
+ @param timeInfo Timing information for the first sample of the host
+ buffer(s). This information may be adjusted when buffer adaption is being
+ performed.
+
+ @param callbackStatusFlags Flags indicating whether underruns and overruns
+ have occurred since the last time the buffer processor was called.
+*/
+void PaUtil_BeginBufferProcessing( PaUtilBufferProcessor* bufferProcessor,
+ PaStreamCallbackTimeInfo* timeInfo, PaStreamCallbackFlags callbackStatusFlags );
+
+
+/** Finish processing a host buffer (or a pair of host buffers in the
+ full-duplex case) for a callback stream.
+
+ @param bufferProcessor The buffer processor.
+
+ @param callbackResult On input, indicates a previous callback result, and on
+ exit, the result of the user stream callback, if it is called.
+ On entry callbackResult should contain one of { paContinue, paComplete, or
+ paAbort}. If paComplete is passed, the stream callback will not be called
+ but any audio that was generated by previous stream callbacks will be copied
+ to the output buffer(s). You can check whether the buffer processor's internal
+ buffer is empty by calling PaUtil_IsBufferProcessorOutputEmpty.
+
+ If the stream callback is called its result is stored in *callbackResult. If
+ the stream callback returns paComplete or paAbort, all output buffers will be
+ full of valid data - some of which may be zeros to acount for data that
+ wasn't generated by the terminating callback.
+
+ @return The number of frames processed. This usually corresponds to the
+ number of frames specified by the PaUtil_Set*FrameCount functions, exept in
+ the paUtilVariableHostBufferSizePartialUsageAllowed buffer size mode when a
+ smaller value may be returned.
+*/
+unsigned long PaUtil_EndBufferProcessing( PaUtilBufferProcessor* bufferProcessor,
+ int *callbackResult );
+
+
+/** Determine whether any callback generated output remains in the bufffer
+ processor's internal buffers. This method may be used to determine when to
+ continue calling PaUtil_EndBufferProcessing() after the callback has returned
+ a callbackResult of paComplete.
+
+ @param bufferProcessor The buffer processor.
+
+ @return Returns non-zero when callback generated output remains in the internal
+ buffer and zero (0) when there internal buffer contains no callback generated
+ data.
+*/
+int PaUtil_IsBufferProcessorOutputEmpty( PaUtilBufferProcessor* bufferProcessor );
+
+/*@}*/
+
+
+/** @name Buffer processing functions for blocking read/write streams
+*/
+/*@{*/
+
+/** Copy samples from host input channels set up by the PaUtil_Set*InputChannels
+ functions to a user supplied buffer. This function is intended for use with
+ blocking read/write streams. Copies the minimum of the number of
+ user frames (specified by the frameCount parameter) and the number of available
+ host frames (specified in a previous call to SetInputFrameCount()).
+
+ @param bufferProcessor The buffer processor.
+
+ @param buffer A pointer to the user buffer pointer, or a pointer to a pointer
+ to an array of user buffer pointers for a non-interleaved stream. It is
+ important that this parameter points to a copy of the user buffer pointers,
+ not to the actual user buffer pointers, because this function updates the
+ pointers before returning.
+
+ @param frameCount The number of frames of data in the buffer(s) pointed to by
+ the buffer parameter.
+
+ @return The number of frames copied. The buffer pointer(s) pointed to by the
+ buffer parameter are advanced to point to the frame(s) following the last one
+ filled.
+*/
+unsigned long PaUtil_CopyInput( PaUtilBufferProcessor* bufferProcessor,
+ void **buffer, unsigned long frameCount );
+
+
+/* Copy samples from a user supplied buffer to host output channels set up by
+ the PaUtil_Set*OutputChannels functions. This function is intended for use with
+ blocking read/write streams. Copies the minimum of the number of
+ user frames (specified by the frameCount parameter) and the number of
+ host frames (specified in a previous call to SetOutputFrameCount()).
+
+ @param bufferProcessor The buffer processor.
+
+ @param buffer A pointer to the user buffer pointer, or a pointer to a pointer
+ to an array of user buffer pointers for a non-interleaved stream. It is
+ important that this parameter points to a copy of the user buffer pointers,
+ not to the actual user buffer pointers, because this function updates the
+ pointers before returning.
+
+ @param frameCount The number of frames of data in the buffer(s) pointed to by
+ the buffer parameter.
+
+ @return The number of frames copied. The buffer pointer(s) pointed to by the
+ buffer parameter are advanced to point to the frame(s) following the last one
+ copied.
+*/
+unsigned long PaUtil_CopyOutput( PaUtilBufferProcessor* bufferProcessor,
+ const void ** buffer, unsigned long frameCount );
+
+
+/* Zero samples in host output channels set up by the PaUtil_Set*OutputChannels
+ functions. This function is useful for flushing streams.
+ Zeros the minimum of frameCount and the number of host frames specified in a
+ previous call to SetOutputFrameCount().
+
+ @param bufferProcessor The buffer processor.
+
+ @param frameCount The maximum number of frames to zero.
+
+ @return The number of frames zeroed.
+*/
+unsigned long PaUtil_ZeroOutput( PaUtilBufferProcessor* bufferProcessor,
+ unsigned long frameCount );
+
+
+/*@}*/
+
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+#endif /* PA_PROCESS_H */
diff --git a/pjmedia/src/pjmedia/portaudio/pa_skeleton.c b/pjmedia/src/pjmedia/portaudio/pa_skeleton.c
index ebc1f501..5df50c9d 100644
--- a/pjmedia/src/pjmedia/portaudio/pa_skeleton.c
+++ b/pjmedia/src/pjmedia/portaudio/pa_skeleton.c
@@ -1,807 +1,828 @@
-/*
- * $Id: pa_skeleton.c,v 1.1.2.39 2003/11/26 14:56:09 rossbencina Exp $
- * Portable Audio I/O Library skeleton implementation
- * demonstrates how to use the common functions to implement support
- * for a host API
- *
- * Based on the Open Source API proposed by Ross Bencina
- * Copyright (c) 1999-2002 Ross Bencina, Phil Burk
- *
- * Permission is hereby granted, free of charge, to any person obtaining
- * a copy of this software and associated documentation files
- * (the "Software"), to deal in the Software without restriction,
- * including without limitation the rights to use, copy, modify, merge,
- * publish, distribute, sublicense, and/or sell copies of the Software,
- * and to permit persons to whom the Software is furnished to do so,
- * subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be
- * included in all copies or substantial portions of the Software.
- *
- * Any person wishing to distribute modifications to the Software is
- * requested to send the modifications to the original developer so that
- * they can be incorporated into the canonical version.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
- * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
- * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
- * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
- * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
- */
-
-/** @file
- @brief Skeleton implementation of support for a host API.
-
- @note This file is provided as a starting point for implementing support for
- a new host API. IMPLEMENT ME comments are used to indicate functionality
- which much be customised for each implementation.
-*/
-
-
-#include <string.h> /* strlen() */
-
-#include "pa_util.h"
-#include "pa_allocation.h"
-#include "pa_hostapi.h"
-#include "pa_stream.h"
-#include "pa_cpuload.h"
-#include "pa_process.h"
-
-
-/* prototypes for functions declared in this file */
-
-#ifdef __cplusplus
-extern "C"
-{
-#endif /* __cplusplus */
-
-PaError PaSkeleton_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index );
-
-#ifdef __cplusplus
-}
-#endif /* __cplusplus */
-
-
-static void Terminate( struct PaUtilHostApiRepresentation *hostApi );
-static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
- const PaStreamParameters *inputParameters,
- const PaStreamParameters *outputParameters,
- double sampleRate );
-static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
- PaStream** s,
- const PaStreamParameters *inputParameters,
- const PaStreamParameters *outputParameters,
- double sampleRate,
- unsigned long framesPerBuffer,
- PaStreamFlags streamFlags,
- PaStreamCallback *streamCallback,
- void *userData );
-static PaError CloseStream( PaStream* stream );
-static PaError StartStream( PaStream *stream );
-static PaError StopStream( PaStream *stream );
-static PaError AbortStream( PaStream *stream );
-static PaError IsStreamStopped( PaStream *s );
-static PaError IsStreamActive( PaStream *stream );
-static PaTime GetStreamTime( PaStream *stream );
-static double GetStreamCpuLoad( PaStream* stream );
-static PaError ReadStream( PaStream* stream, void *buffer, unsigned long frames );
-static PaError WriteStream( PaStream* stream, const void *buffer, unsigned long frames );
-static signed long GetStreamReadAvailable( PaStream* stream );
-static signed long GetStreamWriteAvailable( PaStream* stream );
-
-
-/* IMPLEMENT ME: a macro like the following one should be used for reporting
- host errors */
-#define PA_SKELETON_SET_LAST_HOST_ERROR( errorCode, errorText ) \
- PaUtil_SetLastHostErrorInfo( paInDevelopment, errorCode, errorText )
-
-/* PaSkeletonHostApiRepresentation - host api datastructure specific to this implementation */
-
-typedef struct
-{
- PaUtilHostApiRepresentation inheritedHostApiRep;
- PaUtilStreamInterface callbackStreamInterface;
- PaUtilStreamInterface blockingStreamInterface;
-
- PaUtilAllocationGroup *allocations;
-
- /* implementation specific data goes here */
-}
-PaSkeletonHostApiRepresentation; /* IMPLEMENT ME: rename this */
-
-
-PaError PaSkeleton_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex hostApiIndex )
-{
- PaError result = paNoError;
- int i, deviceCount;
- PaSkeletonHostApiRepresentation *skeletonHostApi;
- PaDeviceInfo *deviceInfoArray;
-
- skeletonHostApi = (PaSkeletonHostApiRepresentation*)PaUtil_AllocateMemory( sizeof(PaSkeletonHostApiRepresentation) );
- if( !skeletonHostApi )
- {
- result = paInsufficientMemory;
- goto error;
- }
-
- skeletonHostApi->allocations = PaUtil_CreateAllocationGroup();
- if( !skeletonHostApi->allocations )
- {
- result = paInsufficientMemory;
- goto error;
- }
-
- *hostApi = &skeletonHostApi->inheritedHostApiRep;
- (*hostApi)->info.structVersion = 1;
- (*hostApi)->info.type = paInDevelopment; /* IMPLEMENT ME: change to correct type id */
- (*hostApi)->info.name = "skeleton implementation"; /* IMPLEMENT ME: change to correct name */
-
- (*hostApi)->info.defaultInputDevice = paNoDevice; /* IMPLEMENT ME */
- (*hostApi)->info.defaultOutputDevice = paNoDevice; /* IMPLEMENT ME */
-
- (*hostApi)->info.deviceCount = 0;
-
- deviceCount = 0; /* IMPLEMENT ME */
-
- if( deviceCount > 0 )
- {
- (*hostApi)->deviceInfos = (PaDeviceInfo**)PaUtil_GroupAllocateMemory(
- skeletonHostApi->allocations, sizeof(PaDeviceInfo*) * deviceCount );
- if( !(*hostApi)->deviceInfos )
- {
- result = paInsufficientMemory;
- goto error;
- }
-
- /* allocate all device info structs in a contiguous block */
- deviceInfoArray = (PaDeviceInfo*)PaUtil_GroupAllocateMemory(
- skeletonHostApi->allocations, sizeof(PaDeviceInfo) * deviceCount );
- if( !deviceInfoArray )
- {
- result = paInsufficientMemory;
- goto error;
- }
-
- for( i=0; i < deviceCount; ++i )
- {
- PaDeviceInfo *deviceInfo = &deviceInfoArray[i];
- deviceInfo->structVersion = 2;
- deviceInfo->hostApi = hostApiIndex;
- deviceInfo->name = 0; /* IMPLEMENT ME: allocate block and copy name eg:
- deviceName = (char*)PaUtil_GroupAllocateMemory( skeletonHostApi->allocations, strlen(srcName) + 1 );
- if( !deviceName )
- {
- result = paInsufficientMemory;
- goto error;
- }
- strcpy( deviceName, srcName );
- deviceInfo->name = deviceName;
- */
-
- deviceInfo->maxInputChannels = 0; /* IMPLEMENT ME */
- deviceInfo->maxOutputChannels = 0; /* IMPLEMENT ME */
-
- deviceInfo->defaultLowInputLatency = 0.; /* IMPLEMENT ME */
- deviceInfo->defaultLowOutputLatency = 0.; /* IMPLEMENT ME */
- deviceInfo->defaultHighInputLatency = 0.; /* IMPLEMENT ME */
- deviceInfo->defaultHighOutputLatency = 0.; /* IMPLEMENT ME */
-
- deviceInfo->defaultSampleRate = 0.; /* IMPLEMENT ME */
-
- (*hostApi)->deviceInfos[i] = deviceInfo;
- ++(*hostApi)->info.deviceCount;
- }
- }
-
- (*hostApi)->Terminate = Terminate;
- (*hostApi)->OpenStream = OpenStream;
- (*hostApi)->IsFormatSupported = IsFormatSupported;
-
- PaUtil_InitializeStreamInterface( &skeletonHostApi->callbackStreamInterface, CloseStream, StartStream,
- StopStream, AbortStream, IsStreamStopped, IsStreamActive,
- GetStreamTime, GetStreamCpuLoad,
- PaUtil_DummyRead, PaUtil_DummyWrite,
- PaUtil_DummyGetReadAvailable, PaUtil_DummyGetWriteAvailable );
-
- PaUtil_InitializeStreamInterface( &skeletonHostApi->blockingStreamInterface, CloseStream, StartStream,
- StopStream, AbortStream, IsStreamStopped, IsStreamActive,
- GetStreamTime, PaUtil_DummyGetCpuLoad,
- ReadStream, WriteStream, GetStreamReadAvailable, GetStreamWriteAvailable );
-
- return result;
-
-error:
- if( skeletonHostApi )
- {
- if( skeletonHostApi->allocations )
- {
- PaUtil_FreeAllAllocations( skeletonHostApi->allocations );
- PaUtil_DestroyAllocationGroup( skeletonHostApi->allocations );
- }
-
- PaUtil_FreeMemory( skeletonHostApi );
- }
- return result;
-}
-
-
-static void Terminate( struct PaUtilHostApiRepresentation *hostApi )
-{
- PaSkeletonHostApiRepresentation *skeletonHostApi = (PaSkeletonHostApiRepresentation*)hostApi;
-
- /*
- IMPLEMENT ME:
- - clean up any resources not handled by the allocation group
- */
-
- if( skeletonHostApi->allocations )
- {
- PaUtil_FreeAllAllocations( skeletonHostApi->allocations );
- PaUtil_DestroyAllocationGroup( skeletonHostApi->allocations );
- }
-
- PaUtil_FreeMemory( skeletonHostApi );
-}
-
-
-static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
- const PaStreamParameters *inputParameters,
- const PaStreamParameters *outputParameters,
- double sampleRate )
-{
- int inputChannelCount, outputChannelCount;
- PaSampleFormat inputSampleFormat, outputSampleFormat;
-
- if( inputParameters )
- {
- inputChannelCount = inputParameters->channelCount;
- inputSampleFormat = inputParameters->sampleFormat;
-
- /* all standard sample formats are supported by the buffer adapter,
- this implementation doesn't support any custom sample formats */
- if( inputSampleFormat & paCustomFormat )
- return paSampleFormatNotSupported;
-
- /* unless alternate device specification is supported, reject the use of
- paUseHostApiSpecificDeviceSpecification */
-
- if( inputParameters->device == paUseHostApiSpecificDeviceSpecification )
- return paInvalidDevice;
-
- /* check that input device can support inputChannelCount */
- if( inputChannelCount > hostApi->deviceInfos[ inputParameters->device ]->maxInputChannels )
- return paInvalidChannelCount;
-
- /* validate inputStreamInfo */
- if( inputParameters->hostApiSpecificStreamInfo )
- return paIncompatibleHostApiSpecificStreamInfo; /* this implementation doesn't use custom stream info */
- }
- else
- {
- inputChannelCount = 0;
- }
-
- if( outputParameters )
- {
- outputChannelCount = outputParameters->channelCount;
- outputSampleFormat = outputParameters->sampleFormat;
-
- /* all standard sample formats are supported by the buffer adapter,
- this implementation doesn't support any custom sample formats */
- if( outputSampleFormat & paCustomFormat )
- return paSampleFormatNotSupported;
-
- /* unless alternate device specification is supported, reject the use of
- paUseHostApiSpecificDeviceSpecification */
-
- if( outputParameters->device == paUseHostApiSpecificDeviceSpecification )
- return paInvalidDevice;
-
- /* check that output device can support outputChannelCount */
- if( outputChannelCount > hostApi->deviceInfos[ outputParameters->device ]->maxOutputChannels )
- return paInvalidChannelCount;
-
- /* validate outputStreamInfo */
- if( outputParameters->hostApiSpecificStreamInfo )
- return paIncompatibleHostApiSpecificStreamInfo; /* this implementation doesn't use custom stream info */
- }
- else
- {
- outputChannelCount = 0;
- }
-
- /*
- IMPLEMENT ME:
-
- - if a full duplex stream is requested, check that the combination
- of input and output parameters is supported if necessary
-
- - check that the device supports sampleRate
-
- Because the buffer adapter handles conversion between all standard
- sample formats, the following checks are only required if paCustomFormat
- is implemented, or under some other unusual conditions.
-
- - check that input device can support inputSampleFormat, or that
- we have the capability to convert from inputSampleFormat to
- a native format
-
- - check that output device can support outputSampleFormat, or that
- we have the capability to convert from outputSampleFormat to
- a native format
- */
-
-
- /* suppress unused variable warnings */
- (void) sampleRate;
-
- return paFormatIsSupported;
-}
-
-/* PaSkeletonStream - a stream data structure specifically for this implementation */
-
-typedef struct PaSkeletonStream
-{ /* IMPLEMENT ME: rename this */
- PaUtilStreamRepresentation streamRepresentation;
- PaUtilCpuLoadMeasurer cpuLoadMeasurer;
- PaUtilBufferProcessor bufferProcessor;
-
- /* IMPLEMENT ME:
- - implementation specific data goes here
- */
- unsigned long framesPerHostCallback; /* just an example */
-}
-PaSkeletonStream;
-
-/* see pa_hostapi.h for a list of validity guarantees made about OpenStream parameters */
-
-static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
- PaStream** s,
- const PaStreamParameters *inputParameters,
- const PaStreamParameters *outputParameters,
- double sampleRate,
- unsigned long framesPerBuffer,
- PaStreamFlags streamFlags,
- PaStreamCallback *streamCallback,
- void *userData )
-{
- PaError result = paNoError;
- PaSkeletonHostApiRepresentation *skeletonHostApi = (PaSkeletonHostApiRepresentation*)hostApi;
- PaSkeletonStream *stream = 0;
- unsigned long framesPerHostBuffer = framesPerBuffer; /* these may not be equivalent for all implementations */
- int inputChannelCount, outputChannelCount;
- PaSampleFormat inputSampleFormat, outputSampleFormat;
- PaSampleFormat hostInputSampleFormat, hostOutputSampleFormat;
-
-
- if( inputParameters )
- {
- inputChannelCount = inputParameters->channelCount;
- inputSampleFormat = inputParameters->sampleFormat;
-
- /* unless alternate device specification is supported, reject the use of
- paUseHostApiSpecificDeviceSpecification */
-
- if( inputParameters->device == paUseHostApiSpecificDeviceSpecification )
- return paInvalidDevice;
-
- /* check that input device can support inputChannelCount */
- if( inputChannelCount > hostApi->deviceInfos[ inputParameters->device ]->maxInputChannels )
- return paInvalidChannelCount;
-
- /* validate inputStreamInfo */
- if( inputParameters->hostApiSpecificStreamInfo )
- return paIncompatibleHostApiSpecificStreamInfo; /* this implementation doesn't use custom stream info */
-
- /* IMPLEMENT ME - establish which host formats are available */
- hostInputSampleFormat =
- PaUtil_SelectClosestAvailableFormat( paInt16 /* native formats */, inputSampleFormat );
- }
- else
- {
- inputChannelCount = 0;
- inputSampleFormat = hostInputSampleFormat = paInt16; /* Surpress 'uninitialised var' warnings. */
- }
-
- if( outputParameters )
- {
- outputChannelCount = outputParameters->channelCount;
- outputSampleFormat = outputParameters->sampleFormat;
-
- /* unless alternate device specification is supported, reject the use of
- paUseHostApiSpecificDeviceSpecification */
-
- if( outputParameters->device == paUseHostApiSpecificDeviceSpecification )
- return paInvalidDevice;
-
- /* check that output device can support inputChannelCount */
- if( outputChannelCount > hostApi->deviceInfos[ outputParameters->device ]->maxOutputChannels )
- return paInvalidChannelCount;
-
- /* validate outputStreamInfo */
- if( outputParameters->hostApiSpecificStreamInfo )
- return paIncompatibleHostApiSpecificStreamInfo; /* this implementation doesn't use custom stream info */
-
- /* IMPLEMENT ME - establish which host formats are available */
- hostOutputSampleFormat =
- PaUtil_SelectClosestAvailableFormat( paInt16 /* native formats */, outputSampleFormat );
- }
- else
- {
- outputChannelCount = 0;
- outputSampleFormat = hostOutputSampleFormat = paInt16; /* Surpress 'uninitialized var' warnings. */
- }
-
- /*
- IMPLEMENT ME:
-
- ( the following two checks are taken care of by PaUtil_InitializeBufferProcessor() FIXME - checks needed? )
-
- - check that input device can support inputSampleFormat, or that
- we have the capability to convert from outputSampleFormat to
- a native format
-
- - check that output device can support outputSampleFormat, or that
- we have the capability to convert from outputSampleFormat to
- a native format
-
- - if a full duplex stream is requested, check that the combination
- of input and output parameters is supported
-
- - check that the device supports sampleRate
-
- - alter sampleRate to a close allowable rate if possible / necessary
-
- - validate suggestedInputLatency and suggestedOutputLatency parameters,
- use default values where necessary
- */
-
-
-
-
- /* validate platform specific flags */
- if( (streamFlags & paPlatformSpecificFlags) != 0 )
- return paInvalidFlag; /* unexpected platform specific flag */
-
-
- stream = (PaSkeletonStream*)PaUtil_AllocateMemory( sizeof(PaSkeletonStream) );
- if( !stream )
- {
- result = paInsufficientMemory;
- goto error;
- }
-
- if( streamCallback )
- {
- PaUtil_InitializeStreamRepresentation( &stream->streamRepresentation,
- &skeletonHostApi->callbackStreamInterface, streamCallback, userData );
- }
- else
- {
- PaUtil_InitializeStreamRepresentation( &stream->streamRepresentation,
- &skeletonHostApi->blockingStreamInterface, streamCallback, userData );
- }
-
- PaUtil_InitializeCpuLoadMeasurer( &stream->cpuLoadMeasurer, sampleRate );
-
-
- /* we assume a fixed host buffer size in this example, but the buffer processor
- can also support bounded and unknown host buffer sizes by passing
- paUtilBoundedHostBufferSize or paUtilUnknownHostBufferSize instead of
- paUtilFixedHostBufferSize below. */
-
- result = PaUtil_InitializeBufferProcessor( &stream->bufferProcessor,
- inputChannelCount, inputSampleFormat, hostInputSampleFormat,
- outputChannelCount, outputSampleFormat, hostOutputSampleFormat,
- sampleRate, streamFlags, framesPerBuffer,
- framesPerHostBuffer, paUtilFixedHostBufferSize,
- streamCallback, userData );
- if( result != paNoError )
- goto error;
-
-
- /*
- IMPLEMENT ME: initialise the following fields with estimated or actual
- values.
- */
- stream->streamRepresentation.streamInfo.inputLatency =
- PaUtil_GetBufferProcessorInputLatency(&stream->bufferProcessor);
- stream->streamRepresentation.streamInfo.outputLatency =
- PaUtil_GetBufferProcessorOutputLatency(&stream->bufferProcessor);
- stream->streamRepresentation.streamInfo.sampleRate = sampleRate;
-
-
- /*
- IMPLEMENT ME:
- - additional stream setup + opening
- */
-
- stream->framesPerHostCallback = framesPerHostBuffer;
-
- *s = (PaStream*)stream;
-
- return result;
-
-error:
- if( stream )
- PaUtil_FreeMemory( stream );
-
- return result;
-}
-
-/*
- ExampleHostProcessingLoop() illustrates the kind of processing which may
- occur in a host implementation.
-
-*/
-static void ExampleHostProcessingLoop( void *inputBuffer, void *outputBuffer, void *userData )
-{
- PaSkeletonStream *stream = (PaSkeletonStream*)userData;
- PaStreamCallbackTimeInfo timeInfo = {0,0,0}; /* IMPLEMENT ME */
- int callbackResult;
- unsigned long framesProcessed;
-
- PaUtil_BeginCpuLoadMeasurement( &stream->cpuLoadMeasurer );
-
- /*
- IMPLEMENT ME:
- - generate timing information
- - handle buffer slips
- */
-
- /*
- If you need to byte swap or shift inputBuffer to convert it into a
- portaudio format, do it here.
- */
-
-
-
- PaUtil_BeginBufferProcessing( &stream->bufferProcessor, &timeInfo, 0 /* IMPLEMENT ME: pass underflow/overflow flags when necessary */ );
-
- /*
- depending on whether the host buffers are interleaved, non-interleaved
- or a mixture, you will want to call PaUtil_SetInterleaved*Channels(),
- PaUtil_SetNonInterleaved*Channel() or PaUtil_Set*Channel() here.
- */
-
- PaUtil_SetInputFrameCount( &stream->bufferProcessor, 0 /* default to host buffer size */ );
- PaUtil_SetInterleavedInputChannels( &stream->bufferProcessor,
- 0, /* first channel of inputBuffer is channel 0 */
- inputBuffer,
- 0 ); /* 0 - use inputChannelCount passed to init buffer processor */
-
- PaUtil_SetOutputFrameCount( &stream->bufferProcessor, 0 /* default to host buffer size */ );
- PaUtil_SetInterleavedOutputChannels( &stream->bufferProcessor,
- 0, /* first channel of outputBuffer is channel 0 */
- outputBuffer,
- 0 ); /* 0 - use outputChannelCount passed to init buffer processor */
-
- /* you must pass a valid value of callback result to PaUtil_EndBufferProcessing()
- in general you would pass paContinue for normal operation, and
- paComplete to drain the buffer processor's internal output buffer.
- You can check whether the buffer processor's output buffer is empty
- using PaUtil_IsBufferProcessorOuputEmpty( bufferProcessor )
- */
- callbackResult = paContinue;
- framesProcessed = PaUtil_EndBufferProcessing( &stream->bufferProcessor, &callbackResult );
-
-
- /*
- If you need to byte swap or shift outputBuffer to convert it to
- host format, do it here.
- */
-
- PaUtil_EndCpuLoadMeasurement( &stream->cpuLoadMeasurer, framesProcessed );
-
-
- if( callbackResult == paContinue )
- {
- /* nothing special to do */
- }
- else if( callbackResult == paAbort )
- {
- /* IMPLEMENT ME - finish playback immediately */
-
- /* once finished, call the finished callback */
- if( stream->streamRepresentation.streamFinishedCallback != 0 )
- stream->streamRepresentation.streamFinishedCallback( stream->streamRepresentation.userData );
- }
- else
- {
- /* User callback has asked us to stop with paComplete or other non-zero value */
-
- /* IMPLEMENT ME - finish playback once currently queued audio has completed */
-
- /* once finished, call the finished callback */
- if( stream->streamRepresentation.streamFinishedCallback != 0 )
- stream->streamRepresentation.streamFinishedCallback( stream->streamRepresentation.userData );
- }
-}
-
-
-/*
- When CloseStream() is called, the multi-api layer ensures that
- the stream has already been stopped or aborted.
-*/
-static PaError CloseStream( PaStream* s )
-{
- PaError result = paNoError;
- PaSkeletonStream *stream = (PaSkeletonStream*)s;
-
- /*
- IMPLEMENT ME:
- - additional stream closing + cleanup
- */
-
- PaUtil_TerminateBufferProcessor( &stream->bufferProcessor );
- PaUtil_TerminateStreamRepresentation( &stream->streamRepresentation );
- PaUtil_FreeMemory( stream );
-
- return result;
-}
-
-
-static PaError StartStream( PaStream *s )
-{
- PaError result = paNoError;
- PaSkeletonStream *stream = (PaSkeletonStream*)s;
-
- PaUtil_ResetBufferProcessor( &stream->bufferProcessor );
-
- /* IMPLEMENT ME, see portaudio.h for required behavior */
-
- /* suppress unused function warning. the code in ExampleHostProcessingLoop or
- something similar should be implemented to feed samples to and from the
- host after StartStream() is called.
- */
- (void) ExampleHostProcessingLoop;
-
- return result;
-}
-
-
-static PaError StopStream( PaStream *s )
-{
- PaError result = paNoError;
- PaSkeletonStream *stream = (PaSkeletonStream*)s;
-
- /* suppress unused variable warnings */
- (void) stream;
-
- /* IMPLEMENT ME, see portaudio.h for required behavior */
-
- return result;
-}
-
-
-static PaError AbortStream( PaStream *s )
-{
- PaError result = paNoError;
- PaSkeletonStream *stream = (PaSkeletonStream*)s;
-
- /* suppress unused variable warnings */
- (void) stream;
-
- /* IMPLEMENT ME, see portaudio.h for required behavior */
-
- return result;
-}
-
-
-static PaError IsStreamStopped( PaStream *s )
-{
- PaSkeletonStream *stream = (PaSkeletonStream*)s;
-
- /* suppress unused variable warnings */
- (void) stream;
-
- /* IMPLEMENT ME, see portaudio.h for required behavior */
-
- return 0;
-}
-
-
-static PaError IsStreamActive( PaStream *s )
-{
- PaSkeletonStream *stream = (PaSkeletonStream*)s;
-
- /* suppress unused variable warnings */
- (void) stream;
-
- /* IMPLEMENT ME, see portaudio.h for required behavior */
-
- return 0;
-}
-
-
-static PaTime GetStreamTime( PaStream *s )
-{
- PaSkeletonStream *stream = (PaSkeletonStream*)s;
-
- /* suppress unused variable warnings */
- (void) stream;
-
- /* IMPLEMENT ME, see portaudio.h for required behavior*/
-
- return 0;
-}
-
-
-static double GetStreamCpuLoad( PaStream* s )
-{
- PaSkeletonStream *stream = (PaSkeletonStream*)s;
-
- return PaUtil_GetCpuLoad( &stream->cpuLoadMeasurer );
-}
-
-
-/*
- As separate stream interfaces are used for blocking and callback
- streams, the following functions can be guaranteed to only be called
- for blocking streams.
-*/
-
-static PaError ReadStream( PaStream* s,
- void *buffer,
- unsigned long frames )
-{
- PaSkeletonStream *stream = (PaSkeletonStream*)s;
-
- /* suppress unused variable warnings */
- (void) buffer;
- (void) frames;
- (void) stream;
-
- /* IMPLEMENT ME, see portaudio.h for required behavior*/
-
- return paNoError;
-}
-
-
-static PaError WriteStream( PaStream* s,
- const void *buffer,
- unsigned long frames )
-{
- PaSkeletonStream *stream = (PaSkeletonStream*)s;
-
- /* suppress unused variable warnings */
- (void) buffer;
- (void) frames;
- (void) stream;
-
- /* IMPLEMENT ME, see portaudio.h for required behavior*/
-
- return paNoError;
-}
-
-
-static signed long GetStreamReadAvailable( PaStream* s )
-{
- PaSkeletonStream *stream = (PaSkeletonStream*)s;
-
- /* suppress unused variable warnings */
- (void) stream;
-
- /* IMPLEMENT ME, see portaudio.h for required behavior*/
-
- return 0;
-}
-
-
-static signed long GetStreamWriteAvailable( PaStream* s )
-{
- PaSkeletonStream *stream = (PaSkeletonStream*)s;
-
- /* suppress unused variable warnings */
- (void) stream;
-
- /* IMPLEMENT ME, see portaudio.h for required behavior*/
-
- return 0;
-}
-
-
-
-
+/*
+ * $Id: pa_skeleton.c,v 1.1.2.39 2003/11/26 14:56:09 rossbencina Exp $
+ * Portable Audio I/O Library skeleton implementation
+ * demonstrates how to use the common functions to implement support
+ * for a host API
+ *
+ * Based on the Open Source API proposed by Ross Bencina
+ * Copyright (c) 1999-2002 Ross Bencina, Phil Burk
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * Any person wishing to distribute modifications to the Software is
+ * requested to send the modifications to the original developer so that
+ * they can be incorporated into the canonical version.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+ * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+/*
+ * PJMEDIA - Multimedia over IP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/** @file
+ @brief Skeleton implementation of support for a host API.
+
+ @note This file is provided as a starting point for implementing support for
+ a new host API. IMPLEMENT ME comments are used to indicate functionality
+ which much be customised for each implementation.
+*/
+
+
+#include <string.h> /* strlen() */
+
+#include "pa_util.h"
+#include "pa_allocation.h"
+#include "pa_hostapi.h"
+#include "pa_stream.h"
+#include "pa_cpuload.h"
+#include "pa_process.h"
+
+
+/* prototypes for functions declared in this file */
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif /* __cplusplus */
+
+PaError PaSkeleton_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index );
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+
+static void Terminate( struct PaUtilHostApiRepresentation *hostApi );
+static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
+ const PaStreamParameters *inputParameters,
+ const PaStreamParameters *outputParameters,
+ double sampleRate );
+static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
+ PaStream** s,
+ const PaStreamParameters *inputParameters,
+ const PaStreamParameters *outputParameters,
+ double sampleRate,
+ unsigned long framesPerBuffer,
+ PaStreamFlags streamFlags,
+ PaStreamCallback *streamCallback,
+ void *userData );
+static PaError CloseStream( PaStream* stream );
+static PaError StartStream( PaStream *stream );
+static PaError StopStream( PaStream *stream );
+static PaError AbortStream( PaStream *stream );
+static PaError IsStreamStopped( PaStream *s );
+static PaError IsStreamActive( PaStream *stream );
+static PaTime GetStreamTime( PaStream *stream );
+static double GetStreamCpuLoad( PaStream* stream );
+static PaError ReadStream( PaStream* stream, void *buffer, unsigned long frames );
+static PaError WriteStream( PaStream* stream, const void *buffer, unsigned long frames );
+static signed long GetStreamReadAvailable( PaStream* stream );
+static signed long GetStreamWriteAvailable( PaStream* stream );
+
+
+/* IMPLEMENT ME: a macro like the following one should be used for reporting
+ host errors */
+#define PA_SKELETON_SET_LAST_HOST_ERROR( errorCode, errorText ) \
+ PaUtil_SetLastHostErrorInfo( paInDevelopment, errorCode, errorText )
+
+/* PaSkeletonHostApiRepresentation - host api datastructure specific to this implementation */
+
+typedef struct
+{
+ PaUtilHostApiRepresentation inheritedHostApiRep;
+ PaUtilStreamInterface callbackStreamInterface;
+ PaUtilStreamInterface blockingStreamInterface;
+
+ PaUtilAllocationGroup *allocations;
+
+ /* implementation specific data goes here */
+}
+PaSkeletonHostApiRepresentation; /* IMPLEMENT ME: rename this */
+
+
+PaError PaSkeleton_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex hostApiIndex )
+{
+ PaError result = paNoError;
+ int i, deviceCount;
+ PaSkeletonHostApiRepresentation *skeletonHostApi;
+ PaDeviceInfo *deviceInfoArray;
+
+ skeletonHostApi = (PaSkeletonHostApiRepresentation*)PaUtil_AllocateMemory( sizeof(PaSkeletonHostApiRepresentation) );
+ if( !skeletonHostApi )
+ {
+ result = paInsufficientMemory;
+ goto error;
+ }
+
+ skeletonHostApi->allocations = PaUtil_CreateAllocationGroup();
+ if( !skeletonHostApi->allocations )
+ {
+ result = paInsufficientMemory;
+ goto error;
+ }
+
+ *hostApi = &skeletonHostApi->inheritedHostApiRep;
+ (*hostApi)->info.structVersion = 1;
+ (*hostApi)->info.type = paInDevelopment; /* IMPLEMENT ME: change to correct type id */
+ (*hostApi)->info.name = "skeleton implementation"; /* IMPLEMENT ME: change to correct name */
+
+ (*hostApi)->info.defaultInputDevice = paNoDevice; /* IMPLEMENT ME */
+ (*hostApi)->info.defaultOutputDevice = paNoDevice; /* IMPLEMENT ME */
+
+ (*hostApi)->info.deviceCount = 0;
+
+ deviceCount = 0; /* IMPLEMENT ME */
+
+ if( deviceCount > 0 )
+ {
+ (*hostApi)->deviceInfos = (PaDeviceInfo**)PaUtil_GroupAllocateMemory(
+ skeletonHostApi->allocations, sizeof(PaDeviceInfo*) * deviceCount );
+ if( !(*hostApi)->deviceInfos )
+ {
+ result = paInsufficientMemory;
+ goto error;
+ }
+
+ /* allocate all device info structs in a contiguous block */
+ deviceInfoArray = (PaDeviceInfo*)PaUtil_GroupAllocateMemory(
+ skeletonHostApi->allocations, sizeof(PaDeviceInfo) * deviceCount );
+ if( !deviceInfoArray )
+ {
+ result = paInsufficientMemory;
+ goto error;
+ }
+
+ for( i=0; i < deviceCount; ++i )
+ {
+ PaDeviceInfo *deviceInfo = &deviceInfoArray[i];
+ deviceInfo->structVersion = 2;
+ deviceInfo->hostApi = hostApiIndex;
+ deviceInfo->name = 0; /* IMPLEMENT ME: allocate block and copy name eg:
+ deviceName = (char*)PaUtil_GroupAllocateMemory( skeletonHostApi->allocations, strlen(srcName) + 1 );
+ if( !deviceName )
+ {
+ result = paInsufficientMemory;
+ goto error;
+ }
+ strcpy( deviceName, srcName );
+ deviceInfo->name = deviceName;
+ */
+
+ deviceInfo->maxInputChannels = 0; /* IMPLEMENT ME */
+ deviceInfo->maxOutputChannels = 0; /* IMPLEMENT ME */
+
+ deviceInfo->defaultLowInputLatency = 0.; /* IMPLEMENT ME */
+ deviceInfo->defaultLowOutputLatency = 0.; /* IMPLEMENT ME */
+ deviceInfo->defaultHighInputLatency = 0.; /* IMPLEMENT ME */
+ deviceInfo->defaultHighOutputLatency = 0.; /* IMPLEMENT ME */
+
+ deviceInfo->defaultSampleRate = 0.; /* IMPLEMENT ME */
+
+ (*hostApi)->deviceInfos[i] = deviceInfo;
+ ++(*hostApi)->info.deviceCount;
+ }
+ }
+
+ (*hostApi)->Terminate = Terminate;
+ (*hostApi)->OpenStream = OpenStream;
+ (*hostApi)->IsFormatSupported = IsFormatSupported;
+
+ PaUtil_InitializeStreamInterface( &skeletonHostApi->callbackStreamInterface, CloseStream, StartStream,
+ StopStream, AbortStream, IsStreamStopped, IsStreamActive,
+ GetStreamTime, GetStreamCpuLoad,
+ PaUtil_DummyRead, PaUtil_DummyWrite,
+ PaUtil_DummyGetReadAvailable, PaUtil_DummyGetWriteAvailable );
+
+ PaUtil_InitializeStreamInterface( &skeletonHostApi->blockingStreamInterface, CloseStream, StartStream,
+ StopStream, AbortStream, IsStreamStopped, IsStreamActive,
+ GetStreamTime, PaUtil_DummyGetCpuLoad,
+ ReadStream, WriteStream, GetStreamReadAvailable, GetStreamWriteAvailable );
+
+ return result;
+
+error:
+ if( skeletonHostApi )
+ {
+ if( skeletonHostApi->allocations )
+ {
+ PaUtil_FreeAllAllocations( skeletonHostApi->allocations );
+ PaUtil_DestroyAllocationGroup( skeletonHostApi->allocations );
+ }
+
+ PaUtil_FreeMemory( skeletonHostApi );
+ }
+ return result;
+}
+
+
+static void Terminate( struct PaUtilHostApiRepresentation *hostApi )
+{
+ PaSkeletonHostApiRepresentation *skeletonHostApi = (PaSkeletonHostApiRepresentation*)hostApi;
+
+ /*
+ IMPLEMENT ME:
+ - clean up any resources not handled by the allocation group
+ */
+
+ if( skeletonHostApi->allocations )
+ {
+ PaUtil_FreeAllAllocations( skeletonHostApi->allocations );
+ PaUtil_DestroyAllocationGroup( skeletonHostApi->allocations );
+ }
+
+ PaUtil_FreeMemory( skeletonHostApi );
+}
+
+
+static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
+ const PaStreamParameters *inputParameters,
+ const PaStreamParameters *outputParameters,
+ double sampleRate )
+{
+ int inputChannelCount, outputChannelCount;
+ PaSampleFormat inputSampleFormat, outputSampleFormat;
+
+ if( inputParameters )
+ {
+ inputChannelCount = inputParameters->channelCount;
+ inputSampleFormat = inputParameters->sampleFormat;
+
+ /* all standard sample formats are supported by the buffer adapter,
+ this implementation doesn't support any custom sample formats */
+ if( inputSampleFormat & paCustomFormat )
+ return paSampleFormatNotSupported;
+
+ /* unless alternate device specification is supported, reject the use of
+ paUseHostApiSpecificDeviceSpecification */
+
+ if( inputParameters->device == paUseHostApiSpecificDeviceSpecification )
+ return paInvalidDevice;
+
+ /* check that input device can support inputChannelCount */
+ if( inputChannelCount > hostApi->deviceInfos[ inputParameters->device ]->maxInputChannels )
+ return paInvalidChannelCount;
+
+ /* validate inputStreamInfo */
+ if( inputParameters->hostApiSpecificStreamInfo )
+ return paIncompatibleHostApiSpecificStreamInfo; /* this implementation doesn't use custom stream info */
+ }
+ else
+ {
+ inputChannelCount = 0;
+ }
+
+ if( outputParameters )
+ {
+ outputChannelCount = outputParameters->channelCount;
+ outputSampleFormat = outputParameters->sampleFormat;
+
+ /* all standard sample formats are supported by the buffer adapter,
+ this implementation doesn't support any custom sample formats */
+ if( outputSampleFormat & paCustomFormat )
+ return paSampleFormatNotSupported;
+
+ /* unless alternate device specification is supported, reject the use of
+ paUseHostApiSpecificDeviceSpecification */
+
+ if( outputParameters->device == paUseHostApiSpecificDeviceSpecification )
+ return paInvalidDevice;
+
+ /* check that output device can support outputChannelCount */
+ if( outputChannelCount > hostApi->deviceInfos[ outputParameters->device ]->maxOutputChannels )
+ return paInvalidChannelCount;
+
+ /* validate outputStreamInfo */
+ if( outputParameters->hostApiSpecificStreamInfo )
+ return paIncompatibleHostApiSpecificStreamInfo; /* this implementation doesn't use custom stream info */
+ }
+ else
+ {
+ outputChannelCount = 0;
+ }
+
+ /*
+ IMPLEMENT ME:
+
+ - if a full duplex stream is requested, check that the combination
+ of input and output parameters is supported if necessary
+
+ - check that the device supports sampleRate
+
+ Because the buffer adapter handles conversion between all standard
+ sample formats, the following checks are only required if paCustomFormat
+ is implemented, or under some other unusual conditions.
+
+ - check that input device can support inputSampleFormat, or that
+ we have the capability to convert from inputSampleFormat to
+ a native format
+
+ - check that output device can support outputSampleFormat, or that
+ we have the capability to convert from outputSampleFormat to
+ a native format
+ */
+
+
+ /* suppress unused variable warnings */
+ (void) sampleRate;
+
+ return paFormatIsSupported;
+}
+
+/* PaSkeletonStream - a stream data structure specifically for this implementation */
+
+typedef struct PaSkeletonStream
+{ /* IMPLEMENT ME: rename this */
+ PaUtilStreamRepresentation streamRepresentation;
+ PaUtilCpuLoadMeasurer cpuLoadMeasurer;
+ PaUtilBufferProcessor bufferProcessor;
+
+ /* IMPLEMENT ME:
+ - implementation specific data goes here
+ */
+ unsigned long framesPerHostCallback; /* just an example */
+}
+PaSkeletonStream;
+
+/* see pa_hostapi.h for a list of validity guarantees made about OpenStream parameters */
+
+static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
+ PaStream** s,
+ const PaStreamParameters *inputParameters,
+ const PaStreamParameters *outputParameters,
+ double sampleRate,
+ unsigned long framesPerBuffer,
+ PaStreamFlags streamFlags,
+ PaStreamCallback *streamCallback,
+ void *userData )
+{
+ PaError result = paNoError;
+ PaSkeletonHostApiRepresentation *skeletonHostApi = (PaSkeletonHostApiRepresentation*)hostApi;
+ PaSkeletonStream *stream = 0;
+ unsigned long framesPerHostBuffer = framesPerBuffer; /* these may not be equivalent for all implementations */
+ int inputChannelCount, outputChannelCount;
+ PaSampleFormat inputSampleFormat, outputSampleFormat;
+ PaSampleFormat hostInputSampleFormat, hostOutputSampleFormat;
+
+
+ if( inputParameters )
+ {
+ inputChannelCount = inputParameters->channelCount;
+ inputSampleFormat = inputParameters->sampleFormat;
+
+ /* unless alternate device specification is supported, reject the use of
+ paUseHostApiSpecificDeviceSpecification */
+
+ if( inputParameters->device == paUseHostApiSpecificDeviceSpecification )
+ return paInvalidDevice;
+
+ /* check that input device can support inputChannelCount */
+ if( inputChannelCount > hostApi->deviceInfos[ inputParameters->device ]->maxInputChannels )
+ return paInvalidChannelCount;
+
+ /* validate inputStreamInfo */
+ if( inputParameters->hostApiSpecificStreamInfo )
+ return paIncompatibleHostApiSpecificStreamInfo; /* this implementation doesn't use custom stream info */
+
+ /* IMPLEMENT ME - establish which host formats are available */
+ hostInputSampleFormat =
+ PaUtil_SelectClosestAvailableFormat( paInt16 /* native formats */, inputSampleFormat );
+ }
+ else
+ {
+ inputChannelCount = 0;
+ inputSampleFormat = hostInputSampleFormat = paInt16; /* Surpress 'uninitialised var' warnings. */
+ }
+
+ if( outputParameters )
+ {
+ outputChannelCount = outputParameters->channelCount;
+ outputSampleFormat = outputParameters->sampleFormat;
+
+ /* unless alternate device specification is supported, reject the use of
+ paUseHostApiSpecificDeviceSpecification */
+
+ if( outputParameters->device == paUseHostApiSpecificDeviceSpecification )
+ return paInvalidDevice;
+
+ /* check that output device can support inputChannelCount */
+ if( outputChannelCount > hostApi->deviceInfos[ outputParameters->device ]->maxOutputChannels )
+ return paInvalidChannelCount;
+
+ /* validate outputStreamInfo */
+ if( outputParameters->hostApiSpecificStreamInfo )
+ return paIncompatibleHostApiSpecificStreamInfo; /* this implementation doesn't use custom stream info */
+
+ /* IMPLEMENT ME - establish which host formats are available */
+ hostOutputSampleFormat =
+ PaUtil_SelectClosestAvailableFormat( paInt16 /* native formats */, outputSampleFormat );
+ }
+ else
+ {
+ outputChannelCount = 0;
+ outputSampleFormat = hostOutputSampleFormat = paInt16; /* Surpress 'uninitialized var' warnings. */
+ }
+
+ /*
+ IMPLEMENT ME:
+
+ ( the following two checks are taken care of by PaUtil_InitializeBufferProcessor() FIXME - checks needed? )
+
+ - check that input device can support inputSampleFormat, or that
+ we have the capability to convert from outputSampleFormat to
+ a native format
+
+ - check that output device can support outputSampleFormat, or that
+ we have the capability to convert from outputSampleFormat to
+ a native format
+
+ - if a full duplex stream is requested, check that the combination
+ of input and output parameters is supported
+
+ - check that the device supports sampleRate
+
+ - alter sampleRate to a close allowable rate if possible / necessary
+
+ - validate suggestedInputLatency and suggestedOutputLatency parameters,
+ use default values where necessary
+ */
+
+
+
+
+ /* validate platform specific flags */
+ if( (streamFlags & paPlatformSpecificFlags) != 0 )
+ return paInvalidFlag; /* unexpected platform specific flag */
+
+
+ stream = (PaSkeletonStream*)PaUtil_AllocateMemory( sizeof(PaSkeletonStream) );
+ if( !stream )
+ {
+ result = paInsufficientMemory;
+ goto error;
+ }
+
+ if( streamCallback )
+ {
+ PaUtil_InitializeStreamRepresentation( &stream->streamRepresentation,
+ &skeletonHostApi->callbackStreamInterface, streamCallback, userData );
+ }
+ else
+ {
+ PaUtil_InitializeStreamRepresentation( &stream->streamRepresentation,
+ &skeletonHostApi->blockingStreamInterface, streamCallback, userData );
+ }
+
+ PaUtil_InitializeCpuLoadMeasurer( &stream->cpuLoadMeasurer, sampleRate );
+
+
+ /* we assume a fixed host buffer size in this example, but the buffer processor
+ can also support bounded and unknown host buffer sizes by passing
+ paUtilBoundedHostBufferSize or paUtilUnknownHostBufferSize instead of
+ paUtilFixedHostBufferSize below. */
+
+ result = PaUtil_InitializeBufferProcessor( &stream->bufferProcessor,
+ inputChannelCount, inputSampleFormat, hostInputSampleFormat,
+ outputChannelCount, outputSampleFormat, hostOutputSampleFormat,
+ sampleRate, streamFlags, framesPerBuffer,
+ framesPerHostBuffer, paUtilFixedHostBufferSize,
+ streamCallback, userData );
+ if( result != paNoError )
+ goto error;
+
+
+ /*
+ IMPLEMENT ME: initialise the following fields with estimated or actual
+ values.
+ */
+ stream->streamRepresentation.streamInfo.inputLatency =
+ PaUtil_GetBufferProcessorInputLatency(&stream->bufferProcessor);
+ stream->streamRepresentation.streamInfo.outputLatency =
+ PaUtil_GetBufferProcessorOutputLatency(&stream->bufferProcessor);
+ stream->streamRepresentation.streamInfo.sampleRate = sampleRate;
+
+
+ /*
+ IMPLEMENT ME:
+ - additional stream setup + opening
+ */
+
+ stream->framesPerHostCallback = framesPerHostBuffer;
+
+ *s = (PaStream*)stream;
+
+ return result;
+
+error:
+ if( stream )
+ PaUtil_FreeMemory( stream );
+
+ return result;
+}
+
+/*
+ ExampleHostProcessingLoop() illustrates the kind of processing which may
+ occur in a host implementation.
+
+*/
+static void ExampleHostProcessingLoop( void *inputBuffer, void *outputBuffer, void *userData )
+{
+ PaSkeletonStream *stream = (PaSkeletonStream*)userData;
+ PaStreamCallbackTimeInfo timeInfo = {0,0,0}; /* IMPLEMENT ME */
+ int callbackResult;
+ unsigned long framesProcessed;
+
+ PaUtil_BeginCpuLoadMeasurement( &stream->cpuLoadMeasurer );
+
+ /*
+ IMPLEMENT ME:
+ - generate timing information
+ - handle buffer slips
+ */
+
+ /*
+ If you need to byte swap or shift inputBuffer to convert it into a
+ portaudio format, do it here.
+ */
+
+
+
+ PaUtil_BeginBufferProcessing( &stream->bufferProcessor, &timeInfo, 0 /* IMPLEMENT ME: pass underflow/overflow flags when necessary */ );
+
+ /*
+ depending on whether the host buffers are interleaved, non-interleaved
+ or a mixture, you will want to call PaUtil_SetInterleaved*Channels(),
+ PaUtil_SetNonInterleaved*Channel() or PaUtil_Set*Channel() here.
+ */
+
+ PaUtil_SetInputFrameCount( &stream->bufferProcessor, 0 /* default to host buffer size */ );
+ PaUtil_SetInterleavedInputChannels( &stream->bufferProcessor,
+ 0, /* first channel of inputBuffer is channel 0 */
+ inputBuffer,
+ 0 ); /* 0 - use inputChannelCount passed to init buffer processor */
+
+ PaUtil_SetOutputFrameCount( &stream->bufferProcessor, 0 /* default to host buffer size */ );
+ PaUtil_SetInterleavedOutputChannels( &stream->bufferProcessor,
+ 0, /* first channel of outputBuffer is channel 0 */
+ outputBuffer,
+ 0 ); /* 0 - use outputChannelCount passed to init buffer processor */
+
+ /* you must pass a valid value of callback result to PaUtil_EndBufferProcessing()
+ in general you would pass paContinue for normal operation, and
+ paComplete to drain the buffer processor's internal output buffer.
+ You can check whether the buffer processor's output buffer is empty
+ using PaUtil_IsBufferProcessorOuputEmpty( bufferProcessor )
+ */
+ callbackResult = paContinue;
+ framesProcessed = PaUtil_EndBufferProcessing( &stream->bufferProcessor, &callbackResult );
+
+
+ /*
+ If you need to byte swap or shift outputBuffer to convert it to
+ host format, do it here.
+ */
+
+ PaUtil_EndCpuLoadMeasurement( &stream->cpuLoadMeasurer, framesProcessed );
+
+
+ if( callbackResult == paContinue )
+ {
+ /* nothing special to do */
+ }
+ else if( callbackResult == paAbort )
+ {
+ /* IMPLEMENT ME - finish playback immediately */
+
+ /* once finished, call the finished callback */
+ if( stream->streamRepresentation.streamFinishedCallback != 0 )
+ stream->streamRepresentation.streamFinishedCallback( stream->streamRepresentation.userData );
+ }
+ else
+ {
+ /* User callback has asked us to stop with paComplete or other non-zero value */
+
+ /* IMPLEMENT ME - finish playback once currently queued audio has completed */
+
+ /* once finished, call the finished callback */
+ if( stream->streamRepresentation.streamFinishedCallback != 0 )
+ stream->streamRepresentation.streamFinishedCallback( stream->streamRepresentation.userData );
+ }
+}
+
+
+/*
+ When CloseStream() is called, the multi-api layer ensures that
+ the stream has already been stopped or aborted.
+*/
+static PaError CloseStream( PaStream* s )
+{
+ PaError result = paNoError;
+ PaSkeletonStream *stream = (PaSkeletonStream*)s;
+
+ /*
+ IMPLEMENT ME:
+ - additional stream closing + cleanup
+ */
+
+ PaUtil_TerminateBufferProcessor( &stream->bufferProcessor );
+ PaUtil_TerminateStreamRepresentation( &stream->streamRepresentation );
+ PaUtil_FreeMemory( stream );
+
+ return result;
+}
+
+
+static PaError StartStream( PaStream *s )
+{
+ PaError result = paNoError;
+ PaSkeletonStream *stream = (PaSkeletonStream*)s;
+
+ PaUtil_ResetBufferProcessor( &stream->bufferProcessor );
+
+ /* IMPLEMENT ME, see portaudio.h for required behavior */
+
+ /* suppress unused function warning. the code in ExampleHostProcessingLoop or
+ something similar should be implemented to feed samples to and from the
+ host after StartStream() is called.
+ */
+ (void) ExampleHostProcessingLoop;
+
+ return result;
+}
+
+
+static PaError StopStream( PaStream *s )
+{
+ PaError result = paNoError;
+ PaSkeletonStream *stream = (PaSkeletonStream*)s;
+
+ /* suppress unused variable warnings */
+ (void) stream;
+
+ /* IMPLEMENT ME, see portaudio.h for required behavior */
+
+ return result;
+}
+
+
+static PaError AbortStream( PaStream *s )
+{
+ PaError result = paNoError;
+ PaSkeletonStream *stream = (PaSkeletonStream*)s;
+
+ /* suppress unused variable warnings */
+ (void) stream;
+
+ /* IMPLEMENT ME, see portaudio.h for required behavior */
+
+ return result;
+}
+
+
+static PaError IsStreamStopped( PaStream *s )
+{
+ PaSkeletonStream *stream = (PaSkeletonStream*)s;
+
+ /* suppress unused variable warnings */
+ (void) stream;
+
+ /* IMPLEMENT ME, see portaudio.h for required behavior */
+
+ return 0;
+}
+
+
+static PaError IsStreamActive( PaStream *s )
+{
+ PaSkeletonStream *stream = (PaSkeletonStream*)s;
+
+ /* suppress unused variable warnings */
+ (void) stream;
+
+ /* IMPLEMENT ME, see portaudio.h for required behavior */
+
+ return 0;
+}
+
+
+static PaTime GetStreamTime( PaStream *s )
+{
+ PaSkeletonStream *stream = (PaSkeletonStream*)s;
+
+ /* suppress unused variable warnings */
+ (void) stream;
+
+ /* IMPLEMENT ME, see portaudio.h for required behavior*/
+
+ return 0;
+}
+
+
+static double GetStreamCpuLoad( PaStream* s )
+{
+ PaSkeletonStream *stream = (PaSkeletonStream*)s;
+
+ return PaUtil_GetCpuLoad( &stream->cpuLoadMeasurer );
+}
+
+
+/*
+ As separate stream interfaces are used for blocking and callback
+ streams, the following functions can be guaranteed to only be called
+ for blocking streams.
+*/
+
+static PaError ReadStream( PaStream* s,
+ void *buffer,
+ unsigned long frames )
+{
+ PaSkeletonStream *stream = (PaSkeletonStream*)s;
+
+ /* suppress unused variable warnings */
+ (void) buffer;
+ (void) frames;
+ (void) stream;
+
+ /* IMPLEMENT ME, see portaudio.h for required behavior*/
+
+ return paNoError;
+}
+
+
+static PaError WriteStream( PaStream* s,
+ const void *buffer,
+ unsigned long frames )
+{
+ PaSkeletonStream *stream = (PaSkeletonStream*)s;
+
+ /* suppress unused variable warnings */
+ (void) buffer;
+ (void) frames;
+ (void) stream;
+
+ /* IMPLEMENT ME, see portaudio.h for required behavior*/
+
+ return paNoError;
+}
+
+
+static signed long GetStreamReadAvailable( PaStream* s )
+{
+ PaSkeletonStream *stream = (PaSkeletonStream*)s;
+
+ /* suppress unused variable warnings */
+ (void) stream;
+
+ /* IMPLEMENT ME, see portaudio.h for required behavior*/
+
+ return 0;
+}
+
+
+static signed long GetStreamWriteAvailable( PaStream* s )
+{
+ PaSkeletonStream *stream = (PaSkeletonStream*)s;
+
+ /* suppress unused variable warnings */
+ (void) stream;
+
+ /* IMPLEMENT ME, see portaudio.h for required behavior*/
+
+ return 0;
+}
+
+
+
+
diff --git a/pjmedia/src/pjmedia/portaudio/pa_stream.c b/pjmedia/src/pjmedia/portaudio/pa_stream.c
index 044dde29..0df0d06b 100644
--- a/pjmedia/src/pjmedia/portaudio/pa_stream.c
+++ b/pjmedia/src/pjmedia/portaudio/pa_stream.c
@@ -1,141 +1,162 @@
-/*
- * $Id: pa_stream.c,v 1.1.2.12 2003/09/20 21:31:00 rossbencina Exp $
- * Portable Audio I/O Library
- *
- *
- * Based on the Open Source API proposed by Ross Bencina
- * Copyright (c) 2002 Ross Bencina
- *
- * Permission is hereby granted, free of charge, to any person obtaining
- * a copy of this software and associated documentation files
- * (the "Software"), to deal in the Software without restriction,
- * including without limitation the rights to use, copy, modify, merge,
- * publish, distribute, sublicense, and/or sell copies of the Software,
- * and to permit persons to whom the Software is furnished to do so,
- * subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be
- * included in all copies or substantial portions of the Software.
- *
- * Any person wishing to distribute modifications to the Software is
- * requested to send the modifications to the original developer so that
- * they can be incorporated into the canonical version.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
- * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
- * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
- * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
- * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
- */
-
-/** @file
- @brief Interface used by pa_front to virtualize functions which operate on
- streams.
-*/
-
-
-#include "pa_stream.h"
-
-
-void PaUtil_InitializeStreamInterface( PaUtilStreamInterface *streamInterface,
- PaError (*Close)( PaStream* ),
- PaError (*Start)( PaStream* ),
- PaError (*Stop)( PaStream* ),
- PaError (*Abort)( PaStream* ),
- PaError (*IsStopped)( PaStream* ),
- PaError (*IsActive)( PaStream* ),
- PaTime (*GetTime)( PaStream* ),
- double (*GetCpuLoad)( PaStream* ),
- PaError (*Read)( PaStream*, void *, unsigned long ),
- PaError (*Write)( PaStream*, const void *, unsigned long ),
- signed long (*GetReadAvailable)( PaStream* ),
- signed long (*GetWriteAvailable)( PaStream* ) )
-{
- streamInterface->Close = Close;
- streamInterface->Start = Start;
- streamInterface->Stop = Stop;
- streamInterface->Abort = Abort;
- streamInterface->IsStopped = IsStopped;
- streamInterface->IsActive = IsActive;
- streamInterface->GetTime = GetTime;
- streamInterface->GetCpuLoad = GetCpuLoad;
- streamInterface->Read = Read;
- streamInterface->Write = Write;
- streamInterface->GetReadAvailable = GetReadAvailable;
- streamInterface->GetWriteAvailable = GetWriteAvailable;
-}
-
-
-void PaUtil_InitializeStreamRepresentation( PaUtilStreamRepresentation *streamRepresentation,
- PaUtilStreamInterface *streamInterface,
- PaStreamCallback *streamCallback,
- void *userData )
-{
- streamRepresentation->magic = PA_STREAM_MAGIC;
- streamRepresentation->nextOpenStream = 0;
- streamRepresentation->streamInterface = streamInterface;
- streamRepresentation->streamCallback = streamCallback;
- streamRepresentation->streamFinishedCallback = 0;
-
- streamRepresentation->userData = userData;
-
- streamRepresentation->streamInfo.inputLatency = 0.;
- streamRepresentation->streamInfo.outputLatency = 0.;
- streamRepresentation->streamInfo.sampleRate = 0.;
-}
-
-
-void PaUtil_TerminateStreamRepresentation( PaUtilStreamRepresentation *streamRepresentation )
-{
- streamRepresentation->magic = 0;
-}
-
-
-PaError PaUtil_DummyRead( PaStream* stream,
- void *buffer,
- unsigned long frames )
-{
- (void)stream; /* unused parameter */
- (void)buffer; /* unused parameter */
- (void)frames; /* unused parameter */
-
- return paCanNotReadFromACallbackStream;
-}
-
-
-PaError PaUtil_DummyWrite( PaStream* stream,
- const void *buffer,
- unsigned long frames )
-{
- (void)stream; /* unused parameter */
- (void)buffer; /* unused parameter */
- (void)frames; /* unused parameter */
-
- return paCanNotWriteToACallbackStream;
-}
-
-
-signed long PaUtil_DummyGetReadAvailable( PaStream* stream )
-{
- (void)stream; /* unused parameter */
-
- return paCanNotReadFromACallbackStream;
-}
-
-
-signed long PaUtil_DummyGetWriteAvailable( PaStream* stream )
-{
- (void)stream; /* unused parameter */
-
- return paCanNotWriteToACallbackStream;
-}
-
-
-double PaUtil_DummyGetCpuLoad( PaStream* stream )
-{
- (void)stream; /* unused parameter */
-
- return 0.0;
-}
+/*
+ * $Id: pa_stream.c,v 1.1.2.12 2003/09/20 21:31:00 rossbencina Exp $
+ * Portable Audio I/O Library
+ *
+ *
+ * Based on the Open Source API proposed by Ross Bencina
+ * Copyright (c) 2002 Ross Bencina
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * Any person wishing to distribute modifications to the Software is
+ * requested to send the modifications to the original developer so that
+ * they can be incorporated into the canonical version.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+ * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+/*
+ * PJMEDIA - Multimedia over IP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/** @file
+ @brief Interface used by pa_front to virtualize functions which operate on
+ streams.
+*/
+
+
+#include "pa_stream.h"
+
+
+void PaUtil_InitializeStreamInterface( PaUtilStreamInterface *streamInterface,
+ PaError (*Close)( PaStream* ),
+ PaError (*Start)( PaStream* ),
+ PaError (*Stop)( PaStream* ),
+ PaError (*Abort)( PaStream* ),
+ PaError (*IsStopped)( PaStream* ),
+ PaError (*IsActive)( PaStream* ),
+ PaTime (*GetTime)( PaStream* ),
+ double (*GetCpuLoad)( PaStream* ),
+ PaError (*Read)( PaStream*, void *, unsigned long ),
+ PaError (*Write)( PaStream*, const void *, unsigned long ),
+ signed long (*GetReadAvailable)( PaStream* ),
+ signed long (*GetWriteAvailable)( PaStream* ) )
+{
+ streamInterface->Close = Close;
+ streamInterface->Start = Start;
+ streamInterface->Stop = Stop;
+ streamInterface->Abort = Abort;
+ streamInterface->IsStopped = IsStopped;
+ streamInterface->IsActive = IsActive;
+ streamInterface->GetTime = GetTime;
+ streamInterface->GetCpuLoad = GetCpuLoad;
+ streamInterface->Read = Read;
+ streamInterface->Write = Write;
+ streamInterface->GetReadAvailable = GetReadAvailable;
+ streamInterface->GetWriteAvailable = GetWriteAvailable;
+}
+
+
+void PaUtil_InitializeStreamRepresentation( PaUtilStreamRepresentation *streamRepresentation,
+ PaUtilStreamInterface *streamInterface,
+ PaStreamCallback *streamCallback,
+ void *userData )
+{
+ streamRepresentation->magic = PA_STREAM_MAGIC;
+ streamRepresentation->nextOpenStream = 0;
+ streamRepresentation->streamInterface = streamInterface;
+ streamRepresentation->streamCallback = streamCallback;
+ streamRepresentation->streamFinishedCallback = 0;
+
+ streamRepresentation->userData = userData;
+
+ streamRepresentation->streamInfo.inputLatency = 0.;
+ streamRepresentation->streamInfo.outputLatency = 0.;
+ streamRepresentation->streamInfo.sampleRate = 0.;
+}
+
+
+void PaUtil_TerminateStreamRepresentation( PaUtilStreamRepresentation *streamRepresentation )
+{
+ streamRepresentation->magic = 0;
+}
+
+
+PaError PaUtil_DummyRead( PaStream* stream,
+ void *buffer,
+ unsigned long frames )
+{
+ (void)stream; /* unused parameter */
+ (void)buffer; /* unused parameter */
+ (void)frames; /* unused parameter */
+
+ return paCanNotReadFromACallbackStream;
+}
+
+
+PaError PaUtil_DummyWrite( PaStream* stream,
+ const void *buffer,
+ unsigned long frames )
+{
+ (void)stream; /* unused parameter */
+ (void)buffer; /* unused parameter */
+ (void)frames; /* unused parameter */
+
+ return paCanNotWriteToACallbackStream;
+}
+
+
+signed long PaUtil_DummyGetReadAvailable( PaStream* stream )
+{
+ (void)stream; /* unused parameter */
+
+ return paCanNotReadFromACallbackStream;
+}
+
+
+signed long PaUtil_DummyGetWriteAvailable( PaStream* stream )
+{
+ (void)stream; /* unused parameter */
+
+ return paCanNotWriteToACallbackStream;
+}
+
+
+double PaUtil_DummyGetCpuLoad( PaStream* stream )
+{
+ (void)stream; /* unused parameter */
+
+ return 0.0;
+}
diff --git a/pjmedia/src/pjmedia/portaudio/pa_stream.h b/pjmedia/src/pjmedia/portaudio/pa_stream.h
index 8b86943b..9e934296 100644
--- a/pjmedia/src/pjmedia/portaudio/pa_stream.h
+++ b/pjmedia/src/pjmedia/portaudio/pa_stream.h
@@ -1,196 +1,217 @@
-#ifndef PA_STREAM_H
-#define PA_STREAM_H
-/*
- * $Id: pa_stream.h,v 1.1.2.13 2003/11/01 06:37:28 rossbencina Exp $
- * Portable Audio I/O Library
- * stream interface
- *
- * Based on the Open Source API proposed by Ross Bencina
- * Copyright (c) 1999-2002 Ross Bencina, Phil Burk
- *
- * Permission is hereby granted, free of charge, to any person obtaining
- * a copy of this software and associated documentation files
- * (the "Software"), to deal in the Software without restriction,
- * including without limitation the rights to use, copy, modify, merge,
- * publish, distribute, sublicense, and/or sell copies of the Software,
- * and to permit persons to whom the Software is furnished to do so,
- * subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be
- * included in all copies or substantial portions of the Software.
- *
- * Any person wishing to distribute modifications to the Software is
- * requested to send the modifications to the original developer so that
- * they can be incorporated into the canonical version.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
- * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
- * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
- * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
- * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
- */
-
-/** @file
- @brief Interface used by pa_front to virtualize functions which operate on
- streams.
-*/
-
-
-#include "portaudio.h"
-
-#ifdef __cplusplus
-extern "C"
-{
-#endif /* __cplusplus */
-
-
-#define PA_STREAM_MAGIC (0x18273645)
-
-
-/** A structure representing an (abstract) interface to a host API. Contains
- pointers to functions which implement the interface.
-
- All PaStreamInterface functions are guaranteed to be called with a non-null,
- valid stream parameter.
-*/
-typedef struct {
- PaError (*Close)( PaStream* stream );
- PaError (*Start)( PaStream *stream );
- PaError (*Stop)( PaStream *stream );
- PaError (*Abort)( PaStream *stream );
- PaError (*IsStopped)( PaStream *stream );
- PaError (*IsActive)( PaStream *stream );
- PaTime (*GetTime)( PaStream *stream );
- double (*GetCpuLoad)( PaStream* stream );
- PaError (*Read)( PaStream* stream, void *buffer, unsigned long frames );
- PaError (*Write)( PaStream* stream, const void *buffer, unsigned long frames );
- signed long (*GetReadAvailable)( PaStream* stream );
- signed long (*GetWriteAvailable)( PaStream* stream );
-} PaUtilStreamInterface;
-
-
-/** Initialize the fields of a PaUtilStreamInterface structure.
-*/
-void PaUtil_InitializeStreamInterface( PaUtilStreamInterface *streamInterface,
- PaError (*Close)( PaStream* ),
- PaError (*Start)( PaStream* ),
- PaError (*Stop)( PaStream* ),
- PaError (*Abort)( PaStream* ),
- PaError (*IsStopped)( PaStream* ),
- PaError (*IsActive)( PaStream* ),
- PaTime (*GetTime)( PaStream* ),
- double (*GetCpuLoad)( PaStream* ),
- PaError (*Read)( PaStream* stream, void *buffer, unsigned long frames ),
- PaError (*Write)( PaStream* stream, const void *buffer, unsigned long frames ),
- signed long (*GetReadAvailable)( PaStream* stream ),
- signed long (*GetWriteAvailable)( PaStream* stream ) );
-
-
-/** Dummy Read function for use in interfaces to a callback based streams.
- Pass to the Read parameter of PaUtil_InitializeStreamInterface.
- @return An error code indicating that the function has no effect
- because the stream is a callback stream.
-*/
-PaError PaUtil_DummyRead( PaStream* stream,
- void *buffer,
- unsigned long frames );
-
-
-/** Dummy Write function for use in an interfaces to callback based streams.
- Pass to the Write parameter of PaUtil_InitializeStreamInterface.
- @return An error code indicating that the function has no effect
- because the stream is a callback stream.
-*/
-PaError PaUtil_DummyWrite( PaStream* stream,
- const void *buffer,
- unsigned long frames );
-
-
-/** Dummy GetReadAvailable function for use in interfaces to callback based
- streams. Pass to the GetReadAvailable parameter of PaUtil_InitializeStreamInterface.
- @return An error code indicating that the function has no effect
- because the stream is a callback stream.
-*/
-signed long PaUtil_DummyGetReadAvailable( PaStream* stream );
-
-
-/** Dummy GetWriteAvailable function for use in interfaces to callback based
- streams. Pass to the GetWriteAvailable parameter of PaUtil_InitializeStreamInterface.
- @return An error code indicating that the function has no effect
- because the stream is a callback stream.
-*/
-signed long PaUtil_DummyGetWriteAvailable( PaStream* stream );
-
-
-
-/** Dummy GetCpuLoad function for use in an interface to a read/write stream.
- Pass to the GetCpuLoad parameter of PaUtil_InitializeStreamInterface.
- @return Returns 0.
-*/
-double PaUtil_DummyGetCpuLoad( PaStream* stream );
-
-
-/** Non host specific data for a stream. This data is used by pa_front to
- forward to the appropriate functions in the streamInterface structure.
-*/
-typedef struct PaUtilStreamRepresentation {
- unsigned long magic; /**< set to PA_STREAM_MAGIC */
- struct PaUtilStreamRepresentation *nextOpenStream; /**< field used by multi-api code */
- PaUtilStreamInterface *streamInterface;
- PaStreamCallback *streamCallback;
- PaStreamFinishedCallback *streamFinishedCallback;
- void *userData;
- PaStreamInfo streamInfo;
-} PaUtilStreamRepresentation;
-
-
-/** Initialize a PaUtilStreamRepresentation structure.
-
- @see PaUtil_InitializeStreamRepresentation
-*/
-void PaUtil_InitializeStreamRepresentation(
- PaUtilStreamRepresentation *streamRepresentation,
- PaUtilStreamInterface *streamInterface,
- PaStreamCallback *streamCallback,
- void *userData );
-
-
-/** Clean up a PaUtilStreamRepresentation structure previously initialized
- by a call to PaUtil_InitializeStreamRepresentation.
-
- @see PaUtil_InitializeStreamRepresentation
-*/
-void PaUtil_TerminateStreamRepresentation( PaUtilStreamRepresentation *streamRepresentation );
-
-
-/** Check that the stream pointer is valid.
-
- @return Returns paNoError if the stream pointer appears to be OK, otherwise
- returns an error indicating the cause of failure.
-*/
-PaError PaUtil_ValidateStreamPointer( PaStream *stream );
-
-
-/** Cast an opaque stream pointer into a pointer to a PaUtilStreamRepresentation.
-
- @see PaUtilStreamRepresentation
-*/
-#define PA_STREAM_REP( stream )\
- ((PaUtilStreamRepresentation*) (stream) )
-
-
-/** Cast an opaque stream pointer into a pointer to a PaUtilStreamInterface.
-
- @see PaUtilStreamRepresentation, PaUtilStreamInterface
-*/
-#define PA_STREAM_INTERFACE( stream )\
- PA_STREAM_REP( (stream) )->streamInterface
-
-
-
-#ifdef __cplusplus
-}
-#endif /* __cplusplus */
-#endif /* PA_STREAM_H */
+/*
+ * PJMEDIA - Multimedia over IP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef PA_STREAM_H
+#define PA_STREAM_H
+/*
+ * $Id: pa_stream.h,v 1.1.2.13 2003/11/01 06:37:28 rossbencina Exp $
+ * Portable Audio I/O Library
+ * stream interface
+ *
+ * Based on the Open Source API proposed by Ross Bencina
+ * Copyright (c) 1999-2002 Ross Bencina, Phil Burk
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * Any person wishing to distribute modifications to the Software is
+ * requested to send the modifications to the original developer so that
+ * they can be incorporated into the canonical version.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+ * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+/** @file
+ @brief Interface used by pa_front to virtualize functions which operate on
+ streams.
+*/
+
+
+#include "portaudio.h"
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif /* __cplusplus */
+
+
+#define PA_STREAM_MAGIC (0x18273645)
+
+
+/** A structure representing an (abstract) interface to a host API. Contains
+ pointers to functions which implement the interface.
+
+ All PaStreamInterface functions are guaranteed to be called with a non-null,
+ valid stream parameter.
+*/
+typedef struct {
+ PaError (*Close)( PaStream* stream );
+ PaError (*Start)( PaStream *stream );
+ PaError (*Stop)( PaStream *stream );
+ PaError (*Abort)( PaStream *stream );
+ PaError (*IsStopped)( PaStream *stream );
+ PaError (*IsActive)( PaStream *stream );
+ PaTime (*GetTime)( PaStream *stream );
+ double (*GetCpuLoad)( PaStream* stream );
+ PaError (*Read)( PaStream* stream, void *buffer, unsigned long frames );
+ PaError (*Write)( PaStream* stream, const void *buffer, unsigned long frames );
+ signed long (*GetReadAvailable)( PaStream* stream );
+ signed long (*GetWriteAvailable)( PaStream* stream );
+} PaUtilStreamInterface;
+
+
+/** Initialize the fields of a PaUtilStreamInterface structure.
+*/
+void PaUtil_InitializeStreamInterface( PaUtilStreamInterface *streamInterface,
+ PaError (*Close)( PaStream* ),
+ PaError (*Start)( PaStream* ),
+ PaError (*Stop)( PaStream* ),
+ PaError (*Abort)( PaStream* ),
+ PaError (*IsStopped)( PaStream* ),
+ PaError (*IsActive)( PaStream* ),
+ PaTime (*GetTime)( PaStream* ),
+ double (*GetCpuLoad)( PaStream* ),
+ PaError (*Read)( PaStream* stream, void *buffer, unsigned long frames ),
+ PaError (*Write)( PaStream* stream, const void *buffer, unsigned long frames ),
+ signed long (*GetReadAvailable)( PaStream* stream ),
+ signed long (*GetWriteAvailable)( PaStream* stream ) );
+
+
+/** Dummy Read function for use in interfaces to a callback based streams.
+ Pass to the Read parameter of PaUtil_InitializeStreamInterface.
+ @return An error code indicating that the function has no effect
+ because the stream is a callback stream.
+*/
+PaError PaUtil_DummyRead( PaStream* stream,
+ void *buffer,
+ unsigned long frames );
+
+
+/** Dummy Write function for use in an interfaces to callback based streams.
+ Pass to the Write parameter of PaUtil_InitializeStreamInterface.
+ @return An error code indicating that the function has no effect
+ because the stream is a callback stream.
+*/
+PaError PaUtil_DummyWrite( PaStream* stream,
+ const void *buffer,
+ unsigned long frames );
+
+
+/** Dummy GetReadAvailable function for use in interfaces to callback based
+ streams. Pass to the GetReadAvailable parameter of PaUtil_InitializeStreamInterface.
+ @return An error code indicating that the function has no effect
+ because the stream is a callback stream.
+*/
+signed long PaUtil_DummyGetReadAvailable( PaStream* stream );
+
+
+/** Dummy GetWriteAvailable function for use in interfaces to callback based
+ streams. Pass to the GetWriteAvailable parameter of PaUtil_InitializeStreamInterface.
+ @return An error code indicating that the function has no effect
+ because the stream is a callback stream.
+*/
+signed long PaUtil_DummyGetWriteAvailable( PaStream* stream );
+
+
+
+/** Dummy GetCpuLoad function for use in an interface to a read/write stream.
+ Pass to the GetCpuLoad parameter of PaUtil_InitializeStreamInterface.
+ @return Returns 0.
+*/
+double PaUtil_DummyGetCpuLoad( PaStream* stream );
+
+
+/** Non host specific data for a stream. This data is used by pa_front to
+ forward to the appropriate functions in the streamInterface structure.
+*/
+typedef struct PaUtilStreamRepresentation {
+ unsigned long magic; /**< set to PA_STREAM_MAGIC */
+ struct PaUtilStreamRepresentation *nextOpenStream; /**< field used by multi-api code */
+ PaUtilStreamInterface *streamInterface;
+ PaStreamCallback *streamCallback;
+ PaStreamFinishedCallback *streamFinishedCallback;
+ void *userData;
+ PaStreamInfo streamInfo;
+} PaUtilStreamRepresentation;
+
+
+/** Initialize a PaUtilStreamRepresentation structure.
+
+ @see PaUtil_InitializeStreamRepresentation
+*/
+void PaUtil_InitializeStreamRepresentation(
+ PaUtilStreamRepresentation *streamRepresentation,
+ PaUtilStreamInterface *streamInterface,
+ PaStreamCallback *streamCallback,
+ void *userData );
+
+
+/** Clean up a PaUtilStreamRepresentation structure previously initialized
+ by a call to PaUtil_InitializeStreamRepresentation.
+
+ @see PaUtil_InitializeStreamRepresentation
+*/
+void PaUtil_TerminateStreamRepresentation( PaUtilStreamRepresentation *streamRepresentation );
+
+
+/** Check that the stream pointer is valid.
+
+ @return Returns paNoError if the stream pointer appears to be OK, otherwise
+ returns an error indicating the cause of failure.
+*/
+PaError PaUtil_ValidateStreamPointer( PaStream *stream );
+
+
+/** Cast an opaque stream pointer into a pointer to a PaUtilStreamRepresentation.
+
+ @see PaUtilStreamRepresentation
+*/
+#define PA_STREAM_REP( stream )\
+ ((PaUtilStreamRepresentation*) (stream) )
+
+
+/** Cast an opaque stream pointer into a pointer to a PaUtilStreamInterface.
+
+ @see PaUtilStreamRepresentation, PaUtilStreamInterface
+*/
+#define PA_STREAM_INTERFACE( stream )\
+ PA_STREAM_REP( (stream) )->streamInterface
+
+
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+#endif /* PA_STREAM_H */
diff --git a/pjmedia/src/pjmedia/portaudio/pa_trace.c b/pjmedia/src/pjmedia/portaudio/pa_trace.c
index 83514a05..e980e24d 100644
--- a/pjmedia/src/pjmedia/portaudio/pa_trace.c
+++ b/pjmedia/src/pjmedia/portaudio/pa_trace.c
@@ -1,88 +1,109 @@
-/*
- * $Id: pa_trace.c,v 1.1.1.1.2.3 2003/09/20 21:09:15 rossbencina Exp $
- * Portable Audio I/O Library Trace Facility
- * Store trace information in real-time for later printing.
- *
- * Based on the Open Source API proposed by Ross Bencina
- * Copyright (c) 1999-2000 Phil Burk
- *
- * Permission is hereby granted, free of charge, to any person obtaining
- * a copy of this software and associated documentation files
- * (the "Software"), to deal in the Software without restriction,
- * including without limitation the rights to use, copy, modify, merge,
- * publish, distribute, sublicense, and/or sell copies of the Software,
- * and to permit persons to whom the Software is furnished to do so,
- * subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be
- * included in all copies or substantial portions of the Software.
- *
- * Any person wishing to distribute modifications to the Software is
- * requested to send the modifications to the original developer so that
- * they can be incorporated into the canonical version.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
- * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
- * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
- * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
- * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
- */
-
-/** @file
- @brief Event trace mechanism for debugging.
-*/
-
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include "pa_trace.h"
-
-#if PA_TRACE_REALTIME_EVENTS
-
-static char *traceTextArray[MAX_TRACE_RECORDS];
-static int traceIntArray[MAX_TRACE_RECORDS];
-static int traceIndex = 0;
-static int traceBlock = 0;
-
-/*********************************************************************/
-void PaUtil_ResetTraceMessages()
-{
- traceIndex = 0;
-}
-
-/*********************************************************************/
-void PaUtil_DumpTraceMessages()
-{
- int i;
- int messageCount = (traceIndex < PA_MAX_TRACE_RECORDS) ? traceIndex : PA_MAX_TRACE_RECORDS;
-
- printf("DumpTraceMessages: traceIndex = %d\n", traceIndex );
- for( i=0; i<messageCount; i++ )
- {
- printf("%3d: %s = 0x%08X\n",
- i, traceTextArray[i], traceIntArray[i] );
- }
- ResetTraceMessages();
- fflush(stdout);
-}
-
-/*********************************************************************/
-void PaUtil_AddTraceMessage( const char *msg, int data )
-{
- if( (traceIndex == PA_MAX_TRACE_RECORDS) && (traceBlock == 0) )
- {
- traceBlock = 1;
- /* PaUtil_DumpTraceMessages(); */
- }
- else if( traceIndex < PA_MAX_TRACE_RECORDS )
- {
- traceTextArray[traceIndex] = msg;
- traceIntArray[traceIndex] = data;
- traceIndex++;
- }
-}
-
-#endif /* TRACE_REALTIME_EVENTS */
+/*
+ * $Id: pa_trace.c,v 1.1.1.1.2.3 2003/09/20 21:09:15 rossbencina Exp $
+ * Portable Audio I/O Library Trace Facility
+ * Store trace information in real-time for later printing.
+ *
+ * Based on the Open Source API proposed by Ross Bencina
+ * Copyright (c) 1999-2000 Phil Burk
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * Any person wishing to distribute modifications to the Software is
+ * requested to send the modifications to the original developer so that
+ * they can be incorporated into the canonical version.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+ * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+/*
+ * PJMEDIA - Multimedia over IP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/** @file
+ @brief Event trace mechanism for debugging.
+*/
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "pa_trace.h"
+
+#if PA_TRACE_REALTIME_EVENTS
+
+static char *traceTextArray[MAX_TRACE_RECORDS];
+static int traceIntArray[MAX_TRACE_RECORDS];
+static int traceIndex = 0;
+static int traceBlock = 0;
+
+/*********************************************************************/
+void PaUtil_ResetTraceMessages()
+{
+ traceIndex = 0;
+}
+
+/*********************************************************************/
+void PaUtil_DumpTraceMessages()
+{
+ int i;
+ int messageCount = (traceIndex < PA_MAX_TRACE_RECORDS) ? traceIndex : PA_MAX_TRACE_RECORDS;
+
+ printf("DumpTraceMessages: traceIndex = %d\n", traceIndex );
+ for( i=0; i<messageCount; i++ )
+ {
+ printf("%3d: %s = 0x%08X\n",
+ i, traceTextArray[i], traceIntArray[i] );
+ }
+ ResetTraceMessages();
+ fflush(stdout);
+}
+
+/*********************************************************************/
+void PaUtil_AddTraceMessage( const char *msg, int data )
+{
+ if( (traceIndex == PA_MAX_TRACE_RECORDS) && (traceBlock == 0) )
+ {
+ traceBlock = 1;
+ /* PaUtil_DumpTraceMessages(); */
+ }
+ else if( traceIndex < PA_MAX_TRACE_RECORDS )
+ {
+ traceTextArray[traceIndex] = msg;
+ traceIntArray[traceIndex] = data;
+ traceIndex++;
+ }
+}
+
+#endif /* TRACE_REALTIME_EVENTS */
diff --git a/pjmedia/src/pjmedia/portaudio/pa_trace.h b/pjmedia/src/pjmedia/portaudio/pa_trace.h
index f72a78b3..0ef6794f 100644
--- a/pjmedia/src/pjmedia/portaudio/pa_trace.h
+++ b/pjmedia/src/pjmedia/portaudio/pa_trace.h
@@ -1,70 +1,91 @@
-#ifndef PA_TRACE_H
-#define PA_TRACE_H
-/*
- * $Id: pa_trace.h,v 1.1.1.1.2.3 2003/09/20 21:09:15 rossbencina Exp $
- * Portable Audio I/O Library Trace Facility
- * Store trace information in real-time for later printing.
- *
- * Based on the Open Source API proposed by Ross Bencina
- * Copyright (c) 1999-2000 Phil Burk
- *
- * Permission is hereby granted, free of charge, to any person obtaining
- * a copy of this software and associated documentation files
- * (the "Software"), to deal in the Software without restriction,
- * including without limitation the rights to use, copy, modify, merge,
- * publish, distribute, sublicense, and/or sell copies of the Software,
- * and to permit persons to whom the Software is furnished to do so,
- * subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be
- * included in all copies or substantial portions of the Software.
- *
- * Any person wishing to distribute modifications to the Software is
- * requested to send the modifications to the original developer so that
- * they can be incorporated into the canonical version.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
- * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
- * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
- * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
- * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
- */
-
-/** @file
- @brief Event trace mechanism for debugging.
-
- Allows data to be written to the buffer at interrupt time and dumped later.
-*/
-
-
-#define PA_TRACE_REALTIME_EVENTS (0) /* Keep log of various real-time events. */
-#define PA_MAX_TRACE_RECORDS (2048)
-
-#ifdef __cplusplus
-extern "C"
-{
-#endif /* __cplusplus */
-
-
-#if PA_TRACE_REALTIME_EVENTS
-
-void PaUtil_ResetTraceMessages();
-void PaUtil_AddTraceMessage( const char *msg, int data );
-void PaUtil_DumpTraceMessages();
-
-#else
-
-#define PaUtil_ResetTraceMessages() /* noop */
-#define PaUtil_AddTraceMessage(msg,data) /* noop */
-#define PaUtil_DumpTraceMessages() /* noop */
-
-#endif
-
-
-#ifdef __cplusplus
-}
-#endif /* __cplusplus */
-
-#endif /* PA_TRACE_H */
+/*
+ * PJMEDIA - Multimedia over IP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef PA_TRACE_H
+#define PA_TRACE_H
+/*
+ * $Id: pa_trace.h,v 1.1.1.1.2.3 2003/09/20 21:09:15 rossbencina Exp $
+ * Portable Audio I/O Library Trace Facility
+ * Store trace information in real-time for later printing.
+ *
+ * Based on the Open Source API proposed by Ross Bencina
+ * Copyright (c) 1999-2000 Phil Burk
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * Any person wishing to distribute modifications to the Software is
+ * requested to send the modifications to the original developer so that
+ * they can be incorporated into the canonical version.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+ * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+/** @file
+ @brief Event trace mechanism for debugging.
+
+ Allows data to be written to the buffer at interrupt time and dumped later.
+*/
+
+
+#define PA_TRACE_REALTIME_EVENTS (0) /* Keep log of various real-time events. */
+#define PA_MAX_TRACE_RECORDS (2048)
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif /* __cplusplus */
+
+
+#if PA_TRACE_REALTIME_EVENTS
+
+void PaUtil_ResetTraceMessages();
+void PaUtil_AddTraceMessage( const char *msg, int data );
+void PaUtil_DumpTraceMessages();
+
+#else
+
+#define PaUtil_ResetTraceMessages() /* noop */
+#define PaUtil_AddTraceMessage(msg,data) /* noop */
+#define PaUtil_DumpTraceMessages() /* noop */
+
+#endif
+
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* PA_TRACE_H */
diff --git a/pjmedia/src/pjmedia/portaudio/pa_types.h b/pjmedia/src/pjmedia/portaudio/pa_types.h
index 343dc8cf..e8d924fe 100644
--- a/pjmedia/src/pjmedia/portaudio/pa_types.h
+++ b/pjmedia/src/pjmedia/portaudio/pa_types.h
@@ -1,65 +1,86 @@
-#ifndef PA_TYPES_H
-#define PA_TYPES_H
-
-/*
- SIZEOF_SHORT, SIZEOF_INT and SIZEOF_LONG are set by the configure script
- when it is used. Otherwise we default to the common 32 bit values, if your
- platform doesn't use configure, and doesn't use the default values below
- you will need to explicitly define these symbols in your make file.
-
- A PA_VALIDATE_SIZES macro is provided to assert that the values set in this
- file are correct.
-*/
-
-#ifndef SIZEOF_SHORT
-#define SIZEOF_SHORT 2
-#endif
-
-#ifndef SIZEOF_INT
-#define SIZEOF_INT 4
-#endif
-
-#ifndef SIZEOF_LONG
-#define SIZEOF_LONG 4
-#endif
-
-
-#if SIZEOF_SHORT == 2
-typedef signed short PaInt16;
-typedef unsigned short PaUint16;
-#elif SIZEOF_INT == 2
-typedef signed int PaInt16;
-typedef unsigned int PaUint16;
-#else
-#error pa_types.h was unable to determine which type to use for 16bit integers on the target platform
-#endif
-
-#if SIZEOF_SHORT == 4
-typedef signed short PaInt32;
-typedef unsigned short PaUint32;
-#elif SIZEOF_INT == 4
-typedef signed int PaInt32;
-typedef unsigned int PaUint32;
-#elif SIZEOF_LONG == 4
-typedef signed long PaInt32;
-typedef unsigned long PaUint32;
-#else
-#error pa_types.h was unable to determine which type to use for 32bit integers on the target platform
-#endif
-
-
-/* PA_VALIDATE_TYPE_SIZES compares the size of the integer types at runtime to
- ensure that PortAudio was configured correctly, and raises an assertion if
- they don't match the expected values. <assert.h> must be included in the
- context in which this macro is used.
-*/
-#define PA_VALIDATE_TYPE_SIZES \
- { \
- assert( "PortAudio: type sizes are not correct in pa_types.h" && sizeof( PaUint16 ) == 2 ); \
- assert( "PortAudio: type sizes are not correct in pa_types.h" && sizeof( PaInt16 ) == 2 ); \
- assert( "PortAudio: type sizes are not correct in pa_types.h" && sizeof( PaUint32 ) == 4 ); \
- assert( "PortAudio: type sizes are not correct in pa_types.h" && sizeof( PaInt32 ) == 4 ); \
- }
-
-
-#endif /* PA_TYPES_H */
+/*
+ * PJMEDIA - Multimedia over IP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef PA_TYPES_H
+#define PA_TYPES_H
+
+/*
+ SIZEOF_SHORT, SIZEOF_INT and SIZEOF_LONG are set by the configure script
+ when it is used. Otherwise we default to the common 32 bit values, if your
+ platform doesn't use configure, and doesn't use the default values below
+ you will need to explicitly define these symbols in your make file.
+
+ A PA_VALIDATE_SIZES macro is provided to assert that the values set in this
+ file are correct.
+*/
+
+#ifndef SIZEOF_SHORT
+#define SIZEOF_SHORT 2
+#endif
+
+#ifndef SIZEOF_INT
+#define SIZEOF_INT 4
+#endif
+
+#ifndef SIZEOF_LONG
+#define SIZEOF_LONG 4
+#endif
+
+
+#if SIZEOF_SHORT == 2
+typedef signed short PaInt16;
+typedef unsigned short PaUint16;
+#elif SIZEOF_INT == 2
+typedef signed int PaInt16;
+typedef unsigned int PaUint16;
+#else
+#error pa_types.h was unable to determine which type to use for 16bit integers on the target platform
+#endif
+
+#if SIZEOF_SHORT == 4
+typedef signed short PaInt32;
+typedef unsigned short PaUint32;
+#elif SIZEOF_INT == 4
+typedef signed int PaInt32;
+typedef unsigned int PaUint32;
+#elif SIZEOF_LONG == 4
+typedef signed long PaInt32;
+typedef unsigned long PaUint32;
+#else
+#error pa_types.h was unable to determine which type to use for 32bit integers on the target platform
+#endif
+
+
+/* PA_VALIDATE_TYPE_SIZES compares the size of the integer types at runtime to
+ ensure that PortAudio was configured correctly, and raises an assertion if
+ they don't match the expected values. <assert.h> must be included in the
+ context in which this macro is used.
+*/
+#define PA_VALIDATE_TYPE_SIZES \
+ { \
+ assert( "PortAudio: type sizes are not correct in pa_types.h" && sizeof( PaUint16 ) == 2 ); \
+ assert( "PortAudio: type sizes are not correct in pa_types.h" && sizeof( PaInt16 ) == 2 ); \
+ assert( "PortAudio: type sizes are not correct in pa_types.h" && sizeof( PaUint32 ) == 4 ); \
+ assert( "PortAudio: type sizes are not correct in pa_types.h" && sizeof( PaInt32 ) == 4 ); \
+ }
+
+
+#endif /* PA_TYPES_H */
diff --git a/pjmedia/src/pjmedia/portaudio/pa_unix_hostapis.c b/pjmedia/src/pjmedia/portaudio/pa_unix_hostapis.c
index 9bddc2e0..5817af18 100644
--- a/pjmedia/src/pjmedia/portaudio/pa_unix_hostapis.c
+++ b/pjmedia/src/pjmedia/portaudio/pa_unix_hostapis.c
@@ -1,64 +1,85 @@
-/*
- * $Id: pa_unix_hostapis.c,v 1.1.2.5 2003/10/02 12:35:46 pieter Exp $
- * Portable Audio I/O Library UNIX initialization table
- *
- * Based on the Open Source API proposed by Ross Bencina
- * Copyright (c) 1999-2002 Ross Bencina, Phil Burk
- *
- * Permission is hereby granted, free of charge, to any person obtaining
- * a copy of this software and associated documentation files
- * (the "Software"), to deal in the Software without restriction,
- * including without limitation the rights to use, copy, modify, merge,
- * publish, distribute, sublicense, and/or sell copies of the Software,
- * and to permit persons to whom the Software is furnished to do so,
- * subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be
- * included in all copies or substantial portions of the Software.
- *
- * Any person wishing to distribute modifications to the Software is
- * requested to send the modifications to the original developer so that
- * they can be incorporated into the canonical version.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
- * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
- * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
- * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
- * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
- */
-
-
-#include "pa_hostapi.h"
-
-PaError PaJack_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index );
-PaError PaAlsa_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index );
-PaError PaOSS_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index );
-/* Added for IRIX, Pieter, oct 2, 2003: */
-PaError PaSGI_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index );
-
-
-PaUtilHostApiInitializer *paHostApiInitializers[] =
- {
-#ifdef PA_USE_OSS
- PaOSS_Initialize,
-#endif
-
-#ifdef PA_USE_ALSA
- PaAlsa_Initialize,
-#endif
-
-#ifdef PA_USE_JACK
- PaJack_Initialize,
-#endif
- /* Added for IRIX, Pieter, oct 2, 2003: */
-#ifdef PA_USE_SGI
- PaSGI_Initialize,
-#endif
- 0 /* NULL terminated array */
- };
-
-int paDefaultHostApiIndex = 0;
-
-
+/*
+ * $Id: pa_unix_hostapis.c,v 1.1.2.5 2003/10/02 12:35:46 pieter Exp $
+ * Portable Audio I/O Library UNIX initialization table
+ *
+ * Based on the Open Source API proposed by Ross Bencina
+ * Copyright (c) 1999-2002 Ross Bencina, Phil Burk
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * Any person wishing to distribute modifications to the Software is
+ * requested to send the modifications to the original developer so that
+ * they can be incorporated into the canonical version.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+ * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+/*
+ * PJMEDIA - Multimedia over IP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+
+#include "pa_hostapi.h"
+
+PaError PaJack_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index );
+PaError PaAlsa_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index );
+PaError PaOSS_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index );
+/* Added for IRIX, Pieter, oct 2, 2003: */
+PaError PaSGI_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index );
+
+
+PaUtilHostApiInitializer *paHostApiInitializers[] =
+ {
+#ifdef PA_USE_OSS
+ PaOSS_Initialize,
+#endif
+
+#ifdef PA_USE_ALSA
+ PaAlsa_Initialize,
+#endif
+
+#ifdef PA_USE_JACK
+ PaJack_Initialize,
+#endif
+ /* Added for IRIX, Pieter, oct 2, 2003: */
+#ifdef PA_USE_SGI
+ PaSGI_Initialize,
+#endif
+ 0 /* NULL terminated array */
+ };
+
+int paDefaultHostApiIndex = 0;
+
+
diff --git a/pjmedia/src/pjmedia/portaudio/pa_unix_oss.c b/pjmedia/src/pjmedia/portaudio/pa_unix_oss.c
index 2490e68f..6597657f 100644
--- a/pjmedia/src/pjmedia/portaudio/pa_unix_oss.c
+++ b/pjmedia/src/pjmedia/portaudio/pa_unix_oss.c
@@ -1,1918 +1,1939 @@
-/*
- * $Id: pa_unix_oss.c,v 1.6.2.22 2005/03/08 21:26:53 aknudsen Exp $
- * PortAudio Portable Real-Time Audio Library
- * Latest Version at: http://www.portaudio.com
- * OSS implementation by:
- * Douglas Repetto
- * Phil Burk
- * Dominic Mazzoni
- * Arve Knudsen
- *
- * Based on the Open Source API proposed by Ross Bencina
- * Copyright (c) 1999-2002 Ross Bencina, Phil Burk
- *
- * Permission is hereby granted, free of charge, to any person obtaining
- * a copy of this software and associated documentation files
- * (the "Software"), to deal in the Software without restriction,
- * including without limitation the rights to use, copy, modify, merge,
- * publish, distribute, sublicense, and/or sell copies of the Software,
- * and to permit persons to whom the Software is furnished to do so,
- * subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be
- * included in all copies or substantial portions of the Software.
- *
- * Any person wishing to distribute modifications to the Software is
- * requested to send the modifications to the original developer so that
- * they can be incorporated into the canonical version.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
- * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
- * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
- * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
- * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
- */
-
-#include <stdio.h>
-#include <string.h>
-#include <math.h>
-#include <fcntl.h>
-#include <sys/ioctl.h>
-#include <unistd.h>
-#include <pthread.h>
-#include <alloca.h>
-#include <malloc.h>
-#include <assert.h>
-#include <errno.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <sys/poll.h>
-#include <limits.h>
-#include <semaphore.h>
-
-#ifdef __linux__
-# include <linux/soundcard.h>
-# define DEVICE_NAME_BASE "/dev/dsp"
-#else
-# include <machine/soundcard.h> /* JH20010905 */
-# define DEVICE_NAME_BASE "/dev/audio"
-#endif
-
-#include "portaudio.h"
-#include "pa_util.h"
-#include "pa_allocation.h"
-#include "pa_hostapi.h"
-#include "pa_stream.h"
-#include "pa_cpuload.h"
-#include "pa_process.h"
-#include "pa_unix_util.h"
-
-static int sysErr_;
-static pthread_t mainThread_;
-
-/* Check return value of system call, and map it to PaError */
-#define ENSURE_(expr, code) \
- do { \
- if( UNLIKELY( (sysErr_ = (expr)) < 0 ) ) \
- { \
- /* PaUtil_SetLastHostErrorInfo should only be used in the main thread */ \
- if( (code) == paUnanticipatedHostError && pthread_self() == mainThread_ ) \
- { \
- PaUtil_SetLastHostErrorInfo( paALSA, sysErr_, strerror( errno ) ); \
- } \
- \
- PaUtil_DebugPrint(( "Expression '" #expr "' failed in '" __FILE__ "', line: " STRINGIZE( __LINE__ ) "\n" )); \
- result = (code); \
- goto error; \
- } \
- } while( 0 );
-
-#ifndef AFMT_S16_NE
-#define AFMT_S16_NE Get_AFMT_S16_NE()
-/*********************************************************************
- * Some versions of OSS do not define AFMT_S16_NE. So check CPU.
- * PowerPC is Big Endian. X86 is Little Endian.
- */
-static int Get_AFMT_S16_NE( void )
-{
- long testData = 1;
- char *ptr = (char *) &testData;
- int isLittle = ( *ptr == 1 ); /* Does address point to least significant byte? */
- return isLittle ? AFMT_S16_LE : AFMT_S16_BE;
-}
-#endif
-
-/* PaOSSHostApiRepresentation - host api datastructure specific to this implementation */
-
-typedef struct
-{
- PaUtilHostApiRepresentation inheritedHostApiRep;
- PaUtilStreamInterface callbackStreamInterface;
- PaUtilStreamInterface blockingStreamInterface;
-
- PaUtilAllocationGroup *allocations;
-
- PaHostApiIndex hostApiIndex;
-}
-PaOSSHostApiRepresentation;
-
-/** Per-direction structure for PaOssStream.
- *
- * Aspect StreamChannels: In case the user requests to open the same device for both capture and playback,
- * but with different number of channels we will have to adapt between the number of user and host
- * channels for at least one direction, since the configuration space is the same for both directions
- * of an OSS device.
- */
-typedef struct
-{
- int fd;
- const char *devName;
- int userChannelCount, hostChannelCount;
- int userInterleaved;
- void *buffer;
- PaSampleFormat userFormat, hostFormat;
- double latency;
- unsigned long hostFrames, numBufs;
- void **userBuffers; /* For non-interleaved blocking */
-} PaOssStreamComponent;
-
-/** Implementation specific representation of a PaStream.
- *
- */
-typedef struct PaOssStream
-{
- PaUtilStreamRepresentation streamRepresentation;
- PaUtilCpuLoadMeasurer cpuLoadMeasurer;
- PaUtilBufferProcessor bufferProcessor;
-
- PaUtilThreading threading;
-
- int sharedDevice;
- unsigned long framesPerHostBuffer;
- int triggered; /* Have the devices been triggered yet (first start) */
-
- int isActive;
- int isStopped;
-
- int lastPosPtr;
- double lastStreamBytes;
-
- int framesProcessed;
-
- double sampleRate;
-
- int callbackMode;
- int callbackStop, callbackAbort;
-
- PaOssStreamComponent *capture, *playback;
- unsigned long pollTimeout;
- sem_t semaphore;
-}
-PaOssStream;
-
-typedef enum {
- StreamMode_In,
- StreamMode_Out
-} StreamMode;
-
-/* prototypes for functions declared in this file */
-
-static void Terminate( struct PaUtilHostApiRepresentation *hostApi );
-static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
- const PaStreamParameters *inputParameters,
- const PaStreamParameters *outputParameters,
- double sampleRate );
-static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
- PaStream** s,
- const PaStreamParameters *inputParameters,
- const PaStreamParameters *outputParameters,
- double sampleRate,
- unsigned long framesPerBuffer,
- PaStreamFlags streamFlags,
- PaStreamCallback *streamCallback,
- void *userData );
-static PaError CloseStream( PaStream* stream );
-static PaError StartStream( PaStream *stream );
-static PaError StopStream( PaStream *stream );
-static PaError AbortStream( PaStream *stream );
-static PaError IsStreamStopped( PaStream *s );
-static PaError IsStreamActive( PaStream *stream );
-static PaTime GetStreamTime( PaStream *stream );
-static double GetStreamCpuLoad( PaStream* stream );
-static PaError ReadStream( PaStream* stream, void *buffer, unsigned long frames );
-static PaError WriteStream( PaStream* stream, const void *buffer, unsigned long frames );
-static signed long GetStreamReadAvailable( PaStream* stream );
-static signed long GetStreamWriteAvailable( PaStream* stream );
-static PaError BuildDeviceList( PaOSSHostApiRepresentation *hostApi );
-
-
-/** Initialize the OSS API implementation.
- *
- * This function will initialize host API datastructures and query host devices for information.
- *
- * Aspect DeviceCapabilities: Enumeration of host API devices is initiated from here
- *
- * Aspect FreeResources: If an error is encountered under way we have to free each resource allocated in this function,
- * this happens with the usual "error" label.
- */
-PaError PaOSS_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex hostApiIndex )
-{
- PaError result = paNoError;
- PaOSSHostApiRepresentation *ossHostApi = NULL;
-
- PA_UNLESS( ossHostApi = (PaOSSHostApiRepresentation*)PaUtil_AllocateMemory( sizeof(PaOSSHostApiRepresentation) ),
- paInsufficientMemory );
- PA_UNLESS( ossHostApi->allocations = PaUtil_CreateAllocationGroup(), paInsufficientMemory );
- ossHostApi->hostApiIndex = hostApiIndex;
-
- /* Initialize host API structure */
- *hostApi = &ossHostApi->inheritedHostApiRep;
- (*hostApi)->info.structVersion = 1;
- (*hostApi)->info.type = paOSS;
- (*hostApi)->info.name = "OSS";
- (*hostApi)->Terminate = Terminate;
- (*hostApi)->OpenStream = OpenStream;
- (*hostApi)->IsFormatSupported = IsFormatSupported;
-
- PA_ENSURE( BuildDeviceList( ossHostApi ) );
-
- PaUtil_InitializeStreamInterface( &ossHostApi->callbackStreamInterface, CloseStream, StartStream,
- StopStream, AbortStream, IsStreamStopped, IsStreamActive,
- GetStreamTime, GetStreamCpuLoad,
- PaUtil_DummyRead, PaUtil_DummyWrite,
- PaUtil_DummyGetReadAvailable,
- PaUtil_DummyGetWriteAvailable );
-
- PaUtil_InitializeStreamInterface( &ossHostApi->blockingStreamInterface, CloseStream, StartStream,
- StopStream, AbortStream, IsStreamStopped, IsStreamActive,
- GetStreamTime, PaUtil_DummyGetCpuLoad,
- ReadStream, WriteStream, GetStreamReadAvailable, GetStreamWriteAvailable );
-
- mainThread_ = pthread_self();
-
- return result;
-
-error:
- if( ossHostApi )
- {
- if( ossHostApi->allocations )
- {
- PaUtil_FreeAllAllocations( ossHostApi->allocations );
- PaUtil_DestroyAllocationGroup( ossHostApi->allocations );
- }
-
- PaUtil_FreeMemory( ossHostApi );
- }
- return result;
-}
-
-PaError PaUtil_InitializeDeviceInfo( PaDeviceInfo *deviceInfo, const char *name, PaHostApiIndex hostApiIndex, int maxInputChannels,
- int maxOutputChannels, PaTime defaultLowInputLatency, PaTime defaultLowOutputLatency, PaTime defaultHighInputLatency,
- PaTime defaultHighOutputLatency, double defaultSampleRate, PaUtilAllocationGroup *allocations )
-{
- PaError result = paNoError;
-
- deviceInfo->structVersion = 2;
- if( allocations )
- {
- size_t len = strlen( name ) + 1;
- PA_UNLESS( deviceInfo->name = PaUtil_GroupAllocateMemory( allocations, len ), paInsufficientMemory );
- strncpy( (char *)deviceInfo->name, name, len );
- }
- else
- deviceInfo->name = name;
-
- deviceInfo->hostApi = hostApiIndex;
- deviceInfo->maxInputChannels = maxInputChannels;
- deviceInfo->maxOutputChannels = maxOutputChannels;
- deviceInfo->defaultLowInputLatency = defaultLowInputLatency;
- deviceInfo->defaultLowOutputLatency = defaultLowOutputLatency;
- deviceInfo->defaultHighInputLatency = defaultHighInputLatency;
- deviceInfo->defaultHighOutputLatency = defaultHighOutputLatency;
- deviceInfo->defaultSampleRate = defaultSampleRate;
-
-error:
- return result;
-}
-
-static PaError QueryDirection( const char *deviceName, StreamMode mode, double *defaultSampleRate, int *maxChannelCount,
- double *defaultLowLatency, double *defaultHighLatency )
-{
- PaError result = paNoError;
- int numChannels, maxNumChannels;
- int busy = 0;
- int devHandle = -1;
- int sr;
- *maxChannelCount = 0; /* Default value in case this fails */
-
- if ( (devHandle = open( deviceName, (mode == StreamMode_In ? O_RDONLY : O_WRONLY) | O_NONBLOCK )) < 0 )
- {
- if( errno == EBUSY || errno == EAGAIN )
- {
- PA_DEBUG(( "%s: Device %s busy\n", __FUNCTION__, deviceName ));
- }
- else
- {
- PA_DEBUG(( "%s: Can't access device: %s\n", __FUNCTION__, strerror( errno ) ));
- }
-
- return paDeviceUnavailable;
- }
-
- /* Negotiate for the maximum number of channels for this device. PLB20010927
- * Consider up to 16 as the upper number of channels.
- * Variable maxNumChannels should contain the actual upper limit after the call.
- * Thanks to John Lazzaro and Heiko Purnhagen for suggestions.
- */
- maxNumChannels = 0;
- for( numChannels = 1; numChannels <= 16; numChannels++ )
- {
- int temp = numChannels;
- if( ioctl( devHandle, SNDCTL_DSP_CHANNELS, &temp ) < 0 )
- {
- busy = EAGAIN == errno || EBUSY == errno;
- /* ioctl() failed so bail out if we already have stereo */
- if( maxNumChannels >= 2 )
- break;
- }
- else
- {
- /* ioctl() worked but bail out if it does not support numChannels.
- * We don't want to leave gaps in the numChannels supported.
- */
- if( (numChannels > 2) && (temp != numChannels) )
- break;
- if( temp > maxNumChannels )
- maxNumChannels = temp; /* Save maximum. */
- }
- }
- /* A: We're able to open a device for capture if it's busy playing back and vice versa,
- * but we can't configure anything */
- if( 0 == maxNumChannels && busy )
- {
- result = paDeviceUnavailable;
- goto error;
- }
-
- /* The above negotiation may fail for an old driver so try this older technique. */
- if( maxNumChannels < 1 )
- {
- int stereo = 1;
- if( ioctl( devHandle, SNDCTL_DSP_STEREO, &stereo ) < 0 )
- {
- maxNumChannels = 1;
- }
- else
- {
- maxNumChannels = (stereo) ? 2 : 1;
- }
- PA_DEBUG(( "%s: use SNDCTL_DSP_STEREO, maxNumChannels = %d\n", __FUNCTION__, maxNumChannels ))
- }
-
- /* During channel negotiation, the last ioctl() may have failed. This can
- * also cause sample rate negotiation to fail. Hence the following, to return
- * to a supported number of channels. SG20011005 */
- {
- /* use most reasonable default value */
- int temp = PA_MIN( maxNumChannels, 2 );
- ENSURE_( ioctl( devHandle, SNDCTL_DSP_CHANNELS, &temp ), paUnanticipatedHostError );
- }
-
- /* Get supported sample rate closest to 44100 Hz */
- if( *defaultSampleRate < 0 )
- {
- sr = 44100;
- if( ioctl( devHandle, SNDCTL_DSP_SPEED, &sr ) < 0 )
- {
- result = paUnanticipatedHostError;
- goto error;
- }
-
- *defaultSampleRate = sr;
- }
-
- *maxChannelCount = maxNumChannels;
- /* TODO */
- *defaultLowLatency = 512. / *defaultSampleRate;
- *defaultHighLatency = 2048. / *defaultSampleRate;
-
-error:
- if( devHandle >= 0 )
- close( devHandle );
-
- return result;
-}
-
-/** Query OSS device.
- *
- * This is where PaDeviceInfo objects are constructed and filled in with relevant information.
- *
- * Aspect DeviceCapabilities: The inferred device capabilities are recorded in a PaDeviceInfo object that is constructed
- * in place.
- */
-static PaError QueryDevice( char *deviceName, PaOSSHostApiRepresentation *ossApi, PaDeviceInfo **deviceInfo )
-{
- PaError result = paNoError;
- double sampleRate = -1.;
- int maxInputChannels, maxOutputChannels;
- PaTime defaultLowInputLatency, defaultLowOutputLatency, defaultHighInputLatency, defaultHighOutputLatency;
- PaError tmpRes = paNoError;
- int busy = 0;
- *deviceInfo = NULL;
-
- /* douglas:
- we have to do this querying in a slightly different order. apparently
- some sound cards will give you different info based on their settins.
- e.g. a card might give you stereo at 22kHz but only mono at 44kHz.
- the correct order for OSS is: format, channels, sample rate
- */
-
- /* Aspect StreamChannels: The number of channels supported for a device may depend on the mode it is
- * opened in, it may have more channels available for capture than playback and vice versa. Therefore
- * we will open the device in both read- and write-only mode to determine the supported number.
- */
- if( (tmpRes = QueryDirection( deviceName, StreamMode_In, &sampleRate, &maxInputChannels, &defaultLowInputLatency,
- &defaultHighInputLatency )) != paNoError )
- {
- if( tmpRes != paDeviceUnavailable )
- {
- PA_DEBUG(( "%s: Querying device %s for capture failed!\n", __FUNCTION__, deviceName ));
- /* PA_ENSURE( tmpRes ); */
- }
- ++busy;
- }
- if( (tmpRes = QueryDirection( deviceName, StreamMode_Out, &sampleRate, &maxOutputChannels, &defaultLowOutputLatency,
- &defaultHighOutputLatency )) != paNoError )
- {
- if( tmpRes != paDeviceUnavailable )
- {
- PA_DEBUG(( "%s: Querying device %s for playback failed!\n", __FUNCTION__, deviceName ));
- /* PA_ENSURE( tmpRes ); */
- }
- ++busy;
- }
- assert( 0 <= busy && busy <= 2 );
- if( 2 == busy ) /* Both directions are unavailable to us */
- {
- result = paDeviceUnavailable;
- goto error;
- }
-
- PA_UNLESS( *deviceInfo = PaUtil_GroupAllocateMemory( ossApi->allocations, sizeof (PaDeviceInfo) ), paInsufficientMemory );
- PA_ENSURE( PaUtil_InitializeDeviceInfo( *deviceInfo, deviceName, ossApi->hostApiIndex, maxInputChannels, maxOutputChannels,
- defaultLowInputLatency, defaultLowOutputLatency, defaultHighInputLatency, defaultHighOutputLatency, sampleRate,
- ossApi->allocations ) );
-
-error:
- return result;
-}
-
-/** Query host devices.
- *
- * Loop over host devices and query their capabilitiesu
- *
- * Aspect DeviceCapabilities: This function calls QueryDevice on each device entry and receives a filled in PaDeviceInfo object
- * per device, these are placed in the host api representation's deviceInfos array.
- */
-static PaError BuildDeviceList( PaOSSHostApiRepresentation *ossApi )
-{
- PaError result = paNoError;
- PaUtilHostApiRepresentation *commonApi = &ossApi->inheritedHostApiRep;
- int i;
- int numDevices = 0, maxDeviceInfos = 1;
- PaDeviceInfo **deviceInfos = NULL;
-
- /* These two will be set to the first working input and output device, respectively */
- commonApi->info.defaultInputDevice = paNoDevice;
- commonApi->info.defaultOutputDevice = paNoDevice;
-
- /* Find devices by calling QueryDevice on each
- * potential device names. When we find a valid one,
- * add it to a linked list.
- * A: Can there only be 10 devices? */
-
- for( i = 0; i < 10; i++ )
- {
- char deviceName[32];
- PaDeviceInfo *deviceInfo;
- int testResult;
- struct stat stbuf;
-
- if( i == 0 )
- snprintf(deviceName, sizeof (deviceName), "%s", DEVICE_NAME_BASE);
- else
- snprintf(deviceName, sizeof (deviceName), "%s%d", DEVICE_NAME_BASE, i);
-
- /* PA_DEBUG(("PaOSS BuildDeviceList: trying device %s\n", deviceName )); */
- if( stat( deviceName, &stbuf ) < 0 )
- {
- if( ENOENT != errno )
- PA_DEBUG(( "%s: Error stat'ing %s: %s\n", __FUNCTION__, deviceName, strerror( errno ) ));
- continue;
- }
- if( (testResult = QueryDevice( deviceName, ossApi, &deviceInfo )) != paNoError )
- {
- if( testResult != paDeviceUnavailable )
- PA_ENSURE( testResult );
-
- continue;
- }
-
- ++numDevices;
- if( !deviceInfos || numDevices > maxDeviceInfos )
- {
- maxDeviceInfos *= 2;
- PA_UNLESS( deviceInfos = (PaDeviceInfo **) realloc( deviceInfos, maxDeviceInfos * sizeof (PaDeviceInfo *) ),
- paInsufficientMemory );
- }
- deviceInfos[numDevices - 1] = deviceInfo;
-
- if( commonApi->info.defaultInputDevice == paNoDevice && deviceInfo->maxInputChannels > 0 )
- commonApi->info.defaultInputDevice = i;
- if( commonApi->info.defaultOutputDevice == paNoDevice && deviceInfo->maxOutputChannels > 0 )
- commonApi->info.defaultOutputDevice = i;
- }
-
- /* Make an array of PaDeviceInfo pointers out of the linked list */
-
- PA_DEBUG(("PaOSS %s: Total number of devices found: %d\n", __FUNCTION__, numDevices));
-
- commonApi->deviceInfos = (PaDeviceInfo**)PaUtil_GroupAllocateMemory(
- ossApi->allocations, sizeof(PaDeviceInfo*) * numDevices );
- memcpy( commonApi->deviceInfos, deviceInfos, numDevices * sizeof (PaDeviceInfo *) );
-
- commonApi->info.deviceCount = numDevices;
-
-error:
- free( deviceInfos );
-
- return result;
-}
-
-static void Terminate( struct PaUtilHostApiRepresentation *hostApi )
-{
- PaOSSHostApiRepresentation *ossHostApi = (PaOSSHostApiRepresentation*)hostApi;
-
- if( ossHostApi->allocations )
- {
- PaUtil_FreeAllAllocations( ossHostApi->allocations );
- PaUtil_DestroyAllocationGroup( ossHostApi->allocations );
- }
-
- PaUtil_FreeMemory( ossHostApi );
-}
-
-static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
- const PaStreamParameters *inputParameters,
- const PaStreamParameters *outputParameters,
- double sampleRate )
-{
- PaError result = paNoError;
- PaDeviceIndex device;
- PaDeviceInfo *deviceInfo;
- char *deviceName;
- int inputChannelCount, outputChannelCount;
- int tempDevHandle = -1;
- int flags;
- PaSampleFormat inputSampleFormat, outputSampleFormat;
-
- if( inputParameters )
- {
- inputChannelCount = inputParameters->channelCount;
- inputSampleFormat = inputParameters->sampleFormat;
-
- /* unless alternate device specification is supported, reject the use of
- paUseHostApiSpecificDeviceSpecification */
-
- if( inputParameters->device == paUseHostApiSpecificDeviceSpecification )
- return paInvalidDevice;
-
- /* check that input device can support inputChannelCount */
- if( inputChannelCount > hostApi->deviceInfos[ inputParameters->device ]->maxInputChannels )
- return paInvalidChannelCount;
-
- /* validate inputStreamInfo */
- if( inputParameters->hostApiSpecificStreamInfo )
- return paIncompatibleHostApiSpecificStreamInfo; /* this implementation doesn't use custom stream info */
- }
- else
- {
- inputChannelCount = 0;
- }
-
- if( outputParameters )
- {
- outputChannelCount = outputParameters->channelCount;
- outputSampleFormat = outputParameters->sampleFormat;
-
- /* unless alternate device specification is supported, reject the use of
- paUseHostApiSpecificDeviceSpecification */
-
- if( outputParameters->device == paUseHostApiSpecificDeviceSpecification )
- return paInvalidDevice;
-
- /* check that output device can support inputChannelCount */
- if( outputChannelCount > hostApi->deviceInfos[ outputParameters->device ]->maxOutputChannels )
- return paInvalidChannelCount;
-
- /* validate outputStreamInfo */
- if( outputParameters->hostApiSpecificStreamInfo )
- return paIncompatibleHostApiSpecificStreamInfo; /* this implementation doesn't use custom stream info */
- }
- else
- {
- outputChannelCount = 0;
- }
-
- if (inputChannelCount == 0 && outputChannelCount == 0)
- return paInvalidChannelCount;
-
- /* if full duplex, make sure that they're the same device */
-
- if (inputChannelCount > 0 && outputChannelCount > 0 &&
- inputParameters->device != outputParameters->device)
- return paInvalidDevice;
-
- /* if full duplex, also make sure that they're the same number of channels */
-
- if (inputChannelCount > 0 && outputChannelCount > 0 &&
- inputChannelCount != outputChannelCount)
- return paInvalidChannelCount;
-
- /* open the device so we can do more tests */
-
- if( inputChannelCount > 0 )
- {
- result = PaUtil_DeviceIndexToHostApiDeviceIndex(&device, inputParameters->device, hostApi);
- if (result != paNoError)
- return result;
- }
- else
- {
- result = PaUtil_DeviceIndexToHostApiDeviceIndex(&device, outputParameters->device, hostApi);
- if (result != paNoError)
- return result;
- }
-
- deviceInfo = hostApi->deviceInfos[device];
- deviceName = (char *)deviceInfo->name;
-
- flags = O_NONBLOCK;
- if (inputChannelCount > 0 && outputChannelCount > 0)
- flags |= O_RDWR;
- else if (inputChannelCount > 0)
- flags |= O_RDONLY;
- else
- flags |= O_WRONLY;
-
- ENSURE_( tempDevHandle = open( deviceInfo->name, flags ), paDeviceUnavailable );
-
- /* PaOssStream_Configure will do the rest of the checking for us */
- /* PA_ENSURE( PaOssStream_Configure( tempDevHandle, deviceName, outputChannelCount, &sampleRate ) ); */
-
- /* everything succeeded! */
-
- error:
- if( tempDevHandle >= 0 )
- close( tempDevHandle );
-
- return result;
-}
-
-/** Validate stream parameters.
- *
- * Aspect StreamChannels: We verify that the number of channels is within the allowed range for the device
- */
-static PaError ValidateParameters( const PaStreamParameters *parameters, const PaDeviceInfo *deviceInfo, StreamMode mode )
-{
- int maxChans;
-
- assert( parameters );
-
- if( parameters->device == paUseHostApiSpecificDeviceSpecification )
- {
- return paInvalidDevice;
- }
-
- maxChans = (mode == StreamMode_In ? deviceInfo->maxInputChannels :
- deviceInfo->maxOutputChannels);
- if( parameters->channelCount > maxChans )
- {
- return paInvalidChannelCount;
- }
-
- return paNoError;
-}
-
-static PaError PaOssStreamComponent_Initialize( PaOssStreamComponent *component, const PaStreamParameters *parameters,
- int callbackMode, int fd, const char *deviceName )
-{
- PaError result = paNoError;
- assert( component );
-
- memset( component, 0, sizeof (PaOssStreamComponent) );
-
- component->fd = fd;
- component->devName = deviceName;
- component->userChannelCount = parameters->channelCount;
- component->userFormat = parameters->sampleFormat;
- component->latency = parameters->suggestedLatency;
- component->userInterleaved = !(parameters->sampleFormat & paNonInterleaved);
-
- if( !callbackMode && !component->userInterleaved )
- {
- /* Pre-allocate non-interleaved user provided buffers */
- PA_UNLESS( component->userBuffers = PaUtil_AllocateMemory( sizeof (void *) * component->userChannelCount ),
- paInsufficientMemory );
- }
-
-error:
- return result;
-}
-
-static void PaOssStreamComponent_Terminate( PaOssStreamComponent *component )
-{
- assert( component );
-
- if( component->fd >= 0 )
- close( component->fd );
- if( component->buffer )
- PaUtil_FreeMemory( component->buffer );
-
- if( component->userBuffers )
- PaUtil_FreeMemory( component->userBuffers );
-
- PaUtil_FreeMemory( component );
-}
-
-static PaError ModifyBlocking( int fd, int blocking )
-{
- PaError result = paNoError;
- int fflags;
-
- ENSURE_( fflags = fcntl( fd, F_GETFL ), paUnanticipatedHostError );
-
- if( blocking )
- fflags &= ~O_NONBLOCK;
- else
- fflags |= O_NONBLOCK;
-
- ENSURE_( fcntl( fd, F_SETFL, fflags ), paUnanticipatedHostError );
-
-error:
- return result;
-}
-
-static PaError OpenDevices( const char *idevName, const char *odevName, int *idev, int *odev )
-{
- PaError result = paNoError;
- int flags = O_NONBLOCK, duplex = 0;
- int enableBits = 0;
- *idev = *odev = -1;
-
- if( idevName && odevName )
- {
- duplex = 1;
- flags |= O_RDWR;
- }
- else if( idevName )
- flags |= O_RDONLY;
- else
- flags |= O_WRONLY;
-
- /* open first in nonblocking mode, in case it's busy...
- * A: then unset the non-blocking attribute */
- assert( flags & O_NONBLOCK );
- if( idevName )
- {
- ENSURE_( *idev = open( idevName, flags ), paDeviceUnavailable );
- PA_ENSURE( ModifyBlocking( *idev, 1 ) ); /* Blocking */
-
- /* Initially disable */
- enableBits = ~PCM_ENABLE_INPUT;
- ENSURE_( ioctl( *idev, SNDCTL_DSP_SETTRIGGER, &enableBits ), paUnanticipatedHostError );
- }
- if( odevName )
- {
- if( !idevName )
- {
- ENSURE_( *odev = open( odevName, flags ), paDeviceUnavailable );
- PA_ENSURE( ModifyBlocking( *odev, 1 ) ); /* Blocking */
-
- /* Initially disable */
- enableBits = ~PCM_ENABLE_OUTPUT;
- ENSURE_( ioctl( *odev, SNDCTL_DSP_SETTRIGGER, &enableBits ), paUnanticipatedHostError );
- }
- else
- {
- ENSURE_( *odev = dup( *idev ), paUnanticipatedHostError );
- }
- }
-
-error:
- return result;
-}
-
-static PaError PaOssStream_Initialize( PaOssStream *stream, const PaStreamParameters *inputParameters, const PaStreamParameters *outputParameters,
- PaStreamCallback callback, void *userData, PaStreamFlags streamFlags,
- PaOSSHostApiRepresentation *ossApi )
-{
- PaError result = paNoError;
- int idev, odev;
- PaUtilHostApiRepresentation *hostApi = &ossApi->inheritedHostApiRep;
- const char *idevName = NULL, *odevName = NULL;
-
- assert( stream );
-
- memset( stream, 0, sizeof (PaOssStream) );
- stream->isStopped = 1;
-
- PA_ENSURE( PaUtil_InitializeThreading( &stream->threading ) );
-
- if( inputParameters && outputParameters )
- {
- if( inputParameters->device == outputParameters->device )
- stream->sharedDevice = 1;
- }
-
- if( inputParameters )
- idevName = hostApi->deviceInfos[inputParameters->device]->name;
- if( outputParameters )
- odevName = hostApi->deviceInfos[outputParameters->device]->name;
- PA_ENSURE( OpenDevices( idevName, odevName, &idev, &odev ) );
- if( inputParameters )
- {
- PA_UNLESS( stream->capture = PaUtil_AllocateMemory( sizeof (PaOssStreamComponent) ), paInsufficientMemory );
- PA_ENSURE( PaOssStreamComponent_Initialize( stream->capture, inputParameters, callback != NULL, idev, idevName ) );
- }
- if( outputParameters )
- {
- PA_UNLESS( stream->playback = PaUtil_AllocateMemory( sizeof (PaOssStreamComponent) ), paInsufficientMemory );
- PA_ENSURE( PaOssStreamComponent_Initialize( stream->playback, outputParameters, callback != NULL, odev, odevName ) );
- }
-
- if( callback != NULL )
- {
- PaUtil_InitializeStreamRepresentation( &stream->streamRepresentation,
- &ossApi->callbackStreamInterface, callback, userData );
- stream->callbackMode = 1;
- }
- else
- {
- PaUtil_InitializeStreamRepresentation( &stream->streamRepresentation,
- &ossApi->blockingStreamInterface, callback, userData );
- }
-
- ENSURE_( sem_init( &stream->semaphore, 0, 0 ), paInternalError );
-
-error:
- return result;
-}
-
-static void PaOssStream_Terminate( PaOssStream *stream )
-{
- assert( stream );
-
- PaUtil_TerminateStreamRepresentation( &stream->streamRepresentation );
- PaUtil_TerminateThreading( &stream->threading );
-
- if( stream->capture )
- PaOssStreamComponent_Terminate( stream->capture );
- if( stream->playback )
- PaOssStreamComponent_Terminate( stream->playback );
-
- sem_destroy( &stream->semaphore );
-
- PaUtil_FreeMemory( stream );
-}
-
-/** Translate from PA format to OSS native.
- *
- */
-static PaError Pa2OssFormat( PaSampleFormat paFormat, int *ossFormat )
-{
- switch( paFormat )
- {
- case paUInt8:
- *ossFormat = AFMT_U8;
- break;
- case paInt8:
- *ossFormat = AFMT_S8;
- break;
- case paInt16:
- *ossFormat = AFMT_S16_NE;
- break;
- default:
- return paInternalError; /* This shouldn't happen */
- }
-
- return paNoError;
-}
-
-/** Return the PA-compatible formats that this device can support.
- *
- */
-static PaError GetAvailableFormats( PaOssStreamComponent *component, PaSampleFormat *availableFormats )
-{
- PaError result = paNoError;
- int mask = 0;
- PaSampleFormat frmts = 0;
-
- ENSURE_( ioctl( component->fd, SNDCTL_DSP_GETFMTS, &mask ), paUnanticipatedHostError );
- if( mask & AFMT_U8 )
- frmts |= paUInt8;
- if( mask & AFMT_S8 )
- frmts |= paInt8;
- if( mask & AFMT_S16_NE )
- frmts |= paInt16;
- else
- result = paSampleFormatNotSupported;
-
- *availableFormats = frmts;
-
-error:
- return result;
-}
-
-static unsigned int PaOssStreamComponent_FrameSize( PaOssStreamComponent *component )
-{
- return Pa_GetSampleSize( component->hostFormat ) * component->hostChannelCount;
-}
-
-/** Buffer size in bytes.
- *
- */
-static unsigned long PaOssStreamComponent_BufferSize( PaOssStreamComponent *component )
-{
- return PaOssStreamComponent_FrameSize( component ) * component->hostFrames * component->numBufs;
-}
-
-static int CalcHigherLogTwo( int n )
-{
- int log2 = 0;
- while( (1<<log2) < n ) log2++;
- return log2;
-}
-
-static PaError PaOssStreamComponent_Configure( PaOssStreamComponent *component, double sampleRate, unsigned long framesPerBuffer,
- StreamMode streamMode, PaOssStreamComponent *master )
-{
- PaError result = paNoError;
- int temp, nativeFormat;
- int sr = (int)sampleRate;
- PaSampleFormat availableFormats, hostFormat;
- int chans = component->userChannelCount;
- int frgmt;
- int numBufs;
- int bytesPerBuf;
- double bufSz;
- unsigned long fragSz;
- audio_buf_info bufInfo;
-
- /* We may have a situation where only one component (the master) is configured, if both point to the same device.
- * In that case, the second component will copy settings from the other */
- if( !master )
- {
- /* Aspect BufferSettings: If framesPerBuffer is unspecified we have to infer a suitable fragment size.
- * The hardware need not respect the requested fragment size, so we may have to adapt.
- */
- if( framesPerBuffer == paFramesPerBufferUnspecified )
- {
- bufSz = component->latency * sampleRate;
- fragSz = bufSz / 4;
- }
- else
- {
- fragSz = framesPerBuffer;
- bufSz = component->latency * sampleRate + fragSz; /* Latency + 1 buffer */
- }
-
- PA_ENSURE( GetAvailableFormats( component, &availableFormats ) );
- hostFormat = PaUtil_SelectClosestAvailableFormat( availableFormats, component->userFormat );
-
- /* OSS demands at least 2 buffers, and 16 bytes per buffer */
- numBufs = PA_MAX( bufSz / fragSz, 2 );
- bytesPerBuf = PA_MAX( fragSz * Pa_GetSampleSize( hostFormat ) * chans, 16 );
-
- /* The fragment parameters are encoded like this:
- * Most significant byte: number of fragments
- * Least significant byte: exponent of fragment size (i.e., for 256, 8)
- */
- frgmt = (numBufs << 16) + (CalcHigherLogTwo( bytesPerBuf ) & 0xffff);
- ENSURE_( ioctl( component->fd, SNDCTL_DSP_SETFRAGMENT, &frgmt ), paUnanticipatedHostError );
-
- /* A: according to the OSS programmer's guide parameters should be set in this order:
- * format, channels, rate */
-
- /* This format should be deemed good before we get this far */
- PA_ENSURE( Pa2OssFormat( hostFormat, &temp ) );
- nativeFormat = temp;
- ENSURE_( ioctl( component->fd, SNDCTL_DSP_SETFMT, &temp ), paUnanticipatedHostError );
- PA_UNLESS( temp == nativeFormat, paInternalError );
-
- /* try to set the number of channels */
- ENSURE_( ioctl( component->fd, SNDCTL_DSP_CHANNELS, &chans ), paSampleFormatNotSupported ); /* XXX: Should be paInvalidChannelCount? */
- /* It's possible that the minimum number of host channels is greater than what the user requested */
- PA_UNLESS( chans >= component->userChannelCount, paInvalidChannelCount );
-
- /* try to set the sample rate */
- ENSURE_( ioctl( component->fd, SNDCTL_DSP_SPEED, &sr ), paInvalidSampleRate );
-
- /* reject if there's no sample rate within 1% of the one requested */
- if( (fabs( sampleRate - sr ) / sampleRate) > 0.01 )
- {
- PA_DEBUG(("%s: Wanted %f, closest sample rate was %d\n", __FUNCTION__, sampleRate, sr ));
- PA_ENSURE( paInvalidSampleRate );
- }
-
- ENSURE_( ioctl( component->fd, streamMode == StreamMode_In ? SNDCTL_DSP_GETISPACE : SNDCTL_DSP_GETOSPACE, &bufInfo ),
- paUnanticipatedHostError );
- component->numBufs = bufInfo.fragstotal;
-
- /* This needs to be the last ioctl call before the first read/write, according to the OSS programmer's guide */
- ENSURE_( ioctl( component->fd, SNDCTL_DSP_GETBLKSIZE, &bytesPerBuf ), paUnanticipatedHostError );
-
- component->hostFrames = bytesPerBuf / Pa_GetSampleSize( hostFormat ) / chans;
- component->hostChannelCount = chans;
- component->hostFormat = hostFormat;
- }
- else
- {
- component->hostFormat = master->hostFormat;
- component->hostFrames = master->hostFrames;
- component->hostChannelCount = master->hostChannelCount;
- component->numBufs = master->numBufs;
- }
-
- PA_UNLESS( component->buffer = PaUtil_AllocateMemory( PaOssStreamComponent_BufferSize( component ) ),
- paInsufficientMemory );
-
-error:
- return result;
-}
-
-static PaError PaOssStreamComponent_Read( PaOssStreamComponent *component, unsigned long *frames )
-{
- PaError result = paNoError;
- size_t len = *frames * PaOssStreamComponent_FrameSize( component );
- ssize_t bytesRead;
-
- ENSURE_( bytesRead = read( component->fd, component->buffer, len ), paUnanticipatedHostError );
- *frames = bytesRead / PaOssStreamComponent_FrameSize( component );
-
-error:
- return result;
-}
-
-static PaError PaOssStreamComponent_Write( PaOssStreamComponent *component, unsigned long *frames )
-{
- PaError result = paNoError;
- size_t len = *frames * PaOssStreamComponent_FrameSize( component );
- ssize_t bytesWritten;
-
- ENSURE_( bytesWritten = write( component->fd, component->buffer, len ), paUnanticipatedHostError );
- *frames = bytesWritten / PaOssStreamComponent_FrameSize( component );
-
-error:
- return result;
-}
-
-/** Configure the stream according to input/output parameters.
- *
- * Aspect StreamChannels: The minimum number of channels supported by the device may exceed that requested by
- * the user, if so we'll record the actual number of host channels and adapt later.
- */
-static PaError PaOssStream_Configure( PaOssStream *stream, double sampleRate, unsigned long framesPerBuffer,
- double *inputLatency, double *outputLatency )
-{
- PaError result = paNoError;
- int duplex = stream->capture && stream->playback;
- unsigned long framesPerHostBuffer = 0;
-
- /* We should request full duplex first thing after opening the device */
- if( duplex && stream->sharedDevice )
- ENSURE_( ioctl( stream->capture->fd, SNDCTL_DSP_SETDUPLEX, 0 ), paUnanticipatedHostError );
-
- if( stream->capture )
- {
- PaOssStreamComponent *component = stream->capture;
- PaOssStreamComponent_Configure( component, sampleRate, framesPerBuffer, StreamMode_In, NULL );
-
- assert( component->hostChannelCount > 0 );
- assert( component->hostFrames > 0 );
-
- *inputLatency = component->hostFrames * (component->numBufs - 1) / sampleRate;
- }
- if( stream->playback )
- {
- PaOssStreamComponent *component = stream->playback, *master = stream->sharedDevice ? stream->capture : NULL;
- PA_ENSURE( PaOssStreamComponent_Configure( component, sampleRate, framesPerBuffer, StreamMode_Out,
- master ) );
-
- assert( component->hostChannelCount > 0 );
- assert( component->hostFrames > 0 );
-
- *outputLatency = component->hostFrames * (component->numBufs - 1) / sampleRate;
- }
-
- if( duplex )
- framesPerHostBuffer = PA_MIN( stream->capture->hostFrames, stream->playback->hostFrames );
- else if( stream->capture )
- framesPerHostBuffer = stream->capture->hostFrames;
- else if( stream->playback )
- framesPerHostBuffer = stream->playback->hostFrames;
-
- stream->framesPerHostBuffer = framesPerHostBuffer;
- stream->pollTimeout = (int) ceil( 1e6 * framesPerHostBuffer / sampleRate ); /* Period in usecs, rounded up */
-
- stream->streamRepresentation.streamInfo.sampleRate = sampleRate;
-
-error:
- return result;
-}
-
-/* see pa_hostapi.h for a list of validity guarantees made about OpenStream parameters */
-
-/** Open a PA OSS stream.
- *
- * Aspect StreamChannels: The number of channels is specified per direction (in/out), and can differ between the
- * two. However, OSS doesn't support separate configuration spaces for capture and playback so if both
- * directions are the same device we will demand the same number of channels. The number of channels can range
- * from 1 to the maximum supported by the device.
- *
- * Aspect BufferSettings: If framesPerBuffer != paFramesPerBufferUnspecified the number of frames per callback
- * must reflect this, in addition the host latency per device should approximate the corresponding
- * suggestedLatency. Based on these constraints we need to determine a number of frames per host buffer that
- * both capture and playback can agree on (they can be different devices), the buffer processor can adapt
- * between host and user buffer size, but the ratio should preferably be integral.
- */
-static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
- PaStream** s,
- const PaStreamParameters *inputParameters,
- const PaStreamParameters *outputParameters,
- double sampleRate,
- unsigned long framesPerBuffer,
- PaStreamFlags streamFlags,
- PaStreamCallback *streamCallback,
- void *userData )
-{
- PaError result = paNoError;
- PaOSSHostApiRepresentation *ossHostApi = (PaOSSHostApiRepresentation*)hostApi;
- PaOssStream *stream = NULL;
- int inputChannelCount = 0, outputChannelCount = 0;
- PaSampleFormat inputSampleFormat = 0, outputSampleFormat = 0, inputHostFormat = 0, outputHostFormat = 0;
- const PaDeviceInfo *inputDeviceInfo = 0, *outputDeviceInfo = 0;
- int bpInitialized = 0;
- double inLatency, outLatency;
-
- /* validate platform specific flags */
- if( (streamFlags & paPlatformSpecificFlags) != 0 )
- return paInvalidFlag; /* unexpected platform specific flag */
-
- if( inputParameters )
- {
- /* unless alternate device specification is supported, reject the use of
- paUseHostApiSpecificDeviceSpecification */
- inputDeviceInfo = hostApi->deviceInfos[inputParameters->device];
- PA_ENSURE( ValidateParameters( inputParameters, inputDeviceInfo, StreamMode_In ) );
-
- inputChannelCount = inputParameters->channelCount;
- inputSampleFormat = inputParameters->sampleFormat;
- }
- if( outputParameters )
- {
- outputDeviceInfo = hostApi->deviceInfos[outputParameters->device];
- PA_ENSURE( ValidateParameters( outputParameters, outputDeviceInfo, StreamMode_Out ) );
-
- outputChannelCount = outputParameters->channelCount;
- outputSampleFormat = outputParameters->sampleFormat;
- }
-
- /* Aspect StreamChannels: We currently demand that number of input and output channels are the same, if the same
- * device is opened for both directions
- */
- if( inputChannelCount > 0 && outputChannelCount > 0 )
- {
- if( inputParameters->device == outputParameters->device )
- {
- if( inputParameters->channelCount != outputParameters->channelCount )
- return paInvalidChannelCount;
- }
- }
-
- /* allocate and do basic initialization of the stream structure */
- PA_UNLESS( stream = (PaOssStream*)PaUtil_AllocateMemory( sizeof(PaOssStream) ), paInsufficientMemory );
- PaOssStream_Initialize( stream, inputParameters, outputParameters, streamCallback, userData, streamFlags, ossHostApi );
-
- PA_ENSURE( PaOssStream_Configure( stream, sampleRate, framesPerBuffer, &inLatency, &outLatency ) );
-
- PaUtil_InitializeCpuLoadMeasurer( &stream->cpuLoadMeasurer, sampleRate );
-
- if( inputParameters )
- {
- inputHostFormat = stream->capture->hostFormat;
- stream->streamRepresentation.streamInfo.inputLatency = inLatency +
- PaUtil_GetBufferProcessorInputLatency( &stream->bufferProcessor ) / sampleRate;
- }
- if( outputParameters )
- {
- outputHostFormat = stream->playback->hostFormat;
- stream->streamRepresentation.streamInfo.outputLatency = outLatency +
- PaUtil_GetBufferProcessorOutputLatency( &stream->bufferProcessor ) / sampleRate;
- }
-
- /* Initialize buffer processor with fixed host buffer size.
- * Aspect StreamSampleFormat: Here we commit the user and host sample formats, PA infrastructure will
- * convert between the two.
- */
- PA_ENSURE( PaUtil_InitializeBufferProcessor( &stream->bufferProcessor,
- inputChannelCount, inputSampleFormat, inputHostFormat, outputChannelCount, outputSampleFormat,
- outputHostFormat, sampleRate, streamFlags, framesPerBuffer, stream->framesPerHostBuffer,
- paUtilFixedHostBufferSize, streamCallback, userData ) );
- bpInitialized = 1;
-
- *s = (PaStream*)stream;
-
- return result;
-
-error:
- if( bpInitialized )
- PaUtil_TerminateBufferProcessor( &stream->bufferProcessor );
- if( stream )
- PaOssStream_Terminate( stream );
-
- return result;
-}
-
-/*! Poll on I/O filedescriptors.
-
- Poll till we've determined there's data for read or write. In the full-duplex case,
- we don't want to hang around forever waiting for either input or output frames, so
- whenever we have a timed out filedescriptor we check if we're nearing under/overrun
- for the other direction (critical limit set at one buffer). If so, we exit the waiting
- state, and go on with what we got. We align the number of frames on a host buffer
- boundary because it is possible that the buffer size differs for the two directions and
- the host buffer size is a compromise between the two.
- */
-static PaError PaOssStream_WaitForFrames( PaOssStream *stream, unsigned long *frames )
-{
- PaError result = paNoError;
- int pollPlayback = 0, pollCapture = 0;
- int captureAvail = INT_MAX, playbackAvail = INT_MAX, commonAvail;
- audio_buf_info bufInfo;
- /* int ofs = 0, nfds = stream->nfds; */
- fd_set readFds, writeFds;
- int nfds = 0;
- struct timeval selectTimeval = {0, 0};
- unsigned long timeout = stream->pollTimeout; /* In usecs */
- int captureFd = -1, playbackFd = -1;
-
- assert( stream );
- assert( frames );
-
- if( stream->capture )
- {
- pollCapture = 1;
- captureFd = stream->capture->fd;
- /* stream->capture->pfd->events = POLLIN; */
- }
- if( stream->playback )
- {
- pollPlayback = 1;
- playbackFd = stream->playback->fd;
- /* stream->playback->pfd->events = POLLOUT; */
- }
-
- FD_ZERO( &readFds );
- FD_ZERO( &writeFds );
-
- while( pollPlayback || pollCapture )
- {
- pthread_testcancel();
-
- /* select may modify the timeout parameter */
- selectTimeval.tv_usec = timeout;
- nfds = 0;
-
- if( pollCapture )
- {
- FD_SET( captureFd, &readFds );
- nfds = captureFd + 1;
- }
- if( pollPlayback )
- {
- FD_SET( playbackFd, &writeFds );
- nfds = PA_MAX( nfds, playbackFd + 1 );
- }
- ENSURE_( select( nfds, &readFds, &writeFds, NULL, &selectTimeval ), paUnanticipatedHostError );
- /*
- if( poll( stream->pfds + ofs, nfds, stream->pollTimeout ) < 0 )
- {
-
- ENSURE_( -1, paUnanticipatedHostError );
- }
- */
- pthread_testcancel();
-
- if( pollCapture )
- {
- if( FD_ISSET( captureFd, &readFds ) )
- {
- FD_CLR( captureFd, &readFds );
- pollCapture = 0;
- }
- /*
- if( stream->capture->pfd->revents & POLLIN )
- {
- --nfds;
- ++ofs;
- pollCapture = 0;
- }
- */
- else if( stream->playback ) /* Timed out, go on with playback? */
- {
- /*PA_DEBUG(( "%s: Trying to poll again for capture frames, pollTimeout: %d\n",
- __FUNCTION__, stream->pollTimeout ));*/
- }
- }
- if( pollPlayback )
- {
- if( FD_ISSET( playbackFd, &writeFds ) )
- {
- FD_CLR( playbackFd, &writeFds );
- pollPlayback = 0;
- }
- /*
- if( stream->playback->pfd->revents & POLLOUT )
- {
- --nfds;
- pollPlayback = 0;
- }
- */
- else if( stream->capture ) /* Timed out, go on with capture? */
- {
- /*PA_DEBUG(( "%s: Trying to poll again for playback frames, pollTimeout: %d\n\n",
- __FUNCTION__, stream->pollTimeout ));*/
- }
- }
- }
-
- if( stream->capture )
- {
- ENSURE_( ioctl( captureFd, SNDCTL_DSP_GETISPACE, &bufInfo ), paUnanticipatedHostError );
- captureAvail = bufInfo.fragments * stream->capture->hostFrames;
- if( !captureAvail )
- PA_DEBUG(( "%s: captureAvail: 0\n", __FUNCTION__ ));
-
- captureAvail = captureAvail == 0 ? INT_MAX : captureAvail; /* Disregard if zero */
- }
- if( stream->playback )
- {
- ENSURE_( ioctl( playbackFd, SNDCTL_DSP_GETOSPACE, &bufInfo ), paUnanticipatedHostError );
- playbackAvail = bufInfo.fragments * stream->playback->hostFrames;
- if( !playbackAvail )
- {
- PA_DEBUG(( "%s: playbackAvail: 0\n", __FUNCTION__ ));
- }
-
- playbackAvail = playbackAvail == 0 ? INT_MAX : playbackAvail; /* Disregard if zero */
- }
-
- commonAvail = PA_MIN( captureAvail, playbackAvail );
- if( commonAvail == INT_MAX )
- commonAvail = 0;
- commonAvail -= commonAvail % stream->framesPerHostBuffer;
-
- assert( commonAvail != INT_MAX );
- assert( commonAvail >= 0 );
- *frames = commonAvail;
-
-error:
- return result;
-}
-
-/** Prepare stream for capture/playback.
- *
- * In order to synchronize capture and playback properly we use the SETTRIGGER command.
- */
-static PaError PaOssStream_Prepare( PaOssStream *stream )
-{
- PaError result = paNoError;
- int enableBits = 0;
-
- if( stream->triggered )
- return result;
-
- if( stream->playback )
- {
- size_t bufSz = PaOssStreamComponent_BufferSize( stream->playback );
- memset( stream->playback->buffer, 0, bufSz );
-
- /* Looks like we have to turn off blocking before we try this, but if we don't fill the buffer
- * OSS will complain. */
- PA_ENSURE( ModifyBlocking( stream->playback->fd, 0 ) );
- while (1)
- {
- if( write( stream->playback->fd, stream->playback->buffer, bufSz ) < 0 )
- break;
- }
- PA_ENSURE( ModifyBlocking( stream->playback->fd, 1 ) );
- }
-
- if( stream->sharedDevice )
- {
- enableBits = PCM_ENABLE_INPUT | PCM_ENABLE_OUTPUT;
- ENSURE_( ioctl( stream->capture->fd, SNDCTL_DSP_SETTRIGGER, &enableBits ), paUnanticipatedHostError );
- }
- else
- {
- if( stream->capture )
- {
- enableBits = PCM_ENABLE_INPUT;
- ENSURE_( ioctl( stream->capture->fd, SNDCTL_DSP_SETTRIGGER, &enableBits ), paUnanticipatedHostError );
- }
- if( stream->playback )
- {
- enableBits = PCM_ENABLE_OUTPUT;
- ENSURE_( ioctl( stream->playback->fd, SNDCTL_DSP_SETTRIGGER, &enableBits ), paUnanticipatedHostError );
- }
- }
-
- /* Ok, we have triggered the stream */
- stream->triggered = 1;
-
-error:
- return result;
-}
-
-/** Stop audio processing
- *
- */
-static PaError PaOssStream_Stop( PaOssStream *stream, int abort )
-{
- PaError result = paNoError;
-
- /* Looks like the only safe way to stop audio without reopening the device is SNDCTL_DSP_POST.
- * Also disable capture/playback till the stream is started again */
- if( stream->capture )
- {
- ENSURE_( ioctl( stream->capture->fd, SNDCTL_DSP_POST, 0 ), paUnanticipatedHostError );
- }
- if( stream->playback && !stream->sharedDevice )
- {
- ENSURE_( ioctl( stream->playback->fd, SNDCTL_DSP_POST, 0 ), paUnanticipatedHostError );
- }
-
-error:
- return result;
-}
-
-/** Clean up after thread exit.
- *
- * Aspect StreamState: If the user has registered a streamFinishedCallback it will be called here
- */
-static void OnExit( void *data )
-{
- PaOssStream *stream = (PaOssStream *) data;
- assert( data );
-
- PaUtil_ResetCpuLoadMeasurer( &stream->cpuLoadMeasurer );
-
- PaOssStream_Stop( stream, stream->callbackAbort );
-
- PA_DEBUG(( "OnExit: Stoppage\n" ));
-
- /* Eventually notify user all buffers have played */
- if( stream->streamRepresentation.streamFinishedCallback )
- stream->streamRepresentation.streamFinishedCallback( stream->streamRepresentation.userData );
-
- stream->callbackAbort = 0; /* Clear state */
- stream->isActive = 0;
-}
-
-static PaError SetUpBuffers( PaOssStream *stream, unsigned long framesAvail )
-{
- PaError result = paNoError;
-
- if( stream->capture )
- {
- PaUtil_SetInterleavedInputChannels( &stream->bufferProcessor, 0, stream->capture->buffer,
- stream->capture->hostChannelCount );
- PaUtil_SetInputFrameCount( &stream->bufferProcessor, framesAvail );
- }
- if( stream->playback )
- {
- PaUtil_SetInterleavedOutputChannels( &stream->bufferProcessor, 0, stream->playback->buffer,
- stream->playback->hostChannelCount );
- PaUtil_SetOutputFrameCount( &stream->bufferProcessor, framesAvail );
- }
-
- return result;
-}
-
-/** Thread procedure for callback processing.
- *
- * Aspect StreamState: StartStream will wait on this to initiate audio processing, useful in case the
- * callback should be used for buffer priming. When the stream is cancelled a separate function will
- * take care of the transition to the Callback Finished state (the stream isn't considered Stopped
- * before StopStream() or AbortStream() are called).
- */
-static void *PaOSS_AudioThreadProc( void *userData )
-{
- PaError result = paNoError;
- PaOssStream *stream = (PaOssStream*)userData;
- unsigned long framesAvail, framesProcessed;
- int callbackResult = paContinue;
- int triggered = stream->triggered; /* See if SNDCTL_DSP_TRIGGER has been issued already */
- int initiateProcessing = triggered; /* Already triggered? */
- PaStreamCallbackFlags cbFlags = 0; /* We might want to keep state across iterations */
-
- /*
-#if ( SOUND_VERSION > 0x030904 )
- audio_errinfo errinfo;
-#endif
-*/
-
- assert( stream );
-
- pthread_cleanup_push( &OnExit, stream ); /* Execute OnExit when exiting */
-
- /* The first time the stream is started we use SNDCTL_DSP_TRIGGER to accurately start capture and
- * playback in sync, when the stream is restarted after being stopped we simply start by reading/
- * writing.
- */
- PA_ENSURE( PaOssStream_Prepare( stream ) );
-
- /* If we are to initiate processing implicitly by reading/writing data, we start off in blocking mode */
- if( initiateProcessing )
- {
- /* Make sure devices are in blocking mode */
- if( stream->capture )
- ModifyBlocking( stream->capture->fd, 1 );
- if( stream->playback )
- ModifyBlocking( stream->playback->fd, 1 );
- }
-
- while( 1 )
- {
- PaStreamCallbackTimeInfo timeInfo = {0,0,0}; /* TODO: IMPLEMENT ME */
-
- pthread_testcancel();
-
- if( stream->callbackStop && callbackResult == paContinue )
- {
- PA_DEBUG(( "Setting callbackResult to paComplete\n" ));
- callbackResult = paComplete;
- }
-
- /* Aspect StreamState: Because of the messy OSS scheme we can't explicitly trigger device start unless
- * the stream has been recently started, we will have to go right ahead and read/write in blocking
- * fashion to trigger operation. Therefore we begin with processing one host buffer before we switch
- * to non-blocking mode.
- */
- if( !initiateProcessing )
- {
- PA_ENSURE( PaOssStream_WaitForFrames( stream, &framesAvail ) ); /* Wait on available frames */
- assert( framesAvail % stream->framesPerHostBuffer == 0 );
- }
- else
- {
- framesAvail = stream->framesPerHostBuffer;
- }
-
- while( framesAvail > 0 )
- {
- unsigned long frames = framesAvail;
-
- pthread_testcancel();
-
- PaUtil_BeginCpuLoadMeasurement( &stream->cpuLoadMeasurer );
-
- /* Read data */
- if ( stream->capture )
- {
- PA_ENSURE( PaOssStreamComponent_Read( stream->capture, &frames ) );
- assert( frames == framesAvail );
- }
-
-#if ( SOUND_VERSION >= 0x030904 )
- /*
- Check with OSS to see if there have been any under/overruns
- since last time we checked.
- */
- /*
- if( ioctl( stream->deviceHandle, SNDCTL_DSP_GETERROR, &errinfo ) >= 0 )
- {
- if( errinfo.play_underruns )
- cbFlags |= paOutputUnderflow ;
- if( errinfo.record_underruns )
- cbFlags |= paInputUnderflow ;
- }
- else
- PA_DEBUG(( "SNDCTL_DSP_GETERROR command failed: %s\n", strerror( errno ) ));
- */
-#endif
-
- PaUtil_BeginBufferProcessing( &stream->bufferProcessor, &timeInfo,
- cbFlags );
- cbFlags = 0;
- PA_ENSURE( SetUpBuffers( stream, framesAvail ) );
-
- framesProcessed = PaUtil_EndBufferProcessing( &stream->bufferProcessor,
- &callbackResult );
- assert( framesProcessed == framesAvail );
- PaUtil_EndCpuLoadMeasurement( &stream->cpuLoadMeasurer, framesProcessed );
-
- if ( stream->playback )
- {
- frames = framesAvail;
-
- PA_ENSURE( PaOssStreamComponent_Write( stream->playback, &frames ) );
- assert( frames == framesAvail );
-
- /* TODO: handle bytesWritten != bytesRequested (slippage?) */
- }
-
- framesAvail -= framesProcessed;
- stream->framesProcessed += framesProcessed;
-
- if( callbackResult != paContinue )
- break;
- }
-
- if( initiateProcessing || !triggered )
- {
- /* Non-blocking */
- if( stream->capture )
- PA_ENSURE( ModifyBlocking( stream->capture->fd, 0 ) );
- if( stream->playback && !stream->sharedDevice )
- PA_ENSURE( ModifyBlocking( stream->playback->fd, 0 ) );
-
- initiateProcessing = 0;
- sem_post( &stream->semaphore );
- }
-
- if( callbackResult != paContinue )
- {
- stream->callbackAbort = callbackResult == paAbort;
- if( stream->callbackAbort || PaUtil_IsBufferProcessorOutputEmpty( &stream->bufferProcessor ) )
- break;
- }
- }
-
- pthread_cleanup_pop( 1 );
-
-error:
- pthread_exit( NULL );
-}
-
-/** Close the stream.
- *
- */
-static PaError CloseStream( PaStream* s )
-{
- PaError result = paNoError;
- PaOssStream *stream = (PaOssStream*)s;
-
- assert( stream );
-
- PaUtil_TerminateBufferProcessor( &stream->bufferProcessor );
- PaOssStream_Terminate( stream );
-
- return result;
-}
-
-/** Start the stream.
- *
- * Aspect StreamState: After returning, the stream shall be in the Active state, implying that an eventual
- * callback will be repeatedly called in a separate thread. If a separate thread is started this function
- * will block untill it has started processing audio, otherwise audio processing is started directly.
- */
-static PaError StartStream( PaStream *s )
-{
- PaError result = paNoError;
- PaOssStream *stream = (PaOssStream*)s;
-
- stream->isActive = 1;
- stream->isStopped = 0;
- stream->lastPosPtr = 0;
- stream->lastStreamBytes = 0;
- stream->framesProcessed = 0;
-
- /* only use the thread for callback streams */
- if( stream->bufferProcessor.streamCallback )
- {
- PA_ENSURE( PaUtil_StartThreading( &stream->threading, &PaOSS_AudioThreadProc, stream ) );
- sem_wait( &stream->semaphore );
- }
- else
- PA_ENSURE( PaOssStream_Prepare( stream ) );
-
-error:
- return result;
-}
-
-static PaError RealStop( PaOssStream *stream, int abort )
-{
- PaError result = paNoError;
-
- if( stream->callbackMode )
- {
- if( abort )
- stream->callbackAbort = 1;
- else
- stream->callbackStop = 1;
-
- PA_ENSURE( PaUtil_CancelThreading( &stream->threading, !abort, NULL ) );
-
- stream->callbackStop = stream->callbackAbort = 0;
- }
- else
- PA_ENSURE( PaOssStream_Stop( stream, abort ) );
-
- stream->isStopped = 1;
-
-error:
- return result;
-}
-
-/** Stop the stream.
- *
- * Aspect StreamState: This will cause the stream to transition to the Stopped state, playing all enqueued
- * buffers.
- */
-static PaError StopStream( PaStream *s )
-{
- return RealStop( (PaOssStream *)s, 0 );
-}
-
-/** Abort the stream.
- *
- * Aspect StreamState: This will cause the stream to transition to the Stopped state, discarding all enqueued
- * buffers. Note that the buffers are not currently correctly discarded, this is difficult without closing
- * the OSS device.
- */
-static PaError AbortStream( PaStream *s )
-{
- return RealStop( (PaOssStream *)s, 1 );
-}
-
-/** Is the stream in the Stopped state.
- *
- */
-static PaError IsStreamStopped( PaStream *s )
-{
- PaOssStream *stream = (PaOssStream*)s;
-
- return (stream->isStopped);
-}
-
-/** Is the stream in the Active state.
- *
- */
-static PaError IsStreamActive( PaStream *s )
-{
- PaOssStream *stream = (PaOssStream*)s;
-
- return (stream->isActive);
-}
-
-static PaTime GetStreamTime( PaStream *s )
-{
- PaOssStream *stream = (PaOssStream*)s;
- count_info info;
- int delta;
-
- if( stream->playback ) {
- if( ioctl( stream->playback->fd, SNDCTL_DSP_GETOPTR, &info) == 0 ) {
- delta = ( info.bytes - stream->lastPosPtr ) & 0x000FFFFF;
- return ( stream->lastStreamBytes + delta) / PaOssStreamComponent_FrameSize( stream->playback ) / stream->sampleRate;
- }
- }
- else {
- if (ioctl( stream->capture->fd, SNDCTL_DSP_GETIPTR, &info) == 0) {
- delta = (info.bytes - stream->lastPosPtr) & 0x000FFFFF;
- return ( stream->lastStreamBytes + delta) / PaOssStreamComponent_FrameSize( stream->capture ) / stream->sampleRate;
- }
- }
-
- /* the ioctl failed, but we can still give a coarse estimate */
-
- return stream->framesProcessed / stream->sampleRate;
-}
-
-
-static double GetStreamCpuLoad( PaStream* s )
-{
- PaOssStream *stream = (PaOssStream*)s;
-
- return PaUtil_GetCpuLoad( &stream->cpuLoadMeasurer );
-}
-
-
-/*
- As separate stream interfaces are used for blocking and callback
- streams, the following functions can be guaranteed to only be called
- for blocking streams.
-*/
-
-
-static PaError ReadStream( PaStream* s,
- void *buffer,
- unsigned long frames )
-{
- PaOssStream *stream = (PaOssStream*)s;
- int bytesRequested, bytesRead;
- unsigned long framesRequested;
- void *userBuffer;
-
- /* If user input is non-interleaved, PaUtil_CopyInput will manipulate the channel pointers,
- * so we copy the user provided pointers */
- if( stream->bufferProcessor.userInputIsInterleaved )
- userBuffer = buffer;
- else /* Copy channels into local array */
- {
- userBuffer = stream->capture->userBuffers;
- memcpy( (void *)userBuffer, buffer, sizeof (void *) * stream->capture->userChannelCount );
- }
-
- while( frames )
- {
- framesRequested = PA_MIN( frames, stream->capture->hostFrames );
-
- bytesRequested = framesRequested * PaOssStreamComponent_FrameSize( stream->capture );
- bytesRead = read( stream->capture->fd, stream->capture->buffer, bytesRequested );
- if ( bytesRequested != bytesRead )
- return paUnanticipatedHostError;
-
- PaUtil_SetInputFrameCount( &stream->bufferProcessor, stream->capture->hostFrames );
- PaUtil_SetInterleavedInputChannels( &stream->bufferProcessor, 0, stream->capture->buffer, stream->capture->hostChannelCount );
- PaUtil_CopyInput( &stream->bufferProcessor, &userBuffer, framesRequested );
- frames -= framesRequested;
- }
- return paNoError;
-}
-
-
-static PaError WriteStream( PaStream* s,
- const void *buffer,
- unsigned long frames )
-{
- PaOssStream *stream = (PaOssStream*)s;
- int bytesRequested, bytesWritten;
- unsigned long framesConverted;
- const void *userBuffer;
-
- /* If user output is non-interleaved, PaUtil_CopyOutput will manipulate the channel pointers,
- * so we copy the user provided pointers */
- if( stream->bufferProcessor.userOutputIsInterleaved )
- userBuffer = buffer;
- else /* Copy channels into local array */
- {
- userBuffer = stream->playback->userBuffers;
- memcpy( (void *)userBuffer, buffer, sizeof (void *) * stream->playback->userChannelCount );
- }
-
- while( frames )
- {
- PaUtil_SetOutputFrameCount( &stream->bufferProcessor, stream->playback->hostFrames );
- PaUtil_SetInterleavedOutputChannels( &stream->bufferProcessor, 0, stream->playback->buffer, stream->playback->hostChannelCount );
-
- framesConverted = PaUtil_CopyOutput( &stream->bufferProcessor, &userBuffer, frames );
- frames -= framesConverted;
-
- bytesRequested = framesConverted * PaOssStreamComponent_FrameSize( stream->playback );
- bytesWritten = write( stream->playback->fd, stream->playback->buffer, bytesRequested );
-
- if ( bytesRequested != bytesWritten )
- return paUnanticipatedHostError;
- }
- return paNoError;
-}
-
-
-static signed long GetStreamReadAvailable( PaStream* s )
-{
- PaOssStream *stream = (PaOssStream*)s;
- audio_buf_info info;
-
- if( ioctl( stream->capture->fd, SNDCTL_DSP_GETISPACE, &info ) < 0 )
- return paUnanticipatedHostError;
- return info.fragments * stream->capture->hostFrames;
-}
-
-
-/* TODO: Compute number of allocated bytes somewhere else, can we use ODELAY with capture */
-static signed long GetStreamWriteAvailable( PaStream* s )
-{
- PaOssStream *stream = (PaOssStream*)s;
- int delay = 0;
-
- if( ioctl( stream->playback->fd, SNDCTL_DSP_GETODELAY, &delay ) < 0 )
- return paUnanticipatedHostError;
-
- return (PaOssStreamComponent_BufferSize( stream->playback ) - delay) / PaOssStreamComponent_FrameSize( stream->playback );
-}
-
+/*
+ * $Id: pa_unix_oss.c,v 1.6.2.22 2005/03/08 21:26:53 aknudsen Exp $
+ * PortAudio Portable Real-Time Audio Library
+ * Latest Version at: http://www.portaudio.com
+ * OSS implementation by:
+ * Douglas Repetto
+ * Phil Burk
+ * Dominic Mazzoni
+ * Arve Knudsen
+ *
+ * Based on the Open Source API proposed by Ross Bencina
+ * Copyright (c) 1999-2002 Ross Bencina, Phil Burk
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * Any person wishing to distribute modifications to the Software is
+ * requested to send the modifications to the original developer so that
+ * they can be incorporated into the canonical version.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+ * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+/*
+ * PJMEDIA - Multimedia over IP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <math.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <unistd.h>
+#include <pthread.h>
+#include <alloca.h>
+#include <malloc.h>
+#include <assert.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/poll.h>
+#include <limits.h>
+#include <semaphore.h>
+
+#ifdef __linux__
+# include <linux/soundcard.h>
+# define DEVICE_NAME_BASE "/dev/dsp"
+#else
+# include <machine/soundcard.h> /* JH20010905 */
+# define DEVICE_NAME_BASE "/dev/audio"
+#endif
+
+#include "portaudio.h"
+#include "pa_util.h"
+#include "pa_allocation.h"
+#include "pa_hostapi.h"
+#include "pa_stream.h"
+#include "pa_cpuload.h"
+#include "pa_process.h"
+#include "pa_unix_util.h"
+
+static int sysErr_;
+static pthread_t mainThread_;
+
+/* Check return value of system call, and map it to PaError */
+#define ENSURE_(expr, code) \
+ do { \
+ if( UNLIKELY( (sysErr_ = (expr)) < 0 ) ) \
+ { \
+ /* PaUtil_SetLastHostErrorInfo should only be used in the main thread */ \
+ if( (code) == paUnanticipatedHostError && pthread_self() == mainThread_ ) \
+ { \
+ PaUtil_SetLastHostErrorInfo( paALSA, sysErr_, strerror( errno ) ); \
+ } \
+ \
+ PaUtil_DebugPrint(( "Expression '" #expr "' failed in '" __FILE__ "', line: " STRINGIZE( __LINE__ ) "\n" )); \
+ result = (code); \
+ goto error; \
+ } \
+ } while( 0 );
+
+#ifndef AFMT_S16_NE
+#define AFMT_S16_NE Get_AFMT_S16_NE()
+/*********************************************************************
+ * Some versions of OSS do not define AFMT_S16_NE. So check CPU.
+ * PowerPC is Big Endian. X86 is Little Endian.
+ */
+static int Get_AFMT_S16_NE( void )
+{
+ long testData = 1;
+ char *ptr = (char *) &testData;
+ int isLittle = ( *ptr == 1 ); /* Does address point to least significant byte? */
+ return isLittle ? AFMT_S16_LE : AFMT_S16_BE;
+}
+#endif
+
+/* PaOSSHostApiRepresentation - host api datastructure specific to this implementation */
+
+typedef struct
+{
+ PaUtilHostApiRepresentation inheritedHostApiRep;
+ PaUtilStreamInterface callbackStreamInterface;
+ PaUtilStreamInterface blockingStreamInterface;
+
+ PaUtilAllocationGroup *allocations;
+
+ PaHostApiIndex hostApiIndex;
+}
+PaOSSHostApiRepresentation;
+
+/** Per-direction structure for PaOssStream.
+ *
+ * Aspect StreamChannels: In case the user requests to open the same device for both capture and playback,
+ * but with different number of channels we will have to adapt between the number of user and host
+ * channels for at least one direction, since the configuration space is the same for both directions
+ * of an OSS device.
+ */
+typedef struct
+{
+ int fd;
+ const char *devName;
+ int userChannelCount, hostChannelCount;
+ int userInterleaved;
+ void *buffer;
+ PaSampleFormat userFormat, hostFormat;
+ double latency;
+ unsigned long hostFrames, numBufs;
+ void **userBuffers; /* For non-interleaved blocking */
+} PaOssStreamComponent;
+
+/** Implementation specific representation of a PaStream.
+ *
+ */
+typedef struct PaOssStream
+{
+ PaUtilStreamRepresentation streamRepresentation;
+ PaUtilCpuLoadMeasurer cpuLoadMeasurer;
+ PaUtilBufferProcessor bufferProcessor;
+
+ PaUtilThreading threading;
+
+ int sharedDevice;
+ unsigned long framesPerHostBuffer;
+ int triggered; /* Have the devices been triggered yet (first start) */
+
+ int isActive;
+ int isStopped;
+
+ int lastPosPtr;
+ double lastStreamBytes;
+
+ int framesProcessed;
+
+ double sampleRate;
+
+ int callbackMode;
+ int callbackStop, callbackAbort;
+
+ PaOssStreamComponent *capture, *playback;
+ unsigned long pollTimeout;
+ sem_t semaphore;
+}
+PaOssStream;
+
+typedef enum {
+ StreamMode_In,
+ StreamMode_Out
+} StreamMode;
+
+/* prototypes for functions declared in this file */
+
+static void Terminate( struct PaUtilHostApiRepresentation *hostApi );
+static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
+ const PaStreamParameters *inputParameters,
+ const PaStreamParameters *outputParameters,
+ double sampleRate );
+static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
+ PaStream** s,
+ const PaStreamParameters *inputParameters,
+ const PaStreamParameters *outputParameters,
+ double sampleRate,
+ unsigned long framesPerBuffer,
+ PaStreamFlags streamFlags,
+ PaStreamCallback *streamCallback,
+ void *userData );
+static PaError CloseStream( PaStream* stream );
+static PaError StartStream( PaStream *stream );
+static PaError StopStream( PaStream *stream );
+static PaError AbortStream( PaStream *stream );
+static PaError IsStreamStopped( PaStream *s );
+static PaError IsStreamActive( PaStream *stream );
+static PaTime GetStreamTime( PaStream *stream );
+static double GetStreamCpuLoad( PaStream* stream );
+static PaError ReadStream( PaStream* stream, void *buffer, unsigned long frames );
+static PaError WriteStream( PaStream* stream, const void *buffer, unsigned long frames );
+static signed long GetStreamReadAvailable( PaStream* stream );
+static signed long GetStreamWriteAvailable( PaStream* stream );
+static PaError BuildDeviceList( PaOSSHostApiRepresentation *hostApi );
+
+
+/** Initialize the OSS API implementation.
+ *
+ * This function will initialize host API datastructures and query host devices for information.
+ *
+ * Aspect DeviceCapabilities: Enumeration of host API devices is initiated from here
+ *
+ * Aspect FreeResources: If an error is encountered under way we have to free each resource allocated in this function,
+ * this happens with the usual "error" label.
+ */
+PaError PaOSS_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex hostApiIndex )
+{
+ PaError result = paNoError;
+ PaOSSHostApiRepresentation *ossHostApi = NULL;
+
+ PA_UNLESS( ossHostApi = (PaOSSHostApiRepresentation*)PaUtil_AllocateMemory( sizeof(PaOSSHostApiRepresentation) ),
+ paInsufficientMemory );
+ PA_UNLESS( ossHostApi->allocations = PaUtil_CreateAllocationGroup(), paInsufficientMemory );
+ ossHostApi->hostApiIndex = hostApiIndex;
+
+ /* Initialize host API structure */
+ *hostApi = &ossHostApi->inheritedHostApiRep;
+ (*hostApi)->info.structVersion = 1;
+ (*hostApi)->info.type = paOSS;
+ (*hostApi)->info.name = "OSS";
+ (*hostApi)->Terminate = Terminate;
+ (*hostApi)->OpenStream = OpenStream;
+ (*hostApi)->IsFormatSupported = IsFormatSupported;
+
+ PA_ENSURE( BuildDeviceList( ossHostApi ) );
+
+ PaUtil_InitializeStreamInterface( &ossHostApi->callbackStreamInterface, CloseStream, StartStream,
+ StopStream, AbortStream, IsStreamStopped, IsStreamActive,
+ GetStreamTime, GetStreamCpuLoad,
+ PaUtil_DummyRead, PaUtil_DummyWrite,
+ PaUtil_DummyGetReadAvailable,
+ PaUtil_DummyGetWriteAvailable );
+
+ PaUtil_InitializeStreamInterface( &ossHostApi->blockingStreamInterface, CloseStream, StartStream,
+ StopStream, AbortStream, IsStreamStopped, IsStreamActive,
+ GetStreamTime, PaUtil_DummyGetCpuLoad,
+ ReadStream, WriteStream, GetStreamReadAvailable, GetStreamWriteAvailable );
+
+ mainThread_ = pthread_self();
+
+ return result;
+
+error:
+ if( ossHostApi )
+ {
+ if( ossHostApi->allocations )
+ {
+ PaUtil_FreeAllAllocations( ossHostApi->allocations );
+ PaUtil_DestroyAllocationGroup( ossHostApi->allocations );
+ }
+
+ PaUtil_FreeMemory( ossHostApi );
+ }
+ return result;
+}
+
+PaError PaUtil_InitializeDeviceInfo( PaDeviceInfo *deviceInfo, const char *name, PaHostApiIndex hostApiIndex, int maxInputChannels,
+ int maxOutputChannels, PaTime defaultLowInputLatency, PaTime defaultLowOutputLatency, PaTime defaultHighInputLatency,
+ PaTime defaultHighOutputLatency, double defaultSampleRate, PaUtilAllocationGroup *allocations )
+{
+ PaError result = paNoError;
+
+ deviceInfo->structVersion = 2;
+ if( allocations )
+ {
+ size_t len = strlen( name ) + 1;
+ PA_UNLESS( deviceInfo->name = PaUtil_GroupAllocateMemory( allocations, len ), paInsufficientMemory );
+ strncpy( (char *)deviceInfo->name, name, len );
+ }
+ else
+ deviceInfo->name = name;
+
+ deviceInfo->hostApi = hostApiIndex;
+ deviceInfo->maxInputChannels = maxInputChannels;
+ deviceInfo->maxOutputChannels = maxOutputChannels;
+ deviceInfo->defaultLowInputLatency = defaultLowInputLatency;
+ deviceInfo->defaultLowOutputLatency = defaultLowOutputLatency;
+ deviceInfo->defaultHighInputLatency = defaultHighInputLatency;
+ deviceInfo->defaultHighOutputLatency = defaultHighOutputLatency;
+ deviceInfo->defaultSampleRate = defaultSampleRate;
+
+error:
+ return result;
+}
+
+static PaError QueryDirection( const char *deviceName, StreamMode mode, double *defaultSampleRate, int *maxChannelCount,
+ double *defaultLowLatency, double *defaultHighLatency )
+{
+ PaError result = paNoError;
+ int numChannels, maxNumChannels;
+ int busy = 0;
+ int devHandle = -1;
+ int sr;
+ *maxChannelCount = 0; /* Default value in case this fails */
+
+ if ( (devHandle = open( deviceName, (mode == StreamMode_In ? O_RDONLY : O_WRONLY) | O_NONBLOCK )) < 0 )
+ {
+ if( errno == EBUSY || errno == EAGAIN )
+ {
+ PA_DEBUG(( "%s: Device %s busy\n", __FUNCTION__, deviceName ));
+ }
+ else
+ {
+ PA_DEBUG(( "%s: Can't access device: %s\n", __FUNCTION__, strerror( errno ) ));
+ }
+
+ return paDeviceUnavailable;
+ }
+
+ /* Negotiate for the maximum number of channels for this device. PLB20010927
+ * Consider up to 16 as the upper number of channels.
+ * Variable maxNumChannels should contain the actual upper limit after the call.
+ * Thanks to John Lazzaro and Heiko Purnhagen for suggestions.
+ */
+ maxNumChannels = 0;
+ for( numChannels = 1; numChannels <= 16; numChannels++ )
+ {
+ int temp = numChannels;
+ if( ioctl( devHandle, SNDCTL_DSP_CHANNELS, &temp ) < 0 )
+ {
+ busy = EAGAIN == errno || EBUSY == errno;
+ /* ioctl() failed so bail out if we already have stereo */
+ if( maxNumChannels >= 2 )
+ break;
+ }
+ else
+ {
+ /* ioctl() worked but bail out if it does not support numChannels.
+ * We don't want to leave gaps in the numChannels supported.
+ */
+ if( (numChannels > 2) && (temp != numChannels) )
+ break;
+ if( temp > maxNumChannels )
+ maxNumChannels = temp; /* Save maximum. */
+ }
+ }
+ /* A: We're able to open a device for capture if it's busy playing back and vice versa,
+ * but we can't configure anything */
+ if( 0 == maxNumChannels && busy )
+ {
+ result = paDeviceUnavailable;
+ goto error;
+ }
+
+ /* The above negotiation may fail for an old driver so try this older technique. */
+ if( maxNumChannels < 1 )
+ {
+ int stereo = 1;
+ if( ioctl( devHandle, SNDCTL_DSP_STEREO, &stereo ) < 0 )
+ {
+ maxNumChannels = 1;
+ }
+ else
+ {
+ maxNumChannels = (stereo) ? 2 : 1;
+ }
+ PA_DEBUG(( "%s: use SNDCTL_DSP_STEREO, maxNumChannels = %d\n", __FUNCTION__, maxNumChannels ))
+ }
+
+ /* During channel negotiation, the last ioctl() may have failed. This can
+ * also cause sample rate negotiation to fail. Hence the following, to return
+ * to a supported number of channels. SG20011005 */
+ {
+ /* use most reasonable default value */
+ int temp = PA_MIN( maxNumChannels, 2 );
+ ENSURE_( ioctl( devHandle, SNDCTL_DSP_CHANNELS, &temp ), paUnanticipatedHostError );
+ }
+
+ /* Get supported sample rate closest to 44100 Hz */
+ if( *defaultSampleRate < 0 )
+ {
+ sr = 44100;
+ if( ioctl( devHandle, SNDCTL_DSP_SPEED, &sr ) < 0 )
+ {
+ result = paUnanticipatedHostError;
+ goto error;
+ }
+
+ *defaultSampleRate = sr;
+ }
+
+ *maxChannelCount = maxNumChannels;
+ /* TODO */
+ *defaultLowLatency = 512. / *defaultSampleRate;
+ *defaultHighLatency = 2048. / *defaultSampleRate;
+
+error:
+ if( devHandle >= 0 )
+ close( devHandle );
+
+ return result;
+}
+
+/** Query OSS device.
+ *
+ * This is where PaDeviceInfo objects are constructed and filled in with relevant information.
+ *
+ * Aspect DeviceCapabilities: The inferred device capabilities are recorded in a PaDeviceInfo object that is constructed
+ * in place.
+ */
+static PaError QueryDevice( char *deviceName, PaOSSHostApiRepresentation *ossApi, PaDeviceInfo **deviceInfo )
+{
+ PaError result = paNoError;
+ double sampleRate = -1.;
+ int maxInputChannels, maxOutputChannels;
+ PaTime defaultLowInputLatency, defaultLowOutputLatency, defaultHighInputLatency, defaultHighOutputLatency;
+ PaError tmpRes = paNoError;
+ int busy = 0;
+ *deviceInfo = NULL;
+
+ /* douglas:
+ we have to do this querying in a slightly different order. apparently
+ some sound cards will give you different info based on their settins.
+ e.g. a card might give you stereo at 22kHz but only mono at 44kHz.
+ the correct order for OSS is: format, channels, sample rate
+ */
+
+ /* Aspect StreamChannels: The number of channels supported for a device may depend on the mode it is
+ * opened in, it may have more channels available for capture than playback and vice versa. Therefore
+ * we will open the device in both read- and write-only mode to determine the supported number.
+ */
+ if( (tmpRes = QueryDirection( deviceName, StreamMode_In, &sampleRate, &maxInputChannels, &defaultLowInputLatency,
+ &defaultHighInputLatency )) != paNoError )
+ {
+ if( tmpRes != paDeviceUnavailable )
+ {
+ PA_DEBUG(( "%s: Querying device %s for capture failed!\n", __FUNCTION__, deviceName ));
+ /* PA_ENSURE( tmpRes ); */
+ }
+ ++busy;
+ }
+ if( (tmpRes = QueryDirection( deviceName, StreamMode_Out, &sampleRate, &maxOutputChannels, &defaultLowOutputLatency,
+ &defaultHighOutputLatency )) != paNoError )
+ {
+ if( tmpRes != paDeviceUnavailable )
+ {
+ PA_DEBUG(( "%s: Querying device %s for playback failed!\n", __FUNCTION__, deviceName ));
+ /* PA_ENSURE( tmpRes ); */
+ }
+ ++busy;
+ }
+ assert( 0 <= busy && busy <= 2 );
+ if( 2 == busy ) /* Both directions are unavailable to us */
+ {
+ result = paDeviceUnavailable;
+ goto error;
+ }
+
+ PA_UNLESS( *deviceInfo = PaUtil_GroupAllocateMemory( ossApi->allocations, sizeof (PaDeviceInfo) ), paInsufficientMemory );
+ PA_ENSURE( PaUtil_InitializeDeviceInfo( *deviceInfo, deviceName, ossApi->hostApiIndex, maxInputChannels, maxOutputChannels,
+ defaultLowInputLatency, defaultLowOutputLatency, defaultHighInputLatency, defaultHighOutputLatency, sampleRate,
+ ossApi->allocations ) );
+
+error:
+ return result;
+}
+
+/** Query host devices.
+ *
+ * Loop over host devices and query their capabilitiesu
+ *
+ * Aspect DeviceCapabilities: This function calls QueryDevice on each device entry and receives a filled in PaDeviceInfo object
+ * per device, these are placed in the host api representation's deviceInfos array.
+ */
+static PaError BuildDeviceList( PaOSSHostApiRepresentation *ossApi )
+{
+ PaError result = paNoError;
+ PaUtilHostApiRepresentation *commonApi = &ossApi->inheritedHostApiRep;
+ int i;
+ int numDevices = 0, maxDeviceInfos = 1;
+ PaDeviceInfo **deviceInfos = NULL;
+
+ /* These two will be set to the first working input and output device, respectively */
+ commonApi->info.defaultInputDevice = paNoDevice;
+ commonApi->info.defaultOutputDevice = paNoDevice;
+
+ /* Find devices by calling QueryDevice on each
+ * potential device names. When we find a valid one,
+ * add it to a linked list.
+ * A: Can there only be 10 devices? */
+
+ for( i = 0; i < 10; i++ )
+ {
+ char deviceName[32];
+ PaDeviceInfo *deviceInfo;
+ int testResult;
+ struct stat stbuf;
+
+ if( i == 0 )
+ snprintf(deviceName, sizeof (deviceName), "%s", DEVICE_NAME_BASE);
+ else
+ snprintf(deviceName, sizeof (deviceName), "%s%d", DEVICE_NAME_BASE, i);
+
+ /* PA_DEBUG(("PaOSS BuildDeviceList: trying device %s\n", deviceName )); */
+ if( stat( deviceName, &stbuf ) < 0 )
+ {
+ if( ENOENT != errno )
+ PA_DEBUG(( "%s: Error stat'ing %s: %s\n", __FUNCTION__, deviceName, strerror( errno ) ));
+ continue;
+ }
+ if( (testResult = QueryDevice( deviceName, ossApi, &deviceInfo )) != paNoError )
+ {
+ if( testResult != paDeviceUnavailable )
+ PA_ENSURE( testResult );
+
+ continue;
+ }
+
+ ++numDevices;
+ if( !deviceInfos || numDevices > maxDeviceInfos )
+ {
+ maxDeviceInfos *= 2;
+ PA_UNLESS( deviceInfos = (PaDeviceInfo **) realloc( deviceInfos, maxDeviceInfos * sizeof (PaDeviceInfo *) ),
+ paInsufficientMemory );
+ }
+ deviceInfos[numDevices - 1] = deviceInfo;
+
+ if( commonApi->info.defaultInputDevice == paNoDevice && deviceInfo->maxInputChannels > 0 )
+ commonApi->info.defaultInputDevice = i;
+ if( commonApi->info.defaultOutputDevice == paNoDevice && deviceInfo->maxOutputChannels > 0 )
+ commonApi->info.defaultOutputDevice = i;
+ }
+
+ /* Make an array of PaDeviceInfo pointers out of the linked list */
+
+ PA_DEBUG(("PaOSS %s: Total number of devices found: %d\n", __FUNCTION__, numDevices));
+
+ commonApi->deviceInfos = (PaDeviceInfo**)PaUtil_GroupAllocateMemory(
+ ossApi->allocations, sizeof(PaDeviceInfo*) * numDevices );
+ memcpy( commonApi->deviceInfos, deviceInfos, numDevices * sizeof (PaDeviceInfo *) );
+
+ commonApi->info.deviceCount = numDevices;
+
+error:
+ free( deviceInfos );
+
+ return result;
+}
+
+static void Terminate( struct PaUtilHostApiRepresentation *hostApi )
+{
+ PaOSSHostApiRepresentation *ossHostApi = (PaOSSHostApiRepresentation*)hostApi;
+
+ if( ossHostApi->allocations )
+ {
+ PaUtil_FreeAllAllocations( ossHostApi->allocations );
+ PaUtil_DestroyAllocationGroup( ossHostApi->allocations );
+ }
+
+ PaUtil_FreeMemory( ossHostApi );
+}
+
+static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
+ const PaStreamParameters *inputParameters,
+ const PaStreamParameters *outputParameters,
+ double sampleRate )
+{
+ PaError result = paNoError;
+ PaDeviceIndex device;
+ PaDeviceInfo *deviceInfo;
+ char *deviceName;
+ int inputChannelCount, outputChannelCount;
+ int tempDevHandle = -1;
+ int flags;
+ PaSampleFormat inputSampleFormat, outputSampleFormat;
+
+ if( inputParameters )
+ {
+ inputChannelCount = inputParameters->channelCount;
+ inputSampleFormat = inputParameters->sampleFormat;
+
+ /* unless alternate device specification is supported, reject the use of
+ paUseHostApiSpecificDeviceSpecification */
+
+ if( inputParameters->device == paUseHostApiSpecificDeviceSpecification )
+ return paInvalidDevice;
+
+ /* check that input device can support inputChannelCount */
+ if( inputChannelCount > hostApi->deviceInfos[ inputParameters->device ]->maxInputChannels )
+ return paInvalidChannelCount;
+
+ /* validate inputStreamInfo */
+ if( inputParameters->hostApiSpecificStreamInfo )
+ return paIncompatibleHostApiSpecificStreamInfo; /* this implementation doesn't use custom stream info */
+ }
+ else
+ {
+ inputChannelCount = 0;
+ }
+
+ if( outputParameters )
+ {
+ outputChannelCount = outputParameters->channelCount;
+ outputSampleFormat = outputParameters->sampleFormat;
+
+ /* unless alternate device specification is supported, reject the use of
+ paUseHostApiSpecificDeviceSpecification */
+
+ if( outputParameters->device == paUseHostApiSpecificDeviceSpecification )
+ return paInvalidDevice;
+
+ /* check that output device can support inputChannelCount */
+ if( outputChannelCount > hostApi->deviceInfos[ outputParameters->device ]->maxOutputChannels )
+ return paInvalidChannelCount;
+
+ /* validate outputStreamInfo */
+ if( outputParameters->hostApiSpecificStreamInfo )
+ return paIncompatibleHostApiSpecificStreamInfo; /* this implementation doesn't use custom stream info */
+ }
+ else
+ {
+ outputChannelCount = 0;
+ }
+
+ if (inputChannelCount == 0 && outputChannelCount == 0)
+ return paInvalidChannelCount;
+
+ /* if full duplex, make sure that they're the same device */
+
+ if (inputChannelCount > 0 && outputChannelCount > 0 &&
+ inputParameters->device != outputParameters->device)
+ return paInvalidDevice;
+
+ /* if full duplex, also make sure that they're the same number of channels */
+
+ if (inputChannelCount > 0 && outputChannelCount > 0 &&
+ inputChannelCount != outputChannelCount)
+ return paInvalidChannelCount;
+
+ /* open the device so we can do more tests */
+
+ if( inputChannelCount > 0 )
+ {
+ result = PaUtil_DeviceIndexToHostApiDeviceIndex(&device, inputParameters->device, hostApi);
+ if (result != paNoError)
+ return result;
+ }
+ else
+ {
+ result = PaUtil_DeviceIndexToHostApiDeviceIndex(&device, outputParameters->device, hostApi);
+ if (result != paNoError)
+ return result;
+ }
+
+ deviceInfo = hostApi->deviceInfos[device];
+ deviceName = (char *)deviceInfo->name;
+
+ flags = O_NONBLOCK;
+ if (inputChannelCount > 0 && outputChannelCount > 0)
+ flags |= O_RDWR;
+ else if (inputChannelCount > 0)
+ flags |= O_RDONLY;
+ else
+ flags |= O_WRONLY;
+
+ ENSURE_( tempDevHandle = open( deviceInfo->name, flags ), paDeviceUnavailable );
+
+ /* PaOssStream_Configure will do the rest of the checking for us */
+ /* PA_ENSURE( PaOssStream_Configure( tempDevHandle, deviceName, outputChannelCount, &sampleRate ) ); */
+
+ /* everything succeeded! */
+
+ error:
+ if( tempDevHandle >= 0 )
+ close( tempDevHandle );
+
+ return result;
+}
+
+/** Validate stream parameters.
+ *
+ * Aspect StreamChannels: We verify that the number of channels is within the allowed range for the device
+ */
+static PaError ValidateParameters( const PaStreamParameters *parameters, const PaDeviceInfo *deviceInfo, StreamMode mode )
+{
+ int maxChans;
+
+ assert( parameters );
+
+ if( parameters->device == paUseHostApiSpecificDeviceSpecification )
+ {
+ return paInvalidDevice;
+ }
+
+ maxChans = (mode == StreamMode_In ? deviceInfo->maxInputChannels :
+ deviceInfo->maxOutputChannels);
+ if( parameters->channelCount > maxChans )
+ {
+ return paInvalidChannelCount;
+ }
+
+ return paNoError;
+}
+
+static PaError PaOssStreamComponent_Initialize( PaOssStreamComponent *component, const PaStreamParameters *parameters,
+ int callbackMode, int fd, const char *deviceName )
+{
+ PaError result = paNoError;
+ assert( component );
+
+ memset( component, 0, sizeof (PaOssStreamComponent) );
+
+ component->fd = fd;
+ component->devName = deviceName;
+ component->userChannelCount = parameters->channelCount;
+ component->userFormat = parameters->sampleFormat;
+ component->latency = parameters->suggestedLatency;
+ component->userInterleaved = !(parameters->sampleFormat & paNonInterleaved);
+
+ if( !callbackMode && !component->userInterleaved )
+ {
+ /* Pre-allocate non-interleaved user provided buffers */
+ PA_UNLESS( component->userBuffers = PaUtil_AllocateMemory( sizeof (void *) * component->userChannelCount ),
+ paInsufficientMemory );
+ }
+
+error:
+ return result;
+}
+
+static void PaOssStreamComponent_Terminate( PaOssStreamComponent *component )
+{
+ assert( component );
+
+ if( component->fd >= 0 )
+ close( component->fd );
+ if( component->buffer )
+ PaUtil_FreeMemory( component->buffer );
+
+ if( component->userBuffers )
+ PaUtil_FreeMemory( component->userBuffers );
+
+ PaUtil_FreeMemory( component );
+}
+
+static PaError ModifyBlocking( int fd, int blocking )
+{
+ PaError result = paNoError;
+ int fflags;
+
+ ENSURE_( fflags = fcntl( fd, F_GETFL ), paUnanticipatedHostError );
+
+ if( blocking )
+ fflags &= ~O_NONBLOCK;
+ else
+ fflags |= O_NONBLOCK;
+
+ ENSURE_( fcntl( fd, F_SETFL, fflags ), paUnanticipatedHostError );
+
+error:
+ return result;
+}
+
+static PaError OpenDevices( const char *idevName, const char *odevName, int *idev, int *odev )
+{
+ PaError result = paNoError;
+ int flags = O_NONBLOCK, duplex = 0;
+ int enableBits = 0;
+ *idev = *odev = -1;
+
+ if( idevName && odevName )
+ {
+ duplex = 1;
+ flags |= O_RDWR;
+ }
+ else if( idevName )
+ flags |= O_RDONLY;
+ else
+ flags |= O_WRONLY;
+
+ /* open first in nonblocking mode, in case it's busy...
+ * A: then unset the non-blocking attribute */
+ assert( flags & O_NONBLOCK );
+ if( idevName )
+ {
+ ENSURE_( *idev = open( idevName, flags ), paDeviceUnavailable );
+ PA_ENSURE( ModifyBlocking( *idev, 1 ) ); /* Blocking */
+
+ /* Initially disable */
+ enableBits = ~PCM_ENABLE_INPUT;
+ ENSURE_( ioctl( *idev, SNDCTL_DSP_SETTRIGGER, &enableBits ), paUnanticipatedHostError );
+ }
+ if( odevName )
+ {
+ if( !idevName )
+ {
+ ENSURE_( *odev = open( odevName, flags ), paDeviceUnavailable );
+ PA_ENSURE( ModifyBlocking( *odev, 1 ) ); /* Blocking */
+
+ /* Initially disable */
+ enableBits = ~PCM_ENABLE_OUTPUT;
+ ENSURE_( ioctl( *odev, SNDCTL_DSP_SETTRIGGER, &enableBits ), paUnanticipatedHostError );
+ }
+ else
+ {
+ ENSURE_( *odev = dup( *idev ), paUnanticipatedHostError );
+ }
+ }
+
+error:
+ return result;
+}
+
+static PaError PaOssStream_Initialize( PaOssStream *stream, const PaStreamParameters *inputParameters, const PaStreamParameters *outputParameters,
+ PaStreamCallback callback, void *userData, PaStreamFlags streamFlags,
+ PaOSSHostApiRepresentation *ossApi )
+{
+ PaError result = paNoError;
+ int idev, odev;
+ PaUtilHostApiRepresentation *hostApi = &ossApi->inheritedHostApiRep;
+ const char *idevName = NULL, *odevName = NULL;
+
+ assert( stream );
+
+ memset( stream, 0, sizeof (PaOssStream) );
+ stream->isStopped = 1;
+
+ PA_ENSURE( PaUtil_InitializeThreading( &stream->threading ) );
+
+ if( inputParameters && outputParameters )
+ {
+ if( inputParameters->device == outputParameters->device )
+ stream->sharedDevice = 1;
+ }
+
+ if( inputParameters )
+ idevName = hostApi->deviceInfos[inputParameters->device]->name;
+ if( outputParameters )
+ odevName = hostApi->deviceInfos[outputParameters->device]->name;
+ PA_ENSURE( OpenDevices( idevName, odevName, &idev, &odev ) );
+ if( inputParameters )
+ {
+ PA_UNLESS( stream->capture = PaUtil_AllocateMemory( sizeof (PaOssStreamComponent) ), paInsufficientMemory );
+ PA_ENSURE( PaOssStreamComponent_Initialize( stream->capture, inputParameters, callback != NULL, idev, idevName ) );
+ }
+ if( outputParameters )
+ {
+ PA_UNLESS( stream->playback = PaUtil_AllocateMemory( sizeof (PaOssStreamComponent) ), paInsufficientMemory );
+ PA_ENSURE( PaOssStreamComponent_Initialize( stream->playback, outputParameters, callback != NULL, odev, odevName ) );
+ }
+
+ if( callback != NULL )
+ {
+ PaUtil_InitializeStreamRepresentation( &stream->streamRepresentation,
+ &ossApi->callbackStreamInterface, callback, userData );
+ stream->callbackMode = 1;
+ }
+ else
+ {
+ PaUtil_InitializeStreamRepresentation( &stream->streamRepresentation,
+ &ossApi->blockingStreamInterface, callback, userData );
+ }
+
+ ENSURE_( sem_init( &stream->semaphore, 0, 0 ), paInternalError );
+
+error:
+ return result;
+}
+
+static void PaOssStream_Terminate( PaOssStream *stream )
+{
+ assert( stream );
+
+ PaUtil_TerminateStreamRepresentation( &stream->streamRepresentation );
+ PaUtil_TerminateThreading( &stream->threading );
+
+ if( stream->capture )
+ PaOssStreamComponent_Terminate( stream->capture );
+ if( stream->playback )
+ PaOssStreamComponent_Terminate( stream->playback );
+
+ sem_destroy( &stream->semaphore );
+
+ PaUtil_FreeMemory( stream );
+}
+
+/** Translate from PA format to OSS native.
+ *
+ */
+static PaError Pa2OssFormat( PaSampleFormat paFormat, int *ossFormat )
+{
+ switch( paFormat )
+ {
+ case paUInt8:
+ *ossFormat = AFMT_U8;
+ break;
+ case paInt8:
+ *ossFormat = AFMT_S8;
+ break;
+ case paInt16:
+ *ossFormat = AFMT_S16_NE;
+ break;
+ default:
+ return paInternalError; /* This shouldn't happen */
+ }
+
+ return paNoError;
+}
+
+/** Return the PA-compatible formats that this device can support.
+ *
+ */
+static PaError GetAvailableFormats( PaOssStreamComponent *component, PaSampleFormat *availableFormats )
+{
+ PaError result = paNoError;
+ int mask = 0;
+ PaSampleFormat frmts = 0;
+
+ ENSURE_( ioctl( component->fd, SNDCTL_DSP_GETFMTS, &mask ), paUnanticipatedHostError );
+ if( mask & AFMT_U8 )
+ frmts |= paUInt8;
+ if( mask & AFMT_S8 )
+ frmts |= paInt8;
+ if( mask & AFMT_S16_NE )
+ frmts |= paInt16;
+ else
+ result = paSampleFormatNotSupported;
+
+ *availableFormats = frmts;
+
+error:
+ return result;
+}
+
+static unsigned int PaOssStreamComponent_FrameSize( PaOssStreamComponent *component )
+{
+ return Pa_GetSampleSize( component->hostFormat ) * component->hostChannelCount;
+}
+
+/** Buffer size in bytes.
+ *
+ */
+static unsigned long PaOssStreamComponent_BufferSize( PaOssStreamComponent *component )
+{
+ return PaOssStreamComponent_FrameSize( component ) * component->hostFrames * component->numBufs;
+}
+
+static int CalcHigherLogTwo( int n )
+{
+ int log2 = 0;
+ while( (1<<log2) < n ) log2++;
+ return log2;
+}
+
+static PaError PaOssStreamComponent_Configure( PaOssStreamComponent *component, double sampleRate, unsigned long framesPerBuffer,
+ StreamMode streamMode, PaOssStreamComponent *master )
+{
+ PaError result = paNoError;
+ int temp, nativeFormat;
+ int sr = (int)sampleRate;
+ PaSampleFormat availableFormats, hostFormat;
+ int chans = component->userChannelCount;
+ int frgmt;
+ int numBufs;
+ int bytesPerBuf;
+ double bufSz;
+ unsigned long fragSz;
+ audio_buf_info bufInfo;
+
+ /* We may have a situation where only one component (the master) is configured, if both point to the same device.
+ * In that case, the second component will copy settings from the other */
+ if( !master )
+ {
+ /* Aspect BufferSettings: If framesPerBuffer is unspecified we have to infer a suitable fragment size.
+ * The hardware need not respect the requested fragment size, so we may have to adapt.
+ */
+ if( framesPerBuffer == paFramesPerBufferUnspecified )
+ {
+ bufSz = component->latency * sampleRate;
+ fragSz = bufSz / 4;
+ }
+ else
+ {
+ fragSz = framesPerBuffer;
+ bufSz = component->latency * sampleRate + fragSz; /* Latency + 1 buffer */
+ }
+
+ PA_ENSURE( GetAvailableFormats( component, &availableFormats ) );
+ hostFormat = PaUtil_SelectClosestAvailableFormat( availableFormats, component->userFormat );
+
+ /* OSS demands at least 2 buffers, and 16 bytes per buffer */
+ numBufs = PA_MAX( bufSz / fragSz, 2 );
+ bytesPerBuf = PA_MAX( fragSz * Pa_GetSampleSize( hostFormat ) * chans, 16 );
+
+ /* The fragment parameters are encoded like this:
+ * Most significant byte: number of fragments
+ * Least significant byte: exponent of fragment size (i.e., for 256, 8)
+ */
+ frgmt = (numBufs << 16) + (CalcHigherLogTwo( bytesPerBuf ) & 0xffff);
+ ENSURE_( ioctl( component->fd, SNDCTL_DSP_SETFRAGMENT, &frgmt ), paUnanticipatedHostError );
+
+ /* A: according to the OSS programmer's guide parameters should be set in this order:
+ * format, channels, rate */
+
+ /* This format should be deemed good before we get this far */
+ PA_ENSURE( Pa2OssFormat( hostFormat, &temp ) );
+ nativeFormat = temp;
+ ENSURE_( ioctl( component->fd, SNDCTL_DSP_SETFMT, &temp ), paUnanticipatedHostError );
+ PA_UNLESS( temp == nativeFormat, paInternalError );
+
+ /* try to set the number of channels */
+ ENSURE_( ioctl( component->fd, SNDCTL_DSP_CHANNELS, &chans ), paSampleFormatNotSupported ); /* XXX: Should be paInvalidChannelCount? */
+ /* It's possible that the minimum number of host channels is greater than what the user requested */
+ PA_UNLESS( chans >= component->userChannelCount, paInvalidChannelCount );
+
+ /* try to set the sample rate */
+ ENSURE_( ioctl( component->fd, SNDCTL_DSP_SPEED, &sr ), paInvalidSampleRate );
+
+ /* reject if there's no sample rate within 1% of the one requested */
+ if( (fabs( sampleRate - sr ) / sampleRate) > 0.01 )
+ {
+ PA_DEBUG(("%s: Wanted %f, closest sample rate was %d\n", __FUNCTION__, sampleRate, sr ));
+ PA_ENSURE( paInvalidSampleRate );
+ }
+
+ ENSURE_( ioctl( component->fd, streamMode == StreamMode_In ? SNDCTL_DSP_GETISPACE : SNDCTL_DSP_GETOSPACE, &bufInfo ),
+ paUnanticipatedHostError );
+ component->numBufs = bufInfo.fragstotal;
+
+ /* This needs to be the last ioctl call before the first read/write, according to the OSS programmer's guide */
+ ENSURE_( ioctl( component->fd, SNDCTL_DSP_GETBLKSIZE, &bytesPerBuf ), paUnanticipatedHostError );
+
+ component->hostFrames = bytesPerBuf / Pa_GetSampleSize( hostFormat ) / chans;
+ component->hostChannelCount = chans;
+ component->hostFormat = hostFormat;
+ }
+ else
+ {
+ component->hostFormat = master->hostFormat;
+ component->hostFrames = master->hostFrames;
+ component->hostChannelCount = master->hostChannelCount;
+ component->numBufs = master->numBufs;
+ }
+
+ PA_UNLESS( component->buffer = PaUtil_AllocateMemory( PaOssStreamComponent_BufferSize( component ) ),
+ paInsufficientMemory );
+
+error:
+ return result;
+}
+
+static PaError PaOssStreamComponent_Read( PaOssStreamComponent *component, unsigned long *frames )
+{
+ PaError result = paNoError;
+ size_t len = *frames * PaOssStreamComponent_FrameSize( component );
+ ssize_t bytesRead;
+
+ ENSURE_( bytesRead = read( component->fd, component->buffer, len ), paUnanticipatedHostError );
+ *frames = bytesRead / PaOssStreamComponent_FrameSize( component );
+
+error:
+ return result;
+}
+
+static PaError PaOssStreamComponent_Write( PaOssStreamComponent *component, unsigned long *frames )
+{
+ PaError result = paNoError;
+ size_t len = *frames * PaOssStreamComponent_FrameSize( component );
+ ssize_t bytesWritten;
+
+ ENSURE_( bytesWritten = write( component->fd, component->buffer, len ), paUnanticipatedHostError );
+ *frames = bytesWritten / PaOssStreamComponent_FrameSize( component );
+
+error:
+ return result;
+}
+
+/** Configure the stream according to input/output parameters.
+ *
+ * Aspect StreamChannels: The minimum number of channels supported by the device may exceed that requested by
+ * the user, if so we'll record the actual number of host channels and adapt later.
+ */
+static PaError PaOssStream_Configure( PaOssStream *stream, double sampleRate, unsigned long framesPerBuffer,
+ double *inputLatency, double *outputLatency )
+{
+ PaError result = paNoError;
+ int duplex = stream->capture && stream->playback;
+ unsigned long framesPerHostBuffer = 0;
+
+ /* We should request full duplex first thing after opening the device */
+ if( duplex && stream->sharedDevice )
+ ENSURE_( ioctl( stream->capture->fd, SNDCTL_DSP_SETDUPLEX, 0 ), paUnanticipatedHostError );
+
+ if( stream->capture )
+ {
+ PaOssStreamComponent *component = stream->capture;
+ PaOssStreamComponent_Configure( component, sampleRate, framesPerBuffer, StreamMode_In, NULL );
+
+ assert( component->hostChannelCount > 0 );
+ assert( component->hostFrames > 0 );
+
+ *inputLatency = component->hostFrames * (component->numBufs - 1) / sampleRate;
+ }
+ if( stream->playback )
+ {
+ PaOssStreamComponent *component = stream->playback, *master = stream->sharedDevice ? stream->capture : NULL;
+ PA_ENSURE( PaOssStreamComponent_Configure( component, sampleRate, framesPerBuffer, StreamMode_Out,
+ master ) );
+
+ assert( component->hostChannelCount > 0 );
+ assert( component->hostFrames > 0 );
+
+ *outputLatency = component->hostFrames * (component->numBufs - 1) / sampleRate;
+ }
+
+ if( duplex )
+ framesPerHostBuffer = PA_MIN( stream->capture->hostFrames, stream->playback->hostFrames );
+ else if( stream->capture )
+ framesPerHostBuffer = stream->capture->hostFrames;
+ else if( stream->playback )
+ framesPerHostBuffer = stream->playback->hostFrames;
+
+ stream->framesPerHostBuffer = framesPerHostBuffer;
+ stream->pollTimeout = (int) ceil( 1e6 * framesPerHostBuffer / sampleRate ); /* Period in usecs, rounded up */
+
+ stream->streamRepresentation.streamInfo.sampleRate = sampleRate;
+
+error:
+ return result;
+}
+
+/* see pa_hostapi.h for a list of validity guarantees made about OpenStream parameters */
+
+/** Open a PA OSS stream.
+ *
+ * Aspect StreamChannels: The number of channels is specified per direction (in/out), and can differ between the
+ * two. However, OSS doesn't support separate configuration spaces for capture and playback so if both
+ * directions are the same device we will demand the same number of channels. The number of channels can range
+ * from 1 to the maximum supported by the device.
+ *
+ * Aspect BufferSettings: If framesPerBuffer != paFramesPerBufferUnspecified the number of frames per callback
+ * must reflect this, in addition the host latency per device should approximate the corresponding
+ * suggestedLatency. Based on these constraints we need to determine a number of frames per host buffer that
+ * both capture and playback can agree on (they can be different devices), the buffer processor can adapt
+ * between host and user buffer size, but the ratio should preferably be integral.
+ */
+static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
+ PaStream** s,
+ const PaStreamParameters *inputParameters,
+ const PaStreamParameters *outputParameters,
+ double sampleRate,
+ unsigned long framesPerBuffer,
+ PaStreamFlags streamFlags,
+ PaStreamCallback *streamCallback,
+ void *userData )
+{
+ PaError result = paNoError;
+ PaOSSHostApiRepresentation *ossHostApi = (PaOSSHostApiRepresentation*)hostApi;
+ PaOssStream *stream = NULL;
+ int inputChannelCount = 0, outputChannelCount = 0;
+ PaSampleFormat inputSampleFormat = 0, outputSampleFormat = 0, inputHostFormat = 0, outputHostFormat = 0;
+ const PaDeviceInfo *inputDeviceInfo = 0, *outputDeviceInfo = 0;
+ int bpInitialized = 0;
+ double inLatency, outLatency;
+
+ /* validate platform specific flags */
+ if( (streamFlags & paPlatformSpecificFlags) != 0 )
+ return paInvalidFlag; /* unexpected platform specific flag */
+
+ if( inputParameters )
+ {
+ /* unless alternate device specification is supported, reject the use of
+ paUseHostApiSpecificDeviceSpecification */
+ inputDeviceInfo = hostApi->deviceInfos[inputParameters->device];
+ PA_ENSURE( ValidateParameters( inputParameters, inputDeviceInfo, StreamMode_In ) );
+
+ inputChannelCount = inputParameters->channelCount;
+ inputSampleFormat = inputParameters->sampleFormat;
+ }
+ if( outputParameters )
+ {
+ outputDeviceInfo = hostApi->deviceInfos[outputParameters->device];
+ PA_ENSURE( ValidateParameters( outputParameters, outputDeviceInfo, StreamMode_Out ) );
+
+ outputChannelCount = outputParameters->channelCount;
+ outputSampleFormat = outputParameters->sampleFormat;
+ }
+
+ /* Aspect StreamChannels: We currently demand that number of input and output channels are the same, if the same
+ * device is opened for both directions
+ */
+ if( inputChannelCount > 0 && outputChannelCount > 0 )
+ {
+ if( inputParameters->device == outputParameters->device )
+ {
+ if( inputParameters->channelCount != outputParameters->channelCount )
+ return paInvalidChannelCount;
+ }
+ }
+
+ /* allocate and do basic initialization of the stream structure */
+ PA_UNLESS( stream = (PaOssStream*)PaUtil_AllocateMemory( sizeof(PaOssStream) ), paInsufficientMemory );
+ PaOssStream_Initialize( stream, inputParameters, outputParameters, streamCallback, userData, streamFlags, ossHostApi );
+
+ PA_ENSURE( PaOssStream_Configure( stream, sampleRate, framesPerBuffer, &inLatency, &outLatency ) );
+
+ PaUtil_InitializeCpuLoadMeasurer( &stream->cpuLoadMeasurer, sampleRate );
+
+ if( inputParameters )
+ {
+ inputHostFormat = stream->capture->hostFormat;
+ stream->streamRepresentation.streamInfo.inputLatency = inLatency +
+ PaUtil_GetBufferProcessorInputLatency( &stream->bufferProcessor ) / sampleRate;
+ }
+ if( outputParameters )
+ {
+ outputHostFormat = stream->playback->hostFormat;
+ stream->streamRepresentation.streamInfo.outputLatency = outLatency +
+ PaUtil_GetBufferProcessorOutputLatency( &stream->bufferProcessor ) / sampleRate;
+ }
+
+ /* Initialize buffer processor with fixed host buffer size.
+ * Aspect StreamSampleFormat: Here we commit the user and host sample formats, PA infrastructure will
+ * convert between the two.
+ */
+ PA_ENSURE( PaUtil_InitializeBufferProcessor( &stream->bufferProcessor,
+ inputChannelCount, inputSampleFormat, inputHostFormat, outputChannelCount, outputSampleFormat,
+ outputHostFormat, sampleRate, streamFlags, framesPerBuffer, stream->framesPerHostBuffer,
+ paUtilFixedHostBufferSize, streamCallback, userData ) );
+ bpInitialized = 1;
+
+ *s = (PaStream*)stream;
+
+ return result;
+
+error:
+ if( bpInitialized )
+ PaUtil_TerminateBufferProcessor( &stream->bufferProcessor );
+ if( stream )
+ PaOssStream_Terminate( stream );
+
+ return result;
+}
+
+/*! Poll on I/O filedescriptors.
+
+ Poll till we've determined there's data for read or write. In the full-duplex case,
+ we don't want to hang around forever waiting for either input or output frames, so
+ whenever we have a timed out filedescriptor we check if we're nearing under/overrun
+ for the other direction (critical limit set at one buffer). If so, we exit the waiting
+ state, and go on with what we got. We align the number of frames on a host buffer
+ boundary because it is possible that the buffer size differs for the two directions and
+ the host buffer size is a compromise between the two.
+ */
+static PaError PaOssStream_WaitForFrames( PaOssStream *stream, unsigned long *frames )
+{
+ PaError result = paNoError;
+ int pollPlayback = 0, pollCapture = 0;
+ int captureAvail = INT_MAX, playbackAvail = INT_MAX, commonAvail;
+ audio_buf_info bufInfo;
+ /* int ofs = 0, nfds = stream->nfds; */
+ fd_set readFds, writeFds;
+ int nfds = 0;
+ struct timeval selectTimeval = {0, 0};
+ unsigned long timeout = stream->pollTimeout; /* In usecs */
+ int captureFd = -1, playbackFd = -1;
+
+ assert( stream );
+ assert( frames );
+
+ if( stream->capture )
+ {
+ pollCapture = 1;
+ captureFd = stream->capture->fd;
+ /* stream->capture->pfd->events = POLLIN; */
+ }
+ if( stream->playback )
+ {
+ pollPlayback = 1;
+ playbackFd = stream->playback->fd;
+ /* stream->playback->pfd->events = POLLOUT; */
+ }
+
+ FD_ZERO( &readFds );
+ FD_ZERO( &writeFds );
+
+ while( pollPlayback || pollCapture )
+ {
+ pthread_testcancel();
+
+ /* select may modify the timeout parameter */
+ selectTimeval.tv_usec = timeout;
+ nfds = 0;
+
+ if( pollCapture )
+ {
+ FD_SET( captureFd, &readFds );
+ nfds = captureFd + 1;
+ }
+ if( pollPlayback )
+ {
+ FD_SET( playbackFd, &writeFds );
+ nfds = PA_MAX( nfds, playbackFd + 1 );
+ }
+ ENSURE_( select( nfds, &readFds, &writeFds, NULL, &selectTimeval ), paUnanticipatedHostError );
+ /*
+ if( poll( stream->pfds + ofs, nfds, stream->pollTimeout ) < 0 )
+ {
+
+ ENSURE_( -1, paUnanticipatedHostError );
+ }
+ */
+ pthread_testcancel();
+
+ if( pollCapture )
+ {
+ if( FD_ISSET( captureFd, &readFds ) )
+ {
+ FD_CLR( captureFd, &readFds );
+ pollCapture = 0;
+ }
+ /*
+ if( stream->capture->pfd->revents & POLLIN )
+ {
+ --nfds;
+ ++ofs;
+ pollCapture = 0;
+ }
+ */
+ else if( stream->playback ) /* Timed out, go on with playback? */
+ {
+ /*PA_DEBUG(( "%s: Trying to poll again for capture frames, pollTimeout: %d\n",
+ __FUNCTION__, stream->pollTimeout ));*/
+ }
+ }
+ if( pollPlayback )
+ {
+ if( FD_ISSET( playbackFd, &writeFds ) )
+ {
+ FD_CLR( playbackFd, &writeFds );
+ pollPlayback = 0;
+ }
+ /*
+ if( stream->playback->pfd->revents & POLLOUT )
+ {
+ --nfds;
+ pollPlayback = 0;
+ }
+ */
+ else if( stream->capture ) /* Timed out, go on with capture? */
+ {
+ /*PA_DEBUG(( "%s: Trying to poll again for playback frames, pollTimeout: %d\n\n",
+ __FUNCTION__, stream->pollTimeout ));*/
+ }
+ }
+ }
+
+ if( stream->capture )
+ {
+ ENSURE_( ioctl( captureFd, SNDCTL_DSP_GETISPACE, &bufInfo ), paUnanticipatedHostError );
+ captureAvail = bufInfo.fragments * stream->capture->hostFrames;
+ if( !captureAvail )
+ PA_DEBUG(( "%s: captureAvail: 0\n", __FUNCTION__ ));
+
+ captureAvail = captureAvail == 0 ? INT_MAX : captureAvail; /* Disregard if zero */
+ }
+ if( stream->playback )
+ {
+ ENSURE_( ioctl( playbackFd, SNDCTL_DSP_GETOSPACE, &bufInfo ), paUnanticipatedHostError );
+ playbackAvail = bufInfo.fragments * stream->playback->hostFrames;
+ if( !playbackAvail )
+ {
+ PA_DEBUG(( "%s: playbackAvail: 0\n", __FUNCTION__ ));
+ }
+
+ playbackAvail = playbackAvail == 0 ? INT_MAX : playbackAvail; /* Disregard if zero */
+ }
+
+ commonAvail = PA_MIN( captureAvail, playbackAvail );
+ if( commonAvail == INT_MAX )
+ commonAvail = 0;
+ commonAvail -= commonAvail % stream->framesPerHostBuffer;
+
+ assert( commonAvail != INT_MAX );
+ assert( commonAvail >= 0 );
+ *frames = commonAvail;
+
+error:
+ return result;
+}
+
+/** Prepare stream for capture/playback.
+ *
+ * In order to synchronize capture and playback properly we use the SETTRIGGER command.
+ */
+static PaError PaOssStream_Prepare( PaOssStream *stream )
+{
+ PaError result = paNoError;
+ int enableBits = 0;
+
+ if( stream->triggered )
+ return result;
+
+ if( stream->playback )
+ {
+ size_t bufSz = PaOssStreamComponent_BufferSize( stream->playback );
+ memset( stream->playback->buffer, 0, bufSz );
+
+ /* Looks like we have to turn off blocking before we try this, but if we don't fill the buffer
+ * OSS will complain. */
+ PA_ENSURE( ModifyBlocking( stream->playback->fd, 0 ) );
+ while (1)
+ {
+ if( write( stream->playback->fd, stream->playback->buffer, bufSz ) < 0 )
+ break;
+ }
+ PA_ENSURE( ModifyBlocking( stream->playback->fd, 1 ) );
+ }
+
+ if( stream->sharedDevice )
+ {
+ enableBits = PCM_ENABLE_INPUT | PCM_ENABLE_OUTPUT;
+ ENSURE_( ioctl( stream->capture->fd, SNDCTL_DSP_SETTRIGGER, &enableBits ), paUnanticipatedHostError );
+ }
+ else
+ {
+ if( stream->capture )
+ {
+ enableBits = PCM_ENABLE_INPUT;
+ ENSURE_( ioctl( stream->capture->fd, SNDCTL_DSP_SETTRIGGER, &enableBits ), paUnanticipatedHostError );
+ }
+ if( stream->playback )
+ {
+ enableBits = PCM_ENABLE_OUTPUT;
+ ENSURE_( ioctl( stream->playback->fd, SNDCTL_DSP_SETTRIGGER, &enableBits ), paUnanticipatedHostError );
+ }
+ }
+
+ /* Ok, we have triggered the stream */
+ stream->triggered = 1;
+
+error:
+ return result;
+}
+
+/** Stop audio processing
+ *
+ */
+static PaError PaOssStream_Stop( PaOssStream *stream, int abort )
+{
+ PaError result = paNoError;
+
+ /* Looks like the only safe way to stop audio without reopening the device is SNDCTL_DSP_POST.
+ * Also disable capture/playback till the stream is started again */
+ if( stream->capture )
+ {
+ ENSURE_( ioctl( stream->capture->fd, SNDCTL_DSP_POST, 0 ), paUnanticipatedHostError );
+ }
+ if( stream->playback && !stream->sharedDevice )
+ {
+ ENSURE_( ioctl( stream->playback->fd, SNDCTL_DSP_POST, 0 ), paUnanticipatedHostError );
+ }
+
+error:
+ return result;
+}
+
+/** Clean up after thread exit.
+ *
+ * Aspect StreamState: If the user has registered a streamFinishedCallback it will be called here
+ */
+static void OnExit( void *data )
+{
+ PaOssStream *stream = (PaOssStream *) data;
+ assert( data );
+
+ PaUtil_ResetCpuLoadMeasurer( &stream->cpuLoadMeasurer );
+
+ PaOssStream_Stop( stream, stream->callbackAbort );
+
+ PA_DEBUG(( "OnExit: Stoppage\n" ));
+
+ /* Eventually notify user all buffers have played */
+ if( stream->streamRepresentation.streamFinishedCallback )
+ stream->streamRepresentation.streamFinishedCallback( stream->streamRepresentation.userData );
+
+ stream->callbackAbort = 0; /* Clear state */
+ stream->isActive = 0;
+}
+
+static PaError SetUpBuffers( PaOssStream *stream, unsigned long framesAvail )
+{
+ PaError result = paNoError;
+
+ if( stream->capture )
+ {
+ PaUtil_SetInterleavedInputChannels( &stream->bufferProcessor, 0, stream->capture->buffer,
+ stream->capture->hostChannelCount );
+ PaUtil_SetInputFrameCount( &stream->bufferProcessor, framesAvail );
+ }
+ if( stream->playback )
+ {
+ PaUtil_SetInterleavedOutputChannels( &stream->bufferProcessor, 0, stream->playback->buffer,
+ stream->playback->hostChannelCount );
+ PaUtil_SetOutputFrameCount( &stream->bufferProcessor, framesAvail );
+ }
+
+ return result;
+}
+
+/** Thread procedure for callback processing.
+ *
+ * Aspect StreamState: StartStream will wait on this to initiate audio processing, useful in case the
+ * callback should be used for buffer priming. When the stream is cancelled a separate function will
+ * take care of the transition to the Callback Finished state (the stream isn't considered Stopped
+ * before StopStream() or AbortStream() are called).
+ */
+static void *PaOSS_AudioThreadProc( void *userData )
+{
+ PaError result = paNoError;
+ PaOssStream *stream = (PaOssStream*)userData;
+ unsigned long framesAvail, framesProcessed;
+ int callbackResult = paContinue;
+ int triggered = stream->triggered; /* See if SNDCTL_DSP_TRIGGER has been issued already */
+ int initiateProcessing = triggered; /* Already triggered? */
+ PaStreamCallbackFlags cbFlags = 0; /* We might want to keep state across iterations */
+
+ /*
+#if ( SOUND_VERSION > 0x030904 )
+ audio_errinfo errinfo;
+#endif
+*/
+
+ assert( stream );
+
+ pthread_cleanup_push( &OnExit, stream ); /* Execute OnExit when exiting */
+
+ /* The first time the stream is started we use SNDCTL_DSP_TRIGGER to accurately start capture and
+ * playback in sync, when the stream is restarted after being stopped we simply start by reading/
+ * writing.
+ */
+ PA_ENSURE( PaOssStream_Prepare( stream ) );
+
+ /* If we are to initiate processing implicitly by reading/writing data, we start off in blocking mode */
+ if( initiateProcessing )
+ {
+ /* Make sure devices are in blocking mode */
+ if( stream->capture )
+ ModifyBlocking( stream->capture->fd, 1 );
+ if( stream->playback )
+ ModifyBlocking( stream->playback->fd, 1 );
+ }
+
+ while( 1 )
+ {
+ PaStreamCallbackTimeInfo timeInfo = {0,0,0}; /* TODO: IMPLEMENT ME */
+
+ pthread_testcancel();
+
+ if( stream->callbackStop && callbackResult == paContinue )
+ {
+ PA_DEBUG(( "Setting callbackResult to paComplete\n" ));
+ callbackResult = paComplete;
+ }
+
+ /* Aspect StreamState: Because of the messy OSS scheme we can't explicitly trigger device start unless
+ * the stream has been recently started, we will have to go right ahead and read/write in blocking
+ * fashion to trigger operation. Therefore we begin with processing one host buffer before we switch
+ * to non-blocking mode.
+ */
+ if( !initiateProcessing )
+ {
+ PA_ENSURE( PaOssStream_WaitForFrames( stream, &framesAvail ) ); /* Wait on available frames */
+ assert( framesAvail % stream->framesPerHostBuffer == 0 );
+ }
+ else
+ {
+ framesAvail = stream->framesPerHostBuffer;
+ }
+
+ while( framesAvail > 0 )
+ {
+ unsigned long frames = framesAvail;
+
+ pthread_testcancel();
+
+ PaUtil_BeginCpuLoadMeasurement( &stream->cpuLoadMeasurer );
+
+ /* Read data */
+ if ( stream->capture )
+ {
+ PA_ENSURE( PaOssStreamComponent_Read( stream->capture, &frames ) );
+ assert( frames == framesAvail );
+ }
+
+#if ( SOUND_VERSION >= 0x030904 )
+ /*
+ Check with OSS to see if there have been any under/overruns
+ since last time we checked.
+ */
+ /*
+ if( ioctl( stream->deviceHandle, SNDCTL_DSP_GETERROR, &errinfo ) >= 0 )
+ {
+ if( errinfo.play_underruns )
+ cbFlags |= paOutputUnderflow ;
+ if( errinfo.record_underruns )
+ cbFlags |= paInputUnderflow ;
+ }
+ else
+ PA_DEBUG(( "SNDCTL_DSP_GETERROR command failed: %s\n", strerror( errno ) ));
+ */
+#endif
+
+ PaUtil_BeginBufferProcessing( &stream->bufferProcessor, &timeInfo,
+ cbFlags );
+ cbFlags = 0;
+ PA_ENSURE( SetUpBuffers( stream, framesAvail ) );
+
+ framesProcessed = PaUtil_EndBufferProcessing( &stream->bufferProcessor,
+ &callbackResult );
+ assert( framesProcessed == framesAvail );
+ PaUtil_EndCpuLoadMeasurement( &stream->cpuLoadMeasurer, framesProcessed );
+
+ if ( stream->playback )
+ {
+ frames = framesAvail;
+
+ PA_ENSURE( PaOssStreamComponent_Write( stream->playback, &frames ) );
+ assert( frames == framesAvail );
+
+ /* TODO: handle bytesWritten != bytesRequested (slippage?) */
+ }
+
+ framesAvail -= framesProcessed;
+ stream->framesProcessed += framesProcessed;
+
+ if( callbackResult != paContinue )
+ break;
+ }
+
+ if( initiateProcessing || !triggered )
+ {
+ /* Non-blocking */
+ if( stream->capture )
+ PA_ENSURE( ModifyBlocking( stream->capture->fd, 0 ) );
+ if( stream->playback && !stream->sharedDevice )
+ PA_ENSURE( ModifyBlocking( stream->playback->fd, 0 ) );
+
+ initiateProcessing = 0;
+ sem_post( &stream->semaphore );
+ }
+
+ if( callbackResult != paContinue )
+ {
+ stream->callbackAbort = callbackResult == paAbort;
+ if( stream->callbackAbort || PaUtil_IsBufferProcessorOutputEmpty( &stream->bufferProcessor ) )
+ break;
+ }
+ }
+
+ pthread_cleanup_pop( 1 );
+
+error:
+ pthread_exit( NULL );
+}
+
+/** Close the stream.
+ *
+ */
+static PaError CloseStream( PaStream* s )
+{
+ PaError result = paNoError;
+ PaOssStream *stream = (PaOssStream*)s;
+
+ assert( stream );
+
+ PaUtil_TerminateBufferProcessor( &stream->bufferProcessor );
+ PaOssStream_Terminate( stream );
+
+ return result;
+}
+
+/** Start the stream.
+ *
+ * Aspect StreamState: After returning, the stream shall be in the Active state, implying that an eventual
+ * callback will be repeatedly called in a separate thread. If a separate thread is started this function
+ * will block untill it has started processing audio, otherwise audio processing is started directly.
+ */
+static PaError StartStream( PaStream *s )
+{
+ PaError result = paNoError;
+ PaOssStream *stream = (PaOssStream*)s;
+
+ stream->isActive = 1;
+ stream->isStopped = 0;
+ stream->lastPosPtr = 0;
+ stream->lastStreamBytes = 0;
+ stream->framesProcessed = 0;
+
+ /* only use the thread for callback streams */
+ if( stream->bufferProcessor.streamCallback )
+ {
+ PA_ENSURE( PaUtil_StartThreading( &stream->threading, &PaOSS_AudioThreadProc, stream ) );
+ sem_wait( &stream->semaphore );
+ }
+ else
+ PA_ENSURE( PaOssStream_Prepare( stream ) );
+
+error:
+ return result;
+}
+
+static PaError RealStop( PaOssStream *stream, int abort )
+{
+ PaError result = paNoError;
+
+ if( stream->callbackMode )
+ {
+ if( abort )
+ stream->callbackAbort = 1;
+ else
+ stream->callbackStop = 1;
+
+ PA_ENSURE( PaUtil_CancelThreading( &stream->threading, !abort, NULL ) );
+
+ stream->callbackStop = stream->callbackAbort = 0;
+ }
+ else
+ PA_ENSURE( PaOssStream_Stop( stream, abort ) );
+
+ stream->isStopped = 1;
+
+error:
+ return result;
+}
+
+/** Stop the stream.
+ *
+ * Aspect StreamState: This will cause the stream to transition to the Stopped state, playing all enqueued
+ * buffers.
+ */
+static PaError StopStream( PaStream *s )
+{
+ return RealStop( (PaOssStream *)s, 0 );
+}
+
+/** Abort the stream.
+ *
+ * Aspect StreamState: This will cause the stream to transition to the Stopped state, discarding all enqueued
+ * buffers. Note that the buffers are not currently correctly discarded, this is difficult without closing
+ * the OSS device.
+ */
+static PaError AbortStream( PaStream *s )
+{
+ return RealStop( (PaOssStream *)s, 1 );
+}
+
+/** Is the stream in the Stopped state.
+ *
+ */
+static PaError IsStreamStopped( PaStream *s )
+{
+ PaOssStream *stream = (PaOssStream*)s;
+
+ return (stream->isStopped);
+}
+
+/** Is the stream in the Active state.
+ *
+ */
+static PaError IsStreamActive( PaStream *s )
+{
+ PaOssStream *stream = (PaOssStream*)s;
+
+ return (stream->isActive);
+}
+
+static PaTime GetStreamTime( PaStream *s )
+{
+ PaOssStream *stream = (PaOssStream*)s;
+ count_info info;
+ int delta;
+
+ if( stream->playback ) {
+ if( ioctl( stream->playback->fd, SNDCTL_DSP_GETOPTR, &info) == 0 ) {
+ delta = ( info.bytes - stream->lastPosPtr ) & 0x000FFFFF;
+ return ( stream->lastStreamBytes + delta) / PaOssStreamComponent_FrameSize( stream->playback ) / stream->sampleRate;
+ }
+ }
+ else {
+ if (ioctl( stream->capture->fd, SNDCTL_DSP_GETIPTR, &info) == 0) {
+ delta = (info.bytes - stream->lastPosPtr) & 0x000FFFFF;
+ return ( stream->lastStreamBytes + delta) / PaOssStreamComponent_FrameSize( stream->capture ) / stream->sampleRate;
+ }
+ }
+
+ /* the ioctl failed, but we can still give a coarse estimate */
+
+ return stream->framesProcessed / stream->sampleRate;
+}
+
+
+static double GetStreamCpuLoad( PaStream* s )
+{
+ PaOssStream *stream = (PaOssStream*)s;
+
+ return PaUtil_GetCpuLoad( &stream->cpuLoadMeasurer );
+}
+
+
+/*
+ As separate stream interfaces are used for blocking and callback
+ streams, the following functions can be guaranteed to only be called
+ for blocking streams.
+*/
+
+
+static PaError ReadStream( PaStream* s,
+ void *buffer,
+ unsigned long frames )
+{
+ PaOssStream *stream = (PaOssStream*)s;
+ int bytesRequested, bytesRead;
+ unsigned long framesRequested;
+ void *userBuffer;
+
+ /* If user input is non-interleaved, PaUtil_CopyInput will manipulate the channel pointers,
+ * so we copy the user provided pointers */
+ if( stream->bufferProcessor.userInputIsInterleaved )
+ userBuffer = buffer;
+ else /* Copy channels into local array */
+ {
+ userBuffer = stream->capture->userBuffers;
+ memcpy( (void *)userBuffer, buffer, sizeof (void *) * stream->capture->userChannelCount );
+ }
+
+ while( frames )
+ {
+ framesRequested = PA_MIN( frames, stream->capture->hostFrames );
+
+ bytesRequested = framesRequested * PaOssStreamComponent_FrameSize( stream->capture );
+ bytesRead = read( stream->capture->fd, stream->capture->buffer, bytesRequested );
+ if ( bytesRequested != bytesRead )
+ return paUnanticipatedHostError;
+
+ PaUtil_SetInputFrameCount( &stream->bufferProcessor, stream->capture->hostFrames );
+ PaUtil_SetInterleavedInputChannels( &stream->bufferProcessor, 0, stream->capture->buffer, stream->capture->hostChannelCount );
+ PaUtil_CopyInput( &stream->bufferProcessor, &userBuffer, framesRequested );
+ frames -= framesRequested;
+ }
+ return paNoError;
+}
+
+
+static PaError WriteStream( PaStream* s,
+ const void *buffer,
+ unsigned long frames )
+{
+ PaOssStream *stream = (PaOssStream*)s;
+ int bytesRequested, bytesWritten;
+ unsigned long framesConverted;
+ const void *userBuffer;
+
+ /* If user output is non-interleaved, PaUtil_CopyOutput will manipulate the channel pointers,
+ * so we copy the user provided pointers */
+ if( stream->bufferProcessor.userOutputIsInterleaved )
+ userBuffer = buffer;
+ else /* Copy channels into local array */
+ {
+ userBuffer = stream->playback->userBuffers;
+ memcpy( (void *)userBuffer, buffer, sizeof (void *) * stream->playback->userChannelCount );
+ }
+
+ while( frames )
+ {
+ PaUtil_SetOutputFrameCount( &stream->bufferProcessor, stream->playback->hostFrames );
+ PaUtil_SetInterleavedOutputChannels( &stream->bufferProcessor, 0, stream->playback->buffer, stream->playback->hostChannelCount );
+
+ framesConverted = PaUtil_CopyOutput( &stream->bufferProcessor, &userBuffer, frames );
+ frames -= framesConverted;
+
+ bytesRequested = framesConverted * PaOssStreamComponent_FrameSize( stream->playback );
+ bytesWritten = write( stream->playback->fd, stream->playback->buffer, bytesRequested );
+
+ if ( bytesRequested != bytesWritten )
+ return paUnanticipatedHostError;
+ }
+ return paNoError;
+}
+
+
+static signed long GetStreamReadAvailable( PaStream* s )
+{
+ PaOssStream *stream = (PaOssStream*)s;
+ audio_buf_info info;
+
+ if( ioctl( stream->capture->fd, SNDCTL_DSP_GETISPACE, &info ) < 0 )
+ return paUnanticipatedHostError;
+ return info.fragments * stream->capture->hostFrames;
+}
+
+
+/* TODO: Compute number of allocated bytes somewhere else, can we use ODELAY with capture */
+static signed long GetStreamWriteAvailable( PaStream* s )
+{
+ PaOssStream *stream = (PaOssStream*)s;
+ int delay = 0;
+
+ if( ioctl( stream->playback->fd, SNDCTL_DSP_GETODELAY, &delay ) < 0 )
+ return paUnanticipatedHostError;
+
+ return (PaOssStreamComponent_BufferSize( stream->playback ) - delay) / PaOssStreamComponent_FrameSize( stream->playback );
+}
+
diff --git a/pjmedia/src/pjmedia/portaudio/pa_unix_util.c b/pjmedia/src/pjmedia/portaudio/pa_unix_util.c
index f45848fb..55f83f71 100644
--- a/pjmedia/src/pjmedia/portaudio/pa_unix_util.c
+++ b/pjmedia/src/pjmedia/portaudio/pa_unix_util.c
@@ -1,175 +1,196 @@
-/*
- * $Id: pa_unix_util.c,v 1.1.2.7 2005/03/31 15:02:48 aknudsen Exp $
- * Portable Audio I/O Library
- * UNIX platform-specific support functions
- *
- * Based on the Open Source API proposed by Ross Bencina
- * Copyright (c) 1999-2000 Ross Bencina
- *
- * Permission is hereby granted, free of charge, to any person obtaining
- * a copy of this software and associated documentation files
- * (the "Software"), to deal in the Software without restriction,
- * including without limitation the rights to use, copy, modify, merge,
- * publish, distribute, sublicense, and/or sell copies of the Software,
- * and to permit persons to whom the Software is furnished to do so,
- * subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be
- * included in all copies or substantial portions of the Software.
- *
- * Any person wishing to distribute modifications to the Software is
- * requested to send the modifications to the original developer so that
- * they can be incorporated into the canonical version.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
- * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
- * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
- * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
- * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
- */
-
-
-#include <pthread.h>
-#include <unistd.h>
-#include <stdlib.h>
-#include <sys/time.h>
-#include <assert.h>
-#include <string.h> /* For memset */
-
-#include "pa_util.h"
-#include "pa_unix_util.h"
-
-/*
- Track memory allocations to avoid leaks.
- */
-
-#if PA_TRACK_MEMORY
-static int numAllocations_ = 0;
-#endif
-
-
-void *PaUtil_AllocateMemory( long size )
-{
- void *result = malloc( size );
-
-#if PA_TRACK_MEMORY
- if( result != NULL ) numAllocations_ += 1;
-#endif
- return result;
-}
-
-
-void PaUtil_FreeMemory( void *block )
-{
- if( block != NULL )
- {
- free( block );
-#if PA_TRACK_MEMORY
- numAllocations_ -= 1;
-#endif
-
- }
-}
-
-
-int PaUtil_CountCurrentlyAllocatedBlocks( void )
-{
-#if PA_TRACK_MEMORY
- return numAllocations_;
-#else
- return 0;
-#endif
-}
-
-
-void Pa_Sleep( long msec )
-{
- while( msec > 999 ) /* For OpenBSD and IRIX, argument */
- { /* to usleep must be < 1000000. */
- usleep( 999000 );
- msec -= 999;
- }
- usleep( msec * 1000 );
-}
-
-/* *** NOT USED YET: ***
-static int usePerformanceCounter_;
-static double microsecondsPerTick_;
-*/
-
-void PaUtil_InitializeClock( void )
-{
- /* TODO */
-}
-
-
-PaTime PaUtil_GetTime( void )
-{
- struct timeval tv;
- gettimeofday( &tv, NULL );
- return (PaTime) tv.tv_usec / 1000000. + tv.tv_sec;
-}
-
-PaError PaUtil_InitializeThreading( PaUtilThreading *threading )
-{
- (void) paUtilErr_;
- return paNoError;
-}
-
-void PaUtil_TerminateThreading( PaUtilThreading *threading )
-{
-}
-
-PaError PaUtil_StartThreading( PaUtilThreading *threading, void *(*threadRoutine)(void *), void *data )
-{
- pthread_create( &threading->callbackThread, NULL, threadRoutine, data );
- return paNoError;
-}
-
-PaError PaUtil_CancelThreading( PaUtilThreading *threading, int wait, PaError *exitResult )
-{
- PaError result = paNoError;
- void *pret;
-
- if( exitResult )
- *exitResult = paNoError;
-
- /* Only kill the thread if it isn't in the process of stopping (flushing adaptation buffers) */
- if( !wait )
- pthread_cancel( threading->callbackThread ); /* XXX: Safe to call this if the thread has exited on its own? */
- pthread_join( threading->callbackThread, &pret );
-
-#ifdef PTHREAD_CANCELED
- if( pret && PTHREAD_CANCELED != pret )
-#else
- /* !wait means the thread may have been canceled */
- if( pret && wait )
-#endif
- {
- if( exitResult )
- *exitResult = *(PaError *) pret;
- free( pret );
- }
-
- return result;
-}
-
-/*
-static void *CanaryFunc( void *userData )
-{
- const unsigned intervalMsec = 1000;
- PaUtilThreading *th = (PaUtilThreading *) userData;
-
- while( 1 )
- {
- th->canaryTime = PaUtil_GetTime();
-
- pthread_testcancel();
- Pa_Sleep( intervalMsec );
- }
-
- pthread_exit( NULL );
-}
-*/
+/*
+ * $Id: pa_unix_util.c,v 1.1.2.7 2005/03/31 15:02:48 aknudsen Exp $
+ * Portable Audio I/O Library
+ * UNIX platform-specific support functions
+ *
+ * Based on the Open Source API proposed by Ross Bencina
+ * Copyright (c) 1999-2000 Ross Bencina
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * Any person wishing to distribute modifications to the Software is
+ * requested to send the modifications to the original developer so that
+ * they can be incorporated into the canonical version.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+ * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+/*
+ * PJMEDIA - Multimedia over IP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+
+#include <pthread.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <sys/time.h>
+#include <assert.h>
+#include <string.h> /* For memset */
+
+#include "pa_util.h"
+#include "pa_unix_util.h"
+
+/*
+ Track memory allocations to avoid leaks.
+ */
+
+#if PA_TRACK_MEMORY
+static int numAllocations_ = 0;
+#endif
+
+
+void *PaUtil_AllocateMemory( long size )
+{
+ void *result = malloc( size );
+
+#if PA_TRACK_MEMORY
+ if( result != NULL ) numAllocations_ += 1;
+#endif
+ return result;
+}
+
+
+void PaUtil_FreeMemory( void *block )
+{
+ if( block != NULL )
+ {
+ free( block );
+#if PA_TRACK_MEMORY
+ numAllocations_ -= 1;
+#endif
+
+ }
+}
+
+
+int PaUtil_CountCurrentlyAllocatedBlocks( void )
+{
+#if PA_TRACK_MEMORY
+ return numAllocations_;
+#else
+ return 0;
+#endif
+}
+
+
+void Pa_Sleep( long msec )
+{
+ while( msec > 999 ) /* For OpenBSD and IRIX, argument */
+ { /* to usleep must be < 1000000. */
+ usleep( 999000 );
+ msec -= 999;
+ }
+ usleep( msec * 1000 );
+}
+
+/* *** NOT USED YET: ***
+static int usePerformanceCounter_;
+static double microsecondsPerTick_;
+*/
+
+void PaUtil_InitializeClock( void )
+{
+ /* TODO */
+}
+
+
+PaTime PaUtil_GetTime( void )
+{
+ struct timeval tv;
+ gettimeofday( &tv, NULL );
+ return (PaTime) tv.tv_usec / 1000000. + tv.tv_sec;
+}
+
+PaError PaUtil_InitializeThreading( PaUtilThreading *threading )
+{
+ (void) paUtilErr_;
+ return paNoError;
+}
+
+void PaUtil_TerminateThreading( PaUtilThreading *threading )
+{
+}
+
+PaError PaUtil_StartThreading( PaUtilThreading *threading, void *(*threadRoutine)(void *), void *data )
+{
+ pthread_create( &threading->callbackThread, NULL, threadRoutine, data );
+ return paNoError;
+}
+
+PaError PaUtil_CancelThreading( PaUtilThreading *threading, int wait, PaError *exitResult )
+{
+ PaError result = paNoError;
+ void *pret;
+
+ if( exitResult )
+ *exitResult = paNoError;
+
+ /* Only kill the thread if it isn't in the process of stopping (flushing adaptation buffers) */
+ if( !wait )
+ pthread_cancel( threading->callbackThread ); /* XXX: Safe to call this if the thread has exited on its own? */
+ pthread_join( threading->callbackThread, &pret );
+
+#ifdef PTHREAD_CANCELED
+ if( pret && PTHREAD_CANCELED != pret )
+#else
+ /* !wait means the thread may have been canceled */
+ if( pret && wait )
+#endif
+ {
+ if( exitResult )
+ *exitResult = *(PaError *) pret;
+ free( pret );
+ }
+
+ return result;
+}
+
+/*
+static void *CanaryFunc( void *userData )
+{
+ const unsigned intervalMsec = 1000;
+ PaUtilThreading *th = (PaUtilThreading *) userData;
+
+ while( 1 )
+ {
+ th->canaryTime = PaUtil_GetTime();
+
+ pthread_testcancel();
+ Pa_Sleep( intervalMsec );
+ }
+
+ pthread_exit( NULL );
+}
+*/
diff --git a/pjmedia/src/pjmedia/portaudio/pa_unix_util.h b/pjmedia/src/pjmedia/portaudio/pa_unix_util.h
index 01dda01e..c51f3bb5 100644
--- a/pjmedia/src/pjmedia/portaudio/pa_unix_util.h
+++ b/pjmedia/src/pjmedia/portaudio/pa_unix_util.h
@@ -1,73 +1,94 @@
-#ifndef PA_UNIX_UTIL_H
-#define PA_UNIX_UTIL_H
-
-#include "pa_cpuload.h"
-
-#ifdef __cplusplus
-extern "C"
-{
-#endif /* __cplusplus */
-
-#define PA_MIN(x,y) ( (x) < (y) ? (x) : (y) )
-#define PA_MAX(x,y) ( (x) > (y) ? (x) : (y) )
-
-/* Utilize GCC branch prediction for error tests */
-#if defined __GNUC__ && __GNUC__ >= 3
-#define UNLIKELY(expr) __builtin_expect( (expr), 0 )
-#else
-#define UNLIKELY(expr) (expr)
-#endif
-
-#define STRINGIZE_HELPER(expr) #expr
-#define STRINGIZE(expr) STRINGIZE_HELPER(expr)
-
-#define PA_UNLESS(expr, code) \
- do { \
- if( UNLIKELY( (expr) == 0 ) ) \
- { \
- PaUtil_DebugPrint(( "Expression '" #expr "' failed in '" __FILE__ "', line: " STRINGIZE( __LINE__ ) "\n" )); \
- result = (code); \
- goto error; \
- } \
- } while (0);
-
-static PaError paUtilErr_; /* Used with PA_ENSURE */
-
-/* Check PaError */
-#define PA_ENSURE(expr) \
- do { \
- if( UNLIKELY( (paUtilErr_ = (expr)) < paNoError ) ) \
- { \
- PaUtil_DebugPrint(( "Expression '" #expr "' failed in '" __FILE__ "', line: " STRINGIZE( __LINE__ ) "\n" )); \
- result = paUtilErr_; \
- goto error; \
- } \
- } while (0);
-
-typedef struct {
- pthread_t callbackThread;
-} PaUtilThreading;
-
-PaError PaUtil_InitializeThreading( PaUtilThreading *threading );
-void PaUtil_TerminateThreading( PaUtilThreading *threading );
-PaError PaUtil_StartThreading( PaUtilThreading *threading, void *(*threadRoutine)(void *), void *data );
-PaError PaUtil_CancelThreading( PaUtilThreading *threading, int wait, PaError *exitResult );
-
-/* State accessed by utility functions */
-
-/*
-void PaUnix_SetRealtimeScheduling( int rt );
-
-void PaUtil_InitializeThreading( PaUtilThreading *th, PaUtilCpuLoadMeasurer *clm );
-
-PaError PaUtil_CreateCallbackThread( PaUtilThreading *th, void *(*CallbackThreadFunc)( void * ), PaStream *s );
-
-PaError PaUtil_KillCallbackThread( PaUtilThreading *th, PaError *exitResult );
-
-void PaUtil_CallbackUpdate( PaUtilThreading *th );
-*/
-
-#ifdef __cplusplus
-}
-#endif /* __cplusplus */
-#endif
+/*
+ * PJMEDIA - Multimedia over IP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef PA_UNIX_UTIL_H
+#define PA_UNIX_UTIL_H
+
+#include "pa_cpuload.h"
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif /* __cplusplus */
+
+#define PA_MIN(x,y) ( (x) < (y) ? (x) : (y) )
+#define PA_MAX(x,y) ( (x) > (y) ? (x) : (y) )
+
+/* Utilize GCC branch prediction for error tests */
+#if defined __GNUC__ && __GNUC__ >= 3
+#define UNLIKELY(expr) __builtin_expect( (expr), 0 )
+#else
+#define UNLIKELY(expr) (expr)
+#endif
+
+#define STRINGIZE_HELPER(expr) #expr
+#define STRINGIZE(expr) STRINGIZE_HELPER(expr)
+
+#define PA_UNLESS(expr, code) \
+ do { \
+ if( UNLIKELY( (expr) == 0 ) ) \
+ { \
+ PaUtil_DebugPrint(( "Expression '" #expr "' failed in '" __FILE__ "', line: " STRINGIZE( __LINE__ ) "\n" )); \
+ result = (code); \
+ goto error; \
+ } \
+ } while (0);
+
+static PaError paUtilErr_; /* Used with PA_ENSURE */
+
+/* Check PaError */
+#define PA_ENSURE(expr) \
+ do { \
+ if( UNLIKELY( (paUtilErr_ = (expr)) < paNoError ) ) \
+ { \
+ PaUtil_DebugPrint(( "Expression '" #expr "' failed in '" __FILE__ "', line: " STRINGIZE( __LINE__ ) "\n" )); \
+ result = paUtilErr_; \
+ goto error; \
+ } \
+ } while (0);
+
+typedef struct {
+ pthread_t callbackThread;
+} PaUtilThreading;
+
+PaError PaUtil_InitializeThreading( PaUtilThreading *threading );
+void PaUtil_TerminateThreading( PaUtilThreading *threading );
+PaError PaUtil_StartThreading( PaUtilThreading *threading, void *(*threadRoutine)(void *), void *data );
+PaError PaUtil_CancelThreading( PaUtilThreading *threading, int wait, PaError *exitResult );
+
+/* State accessed by utility functions */
+
+/*
+void PaUnix_SetRealtimeScheduling( int rt );
+
+void PaUtil_InitializeThreading( PaUtilThreading *th, PaUtilCpuLoadMeasurer *clm );
+
+PaError PaUtil_CreateCallbackThread( PaUtilThreading *th, void *(*CallbackThreadFunc)( void * ), PaStream *s );
+
+PaError PaUtil_KillCallbackThread( PaUtilThreading *th, PaError *exitResult );
+
+void PaUtil_CallbackUpdate( PaUtilThreading *th );
+*/
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+#endif
diff --git a/pjmedia/src/pjmedia/portaudio/pa_util.h b/pjmedia/src/pjmedia/portaudio/pa_util.h
index 149fbca3..a311d7e5 100644
--- a/pjmedia/src/pjmedia/portaudio/pa_util.h
+++ b/pjmedia/src/pjmedia/portaudio/pa_util.h
@@ -1,167 +1,188 @@
-#ifndef PA_UTIL_H
-#define PA_UTIL_H
-/*
- * $Id: pa_util.h,v 1.1.2.12 2003/09/20 21:09:55 rossbencina Exp $
- * Portable Audio I/O Library implementation utilities header
- * common implementation utilities and interfaces
- *
- * Based on the Open Source API proposed by Ross Bencina
- * Copyright (c) 1999-2002 Ross Bencina, Phil Burk
- *
- * Permission is hereby granted, free of charge, to any person obtaining
- * a copy of this software and associated documentation files
- * (the "Software"), to deal in the Software without restriction,
- * including without limitation the rights to use, copy, modify, merge,
- * publish, distribute, sublicense, and/or sell copies of the Software,
- * and to permit persons to whom the Software is furnished to do so,
- * subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be
- * included in all copies or substantial portions of the Software.
- *
- * Any person wishing to distribute modifications to the Software is
- * requested to send the modifications to the original developer so that
- * they can be incorporated into the canonical version.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
- * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
- * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
- * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
- * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
- */
-
-/** @file
- @brief Prototypes for utility functions used by PortAudio implementations.
-
- @todo Document and adhere to the alignment guarantees provided by
- PaUtil_AllocateMemory().
-*/
-
-
-#include "portaudio.h"
-
-#ifdef __cplusplus
-extern "C"
-{
-#endif /* __cplusplus */
-
-
-struct PaUtilHostApiRepresentation;
-
-
-/** Retrieve a specific host API representation. This function can be used
- by implementations to retrieve a pointer to their representation in
- host api specific extension functions which aren't passed a rep pointer
- by pa_front.c.
-
- @param hostApi A pointer to a host API represenation pointer. Apon success
- this will receive the requested representation pointer.
-
- @param type A valid host API type identifier.
-
- @returns An error code. If the result is PaNoError then a pointer to the
- requested host API representation will be stored in *hostApi. If the host API
- specified by type is not found, this function returns paHostApiNotFound.
-*/
-PaError PaUtil_GetHostApiRepresentation( struct PaUtilHostApiRepresentation **hostApi,
- PaHostApiTypeId type );
-
-
-/** Convert a PortAudio device index into a host API specific device index.
- @param hostApiDevice Pointer to a device index, on success this will recieve the
- converted device index value.
- @param device The PortAudio device index to convert.
- @param hostApi The host api which the index should be converted for.
-
- @returns On success returns PaNoError and places the converted index in the
- hostApiDevice parameter.
-*/
-PaError PaUtil_DeviceIndexToHostApiDeviceIndex(
- PaDeviceIndex *hostApiDevice, PaDeviceIndex device,
- struct PaUtilHostApiRepresentation *hostApi );
-
-
-/** Set the host error information returned by Pa_GetLastHostErrorInfo. This
- function and the paUnanticipatedHostError error code should be used as a
- last resort. Implementors should use existing PA error codes where possible,
- or nominate new ones. Note that at it is always better to use
- PaUtil_SetLastHostErrorInfo() and paUnanticipatedHostError than to return an
- ambiguous or inaccurate PaError code.
-
- @param hostApiType The host API which encountered the error (ie of the caller)
-
- @param errorCode The error code returned by the native API function.
-
- @param errorText A string describing the error. PaUtil_SetLastHostErrorInfo
- makes a copy of the string, so it is not necessary for the pointer to remain
- valid after the call to PaUtil_SetLastHostErrorInfo() returns.
-
-*/
-void PaUtil_SetLastHostErrorInfo( PaHostApiTypeId hostApiType, long errorCode,
- const char *errorText );
-
-
-
-/** PA_DEBUG() provides a simple debug message printing facility. The macro
- passes it's argument to a printf-like function called PaUtil_DebugPrint()
- which prints to stderr and always flushes the stream after printing.
- Because preprocessor macros cannot directly accept variable length argument
- lists, calls to the macro must include an additional set of parenthesis, eg:
- PA_DEBUG(("errorno: %d", 1001 ));
-*/
-
-void PaUtil_DebugPrint( const char *format, ... );
-
-#if (0) /* set to 1 to print debug messages */
-#define PA_DEBUG(x) PaUtil_DebugPrint x ;
-#else
-#define PA_DEBUG(x)
-#endif
-
-
-/* the following functions are implemented in a platform platform specific
- .c file
-*/
-
-/** Allocate size bytes, guaranteed to be aligned to a FIXME byte boundary */
-void *PaUtil_AllocateMemory( long size );
-
-
-/** Realease block if non-NULL. block may be NULL */
-void PaUtil_FreeMemory( void *block );
-
-
-/** Return the number of currently allocated blocks. This function can be
- used for detecting memory leaks.
-
- @note Allocations will only be tracked if PA_TRACK_MEMORY is #defined. If
- it isn't, this function will always return 0.
-*/
-int PaUtil_CountCurrentlyAllocatedBlocks( void );
-
-
-/** Initialize the clock used by PaUtil_GetTime(). Call this before calling
- PaUtil_GetTime.
-
- @see PaUtil_GetTime
-*/
-void PaUtil_InitializeClock( void );
-
-
-/** Return the system time in seconds. Used to implement CPU load functions
-
- @see PaUtil_InitializeClock
-*/
-double PaUtil_GetTime( void );
-
-
-/* void Pa_Sleep( long msec ); must also be implemented in per-platform .c file */
-
-
-
-#ifdef __cplusplus
-}
-#endif /* __cplusplus */
-#endif /* PA_UTIL_H */
+/*
+ * PJMEDIA - Multimedia over IP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef PA_UTIL_H
+#define PA_UTIL_H
+/*
+ * $Id: pa_util.h,v 1.1.2.12 2003/09/20 21:09:55 rossbencina Exp $
+ * Portable Audio I/O Library implementation utilities header
+ * common implementation utilities and interfaces
+ *
+ * Based on the Open Source API proposed by Ross Bencina
+ * Copyright (c) 1999-2002 Ross Bencina, Phil Burk
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * Any person wishing to distribute modifications to the Software is
+ * requested to send the modifications to the original developer so that
+ * they can be incorporated into the canonical version.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+ * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+/** @file
+ @brief Prototypes for utility functions used by PortAudio implementations.
+
+ @todo Document and adhere to the alignment guarantees provided by
+ PaUtil_AllocateMemory().
+*/
+
+
+#include "portaudio.h"
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif /* __cplusplus */
+
+
+struct PaUtilHostApiRepresentation;
+
+
+/** Retrieve a specific host API representation. This function can be used
+ by implementations to retrieve a pointer to their representation in
+ host api specific extension functions which aren't passed a rep pointer
+ by pa_front.c.
+
+ @param hostApi A pointer to a host API represenation pointer. Apon success
+ this will receive the requested representation pointer.
+
+ @param type A valid host API type identifier.
+
+ @returns An error code. If the result is PaNoError then a pointer to the
+ requested host API representation will be stored in *hostApi. If the host API
+ specified by type is not found, this function returns paHostApiNotFound.
+*/
+PaError PaUtil_GetHostApiRepresentation( struct PaUtilHostApiRepresentation **hostApi,
+ PaHostApiTypeId type );
+
+
+/** Convert a PortAudio device index into a host API specific device index.
+ @param hostApiDevice Pointer to a device index, on success this will recieve the
+ converted device index value.
+ @param device The PortAudio device index to convert.
+ @param hostApi The host api which the index should be converted for.
+
+ @returns On success returns PaNoError and places the converted index in the
+ hostApiDevice parameter.
+*/
+PaError PaUtil_DeviceIndexToHostApiDeviceIndex(
+ PaDeviceIndex *hostApiDevice, PaDeviceIndex device,
+ struct PaUtilHostApiRepresentation *hostApi );
+
+
+/** Set the host error information returned by Pa_GetLastHostErrorInfo. This
+ function and the paUnanticipatedHostError error code should be used as a
+ last resort. Implementors should use existing PA error codes where possible,
+ or nominate new ones. Note that at it is always better to use
+ PaUtil_SetLastHostErrorInfo() and paUnanticipatedHostError than to return an
+ ambiguous or inaccurate PaError code.
+
+ @param hostApiType The host API which encountered the error (ie of the caller)
+
+ @param errorCode The error code returned by the native API function.
+
+ @param errorText A string describing the error. PaUtil_SetLastHostErrorInfo
+ makes a copy of the string, so it is not necessary for the pointer to remain
+ valid after the call to PaUtil_SetLastHostErrorInfo() returns.
+
+*/
+void PaUtil_SetLastHostErrorInfo( PaHostApiTypeId hostApiType, long errorCode,
+ const char *errorText );
+
+
+
+/** PA_DEBUG() provides a simple debug message printing facility. The macro
+ passes it's argument to a printf-like function called PaUtil_DebugPrint()
+ which prints to stderr and always flushes the stream after printing.
+ Because preprocessor macros cannot directly accept variable length argument
+ lists, calls to the macro must include an additional set of parenthesis, eg:
+ PA_DEBUG(("errorno: %d", 1001 ));
+*/
+
+void PaUtil_DebugPrint( const char *format, ... );
+
+#if (0) /* set to 1 to print debug messages */
+#define PA_DEBUG(x) PaUtil_DebugPrint x ;
+#else
+#define PA_DEBUG(x)
+#endif
+
+
+/* the following functions are implemented in a platform platform specific
+ .c file
+*/
+
+/** Allocate size bytes, guaranteed to be aligned to a FIXME byte boundary */
+void *PaUtil_AllocateMemory( long size );
+
+
+/** Realease block if non-NULL. block may be NULL */
+void PaUtil_FreeMemory( void *block );
+
+
+/** Return the number of currently allocated blocks. This function can be
+ used for detecting memory leaks.
+
+ @note Allocations will only be tracked if PA_TRACK_MEMORY is #defined. If
+ it isn't, this function will always return 0.
+*/
+int PaUtil_CountCurrentlyAllocatedBlocks( void );
+
+
+/** Initialize the clock used by PaUtil_GetTime(). Call this before calling
+ PaUtil_GetTime.
+
+ @see PaUtil_GetTime
+*/
+void PaUtil_InitializeClock( void );
+
+
+/** Return the system time in seconds. Used to implement CPU load functions
+
+ @see PaUtil_InitializeClock
+*/
+double PaUtil_GetTime( void );
+
+
+/* void Pa_Sleep( long msec ); must also be implemented in per-platform .c file */
+
+
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+#endif /* PA_UTIL_H */
diff --git a/pjmedia/src/pjmedia/portaudio/pa_win_ds.c b/pjmedia/src/pjmedia/portaudio/pa_win_ds.c
index 940867da..eb9d4090 100644
--- a/pjmedia/src/pjmedia/portaudio/pa_win_ds.c
+++ b/pjmedia/src/pjmedia/portaudio/pa_win_ds.c
@@ -1,1828 +1,1849 @@
-/*
- * $Id: pa_win_ds.c,v 1.1.2.49 2004/05/16 04:08:55 rossbencina Exp $
- * Portable Audio I/O Library DirectSound implementation
- *
- * Based on the Open Source API proposed by Ross Bencina
- * Copyright (c) 1999-2002 Ross Bencina, Phil Burk
- *
- * Permission is hereby granted, free of charge, to any person obtaining
- * a copy of this software and associated documentation files
- * (the "Software"), to deal in the Software without restriction,
- * including without limitation the rights to use, copy, modify, merge,
- * publish, distribute, sublicense, and/or sell copies of the Software,
- * and to permit persons to whom the Software is furnished to do so,
- * subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be
- * included in all copies or substantial portions of the Software.
- *
- * Any person wishing to distribute modifications to the Software is
- * requested to send the modifications to the original developer so that
- * they can be incorporated into the canonical version.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
- * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
- * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
- * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
- * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
- */
-
-/** @file
-
- @todo implement paInputOverflow callback status flag
-
- @todo implement paNeverDropInput.
-
- @todo implement host api specific extension to set i/o buffer sizes in frames
-
- @todo implement initialisation of PaDeviceInfo default*Latency fields (currently set to 0.)
-
- @todo implement ReadStream, WriteStream, GetStreamReadAvailable, GetStreamWriteAvailable
-
- @todo audit handling of DirectSound result codes - in many cases we could convert a HRESULT into
- a native portaudio error code. Standard DirectSound result codes are documented at msdn.
-
- @todo implement IsFormatSupported
-
- @todo check that CoInitialize() CoUninitialize() are always correctly
- paired, even in error cases.
-
- @todo call PaUtil_SetLastHostErrorInfo with a specific error string (currently just "DSound error").
-
- @todo make sure all buffers have been played before stopping the stream
- when the stream callback returns paComplete
-
- old TODOs from phil, need to work out if these have been done:
- O- fix "patest_stop.c"
-*/
-
-#include <stdio.h>
-#include <string.h> /* strlen() */
-
-#include "pa_util.h"
-#include "pa_allocation.h"
-#include "pa_hostapi.h"
-#include "pa_stream.h"
-#include "pa_cpuload.h"
-#include "pa_process.h"
-
-#include "dsound_wrapper.h"
-
-#if (defined(WIN32) && (defined(_MSC_VER) && (_MSC_VER >= 1200))) /* MSC version 6 and above */
-#pragma comment( lib, "dsound.lib" )
-#pragma comment( lib, "winmm.lib" )
-#endif
-
-
-#define PRINT(x) /* { printf x; fflush(stdout); } */
-#define ERR_RPT(x) PRINT(x)
-#define DBUG(x) /* PRINT(x) */
-#define DBUGX(x) /* PRINT(x) */
-
-#define PA_USE_HIGH_LATENCY (0)
-#if PA_USE_HIGH_LATENCY
-#define PA_WIN_9X_LATENCY (500)
-#define PA_WIN_NT_LATENCY (600)
-#else
-#define PA_WIN_9X_LATENCY (140)
-#define PA_WIN_NT_LATENCY (280)
-#endif
-
-#define PA_WIN_WDM_LATENCY (120)
-
-#define SECONDS_PER_MSEC (0.001)
-#define MSEC_PER_SECOND (1000)
-
-/* prototypes for functions declared in this file */
-
-#ifdef __cplusplus
-extern "C"
-{
-#endif /* __cplusplus */
-
-PaError PaWinDs_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index );
-
-#ifdef __cplusplus
-}
-#endif /* __cplusplus */
-
-static void Terminate( struct PaUtilHostApiRepresentation *hostApi );
-static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
- PaStream** s,
- const PaStreamParameters *inputParameters,
- const PaStreamParameters *outputParameters,
- double sampleRate,
- unsigned long framesPerBuffer,
- PaStreamFlags streamFlags,
- PaStreamCallback *streamCallback,
- void *userData );
-static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
- const PaStreamParameters *inputParameters,
- const PaStreamParameters *outputParameters,
- double sampleRate );
-static PaError CloseStream( PaStream* stream );
-static PaError StartStream( PaStream *stream );
-static PaError StopStream( PaStream *stream );
-static PaError AbortStream( PaStream *stream );
-static PaError IsStreamStopped( PaStream *s );
-static PaError IsStreamActive( PaStream *stream );
-static PaTime GetStreamTime( PaStream *stream );
-static double GetStreamCpuLoad( PaStream* stream );
-static PaError ReadStream( PaStream* stream, void *buffer, unsigned long frames );
-static PaError WriteStream( PaStream* stream, const void *buffer, unsigned long frames );
-static signed long GetStreamReadAvailable( PaStream* stream );
-static signed long GetStreamWriteAvailable( PaStream* stream );
-
-
-/* FIXME: should convert hr to a string */
-#define PA_DS_SET_LAST_DIRECTSOUND_ERROR( hr ) \
- PaUtil_SetLastHostErrorInfo( paDirectSound, hr, "DirectSound error" )
-
-/************************************************* DX Prototypes **********/
-static BOOL CALLBACK CollectGUIDsProc(LPGUID lpGUID,
- LPCTSTR lpszDesc,
- LPCTSTR lpszDrvName,
- LPVOID lpContext );
-
-/************************************************************************************/
-/********************** Structures **************************************************/
-/************************************************************************************/
-/* PaWinDsHostApiRepresentation - host api datastructure specific to this implementation */
-
-typedef struct PaWinDsDeviceInfo
-{
- GUID guid;
- GUID *lpGUID;
- double sampleRates[3];
-} PaWinDsDeviceInfo;
-
-typedef struct
-{
- PaUtilHostApiRepresentation inheritedHostApiRep;
- PaUtilStreamInterface callbackStreamInterface;
- PaUtilStreamInterface blockingStreamInterface;
-
- PaUtilAllocationGroup *allocations;
-
- /* implementation specific data goes here */
- PaWinDsDeviceInfo *winDsDeviceInfos;
-
-} PaWinDsHostApiRepresentation;
-
-/* PaWinDsStream - a stream data structure specifically for this implementation */
-
-typedef struct PaWinDsStream
-{
- PaUtilStreamRepresentation streamRepresentation;
- PaUtilCpuLoadMeasurer cpuLoadMeasurer;
- PaUtilBufferProcessor bufferProcessor;
-
-/* DirectSound specific data. */
- DSoundWrapper directSoundWrapper;
- MMRESULT timerID;
- BOOL ifInsideCallback; /* Test for reentrancy. */
- int framesPerDSBuffer;
- double framesWritten;
- double secondsPerHostByte; /* Used to optimize latency calculation for outTime */
-
- PaStreamCallbackFlags callbackFlags;
-
-/* FIXME - move all below to PaUtilStreamRepresentation */
- volatile int isStarted;
- volatile int isActive;
- volatile int stopProcessing; /* stop thread once existing buffers have been returned */
- volatile int abortProcessing; /* stop thread immediately */
-} PaWinDsStream;
-
-
-/************************************************************************************
-** Duplicate the input string using the allocations allocator.
-** A NULL string is converted to a zero length string.
-** If memory cannot be allocated, NULL is returned.
-**/
-static char *DuplicateDeviceNameString( PaUtilAllocationGroup *allocations, const char* src )
-{
- char *result = 0;
-
- if( src != NULL )
- {
- size_t len = strlen(src);
- result = (char*)PaUtil_GroupAllocateMemory( allocations, (long)(len + 1) );
- if( result )
- memcpy( (void *) result, src, len+1 );
- }
- else
- {
- result = (char*)PaUtil_GroupAllocateMemory( allocations, 1 );
- if( result )
- result[0] = '\0';
- }
-
- return result;
-}
-
-/************************************************************************************
-** DSDeviceNameAndGUID, DSDeviceNameAndGUIDVector used for collecting preliminary
-** information during device enumeration.
-*/
-typedef struct DSDeviceNameAndGUID{
- char *name; // allocated from parent's allocations, never deleted by this structure
- GUID guid;
- LPGUID lpGUID;
-} DSDeviceNameAndGUID;
-
-typedef struct DSDeviceNameAndGUIDVector{
- PaUtilAllocationGroup *allocations;
- PaError enumerationError;
-
- int count;
- int free;
- DSDeviceNameAndGUID *items; // Allocated using LocalAlloc()
-} DSDeviceNameAndGUIDVector;
-
-static PaError InitializeDSDeviceNameAndGUIDVector(
- DSDeviceNameAndGUIDVector *guidVector, PaUtilAllocationGroup *allocations )
-{
- PaError result = paNoError;
-
- guidVector->allocations = allocations;
- guidVector->enumerationError = paNoError;
-
- guidVector->count = 0;
- guidVector->free = 8;
- guidVector->items = (DSDeviceNameAndGUID*)LocalAlloc( LMEM_FIXED, sizeof(DSDeviceNameAndGUID) * guidVector->free );
- if( guidVector->items == NULL )
- result = paInsufficientMemory;
-
- return result;
-}
-
-static PaError ExpandDSDeviceNameAndGUIDVector( DSDeviceNameAndGUIDVector *guidVector )
-{
- PaError result = paNoError;
- DSDeviceNameAndGUID *newItems;
- int i;
-
- /* double size of vector */
- int size = guidVector->count + guidVector->free;
- guidVector->free += size;
-
- newItems = (DSDeviceNameAndGUID*)LocalAlloc( LMEM_FIXED, sizeof(DSDeviceNameAndGUID) * size * 2 );
- if( newItems == NULL )
- {
- result = paInsufficientMemory;
- }
- else
- {
- for( i=0; i < guidVector->count; ++i )
- {
- newItems[i].name = guidVector->items[i].name;
- if( guidVector->items[i].lpGUID == NULL )
- {
- newItems[i].lpGUID = NULL;
- }
- else
- {
- newItems[i].lpGUID = &newItems[i].guid;
- memcpy( &newItems[i].guid, guidVector->items[i].lpGUID, sizeof(GUID) );;
- }
- }
-
- LocalFree( guidVector->items );
- guidVector->items = newItems;
- }
-
- return result;
-}
-
-/*
- it's safe to call DSDeviceNameAndGUIDVector multiple times
-*/
-static PaError TerminateDSDeviceNameAndGUIDVector( DSDeviceNameAndGUIDVector *guidVector )
-{
- PaError result = paNoError;
-
- if( guidVector->items != NULL )
- {
- if( LocalFree( guidVector->items ) != NULL )
- result = paInsufficientMemory; /** @todo this isn't the correct error to return from a deallocation failure */
-
- guidVector->items = NULL;
- }
-
- return result;
-}
-
-/************************************************************************************
-** Collect preliminary device information during DirectSound enumeration
-*/
-static BOOL CALLBACK CollectGUIDsProc(LPGUID lpGUID,
- LPCTSTR lpszDesc,
- LPCTSTR lpszDrvName,
- LPVOID lpContext )
-{
- DSDeviceNameAndGUIDVector *namesAndGUIDs = (DSDeviceNameAndGUIDVector*)lpContext;
- PaError error;
-
- (void) lpszDrvName; /* unused variable */
-
- if( namesAndGUIDs->free == 0 )
- {
- error = ExpandDSDeviceNameAndGUIDVector( namesAndGUIDs );
- if( error != paNoError )
- {
- namesAndGUIDs->enumerationError = error;
- return FALSE;
- }
- }
-
- /* Set GUID pointer, copy GUID to storage in DSDeviceNameAndGUIDVector. */
- if( lpGUID == NULL )
- {
- namesAndGUIDs->items[namesAndGUIDs->count].lpGUID = NULL;
- }
- else
- {
- namesAndGUIDs->items[namesAndGUIDs->count].lpGUID =
- &namesAndGUIDs->items[namesAndGUIDs->count].guid;
-
- memcpy( &namesAndGUIDs->items[namesAndGUIDs->count].guid, lpGUID, sizeof(GUID) );
- }
-
- namesAndGUIDs->items[namesAndGUIDs->count].name =
- DuplicateDeviceNameString( namesAndGUIDs->allocations, lpszDesc );
- if( namesAndGUIDs->items[namesAndGUIDs->count].name == NULL )
- {
- namesAndGUIDs->enumerationError = paInsufficientMemory;
- return FALSE;
- }
-
- ++namesAndGUIDs->count;
- --namesAndGUIDs->free;
-
- return TRUE;
-}
-
-
-#define PA_DEFAULTSAMPLERATESEARCHORDER_COUNT_ (13) /* must match array length below */
-static double defaultSampleRateSearchOrder_[] =
- { 44100.0, 48000.0, 32000.0, 24000.0, 22050.0, 88200.0, 96000.0, 192000.0,
- 16000.0, 12000.0, 11025.0, 9600.0, 8000.0 };
-
-
-/************************************************************************************
-** Extract capabilities from an output device, and add it to the device info list
-** if successful. This function assumes that there is enough room in the
-** device info list to accomodate all entries.
-**
-** The device will not be added to the device list if any errors are encountered.
-*/
-static PaError AddOutputDeviceInfoFromDirectSound(
- PaWinDsHostApiRepresentation *winDsHostApi, char *name, LPGUID lpGUID )
-{
- PaUtilHostApiRepresentation *hostApi = &winDsHostApi->inheritedHostApiRep;
- PaDeviceInfo *deviceInfo = hostApi->deviceInfos[hostApi->info.deviceCount];
- PaWinDsDeviceInfo *winDsDeviceInfo = &winDsHostApi->winDsDeviceInfos[hostApi->info.deviceCount];
- HRESULT hr;
- LPDIRECTSOUND lpDirectSound;
- DSCAPS caps;
- int deviceOK = TRUE;
- PaError result = paNoError;
- int i;
-
- /* Copy GUID to the device info structure. Set pointer. */
- if( lpGUID == NULL )
- {
- winDsDeviceInfo->lpGUID = NULL;
- }
- else
- {
- memcpy( &winDsDeviceInfo->guid, lpGUID, sizeof(GUID) );
- winDsDeviceInfo->lpGUID = &winDsDeviceInfo->guid;
- }
-
-
- /* Create a DirectSound object for the specified GUID
- Note that using CoCreateInstance doesn't work on windows CE.
- */
- hr = dswDSoundEntryPoints.DirectSoundCreate( lpGUID, &lpDirectSound, NULL );
-
- /** try using CoCreateInstance because DirectSoundCreate was hanging under
- some circumstances - note this was probably related to the
- #define BOOL short bug which has now been fixed
- @todo delete this comment and the following code once we've ensured
- there is no bug.
- */
- /*
- hr = CoCreateInstance( &CLSID_DirectSound, NULL, CLSCTX_INPROC_SERVER,
- &IID_IDirectSound, (void**)&lpDirectSound );
-
- if( hr == S_OK )
- {
- hr = IDirectSound_Initialize( lpDirectSound, lpGUID );
- }
- */
-
- if( hr != DS_OK )
- {
- DBUG(("Cannot create DirectSound for %s. Result = 0x%x\n", name, hr ));
- deviceOK = FALSE;
- }
- else
- {
- /* Query device characteristics. */
- memset( &caps, 0, sizeof(caps) );
- caps.dwSize = sizeof(caps);
- hr = IDirectSound_GetCaps( lpDirectSound, &caps );
- if( hr != DS_OK )
- {
- DBUG(("Cannot GetCaps() for DirectSound device %s. Result = 0x%x\n", name, hr ));
- deviceOK = FALSE;
- }
- else
- {
-
-#ifndef PA_NO_WMME
- if( caps.dwFlags & DSCAPS_EMULDRIVER )
- {
- /* If WMME supported, then reject Emulated drivers because they are lousy. */
- deviceOK = FALSE;
- }
-#endif
-
- if( deviceOK )
- {
- deviceInfo->maxInputChannels = 0;
- /* Mono or stereo device? */
- deviceInfo->maxOutputChannels = ( caps.dwFlags & DSCAPS_PRIMARYSTEREO ) ? 2 : 1;
-
- deviceInfo->defaultLowInputLatency = 0.; /** @todo IMPLEMENT ME */
- deviceInfo->defaultLowOutputLatency = 0.; /** @todo IMPLEMENT ME */
- deviceInfo->defaultHighInputLatency = 0.; /** @todo IMPLEMENT ME */
- deviceInfo->defaultHighOutputLatency = 0.; /** @todo IMPLEMENT ME */
-
- /* initialize defaultSampleRate */
-
- if( caps.dwFlags & DSCAPS_CONTINUOUSRATE )
- {
- /* initialize to caps.dwMaxSecondarySampleRate incase none of the standard rates match */
- deviceInfo->defaultSampleRate = caps.dwMaxSecondarySampleRate;
-
- for( i = 0; i < PA_DEFAULTSAMPLERATESEARCHORDER_COUNT_; ++i )
- {
- if( defaultSampleRateSearchOrder_[i] >= caps.dwMinSecondarySampleRate
- && defaultSampleRateSearchOrder_[i] <= caps.dwMaxSecondarySampleRate ){
-
- deviceInfo->defaultSampleRate = defaultSampleRateSearchOrder_[i];
- break;
- }
- }
- }
- else if( caps.dwMinSecondarySampleRate == caps.dwMaxSecondarySampleRate )
- {
- if( caps.dwMinSecondarySampleRate == 0 )
- {
- /*
- ** On my Thinkpad 380Z, DirectSoundV6 returns min-max=0 !!
- ** But it supports continuous sampling.
- ** So fake range of rates, and hope it really supports it.
- */
- deviceInfo->defaultSampleRate = 44100.0f;
-
- DBUG(("PA - Reported rates both zero. Setting to fake values for device #%d\n", sDeviceIndex ));
- }
- else
- {
- deviceInfo->defaultSampleRate = caps.dwMaxSecondarySampleRate;
- }
- }
- else if( (caps.dwMinSecondarySampleRate < 1000.0) && (caps.dwMaxSecondarySampleRate > 50000.0) )
- {
- /* The EWS88MT drivers lie, lie, lie. The say they only support two rates, 100 & 100000.
- ** But we know that they really support a range of rates!
- ** So when we see a ridiculous set of rates, assume it is a range.
- */
- deviceInfo->defaultSampleRate = 44100.0f;
- DBUG(("PA - Sample rate range used instead of two odd values for device #%d\n", sDeviceIndex ));
- }
- else deviceInfo->defaultSampleRate = caps.dwMaxSecondarySampleRate;
-
-
- //printf( "min %d max %d\n", caps.dwMinSecondarySampleRate, caps.dwMaxSecondarySampleRate );
- // dwFlags | DSCAPS_CONTINUOUSRATE
- }
- }
-
- IDirectSound_Release( lpDirectSound );
- }
-
- if( deviceOK )
- {
- deviceInfo->name = name;
-
- if( lpGUID == NULL )
- hostApi->info.defaultOutputDevice = hostApi->info.deviceCount;
-
- hostApi->info.deviceCount++;
- }
-
- return result;
-}
-
-
-/************************************************************************************
-** Extract capabilities from an input device, and add it to the device info list
-** if successful. This function assumes that there is enough room in the
-** device info list to accomodate all entries.
-**
-** The device will not be added to the device list if any errors are encountered.
-*/
-static PaError AddInputDeviceInfoFromDirectSoundCapture(
- PaWinDsHostApiRepresentation *winDsHostApi, char *name, LPGUID lpGUID )
-{
- PaUtilHostApiRepresentation *hostApi = &winDsHostApi->inheritedHostApiRep;
- PaDeviceInfo *deviceInfo = hostApi->deviceInfos[hostApi->info.deviceCount];
- PaWinDsDeviceInfo *winDsDeviceInfo = &winDsHostApi->winDsDeviceInfos[hostApi->info.deviceCount];
- HRESULT hr;
- LPDIRECTSOUNDCAPTURE lpDirectSoundCapture;
- DSCCAPS caps;
- int deviceOK = TRUE;
- PaError result = paNoError;
-
- /* Copy GUID to the device info structure. Set pointer. */
- if( lpGUID == NULL )
- {
- winDsDeviceInfo->lpGUID = NULL;
- }
- else
- {
- winDsDeviceInfo->lpGUID = &winDsDeviceInfo->guid;
- memcpy( &winDsDeviceInfo->guid, lpGUID, sizeof(GUID) );
- }
-
-
- hr = dswDSoundEntryPoints.DirectSoundCaptureCreate( lpGUID, &lpDirectSoundCapture, NULL );
-
- /** try using CoCreateInstance because DirectSoundCreate was hanging under
- some circumstances - note this was probably related to the
- #define BOOL short bug which has now been fixed
- @todo delete this comment and the following code once we've ensured
- there is no bug.
- */
- /*
- hr = CoCreateInstance( &CLSID_DirectSoundCapture, NULL, CLSCTX_INPROC_SERVER,
- &IID_IDirectSoundCapture, (void**)&lpDirectSoundCapture );
- */
- if( hr != DS_OK )
- {
- DBUG(("Cannot create Capture for %s. Result = 0x%x\n", name, hr ));
- deviceOK = FALSE;
- }
- else
- {
- /* Query device characteristics. */
- memset( &caps, 0, sizeof(caps) );
- caps.dwSize = sizeof(caps);
- hr = IDirectSoundCapture_GetCaps( lpDirectSoundCapture, &caps );
- if( hr != DS_OK )
- {
- DBUG(("Cannot GetCaps() for Capture device %s. Result = 0x%x\n", name, hr ));
- deviceOK = FALSE;
- }
- else
- {
-#ifndef PA_NO_WMME
- if( caps.dwFlags & DSCAPS_EMULDRIVER )
- {
- /* If WMME supported, then reject Emulated drivers because they are lousy. */
- deviceOK = FALSE;
- }
-#endif
-
- if( deviceOK )
- {
- deviceInfo->maxInputChannels = caps.dwChannels;
- deviceInfo->maxOutputChannels = 0;
-
- deviceInfo->defaultLowInputLatency = 0.; /** @todo IMPLEMENT ME */
- deviceInfo->defaultLowOutputLatency = 0.; /** @todo IMPLEMENT ME */
- deviceInfo->defaultHighInputLatency = 0.; /** @todo IMPLEMENT ME */
- deviceInfo->defaultHighOutputLatency = 0.; /** @todo IMPLEMENT ME */
-
-/* constants from a WINE patch by Francois Gouget, see:
- http://www.winehq.com/hypermail/wine-patches/2003/01/0290.html
-
- ---
- Date: Fri, 14 May 2004 10:38:12 +0200 (CEST)
- From: Francois Gouget <fgouget@ ... .fr>
- To: Ross Bencina <rbencina@ ... .au>
- Subject: Re: Permission to use wine 48/96 wave patch in BSD licensed library
-
- [snip]
-
- I give you permission to use the patch below under the BSD license.
- http://www.winehq.com/hypermail/wine-patches/2003/01/0290.html
-
- [snip]
-*/
-#ifndef WAVE_FORMAT_48M08
-#define WAVE_FORMAT_48M08 0x00001000 /* 48 kHz, Mono, 8-bit */
-#define WAVE_FORMAT_48S08 0x00002000 /* 48 kHz, Stereo, 8-bit */
-#define WAVE_FORMAT_48M16 0x00004000 /* 48 kHz, Mono, 16-bit */
-#define WAVE_FORMAT_48S16 0x00008000 /* 48 kHz, Stereo, 16-bit */
-#define WAVE_FORMAT_96M08 0x00010000 /* 96 kHz, Mono, 8-bit */
-#define WAVE_FORMAT_96S08 0x00020000 /* 96 kHz, Stereo, 8-bit */
-#define WAVE_FORMAT_96M16 0x00040000 /* 96 kHz, Mono, 16-bit */
-#define WAVE_FORMAT_96S16 0x00080000 /* 96 kHz, Stereo, 16-bit */
-#endif
-
- /* defaultSampleRate */
- if( caps.dwChannels == 2 )
- {
- if( caps.dwFormats & WAVE_FORMAT_4S16 )
- deviceInfo->defaultSampleRate = 44100.0;
- else if( caps.dwFormats & WAVE_FORMAT_48S16 )
- deviceInfo->defaultSampleRate = 48000.0;
- else if( caps.dwFormats & WAVE_FORMAT_2S16 )
- deviceInfo->defaultSampleRate = 22050.0;
- else if( caps.dwFormats & WAVE_FORMAT_1S16 )
- deviceInfo->defaultSampleRate = 11025.0;
- else if( caps.dwFormats & WAVE_FORMAT_96S16 )
- deviceInfo->defaultSampleRate = 96000.0;
- else
- deviceInfo->defaultSampleRate = 0.;
- }
- else if( caps.dwChannels == 1 )
- {
- if( caps.dwFormats & WAVE_FORMAT_4M16 )
- deviceInfo->defaultSampleRate = 44100.0;
- else if( caps.dwFormats & WAVE_FORMAT_48M16 )
- deviceInfo->defaultSampleRate = 48000.0;
- else if( caps.dwFormats & WAVE_FORMAT_2M16 )
- deviceInfo->defaultSampleRate = 22050.0;
- else if( caps.dwFormats & WAVE_FORMAT_1M16 )
- deviceInfo->defaultSampleRate = 11025.0;
- else if( caps.dwFormats & WAVE_FORMAT_96M16 )
- deviceInfo->defaultSampleRate = 96000.0;
- else
- deviceInfo->defaultSampleRate = 0.;
- }
- else deviceInfo->defaultSampleRate = 0.;
- }
- }
-
- IDirectSoundCapture_Release( lpDirectSoundCapture );
- }
-
- if( deviceOK )
- {
- deviceInfo->name = name;
-
- if( lpGUID == NULL )
- hostApi->info.defaultInputDevice = hostApi->info.deviceCount;
-
- hostApi->info.deviceCount++;
- }
-
- return result;
-}
-
-
-/***********************************************************************************/
-PaError PaWinDs_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex hostApiIndex )
-{
- PaError result = paNoError;
- int i, deviceCount;
- PaWinDsHostApiRepresentation *winDsHostApi;
- DSDeviceNameAndGUIDVector inputNamesAndGUIDs, outputNamesAndGUIDs;
- PaDeviceInfo *deviceInfoArray;
-
- HRESULT hr = CoInitialize(NULL); /** @todo: should uninitialize too */
- if( FAILED(hr) ){
- return paUnanticipatedHostError;
- }
-
- /* initialise guid vectors so they can be safely deleted on error */
- inputNamesAndGUIDs.items = NULL;
- outputNamesAndGUIDs.items = NULL;
-
- DSW_InitializeDSoundEntryPoints();
-
- winDsHostApi = (PaWinDsHostApiRepresentation*)PaUtil_AllocateMemory( sizeof(PaWinDsHostApiRepresentation) );
- if( !winDsHostApi )
- {
- result = paInsufficientMemory;
- goto error;
- }
-
- winDsHostApi->allocations = PaUtil_CreateAllocationGroup();
- if( !winDsHostApi->allocations )
- {
- result = paInsufficientMemory;
- goto error;
- }
-
- *hostApi = &winDsHostApi->inheritedHostApiRep;
- (*hostApi)->info.structVersion = 1;
- (*hostApi)->info.type = paDirectSound;
- (*hostApi)->info.name = "Windows DirectSound";
-
- (*hostApi)->info.deviceCount = 0;
- (*hostApi)->info.defaultInputDevice = paNoDevice;
- (*hostApi)->info.defaultOutputDevice = paNoDevice;
-
-
-/* DSound - enumerate devices to count them and to gather their GUIDs */
-
-
- result = InitializeDSDeviceNameAndGUIDVector( &inputNamesAndGUIDs, winDsHostApi->allocations );
- if( result != paNoError )
- goto error;
-
- result = InitializeDSDeviceNameAndGUIDVector( &outputNamesAndGUIDs, winDsHostApi->allocations );
- if( result != paNoError )
- goto error;
-
- dswDSoundEntryPoints.DirectSoundCaptureEnumerate( (LPDSENUMCALLBACK)CollectGUIDsProc, (void *)&inputNamesAndGUIDs );
-
- dswDSoundEntryPoints.DirectSoundEnumerate( (LPDSENUMCALLBACK)CollectGUIDsProc, (void *)&outputNamesAndGUIDs );
-
- if( inputNamesAndGUIDs.enumerationError != paNoError )
- {
- result = inputNamesAndGUIDs.enumerationError;
- goto error;
- }
-
- if( outputNamesAndGUIDs.enumerationError != paNoError )
- {
- result = outputNamesAndGUIDs.enumerationError;
- goto error;
- }
-
- deviceCount = inputNamesAndGUIDs.count + outputNamesAndGUIDs.count;
-
- if( deviceCount > 0 )
- {
- /* allocate array for pointers to PaDeviceInfo structs */
- (*hostApi)->deviceInfos = (PaDeviceInfo**)PaUtil_GroupAllocateMemory(
- winDsHostApi->allocations, sizeof(PaDeviceInfo*) * deviceCount );
- if( !(*hostApi)->deviceInfos )
- {
- result = paInsufficientMemory;
- goto error;
- }
-
- /* allocate all PaDeviceInfo structs in a contiguous block */
- deviceInfoArray = (PaDeviceInfo*)PaUtil_GroupAllocateMemory(
- winDsHostApi->allocations, sizeof(PaDeviceInfo) * deviceCount );
- if( !deviceInfoArray )
- {
- result = paInsufficientMemory;
- goto error;
- }
-
- /* allocate all DSound specific info structs in a contiguous block */
- winDsHostApi->winDsDeviceInfos = (PaWinDsDeviceInfo*)PaUtil_GroupAllocateMemory(
- winDsHostApi->allocations, sizeof(PaWinDsDeviceInfo) * deviceCount );
- if( !winDsHostApi->winDsDeviceInfos )
- {
- result = paInsufficientMemory;
- goto error;
- }
-
- for( i=0; i < deviceCount; ++i )
- {
- PaDeviceInfo *deviceInfo = &deviceInfoArray[i];
- deviceInfo->structVersion = 2;
- deviceInfo->hostApi = hostApiIndex;
- deviceInfo->name = 0;
- (*hostApi)->deviceInfos[i] = deviceInfo;
- }
-
- for( i=0; i< inputNamesAndGUIDs.count; ++i )
- {
- result = AddInputDeviceInfoFromDirectSoundCapture( winDsHostApi,
- inputNamesAndGUIDs.items[i].name,
- inputNamesAndGUIDs.items[i].lpGUID );
- if( result != paNoError )
- goto error;
- }
-
- for( i=0; i< outputNamesAndGUIDs.count; ++i )
- {
- result = AddOutputDeviceInfoFromDirectSound( winDsHostApi,
- outputNamesAndGUIDs.items[i].name,
- outputNamesAndGUIDs.items[i].lpGUID );
- if( result != paNoError )
- goto error;
- }
- }
-
- result = TerminateDSDeviceNameAndGUIDVector( &inputNamesAndGUIDs );
- if( result != paNoError )
- goto error;
-
- result = TerminateDSDeviceNameAndGUIDVector( &outputNamesAndGUIDs );
- if( result != paNoError )
- goto error;
-
-
- (*hostApi)->Terminate = Terminate;
- (*hostApi)->OpenStream = OpenStream;
- (*hostApi)->IsFormatSupported = IsFormatSupported;
-
- PaUtil_InitializeStreamInterface( &winDsHostApi->callbackStreamInterface, CloseStream, StartStream,
- StopStream, AbortStream, IsStreamStopped, IsStreamActive,
- GetStreamTime, GetStreamCpuLoad,
- PaUtil_DummyRead, PaUtil_DummyWrite,
- PaUtil_DummyGetReadAvailable, PaUtil_DummyGetWriteAvailable );
-
- PaUtil_InitializeStreamInterface( &winDsHostApi->blockingStreamInterface, CloseStream, StartStream,
- StopStream, AbortStream, IsStreamStopped, IsStreamActive,
- GetStreamTime, PaUtil_DummyGetCpuLoad,
- ReadStream, WriteStream, GetStreamReadAvailable, GetStreamWriteAvailable );
-
- return result;
-
-error:
- if( winDsHostApi )
- {
- if( winDsHostApi->allocations )
- {
- PaUtil_FreeAllAllocations( winDsHostApi->allocations );
- PaUtil_DestroyAllocationGroup( winDsHostApi->allocations );
- }
-
- PaUtil_FreeMemory( winDsHostApi );
- }
-
- TerminateDSDeviceNameAndGUIDVector( &inputNamesAndGUIDs );
- TerminateDSDeviceNameAndGUIDVector( &outputNamesAndGUIDs );
-
- return result;
-}
-
-
-/***********************************************************************************/
-static void Terminate( struct PaUtilHostApiRepresentation *hostApi )
-{
- PaWinDsHostApiRepresentation *winDsHostApi = (PaWinDsHostApiRepresentation*)hostApi;
-
- /*
- IMPLEMENT ME:
- - clean up any resources not handled by the allocation group
- */
-
- if( winDsHostApi->allocations )
- {
- PaUtil_FreeAllAllocations( winDsHostApi->allocations );
- PaUtil_DestroyAllocationGroup( winDsHostApi->allocations );
- }
-
- PaUtil_FreeMemory( winDsHostApi );
-
- DSW_TerminateDSoundEntryPoints();
-
- CoUninitialize();
-}
-
-
-/* Set minimal latency based on whether NT or Win95.
- * NT has higher latency.
- */
-static int PaWinDS_GetMinSystemLatency( void )
-{
- int minLatencyMsec;
- /* Set minimal latency based on whether NT or other OS.
- * NT has higher latency.
- */
- OSVERSIONINFO osvi;
- osvi.dwOSVersionInfoSize = sizeof( osvi );
- GetVersionEx( &osvi );
- DBUG(("PA - PlatformId = 0x%x\n", osvi.dwPlatformId ));
- DBUG(("PA - MajorVersion = 0x%x\n", osvi.dwMajorVersion ));
- DBUG(("PA - MinorVersion = 0x%x\n", osvi.dwMinorVersion ));
- /* Check for NT */
- if( (osvi.dwMajorVersion == 4) && (osvi.dwPlatformId == 2) )
- {
- minLatencyMsec = PA_WIN_NT_LATENCY;
- }
- else if(osvi.dwMajorVersion >= 5)
- {
- minLatencyMsec = PA_WIN_WDM_LATENCY;
- }
- else
- {
- minLatencyMsec = PA_WIN_9X_LATENCY;
- }
- return minLatencyMsec;
-}
-
-/***********************************************************************************/
-static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
- const PaStreamParameters *inputParameters,
- const PaStreamParameters *outputParameters,
- double sampleRate )
-{
- int inputChannelCount, outputChannelCount;
- PaSampleFormat inputSampleFormat, outputSampleFormat;
-
- if( inputParameters )
- {
- inputChannelCount = inputParameters->channelCount;
- inputSampleFormat = inputParameters->sampleFormat;
-
- /* unless alternate device specification is supported, reject the use of
- paUseHostApiSpecificDeviceSpecification */
-
- if( inputParameters->device == paUseHostApiSpecificDeviceSpecification )
- return paInvalidDevice;
-
- /* check that input device can support inputChannelCount */
- if( inputChannelCount > hostApi->deviceInfos[ inputParameters->device ]->maxInputChannels )
- return paInvalidChannelCount;
-
- /* validate inputStreamInfo */
- if( inputParameters->hostApiSpecificStreamInfo )
- return paIncompatibleHostApiSpecificStreamInfo; /* this implementation doesn't use custom stream info */
- }
- else
- {
- inputChannelCount = 0;
- }
-
- if( outputParameters )
- {
- outputChannelCount = outputParameters->channelCount;
- outputSampleFormat = outputParameters->sampleFormat;
-
- /* unless alternate device specification is supported, reject the use of
- paUseHostApiSpecificDeviceSpecification */
-
- if( outputParameters->device == paUseHostApiSpecificDeviceSpecification )
- return paInvalidDevice;
-
- /* check that output device can support inputChannelCount */
- if( outputChannelCount > hostApi->deviceInfos[ outputParameters->device ]->maxOutputChannels )
- return paInvalidChannelCount;
-
- /* validate outputStreamInfo */
- if( outputParameters->hostApiSpecificStreamInfo )
- return paIncompatibleHostApiSpecificStreamInfo; /* this implementation doesn't use custom stream info */
- }
- else
- {
- outputChannelCount = 0;
- }
-
- /*
- IMPLEMENT ME:
-
- - if a full duplex stream is requested, check that the combination
- of input and output parameters is supported if necessary
-
- - check that the device supports sampleRate
-
- Because the buffer adapter handles conversion between all standard
- sample formats, the following checks are only required if paCustomFormat
- is implemented, or under some other unusual conditions.
-
- - check that input device can support inputSampleFormat, or that
- we have the capability to convert from outputSampleFormat to
- a native format
-
- - check that output device can support outputSampleFormat, or that
- we have the capability to convert from outputSampleFormat to
- a native format
- */
-
- return paFormatIsSupported;
-}
-
-
-/*************************************************************************
-** Determine minimum number of buffers required for this host based
-** on minimum latency. Latency can be optionally set by user by setting
-** an environment variable. For example, to set latency to 200 msec, put:
-**
-** set PA_MIN_LATENCY_MSEC=200
-**
-** in the AUTOEXEC.BAT file and reboot.
-** If the environment variable is not set, then the latency will be determined
-** based on the OS. Windows NT has higher latency than Win95.
-*/
-#define PA_LATENCY_ENV_NAME ("PA_MIN_LATENCY_MSEC")
-#define PA_ENV_BUF_SIZE (32)
-
-static int PaWinDs_GetMinLatencyFrames( double sampleRate )
-{
- char envbuf[PA_ENV_BUF_SIZE];
- DWORD hresult;
- int minLatencyMsec = 0;
-
- /* Let user determine minimal latency by setting environment variable. */
- hresult = GetEnvironmentVariable( PA_LATENCY_ENV_NAME, envbuf, PA_ENV_BUF_SIZE );
- if( (hresult > 0) && (hresult < PA_ENV_BUF_SIZE) )
- {
- minLatencyMsec = atoi( envbuf );
- }
- else
- {
- minLatencyMsec = PaWinDS_GetMinSystemLatency();
-#if PA_USE_HIGH_LATENCY
- PRINT(("PA - Minimum Latency set to %d msec!\n", minLatencyMsec ));
-#endif
-
- }
-
- return (int) (minLatencyMsec * sampleRate * SECONDS_PER_MSEC);
-}
-
-#ifndef NDEBUG
-#define EZ = 0
-#else
-#define EZ
-#endif
-
-/***********************************************************************************/
-/* see pa_hostapi.h for a list of validity guarantees made about OpenStream parameters */
-
-static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
- PaStream** s,
- const PaStreamParameters *inputParameters,
- const PaStreamParameters *outputParameters,
- double sampleRate,
- unsigned long framesPerBuffer,
- PaStreamFlags streamFlags,
- PaStreamCallback *streamCallback,
- void *userData )
-{
- PaError result = paNoError;
- PaWinDsHostApiRepresentation *winDsHostApi = (PaWinDsHostApiRepresentation*)hostApi;
- PaWinDsStream *stream = 0;
- int inputChannelCount, outputChannelCount;
- PaSampleFormat inputSampleFormat EZ, outputSampleFormat EZ;
- PaSampleFormat hostInputSampleFormat EZ, hostOutputSampleFormat EZ;
- unsigned long suggestedInputLatencyFrames, suggestedOutputLatencyFrames;
-
- if( inputParameters )
- {
- inputChannelCount = inputParameters->channelCount;
- inputSampleFormat = inputParameters->sampleFormat;
- suggestedInputLatencyFrames = (unsigned long)(inputParameters->suggestedLatency * sampleRate);
-
- /* IDEA: the following 3 checks could be performed by default by pa_front
- unless some flag indicated otherwise */
-
- /* unless alternate device specification is supported, reject the use of
- paUseHostApiSpecificDeviceSpecification */
- if( inputParameters->device == paUseHostApiSpecificDeviceSpecification )
- return paInvalidDevice;
-
- /* check that input device can support inputChannelCount */
- if( inputChannelCount > hostApi->deviceInfos[ inputParameters->device ]->maxInputChannels )
- return paInvalidChannelCount;
-
- /* validate hostApiSpecificStreamInfo */
- if( inputParameters->hostApiSpecificStreamInfo )
- return paIncompatibleHostApiSpecificStreamInfo; /* this implementation doesn't use custom stream info */
- }
- else
- {
- inputChannelCount = 0;
- suggestedInputLatencyFrames = 0;
- }
-
-
- if( outputParameters )
- {
- outputChannelCount = outputParameters->channelCount;
- outputSampleFormat = outputParameters->sampleFormat;
- suggestedOutputLatencyFrames = (unsigned long)(outputParameters->suggestedLatency * sampleRate);
-
- /* unless alternate device specification is supported, reject the use of
- paUseHostApiSpecificDeviceSpecification */
- if( outputParameters->device == paUseHostApiSpecificDeviceSpecification )
- return paInvalidDevice;
-
- /* check that output device can support inputChannelCount */
- if( outputChannelCount > hostApi->deviceInfos[ outputParameters->device ]->maxOutputChannels )
- return paInvalidChannelCount;
-
- /* validate hostApiSpecificStreamInfo */
- if( outputParameters->hostApiSpecificStreamInfo )
- return paIncompatibleHostApiSpecificStreamInfo; /* this implementation doesn't use custom stream info */
- }
- else
- {
- outputChannelCount = 0;
- suggestedOutputLatencyFrames = 0;
- }
-
-
- /*
- IMPLEMENT ME:
-
- ( the following two checks are taken care of by PaUtil_InitializeBufferProcessor() )
-
- - check that input device can support inputSampleFormat, or that
- we have the capability to convert from outputSampleFormat to
- a native format
-
- - check that output device can support outputSampleFormat, or that
- we have the capability to convert from outputSampleFormat to
- a native format
-
- - if a full duplex stream is requested, check that the combination
- of input and output parameters is supported
-
- - check that the device supports sampleRate
-
- - alter sampleRate to a close allowable rate if possible / necessary
-
- - validate suggestedInputLatency and suggestedOutputLatency parameters,
- use default values where necessary
- */
-
-
- /* validate platform specific flags */
- if( (streamFlags & paPlatformSpecificFlags) != 0 )
- return paInvalidFlag; /* unexpected platform specific flag */
-
-
- stream = (PaWinDsStream*)PaUtil_AllocateMemory( sizeof(PaWinDsStream) );
- if( !stream )
- {
- result = paInsufficientMemory;
- goto error;
- }
-
- if( streamCallback )
- {
- PaUtil_InitializeStreamRepresentation( &stream->streamRepresentation,
- &winDsHostApi->callbackStreamInterface, streamCallback, userData );
- }
- else
- {
- PaUtil_InitializeStreamRepresentation( &stream->streamRepresentation,
- &winDsHostApi->blockingStreamInterface, streamCallback, userData );
- }
-
- PaUtil_InitializeCpuLoadMeasurer( &stream->cpuLoadMeasurer, sampleRate );
-
-
- if( inputParameters )
- {
- /* IMPLEMENT ME - establish which host formats are available */
- hostInputSampleFormat =
- PaUtil_SelectClosestAvailableFormat( paInt16 /* native formats */, inputParameters->sampleFormat );
- }
-
- if( outputParameters )
- {
- /* IMPLEMENT ME - establish which host formats are available */
- hostOutputSampleFormat =
- PaUtil_SelectClosestAvailableFormat( paInt16 /* native formats */, outputParameters->sampleFormat );
- }
-
- result = PaUtil_InitializeBufferProcessor( &stream->bufferProcessor,
- inputChannelCount, inputSampleFormat, hostInputSampleFormat,
- outputChannelCount, outputSampleFormat, hostOutputSampleFormat,
- sampleRate, streamFlags, framesPerBuffer,
- framesPerBuffer, /* ignored in paUtilVariableHostBufferSizePartialUsageAllowed mode. */
- /* This next mode is required because DS can split the host buffer when it wraps around. */
- paUtilVariableHostBufferSizePartialUsageAllowed,
- streamCallback, userData );
- if( result != paNoError )
- goto error;
-
-
- stream->streamRepresentation.streamInfo.inputLatency =
- PaUtil_GetBufferProcessorInputLatency(&stream->bufferProcessor); /* FIXME: not initialised anywhere else */
- stream->streamRepresentation.streamInfo.outputLatency =
- PaUtil_GetBufferProcessorOutputLatency(&stream->bufferProcessor); /* FIXME: not initialised anywhere else */
- stream->streamRepresentation.streamInfo.sampleRate = sampleRate;
-
-
-/* DirectSound specific initialization */
- {
- HRESULT hr;
- int bytesPerDirectSoundBuffer;
- DSoundWrapper *dsw;
- int userLatencyFrames;
- int minLatencyFrames;
-
- stream->timerID = 0;
- dsw = &stream->directSoundWrapper;
- DSW_Init( dsw );
-
- /* Get system minimum latency. */
- minLatencyFrames = PaWinDs_GetMinLatencyFrames( sampleRate );
-
- /* Let user override latency by passing latency parameter. */
- userLatencyFrames = (suggestedInputLatencyFrames > suggestedOutputLatencyFrames)
- ? suggestedInputLatencyFrames
- : suggestedOutputLatencyFrames;
- if( userLatencyFrames > 0 ) minLatencyFrames = userLatencyFrames;
-
- /* Calculate stream->framesPerDSBuffer depending on framesPerBuffer */
- if( framesPerBuffer == paFramesPerBufferUnspecified )
- {
- /* App support variable framesPerBuffer */
- stream->framesPerDSBuffer = minLatencyFrames;
-
- stream->streamRepresentation.streamInfo.outputLatency = (double)(minLatencyFrames - 1) / sampleRate;
- }
- else
- {
- /* Round up to number of buffers needed to guarantee that latency. */
- int numUserBuffers = (minLatencyFrames + framesPerBuffer - 1) / framesPerBuffer;
- if( numUserBuffers < 1 ) numUserBuffers = 1;
- numUserBuffers += 1; /* So we have latency worth of buffers ahead of current buffer. */
- stream->framesPerDSBuffer = framesPerBuffer * numUserBuffers;
-
- stream->streamRepresentation.streamInfo.outputLatency = (double)(framesPerBuffer * (numUserBuffers-1)) / sampleRate;
- }
-
- {
- /** @todo REVIEW: this calculation seems incorrect to me - rossb. */
- int msecLatency = (int) ((stream->framesPerDSBuffer * MSEC_PER_SECOND) / sampleRate);
- PRINT(("PortAudio on DirectSound - Latency = %d frames, %d msec\n", stream->framesPerDSBuffer, msecLatency ));
- }
-
-
- /* ------------------ OUTPUT */
- if( outputParameters )
- {
- /*
- PaDeviceInfo *deviceInfo = hostApi->deviceInfos[ outputParameters->device ];
- DBUG(("PaHost_OpenStream: deviceID = 0x%x\n", outputParameters->device));
- */
-
- bytesPerDirectSoundBuffer = stream->framesPerDSBuffer * outputParameters->channelCount * sizeof(short);
- if( bytesPerDirectSoundBuffer < DSBSIZE_MIN )
- {
- result = paBufferTooSmall;
- goto error;
- }
- else if( bytesPerDirectSoundBuffer > DSBSIZE_MAX )
- {
- result = paBufferTooBig;
- goto error;
- }
-
-
- hr = dswDSoundEntryPoints.DirectSoundCreate( winDsHostApi->winDsDeviceInfos[outputParameters->device].lpGUID,
- &dsw->dsw_pDirectSound, NULL );
- if( hr != DS_OK )
- {
- ERR_RPT(("PortAudio: DirectSoundCreate() failed!\n"));
- result = paUnanticipatedHostError;
- PA_DS_SET_LAST_DIRECTSOUND_ERROR( hr );
- goto error;
- }
- hr = DSW_InitOutputBuffer( dsw,
- (unsigned long) (sampleRate + 0.5),
- (WORD)outputParameters->channelCount, bytesPerDirectSoundBuffer );
- DBUG(("DSW_InitOutputBuffer() returns %x\n", hr));
- if( hr != DS_OK )
- {
- result = paUnanticipatedHostError;
- PA_DS_SET_LAST_DIRECTSOUND_ERROR( hr );
- goto error;
- }
- /* Calculate value used in latency calculation to avoid real-time divides. */
- stream->secondsPerHostByte = 1.0 /
- (stream->bufferProcessor.bytesPerHostOutputSample *
- outputChannelCount * sampleRate);
- }
-
- /* ------------------ INPUT */
- if( inputParameters )
- {
- /*
- PaDeviceInfo *deviceInfo = hostApi->deviceInfos[ inputParameters->device ];
- DBUG(("PaHost_OpenStream: deviceID = 0x%x\n", inputParameters->device));
- */
-
- bytesPerDirectSoundBuffer = stream->framesPerDSBuffer * inputParameters->channelCount * sizeof(short);
- if( bytesPerDirectSoundBuffer < DSBSIZE_MIN )
- {
- result = paBufferTooSmall;
- goto error;
- }
- else if( bytesPerDirectSoundBuffer > DSBSIZE_MAX )
- {
- result = paBufferTooBig;
- goto error;
- }
-
- hr = dswDSoundEntryPoints.DirectSoundCaptureCreate( winDsHostApi->winDsDeviceInfos[inputParameters->device].lpGUID,
- &dsw->dsw_pDirectSoundCapture, NULL );
- if( hr != DS_OK )
- {
- ERR_RPT(("PortAudio: DirectSoundCaptureCreate() failed!\n"));
- result = paUnanticipatedHostError;
- PA_DS_SET_LAST_DIRECTSOUND_ERROR( hr );
- goto error;
- }
- hr = DSW_InitInputBuffer( dsw,
- (unsigned long) (sampleRate + 0.5),
- (WORD)inputParameters->channelCount, bytesPerDirectSoundBuffer );
- DBUG(("DSW_InitInputBuffer() returns %x\n", hr));
- if( hr != DS_OK )
- {
- ERR_RPT(("PortAudio: DSW_InitInputBuffer() returns %x\n", hr));
- result = paUnanticipatedHostError;
- PA_DS_SET_LAST_DIRECTSOUND_ERROR( hr );
- goto error;
- }
- }
-
- }
-
- *s = (PaStream*)stream;
-
- return result;
-
-error:
- if( stream )
- PaUtil_FreeMemory( stream );
-
- return result;
-}
-
-
-/***********************************************************************************/
-static PaError Pa_TimeSlice( PaWinDsStream *stream )
-{
- PaError result = 0; /* FIXME: this should be declared int and this function should also return that type (same as stream callback return type)*/
- DSoundWrapper *dsw;
- long numFrames = 0;
- long bytesEmpty = 0;
- long bytesFilled = 0;
- long bytesToXfer = 0;
- long framesToXfer = 0;
- long numInFramesReady = 0;
- long numOutFramesReady = 0;
- long bytesProcessed;
- HRESULT hresult;
- double outputLatency = 0;
- PaStreamCallbackTimeInfo timeInfo = {0,0,0}; /** @todo implement inputBufferAdcTime */
-
-/* Input */
- LPBYTE lpInBuf1 = NULL;
- LPBYTE lpInBuf2 = NULL;
- DWORD dwInSize1 = 0;
- DWORD dwInSize2 = 0;
-/* Output */
- LPBYTE lpOutBuf1 = NULL;
- LPBYTE lpOutBuf2 = NULL;
- DWORD dwOutSize1 = 0;
- DWORD dwOutSize2 = 0;
-
- dsw = &stream->directSoundWrapper;
-
- /* How much input data is available? */
- if( stream->bufferProcessor.inputChannelCount > 0 )
- {
- DSW_QueryInputFilled( dsw, &bytesFilled );
- framesToXfer = numInFramesReady = bytesFilled / dsw->dsw_BytesPerInputFrame;
- outputLatency = ((double)bytesFilled) * stream->secondsPerHostByte;
-
- /** @todo Check for overflow */
- }
-
- /* How much output room is available? */
- if( stream->bufferProcessor.outputChannelCount > 0 )
- {
- UINT previousUnderflowCount = dsw->dsw_OutputUnderflows;
- DSW_QueryOutputSpace( dsw, &bytesEmpty );
- framesToXfer = numOutFramesReady = bytesEmpty / dsw->dsw_BytesPerOutputFrame;
-
- /* Check for underflow */
- if( dsw->dsw_OutputUnderflows != previousUnderflowCount )
- stream->callbackFlags |= paOutputUnderflow;
- }
-
- if( (numInFramesReady > 0) && (numOutFramesReady > 0) )
- {
- framesToXfer = (numOutFramesReady < numInFramesReady) ? numOutFramesReady : numInFramesReady;
- }
-
- if( framesToXfer > 0 )
- {
-
- PaUtil_BeginCpuLoadMeasurement( &stream->cpuLoadMeasurer );
-
- /* The outputBufferDacTime parameter should indicates the time at which
- the first sample of the output buffer is heard at the DACs. */
- timeInfo.currentTime = PaUtil_GetTime();
- timeInfo.outputBufferDacTime = timeInfo.currentTime + outputLatency;
-
-
- PaUtil_BeginBufferProcessing( &stream->bufferProcessor, &timeInfo, stream->callbackFlags );
- stream->callbackFlags = 0;
-
- /* Input */
- if( stream->bufferProcessor.inputChannelCount > 0 )
- {
- bytesToXfer = framesToXfer * dsw->dsw_BytesPerInputFrame;
- hresult = IDirectSoundCaptureBuffer_Lock ( dsw->dsw_InputBuffer,
- dsw->dsw_ReadOffset, bytesToXfer,
- (void **) &lpInBuf1, &dwInSize1,
- (void **) &lpInBuf2, &dwInSize2, 0);
- if (hresult != DS_OK)
- {
- ERR_RPT(("DirectSound IDirectSoundCaptureBuffer_Lock failed, hresult = 0x%x\n",hresult));
- result = paUnanticipatedHostError;
- PA_DS_SET_LAST_DIRECTSOUND_ERROR( hresult );
- goto error2;
- }
-
- numFrames = dwInSize1 / dsw->dsw_BytesPerInputFrame;
- PaUtil_SetInputFrameCount( &stream->bufferProcessor, numFrames );
- PaUtil_SetInterleavedInputChannels( &stream->bufferProcessor, 0, lpInBuf1, 0 );
- /* Is input split into two regions. */
- if( dwInSize2 > 0 )
- {
- numFrames = dwInSize2 / dsw->dsw_BytesPerInputFrame;
- PaUtil_Set2ndInputFrameCount( &stream->bufferProcessor, numFrames );
- PaUtil_Set2ndInterleavedInputChannels( &stream->bufferProcessor, 0, lpInBuf2, 0 );
- }
- }
-
- /* Output */
- if( stream->bufferProcessor.outputChannelCount > 0 )
- {
- bytesToXfer = framesToXfer * dsw->dsw_BytesPerOutputFrame;
- hresult = IDirectSoundBuffer_Lock ( dsw->dsw_OutputBuffer,
- dsw->dsw_WriteOffset, bytesToXfer,
- (void **) &lpOutBuf1, &dwOutSize1,
- (void **) &lpOutBuf2, &dwOutSize2, 0);
- if (hresult != DS_OK)
- {
- ERR_RPT(("DirectSound IDirectSoundBuffer_Lock failed, hresult = 0x%x\n",hresult));
- result = paUnanticipatedHostError;
- PA_DS_SET_LAST_DIRECTSOUND_ERROR( hresult );
- goto error1;
- }
-
- numFrames = dwOutSize1 / dsw->dsw_BytesPerOutputFrame;
- PaUtil_SetOutputFrameCount( &stream->bufferProcessor, numFrames );
- PaUtil_SetInterleavedOutputChannels( &stream->bufferProcessor, 0, lpOutBuf1, 0 );
-
- /* Is output split into two regions. */
- if( dwOutSize2 > 0 )
- {
- numFrames = dwOutSize2 / dsw->dsw_BytesPerOutputFrame;
- PaUtil_Set2ndOutputFrameCount( &stream->bufferProcessor, numFrames );
- PaUtil_Set2ndInterleavedOutputChannels( &stream->bufferProcessor, 0, lpOutBuf2, 0 );
- }
- }
-
- result = paContinue;
- numFrames = PaUtil_EndBufferProcessing( &stream->bufferProcessor, &result );
- stream->framesWritten += numFrames;
-
- if( stream->bufferProcessor.outputChannelCount > 0 )
- {
- /* FIXME: an underflow could happen here */
-
- /* Update our buffer offset and unlock sound buffer */
- bytesProcessed = numFrames * dsw->dsw_BytesPerOutputFrame;
- dsw->dsw_WriteOffset = (dsw->dsw_WriteOffset + bytesProcessed) % dsw->dsw_OutputSize;
- IDirectSoundBuffer_Unlock( dsw->dsw_OutputBuffer, lpOutBuf1, dwOutSize1, lpOutBuf2, dwOutSize2);
- dsw->dsw_FramesWritten += numFrames;
- }
-
-error1:
- if( stream->bufferProcessor.inputChannelCount > 0 )
- {
- /* FIXME: an overflow could happen here */
-
- /* Update our buffer offset and unlock sound buffer */
- bytesProcessed = numFrames * dsw->dsw_BytesPerInputFrame;
- dsw->dsw_ReadOffset = (dsw->dsw_ReadOffset + bytesProcessed) % dsw->dsw_InputSize;
- IDirectSoundCaptureBuffer_Unlock( dsw->dsw_InputBuffer, lpInBuf1, dwInSize1, lpInBuf2, dwInSize2);
- }
-error2:
-
- PaUtil_EndCpuLoadMeasurement( &stream->cpuLoadMeasurer, numFrames );
-
- }
-
- return result;
-}
-/*******************************************************************/
-static void CALLBACK Pa_TimerCallback(UINT uID, UINT uMsg, DWORD dwUser, DWORD dw1, DWORD dw2)
-{
- PaWinDsStream *stream;
-
- /* suppress unused variable warnings */
- (void) uID;
- (void) uMsg;
- (void) dw1;
- (void) dw2;
-
- stream = (PaWinDsStream *) dwUser;
- if( stream == NULL ) return;
-
- if( stream->isActive )
- {
- if( stream->abortProcessing )
- {
- stream->isActive = 0;
- }
- else if( stream->stopProcessing )
- {
- DSoundWrapper *dsw = &stream->directSoundWrapper;
- if( stream->bufferProcessor.outputChannelCount > 0 )
- {
- DSW_ZeroEmptySpace( dsw );
- /* clear isActive when all sound played */
- if( dsw->dsw_FramesPlayed >= stream->framesWritten )
- {
- stream->isActive = 0;
- }
- }
- else
- {
- stream->isActive = 0;
- }
- }
- else
- {
- if( Pa_TimeSlice( stream ) != 0) /* Call time slice independant of timing method. */
- {
- /* FIXME implement handling of paComplete and paAbort if possible */
- stream->stopProcessing = 1;
- }
- }
-
- if( !stream->isActive ){
- if( stream->streamRepresentation.streamFinishedCallback != 0 )
- stream->streamRepresentation.streamFinishedCallback( stream->streamRepresentation.userData );
- }
- }
-}
-
-/***********************************************************************************
- When CloseStream() is called, the multi-api layer ensures that
- the stream has already been stopped or aborted.
-*/
-static PaError CloseStream( PaStream* s )
-{
- PaError result = paNoError;
- PaWinDsStream *stream = (PaWinDsStream*)s;
-
- DSW_Term( &stream->directSoundWrapper );
-
- PaUtil_TerminateBufferProcessor( &stream->bufferProcessor );
- PaUtil_TerminateStreamRepresentation( &stream->streamRepresentation );
- PaUtil_FreeMemory( stream );
-
- return result;
-}
-
-/***********************************************************************************/
-static PaError StartStream( PaStream *s )
-{
- PaError result = paNoError;
- PaWinDsStream *stream = (PaWinDsStream*)s;
- HRESULT hr;
-
- PaUtil_ResetBufferProcessor( &stream->bufferProcessor );
-
- if( stream->bufferProcessor.inputChannelCount > 0 )
- {
- hr = DSW_StartInput( &stream->directSoundWrapper );
- DBUG(("StartStream: DSW_StartInput returned = 0x%X.\n", hr));
- if( hr != DS_OK )
- {
- result = paUnanticipatedHostError;
- PA_DS_SET_LAST_DIRECTSOUND_ERROR( hr );
- goto error;
- }
- }
-
- stream->framesWritten = 0;
- stream->callbackFlags = 0;
-
- stream->abortProcessing = 0;
- stream->stopProcessing = 0;
- stream->isActive = 1;
-
- if( stream->bufferProcessor.outputChannelCount > 0 )
- {
- /* Give user callback a chance to pre-fill buffer. REVIEW - i thought we weren't pre-filling, rb. */
- result = Pa_TimeSlice( stream );
- if( result != paNoError ) return result; // FIXME - what if finished?
-
- hr = DSW_StartOutput( &stream->directSoundWrapper );
- DBUG(("PaHost_StartOutput: DSW_StartOutput returned = 0x%X.\n", hr));
- if( hr != DS_OK )
- {
- result = paUnanticipatedHostError;
- PA_DS_SET_LAST_DIRECTSOUND_ERROR( hr );
- goto error;
- }
- }
-
-
- /* Create timer that will wake us up so we can fill the DSound buffer. */
- {
- int resolution;
- int framesPerWakeup = stream->framesPerDSBuffer / 4;
- int msecPerWakeup = MSEC_PER_SECOND * framesPerWakeup / (int) stream->streamRepresentation.streamInfo.sampleRate;
- if( msecPerWakeup < 10 ) msecPerWakeup = 10;
- else if( msecPerWakeup > 100 ) msecPerWakeup = 100;
- resolution = msecPerWakeup/4;
- stream->timerID = timeSetEvent( msecPerWakeup, resolution, (LPTIMECALLBACK) Pa_TimerCallback,
- (DWORD) stream, TIME_PERIODIC );
- }
- if( stream->timerID == 0 )
- {
- stream->isActive = 0;
- result = paUnanticipatedHostError;
- PA_DS_SET_LAST_DIRECTSOUND_ERROR( hr );
- goto error;
- }
-
- stream->isStarted = TRUE;
-
-error:
- return result;
-}
-
-
-/***********************************************************************************/
-static PaError StopStream( PaStream *s )
-{
- PaError result = paNoError;
- PaWinDsStream *stream = (PaWinDsStream*)s;
- HRESULT hr;
- int timeoutMsec;
-
- stream->stopProcessing = 1;
- /* Set timeout at 20% beyond maximum time we might wait. */
- timeoutMsec = (int) (1200.0 * stream->framesPerDSBuffer / stream->streamRepresentation.streamInfo.sampleRate);
- while( stream->isActive && (timeoutMsec > 0) )
- {
- Sleep(10);
- timeoutMsec -= 10;
- }
- if( stream->timerID != 0 )
- {
- timeKillEvent(stream->timerID); /* Stop callback timer. */
- stream->timerID = 0;
- }
-
-
- if( stream->bufferProcessor.outputChannelCount > 0 )
- {
- hr = DSW_StopOutput( &stream->directSoundWrapper );
- }
-
- if( stream->bufferProcessor.inputChannelCount > 0 )
- {
- hr = DSW_StopInput( &stream->directSoundWrapper );
- }
-
- stream->isStarted = FALSE;
-
- return result;
-}
-
-
-/***********************************************************************************/
-static PaError AbortStream( PaStream *s )
-{
- PaWinDsStream *stream = (PaWinDsStream*)s;
-
- stream->abortProcessing = 1;
- return StopStream( s );
-}
-
-
-/***********************************************************************************/
-static PaError IsStreamStopped( PaStream *s )
-{
- PaWinDsStream *stream = (PaWinDsStream*)s;
-
- return !stream->isStarted;
-}
-
-
-/***********************************************************************************/
-static PaError IsStreamActive( PaStream *s )
-{
- PaWinDsStream *stream = (PaWinDsStream*)s;
-
- return stream->isActive;
-}
-
-/***********************************************************************************/
-static PaTime GetStreamTime( PaStream *s )
-{
- /* suppress unused variable warnings */
- (void) s;
-
-
-/*
- new behavior for GetStreamTime is to return a stream based seconds clock
- used for the outTime parameter to the callback.
- FIXME: delete this comment when the other unnecessary related code has
- been cleaned from this file.
-
- PaWinDsStream *stream = (PaWinDsStream*)s;
- DSoundWrapper *dsw;
- dsw = &stream->directSoundWrapper;
- return dsw->dsw_FramesPlayed;
-*/
- return PaUtil_GetTime();
-}
-
-
-/***********************************************************************************/
-static double GetStreamCpuLoad( PaStream* s )
-{
- PaWinDsStream *stream = (PaWinDsStream*)s;
-
- return PaUtil_GetCpuLoad( &stream->cpuLoadMeasurer );
-}
-
-
-
-/***********************************************************************************
- As separate stream interfaces are used for blocking and callback
- streams, the following functions can be guaranteed to only be called
- for blocking streams.
-*/
-
-static PaError ReadStream( PaStream* s,
- void *buffer,
- unsigned long frames )
-{
- PaWinDsStream *stream = (PaWinDsStream*)s;
-
- /* suppress unused variable warnings */
- (void) buffer;
- (void) frames;
- (void) stream;
-
- /* IMPLEMENT ME, see portaudio.h for required behavior*/
-
- return paNoError;
-}
-
-
-/***********************************************************************************/
-static PaError WriteStream( PaStream* s,
- const void *buffer,
- unsigned long frames )
-{
- PaWinDsStream *stream = (PaWinDsStream*)s;
-
- /* suppress unused variable warnings */
- (void) buffer;
- (void) frames;
- (void) stream;
-
- /* IMPLEMENT ME, see portaudio.h for required behavior*/
-
- return paNoError;
-}
-
-
-/***********************************************************************************/
-static signed long GetStreamReadAvailable( PaStream* s )
-{
- PaWinDsStream *stream = (PaWinDsStream*)s;
-
- /* suppress unused variable warnings */
- (void) stream;
-
- /* IMPLEMENT ME, see portaudio.h for required behavior*/
-
- return 0;
-}
-
-
-/***********************************************************************************/
-static signed long GetStreamWriteAvailable( PaStream* s )
-{
- PaWinDsStream *stream = (PaWinDsStream*)s;
-
- /* suppress unused variable warnings */
- (void) stream;
-
- /* IMPLEMENT ME, see portaudio.h for required behavior*/
-
- return 0;
-}
-
-
-
+/*
+ * $Id: pa_win_ds.c,v 1.1.2.49 2004/05/16 04:08:55 rossbencina Exp $
+ * Portable Audio I/O Library DirectSound implementation
+ *
+ * Based on the Open Source API proposed by Ross Bencina
+ * Copyright (c) 1999-2002 Ross Bencina, Phil Burk
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * Any person wishing to distribute modifications to the Software is
+ * requested to send the modifications to the original developer so that
+ * they can be incorporated into the canonical version.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+ * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+/*
+ * PJMEDIA - Multimedia over IP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/** @file
+
+ @todo implement paInputOverflow callback status flag
+
+ @todo implement paNeverDropInput.
+
+ @todo implement host api specific extension to set i/o buffer sizes in frames
+
+ @todo implement initialisation of PaDeviceInfo default*Latency fields (currently set to 0.)
+
+ @todo implement ReadStream, WriteStream, GetStreamReadAvailable, GetStreamWriteAvailable
+
+ @todo audit handling of DirectSound result codes - in many cases we could convert a HRESULT into
+ a native portaudio error code. Standard DirectSound result codes are documented at msdn.
+
+ @todo implement IsFormatSupported
+
+ @todo check that CoInitialize() CoUninitialize() are always correctly
+ paired, even in error cases.
+
+ @todo call PaUtil_SetLastHostErrorInfo with a specific error string (currently just "DSound error").
+
+ @todo make sure all buffers have been played before stopping the stream
+ when the stream callback returns paComplete
+
+ old TODOs from phil, need to work out if these have been done:
+ O- fix "patest_stop.c"
+*/
+
+#include <stdio.h>
+#include <string.h> /* strlen() */
+
+#include "pa_util.h"
+#include "pa_allocation.h"
+#include "pa_hostapi.h"
+#include "pa_stream.h"
+#include "pa_cpuload.h"
+#include "pa_process.h"
+
+#include "dsound_wrapper.h"
+
+#if (defined(WIN32) && (defined(_MSC_VER) && (_MSC_VER >= 1200))) /* MSC version 6 and above */
+#pragma comment( lib, "dsound.lib" )
+#pragma comment( lib, "winmm.lib" )
+#endif
+
+
+#define PRINT(x) /* { printf x; fflush(stdout); } */
+#define ERR_RPT(x) PRINT(x)
+#define DBUG(x) /* PRINT(x) */
+#define DBUGX(x) /* PRINT(x) */
+
+#define PA_USE_HIGH_LATENCY (0)
+#if PA_USE_HIGH_LATENCY
+#define PA_WIN_9X_LATENCY (500)
+#define PA_WIN_NT_LATENCY (600)
+#else
+#define PA_WIN_9X_LATENCY (140)
+#define PA_WIN_NT_LATENCY (280)
+#endif
+
+#define PA_WIN_WDM_LATENCY (120)
+
+#define SECONDS_PER_MSEC (0.001)
+#define MSEC_PER_SECOND (1000)
+
+/* prototypes for functions declared in this file */
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif /* __cplusplus */
+
+PaError PaWinDs_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index );
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+static void Terminate( struct PaUtilHostApiRepresentation *hostApi );
+static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
+ PaStream** s,
+ const PaStreamParameters *inputParameters,
+ const PaStreamParameters *outputParameters,
+ double sampleRate,
+ unsigned long framesPerBuffer,
+ PaStreamFlags streamFlags,
+ PaStreamCallback *streamCallback,
+ void *userData );
+static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
+ const PaStreamParameters *inputParameters,
+ const PaStreamParameters *outputParameters,
+ double sampleRate );
+static PaError CloseStream( PaStream* stream );
+static PaError StartStream( PaStream *stream );
+static PaError StopStream( PaStream *stream );
+static PaError AbortStream( PaStream *stream );
+static PaError IsStreamStopped( PaStream *s );
+static PaError IsStreamActive( PaStream *stream );
+static PaTime GetStreamTime( PaStream *stream );
+static double GetStreamCpuLoad( PaStream* stream );
+static PaError ReadStream( PaStream* stream, void *buffer, unsigned long frames );
+static PaError WriteStream( PaStream* stream, const void *buffer, unsigned long frames );
+static signed long GetStreamReadAvailable( PaStream* stream );
+static signed long GetStreamWriteAvailable( PaStream* stream );
+
+
+/* FIXME: should convert hr to a string */
+#define PA_DS_SET_LAST_DIRECTSOUND_ERROR( hr ) \
+ PaUtil_SetLastHostErrorInfo( paDirectSound, hr, "DirectSound error" )
+
+/************************************************* DX Prototypes **********/
+static BOOL CALLBACK CollectGUIDsProc(LPGUID lpGUID,
+ LPCTSTR lpszDesc,
+ LPCTSTR lpszDrvName,
+ LPVOID lpContext );
+
+/************************************************************************************/
+/********************** Structures **************************************************/
+/************************************************************************************/
+/* PaWinDsHostApiRepresentation - host api datastructure specific to this implementation */
+
+typedef struct PaWinDsDeviceInfo
+{
+ GUID guid;
+ GUID *lpGUID;
+ double sampleRates[3];
+} PaWinDsDeviceInfo;
+
+typedef struct
+{
+ PaUtilHostApiRepresentation inheritedHostApiRep;
+ PaUtilStreamInterface callbackStreamInterface;
+ PaUtilStreamInterface blockingStreamInterface;
+
+ PaUtilAllocationGroup *allocations;
+
+ /* implementation specific data goes here */
+ PaWinDsDeviceInfo *winDsDeviceInfos;
+
+} PaWinDsHostApiRepresentation;
+
+/* PaWinDsStream - a stream data structure specifically for this implementation */
+
+typedef struct PaWinDsStream
+{
+ PaUtilStreamRepresentation streamRepresentation;
+ PaUtilCpuLoadMeasurer cpuLoadMeasurer;
+ PaUtilBufferProcessor bufferProcessor;
+
+/* DirectSound specific data. */
+ DSoundWrapper directSoundWrapper;
+ MMRESULT timerID;
+ BOOL ifInsideCallback; /* Test for reentrancy. */
+ int framesPerDSBuffer;
+ double framesWritten;
+ double secondsPerHostByte; /* Used to optimize latency calculation for outTime */
+
+ PaStreamCallbackFlags callbackFlags;
+
+/* FIXME - move all below to PaUtilStreamRepresentation */
+ volatile int isStarted;
+ volatile int isActive;
+ volatile int stopProcessing; /* stop thread once existing buffers have been returned */
+ volatile int abortProcessing; /* stop thread immediately */
+} PaWinDsStream;
+
+
+/************************************************************************************
+** Duplicate the input string using the allocations allocator.
+** A NULL string is converted to a zero length string.
+** If memory cannot be allocated, NULL is returned.
+**/
+static char *DuplicateDeviceNameString( PaUtilAllocationGroup *allocations, const char* src )
+{
+ char *result = 0;
+
+ if( src != NULL )
+ {
+ size_t len = strlen(src);
+ result = (char*)PaUtil_GroupAllocateMemory( allocations, (long)(len + 1) );
+ if( result )
+ memcpy( (void *) result, src, len+1 );
+ }
+ else
+ {
+ result = (char*)PaUtil_GroupAllocateMemory( allocations, 1 );
+ if( result )
+ result[0] = '\0';
+ }
+
+ return result;
+}
+
+/************************************************************************************
+** DSDeviceNameAndGUID, DSDeviceNameAndGUIDVector used for collecting preliminary
+** information during device enumeration.
+*/
+typedef struct DSDeviceNameAndGUID{
+ char *name; // allocated from parent's allocations, never deleted by this structure
+ GUID guid;
+ LPGUID lpGUID;
+} DSDeviceNameAndGUID;
+
+typedef struct DSDeviceNameAndGUIDVector{
+ PaUtilAllocationGroup *allocations;
+ PaError enumerationError;
+
+ int count;
+ int free;
+ DSDeviceNameAndGUID *items; // Allocated using LocalAlloc()
+} DSDeviceNameAndGUIDVector;
+
+static PaError InitializeDSDeviceNameAndGUIDVector(
+ DSDeviceNameAndGUIDVector *guidVector, PaUtilAllocationGroup *allocations )
+{
+ PaError result = paNoError;
+
+ guidVector->allocations = allocations;
+ guidVector->enumerationError = paNoError;
+
+ guidVector->count = 0;
+ guidVector->free = 8;
+ guidVector->items = (DSDeviceNameAndGUID*)LocalAlloc( LMEM_FIXED, sizeof(DSDeviceNameAndGUID) * guidVector->free );
+ if( guidVector->items == NULL )
+ result = paInsufficientMemory;
+
+ return result;
+}
+
+static PaError ExpandDSDeviceNameAndGUIDVector( DSDeviceNameAndGUIDVector *guidVector )
+{
+ PaError result = paNoError;
+ DSDeviceNameAndGUID *newItems;
+ int i;
+
+ /* double size of vector */
+ int size = guidVector->count + guidVector->free;
+ guidVector->free += size;
+
+ newItems = (DSDeviceNameAndGUID*)LocalAlloc( LMEM_FIXED, sizeof(DSDeviceNameAndGUID) * size * 2 );
+ if( newItems == NULL )
+ {
+ result = paInsufficientMemory;
+ }
+ else
+ {
+ for( i=0; i < guidVector->count; ++i )
+ {
+ newItems[i].name = guidVector->items[i].name;
+ if( guidVector->items[i].lpGUID == NULL )
+ {
+ newItems[i].lpGUID = NULL;
+ }
+ else
+ {
+ newItems[i].lpGUID = &newItems[i].guid;
+ memcpy( &newItems[i].guid, guidVector->items[i].lpGUID, sizeof(GUID) );;
+ }
+ }
+
+ LocalFree( guidVector->items );
+ guidVector->items = newItems;
+ }
+
+ return result;
+}
+
+/*
+ it's safe to call DSDeviceNameAndGUIDVector multiple times
+*/
+static PaError TerminateDSDeviceNameAndGUIDVector( DSDeviceNameAndGUIDVector *guidVector )
+{
+ PaError result = paNoError;
+
+ if( guidVector->items != NULL )
+ {
+ if( LocalFree( guidVector->items ) != NULL )
+ result = paInsufficientMemory; /** @todo this isn't the correct error to return from a deallocation failure */
+
+ guidVector->items = NULL;
+ }
+
+ return result;
+}
+
+/************************************************************************************
+** Collect preliminary device information during DirectSound enumeration
+*/
+static BOOL CALLBACK CollectGUIDsProc(LPGUID lpGUID,
+ LPCTSTR lpszDesc,
+ LPCTSTR lpszDrvName,
+ LPVOID lpContext )
+{
+ DSDeviceNameAndGUIDVector *namesAndGUIDs = (DSDeviceNameAndGUIDVector*)lpContext;
+ PaError error;
+
+ (void) lpszDrvName; /* unused variable */
+
+ if( namesAndGUIDs->free == 0 )
+ {
+ error = ExpandDSDeviceNameAndGUIDVector( namesAndGUIDs );
+ if( error != paNoError )
+ {
+ namesAndGUIDs->enumerationError = error;
+ return FALSE;
+ }
+ }
+
+ /* Set GUID pointer, copy GUID to storage in DSDeviceNameAndGUIDVector. */
+ if( lpGUID == NULL )
+ {
+ namesAndGUIDs->items[namesAndGUIDs->count].lpGUID = NULL;
+ }
+ else
+ {
+ namesAndGUIDs->items[namesAndGUIDs->count].lpGUID =
+ &namesAndGUIDs->items[namesAndGUIDs->count].guid;
+
+ memcpy( &namesAndGUIDs->items[namesAndGUIDs->count].guid, lpGUID, sizeof(GUID) );
+ }
+
+ namesAndGUIDs->items[namesAndGUIDs->count].name =
+ DuplicateDeviceNameString( namesAndGUIDs->allocations, lpszDesc );
+ if( namesAndGUIDs->items[namesAndGUIDs->count].name == NULL )
+ {
+ namesAndGUIDs->enumerationError = paInsufficientMemory;
+ return FALSE;
+ }
+
+ ++namesAndGUIDs->count;
+ --namesAndGUIDs->free;
+
+ return TRUE;
+}
+
+
+#define PA_DEFAULTSAMPLERATESEARCHORDER_COUNT_ (13) /* must match array length below */
+static double defaultSampleRateSearchOrder_[] =
+ { 44100.0, 48000.0, 32000.0, 24000.0, 22050.0, 88200.0, 96000.0, 192000.0,
+ 16000.0, 12000.0, 11025.0, 9600.0, 8000.0 };
+
+
+/************************************************************************************
+** Extract capabilities from an output device, and add it to the device info list
+** if successful. This function assumes that there is enough room in the
+** device info list to accomodate all entries.
+**
+** The device will not be added to the device list if any errors are encountered.
+*/
+static PaError AddOutputDeviceInfoFromDirectSound(
+ PaWinDsHostApiRepresentation *winDsHostApi, char *name, LPGUID lpGUID )
+{
+ PaUtilHostApiRepresentation *hostApi = &winDsHostApi->inheritedHostApiRep;
+ PaDeviceInfo *deviceInfo = hostApi->deviceInfos[hostApi->info.deviceCount];
+ PaWinDsDeviceInfo *winDsDeviceInfo = &winDsHostApi->winDsDeviceInfos[hostApi->info.deviceCount];
+ HRESULT hr;
+ LPDIRECTSOUND lpDirectSound;
+ DSCAPS caps;
+ int deviceOK = TRUE;
+ PaError result = paNoError;
+ int i;
+
+ /* Copy GUID to the device info structure. Set pointer. */
+ if( lpGUID == NULL )
+ {
+ winDsDeviceInfo->lpGUID = NULL;
+ }
+ else
+ {
+ memcpy( &winDsDeviceInfo->guid, lpGUID, sizeof(GUID) );
+ winDsDeviceInfo->lpGUID = &winDsDeviceInfo->guid;
+ }
+
+
+ /* Create a DirectSound object for the specified GUID
+ Note that using CoCreateInstance doesn't work on windows CE.
+ */
+ hr = dswDSoundEntryPoints.DirectSoundCreate( lpGUID, &lpDirectSound, NULL );
+
+ /** try using CoCreateInstance because DirectSoundCreate was hanging under
+ some circumstances - note this was probably related to the
+ #define BOOL short bug which has now been fixed
+ @todo delete this comment and the following code once we've ensured
+ there is no bug.
+ */
+ /*
+ hr = CoCreateInstance( &CLSID_DirectSound, NULL, CLSCTX_INPROC_SERVER,
+ &IID_IDirectSound, (void**)&lpDirectSound );
+
+ if( hr == S_OK )
+ {
+ hr = IDirectSound_Initialize( lpDirectSound, lpGUID );
+ }
+ */
+
+ if( hr != DS_OK )
+ {
+ DBUG(("Cannot create DirectSound for %s. Result = 0x%x\n", name, hr ));
+ deviceOK = FALSE;
+ }
+ else
+ {
+ /* Query device characteristics. */
+ memset( &caps, 0, sizeof(caps) );
+ caps.dwSize = sizeof(caps);
+ hr = IDirectSound_GetCaps( lpDirectSound, &caps );
+ if( hr != DS_OK )
+ {
+ DBUG(("Cannot GetCaps() for DirectSound device %s. Result = 0x%x\n", name, hr ));
+ deviceOK = FALSE;
+ }
+ else
+ {
+
+#ifndef PA_NO_WMME
+ if( caps.dwFlags & DSCAPS_EMULDRIVER )
+ {
+ /* If WMME supported, then reject Emulated drivers because they are lousy. */
+ deviceOK = FALSE;
+ }
+#endif
+
+ if( deviceOK )
+ {
+ deviceInfo->maxInputChannels = 0;
+ /* Mono or stereo device? */
+ deviceInfo->maxOutputChannels = ( caps.dwFlags & DSCAPS_PRIMARYSTEREO ) ? 2 : 1;
+
+ deviceInfo->defaultLowInputLatency = 0.; /** @todo IMPLEMENT ME */
+ deviceInfo->defaultLowOutputLatency = 0.; /** @todo IMPLEMENT ME */
+ deviceInfo->defaultHighInputLatency = 0.; /** @todo IMPLEMENT ME */
+ deviceInfo->defaultHighOutputLatency = 0.; /** @todo IMPLEMENT ME */
+
+ /* initialize defaultSampleRate */
+
+ if( caps.dwFlags & DSCAPS_CONTINUOUSRATE )
+ {
+ /* initialize to caps.dwMaxSecondarySampleRate incase none of the standard rates match */
+ deviceInfo->defaultSampleRate = caps.dwMaxSecondarySampleRate;
+
+ for( i = 0; i < PA_DEFAULTSAMPLERATESEARCHORDER_COUNT_; ++i )
+ {
+ if( defaultSampleRateSearchOrder_[i] >= caps.dwMinSecondarySampleRate
+ && defaultSampleRateSearchOrder_[i] <= caps.dwMaxSecondarySampleRate ){
+
+ deviceInfo->defaultSampleRate = defaultSampleRateSearchOrder_[i];
+ break;
+ }
+ }
+ }
+ else if( caps.dwMinSecondarySampleRate == caps.dwMaxSecondarySampleRate )
+ {
+ if( caps.dwMinSecondarySampleRate == 0 )
+ {
+ /*
+ ** On my Thinkpad 380Z, DirectSoundV6 returns min-max=0 !!
+ ** But it supports continuous sampling.
+ ** So fake range of rates, and hope it really supports it.
+ */
+ deviceInfo->defaultSampleRate = 44100.0f;
+
+ DBUG(("PA - Reported rates both zero. Setting to fake values for device #%d\n", sDeviceIndex ));
+ }
+ else
+ {
+ deviceInfo->defaultSampleRate = caps.dwMaxSecondarySampleRate;
+ }
+ }
+ else if( (caps.dwMinSecondarySampleRate < 1000.0) && (caps.dwMaxSecondarySampleRate > 50000.0) )
+ {
+ /* The EWS88MT drivers lie, lie, lie. The say they only support two rates, 100 & 100000.
+ ** But we know that they really support a range of rates!
+ ** So when we see a ridiculous set of rates, assume it is a range.
+ */
+ deviceInfo->defaultSampleRate = 44100.0f;
+ DBUG(("PA - Sample rate range used instead of two odd values for device #%d\n", sDeviceIndex ));
+ }
+ else deviceInfo->defaultSampleRate = caps.dwMaxSecondarySampleRate;
+
+
+ //printf( "min %d max %d\n", caps.dwMinSecondarySampleRate, caps.dwMaxSecondarySampleRate );
+ // dwFlags | DSCAPS_CONTINUOUSRATE
+ }
+ }
+
+ IDirectSound_Release( lpDirectSound );
+ }
+
+ if( deviceOK )
+ {
+ deviceInfo->name = name;
+
+ if( lpGUID == NULL )
+ hostApi->info.defaultOutputDevice = hostApi->info.deviceCount;
+
+ hostApi->info.deviceCount++;
+ }
+
+ return result;
+}
+
+
+/************************************************************************************
+** Extract capabilities from an input device, and add it to the device info list
+** if successful. This function assumes that there is enough room in the
+** device info list to accomodate all entries.
+**
+** The device will not be added to the device list if any errors are encountered.
+*/
+static PaError AddInputDeviceInfoFromDirectSoundCapture(
+ PaWinDsHostApiRepresentation *winDsHostApi, char *name, LPGUID lpGUID )
+{
+ PaUtilHostApiRepresentation *hostApi = &winDsHostApi->inheritedHostApiRep;
+ PaDeviceInfo *deviceInfo = hostApi->deviceInfos[hostApi->info.deviceCount];
+ PaWinDsDeviceInfo *winDsDeviceInfo = &winDsHostApi->winDsDeviceInfos[hostApi->info.deviceCount];
+ HRESULT hr;
+ LPDIRECTSOUNDCAPTURE lpDirectSoundCapture;
+ DSCCAPS caps;
+ int deviceOK = TRUE;
+ PaError result = paNoError;
+
+ /* Copy GUID to the device info structure. Set pointer. */
+ if( lpGUID == NULL )
+ {
+ winDsDeviceInfo->lpGUID = NULL;
+ }
+ else
+ {
+ winDsDeviceInfo->lpGUID = &winDsDeviceInfo->guid;
+ memcpy( &winDsDeviceInfo->guid, lpGUID, sizeof(GUID) );
+ }
+
+
+ hr = dswDSoundEntryPoints.DirectSoundCaptureCreate( lpGUID, &lpDirectSoundCapture, NULL );
+
+ /** try using CoCreateInstance because DirectSoundCreate was hanging under
+ some circumstances - note this was probably related to the
+ #define BOOL short bug which has now been fixed
+ @todo delete this comment and the following code once we've ensured
+ there is no bug.
+ */
+ /*
+ hr = CoCreateInstance( &CLSID_DirectSoundCapture, NULL, CLSCTX_INPROC_SERVER,
+ &IID_IDirectSoundCapture, (void**)&lpDirectSoundCapture );
+ */
+ if( hr != DS_OK )
+ {
+ DBUG(("Cannot create Capture for %s. Result = 0x%x\n", name, hr ));
+ deviceOK = FALSE;
+ }
+ else
+ {
+ /* Query device characteristics. */
+ memset( &caps, 0, sizeof(caps) );
+ caps.dwSize = sizeof(caps);
+ hr = IDirectSoundCapture_GetCaps( lpDirectSoundCapture, &caps );
+ if( hr != DS_OK )
+ {
+ DBUG(("Cannot GetCaps() for Capture device %s. Result = 0x%x\n", name, hr ));
+ deviceOK = FALSE;
+ }
+ else
+ {
+#ifndef PA_NO_WMME
+ if( caps.dwFlags & DSCAPS_EMULDRIVER )
+ {
+ /* If WMME supported, then reject Emulated drivers because they are lousy. */
+ deviceOK = FALSE;
+ }
+#endif
+
+ if( deviceOK )
+ {
+ deviceInfo->maxInputChannels = caps.dwChannels;
+ deviceInfo->maxOutputChannels = 0;
+
+ deviceInfo->defaultLowInputLatency = 0.; /** @todo IMPLEMENT ME */
+ deviceInfo->defaultLowOutputLatency = 0.; /** @todo IMPLEMENT ME */
+ deviceInfo->defaultHighInputLatency = 0.; /** @todo IMPLEMENT ME */
+ deviceInfo->defaultHighOutputLatency = 0.; /** @todo IMPLEMENT ME */
+
+/* constants from a WINE patch by Francois Gouget, see:
+ http://www.winehq.com/hypermail/wine-patches/2003/01/0290.html
+
+ ---
+ Date: Fri, 14 May 2004 10:38:12 +0200 (CEST)
+ From: Francois Gouget <fgouget@ ... .fr>
+ To: Ross Bencina <rbencina@ ... .au>
+ Subject: Re: Permission to use wine 48/96 wave patch in BSD licensed library
+
+ [snip]
+
+ I give you permission to use the patch below under the BSD license.
+ http://www.winehq.com/hypermail/wine-patches/2003/01/0290.html
+
+ [snip]
+*/
+#ifndef WAVE_FORMAT_48M08
+#define WAVE_FORMAT_48M08 0x00001000 /* 48 kHz, Mono, 8-bit */
+#define WAVE_FORMAT_48S08 0x00002000 /* 48 kHz, Stereo, 8-bit */
+#define WAVE_FORMAT_48M16 0x00004000 /* 48 kHz, Mono, 16-bit */
+#define WAVE_FORMAT_48S16 0x00008000 /* 48 kHz, Stereo, 16-bit */
+#define WAVE_FORMAT_96M08 0x00010000 /* 96 kHz, Mono, 8-bit */
+#define WAVE_FORMAT_96S08 0x00020000 /* 96 kHz, Stereo, 8-bit */
+#define WAVE_FORMAT_96M16 0x00040000 /* 96 kHz, Mono, 16-bit */
+#define WAVE_FORMAT_96S16 0x00080000 /* 96 kHz, Stereo, 16-bit */
+#endif
+
+ /* defaultSampleRate */
+ if( caps.dwChannels == 2 )
+ {
+ if( caps.dwFormats & WAVE_FORMAT_4S16 )
+ deviceInfo->defaultSampleRate = 44100.0;
+ else if( caps.dwFormats & WAVE_FORMAT_48S16 )
+ deviceInfo->defaultSampleRate = 48000.0;
+ else if( caps.dwFormats & WAVE_FORMAT_2S16 )
+ deviceInfo->defaultSampleRate = 22050.0;
+ else if( caps.dwFormats & WAVE_FORMAT_1S16 )
+ deviceInfo->defaultSampleRate = 11025.0;
+ else if( caps.dwFormats & WAVE_FORMAT_96S16 )
+ deviceInfo->defaultSampleRate = 96000.0;
+ else
+ deviceInfo->defaultSampleRate = 0.;
+ }
+ else if( caps.dwChannels == 1 )
+ {
+ if( caps.dwFormats & WAVE_FORMAT_4M16 )
+ deviceInfo->defaultSampleRate = 44100.0;
+ else if( caps.dwFormats & WAVE_FORMAT_48M16 )
+ deviceInfo->defaultSampleRate = 48000.0;
+ else if( caps.dwFormats & WAVE_FORMAT_2M16 )
+ deviceInfo->defaultSampleRate = 22050.0;
+ else if( caps.dwFormats & WAVE_FORMAT_1M16 )
+ deviceInfo->defaultSampleRate = 11025.0;
+ else if( caps.dwFormats & WAVE_FORMAT_96M16 )
+ deviceInfo->defaultSampleRate = 96000.0;
+ else
+ deviceInfo->defaultSampleRate = 0.;
+ }
+ else deviceInfo->defaultSampleRate = 0.;
+ }
+ }
+
+ IDirectSoundCapture_Release( lpDirectSoundCapture );
+ }
+
+ if( deviceOK )
+ {
+ deviceInfo->name = name;
+
+ if( lpGUID == NULL )
+ hostApi->info.defaultInputDevice = hostApi->info.deviceCount;
+
+ hostApi->info.deviceCount++;
+ }
+
+ return result;
+}
+
+
+/***********************************************************************************/
+PaError PaWinDs_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex hostApiIndex )
+{
+ PaError result = paNoError;
+ int i, deviceCount;
+ PaWinDsHostApiRepresentation *winDsHostApi;
+ DSDeviceNameAndGUIDVector inputNamesAndGUIDs, outputNamesAndGUIDs;
+ PaDeviceInfo *deviceInfoArray;
+
+ HRESULT hr = CoInitialize(NULL); /** @todo: should uninitialize too */
+ if( FAILED(hr) ){
+ return paUnanticipatedHostError;
+ }
+
+ /* initialise guid vectors so they can be safely deleted on error */
+ inputNamesAndGUIDs.items = NULL;
+ outputNamesAndGUIDs.items = NULL;
+
+ DSW_InitializeDSoundEntryPoints();
+
+ winDsHostApi = (PaWinDsHostApiRepresentation*)PaUtil_AllocateMemory( sizeof(PaWinDsHostApiRepresentation) );
+ if( !winDsHostApi )
+ {
+ result = paInsufficientMemory;
+ goto error;
+ }
+
+ winDsHostApi->allocations = PaUtil_CreateAllocationGroup();
+ if( !winDsHostApi->allocations )
+ {
+ result = paInsufficientMemory;
+ goto error;
+ }
+
+ *hostApi = &winDsHostApi->inheritedHostApiRep;
+ (*hostApi)->info.structVersion = 1;
+ (*hostApi)->info.type = paDirectSound;
+ (*hostApi)->info.name = "Windows DirectSound";
+
+ (*hostApi)->info.deviceCount = 0;
+ (*hostApi)->info.defaultInputDevice = paNoDevice;
+ (*hostApi)->info.defaultOutputDevice = paNoDevice;
+
+
+/* DSound - enumerate devices to count them and to gather their GUIDs */
+
+
+ result = InitializeDSDeviceNameAndGUIDVector( &inputNamesAndGUIDs, winDsHostApi->allocations );
+ if( result != paNoError )
+ goto error;
+
+ result = InitializeDSDeviceNameAndGUIDVector( &outputNamesAndGUIDs, winDsHostApi->allocations );
+ if( result != paNoError )
+ goto error;
+
+ dswDSoundEntryPoints.DirectSoundCaptureEnumerate( (LPDSENUMCALLBACK)CollectGUIDsProc, (void *)&inputNamesAndGUIDs );
+
+ dswDSoundEntryPoints.DirectSoundEnumerate( (LPDSENUMCALLBACK)CollectGUIDsProc, (void *)&outputNamesAndGUIDs );
+
+ if( inputNamesAndGUIDs.enumerationError != paNoError )
+ {
+ result = inputNamesAndGUIDs.enumerationError;
+ goto error;
+ }
+
+ if( outputNamesAndGUIDs.enumerationError != paNoError )
+ {
+ result = outputNamesAndGUIDs.enumerationError;
+ goto error;
+ }
+
+ deviceCount = inputNamesAndGUIDs.count + outputNamesAndGUIDs.count;
+
+ if( deviceCount > 0 )
+ {
+ /* allocate array for pointers to PaDeviceInfo structs */
+ (*hostApi)->deviceInfos = (PaDeviceInfo**)PaUtil_GroupAllocateMemory(
+ winDsHostApi->allocations, sizeof(PaDeviceInfo*) * deviceCount );
+ if( !(*hostApi)->deviceInfos )
+ {
+ result = paInsufficientMemory;
+ goto error;
+ }
+
+ /* allocate all PaDeviceInfo structs in a contiguous block */
+ deviceInfoArray = (PaDeviceInfo*)PaUtil_GroupAllocateMemory(
+ winDsHostApi->allocations, sizeof(PaDeviceInfo) * deviceCount );
+ if( !deviceInfoArray )
+ {
+ result = paInsufficientMemory;
+ goto error;
+ }
+
+ /* allocate all DSound specific info structs in a contiguous block */
+ winDsHostApi->winDsDeviceInfos = (PaWinDsDeviceInfo*)PaUtil_GroupAllocateMemory(
+ winDsHostApi->allocations, sizeof(PaWinDsDeviceInfo) * deviceCount );
+ if( !winDsHostApi->winDsDeviceInfos )
+ {
+ result = paInsufficientMemory;
+ goto error;
+ }
+
+ for( i=0; i < deviceCount; ++i )
+ {
+ PaDeviceInfo *deviceInfo = &deviceInfoArray[i];
+ deviceInfo->structVersion = 2;
+ deviceInfo->hostApi = hostApiIndex;
+ deviceInfo->name = 0;
+ (*hostApi)->deviceInfos[i] = deviceInfo;
+ }
+
+ for( i=0; i< inputNamesAndGUIDs.count; ++i )
+ {
+ result = AddInputDeviceInfoFromDirectSoundCapture( winDsHostApi,
+ inputNamesAndGUIDs.items[i].name,
+ inputNamesAndGUIDs.items[i].lpGUID );
+ if( result != paNoError )
+ goto error;
+ }
+
+ for( i=0; i< outputNamesAndGUIDs.count; ++i )
+ {
+ result = AddOutputDeviceInfoFromDirectSound( winDsHostApi,
+ outputNamesAndGUIDs.items[i].name,
+ outputNamesAndGUIDs.items[i].lpGUID );
+ if( result != paNoError )
+ goto error;
+ }
+ }
+
+ result = TerminateDSDeviceNameAndGUIDVector( &inputNamesAndGUIDs );
+ if( result != paNoError )
+ goto error;
+
+ result = TerminateDSDeviceNameAndGUIDVector( &outputNamesAndGUIDs );
+ if( result != paNoError )
+ goto error;
+
+
+ (*hostApi)->Terminate = Terminate;
+ (*hostApi)->OpenStream = OpenStream;
+ (*hostApi)->IsFormatSupported = IsFormatSupported;
+
+ PaUtil_InitializeStreamInterface( &winDsHostApi->callbackStreamInterface, CloseStream, StartStream,
+ StopStream, AbortStream, IsStreamStopped, IsStreamActive,
+ GetStreamTime, GetStreamCpuLoad,
+ PaUtil_DummyRead, PaUtil_DummyWrite,
+ PaUtil_DummyGetReadAvailable, PaUtil_DummyGetWriteAvailable );
+
+ PaUtil_InitializeStreamInterface( &winDsHostApi->blockingStreamInterface, CloseStream, StartStream,
+ StopStream, AbortStream, IsStreamStopped, IsStreamActive,
+ GetStreamTime, PaUtil_DummyGetCpuLoad,
+ ReadStream, WriteStream, GetStreamReadAvailable, GetStreamWriteAvailable );
+
+ return result;
+
+error:
+ if( winDsHostApi )
+ {
+ if( winDsHostApi->allocations )
+ {
+ PaUtil_FreeAllAllocations( winDsHostApi->allocations );
+ PaUtil_DestroyAllocationGroup( winDsHostApi->allocations );
+ }
+
+ PaUtil_FreeMemory( winDsHostApi );
+ }
+
+ TerminateDSDeviceNameAndGUIDVector( &inputNamesAndGUIDs );
+ TerminateDSDeviceNameAndGUIDVector( &outputNamesAndGUIDs );
+
+ return result;
+}
+
+
+/***********************************************************************************/
+static void Terminate( struct PaUtilHostApiRepresentation *hostApi )
+{
+ PaWinDsHostApiRepresentation *winDsHostApi = (PaWinDsHostApiRepresentation*)hostApi;
+
+ /*
+ IMPLEMENT ME:
+ - clean up any resources not handled by the allocation group
+ */
+
+ if( winDsHostApi->allocations )
+ {
+ PaUtil_FreeAllAllocations( winDsHostApi->allocations );
+ PaUtil_DestroyAllocationGroup( winDsHostApi->allocations );
+ }
+
+ PaUtil_FreeMemory( winDsHostApi );
+
+ DSW_TerminateDSoundEntryPoints();
+
+ CoUninitialize();
+}
+
+
+/* Set minimal latency based on whether NT or Win95.
+ * NT has higher latency.
+ */
+static int PaWinDS_GetMinSystemLatency( void )
+{
+ int minLatencyMsec;
+ /* Set minimal latency based on whether NT or other OS.
+ * NT has higher latency.
+ */
+ OSVERSIONINFO osvi;
+ osvi.dwOSVersionInfoSize = sizeof( osvi );
+ GetVersionEx( &osvi );
+ DBUG(("PA - PlatformId = 0x%x\n", osvi.dwPlatformId ));
+ DBUG(("PA - MajorVersion = 0x%x\n", osvi.dwMajorVersion ));
+ DBUG(("PA - MinorVersion = 0x%x\n", osvi.dwMinorVersion ));
+ /* Check for NT */
+ if( (osvi.dwMajorVersion == 4) && (osvi.dwPlatformId == 2) )
+ {
+ minLatencyMsec = PA_WIN_NT_LATENCY;
+ }
+ else if(osvi.dwMajorVersion >= 5)
+ {
+ minLatencyMsec = PA_WIN_WDM_LATENCY;
+ }
+ else
+ {
+ minLatencyMsec = PA_WIN_9X_LATENCY;
+ }
+ return minLatencyMsec;
+}
+
+/***********************************************************************************/
+static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
+ const PaStreamParameters *inputParameters,
+ const PaStreamParameters *outputParameters,
+ double sampleRate )
+{
+ int inputChannelCount, outputChannelCount;
+ PaSampleFormat inputSampleFormat, outputSampleFormat;
+
+ if( inputParameters )
+ {
+ inputChannelCount = inputParameters->channelCount;
+ inputSampleFormat = inputParameters->sampleFormat;
+
+ /* unless alternate device specification is supported, reject the use of
+ paUseHostApiSpecificDeviceSpecification */
+
+ if( inputParameters->device == paUseHostApiSpecificDeviceSpecification )
+ return paInvalidDevice;
+
+ /* check that input device can support inputChannelCount */
+ if( inputChannelCount > hostApi->deviceInfos[ inputParameters->device ]->maxInputChannels )
+ return paInvalidChannelCount;
+
+ /* validate inputStreamInfo */
+ if( inputParameters->hostApiSpecificStreamInfo )
+ return paIncompatibleHostApiSpecificStreamInfo; /* this implementation doesn't use custom stream info */
+ }
+ else
+ {
+ inputChannelCount = 0;
+ }
+
+ if( outputParameters )
+ {
+ outputChannelCount = outputParameters->channelCount;
+ outputSampleFormat = outputParameters->sampleFormat;
+
+ /* unless alternate device specification is supported, reject the use of
+ paUseHostApiSpecificDeviceSpecification */
+
+ if( outputParameters->device == paUseHostApiSpecificDeviceSpecification )
+ return paInvalidDevice;
+
+ /* check that output device can support inputChannelCount */
+ if( outputChannelCount > hostApi->deviceInfos[ outputParameters->device ]->maxOutputChannels )
+ return paInvalidChannelCount;
+
+ /* validate outputStreamInfo */
+ if( outputParameters->hostApiSpecificStreamInfo )
+ return paIncompatibleHostApiSpecificStreamInfo; /* this implementation doesn't use custom stream info */
+ }
+ else
+ {
+ outputChannelCount = 0;
+ }
+
+ /*
+ IMPLEMENT ME:
+
+ - if a full duplex stream is requested, check that the combination
+ of input and output parameters is supported if necessary
+
+ - check that the device supports sampleRate
+
+ Because the buffer adapter handles conversion between all standard
+ sample formats, the following checks are only required if paCustomFormat
+ is implemented, or under some other unusual conditions.
+
+ - check that input device can support inputSampleFormat, or that
+ we have the capability to convert from outputSampleFormat to
+ a native format
+
+ - check that output device can support outputSampleFormat, or that
+ we have the capability to convert from outputSampleFormat to
+ a native format
+ */
+
+ return paFormatIsSupported;
+}
+
+
+/*************************************************************************
+** Determine minimum number of buffers required for this host based
+** on minimum latency. Latency can be optionally set by user by setting
+** an environment variable. For example, to set latency to 200 msec, put:
+**
+** set PA_MIN_LATENCY_MSEC=200
+**
+** in the AUTOEXEC.BAT file and reboot.
+** If the environment variable is not set, then the latency will be determined
+** based on the OS. Windows NT has higher latency than Win95.
+*/
+#define PA_LATENCY_ENV_NAME ("PA_MIN_LATENCY_MSEC")
+#define PA_ENV_BUF_SIZE (32)
+
+static int PaWinDs_GetMinLatencyFrames( double sampleRate )
+{
+ char envbuf[PA_ENV_BUF_SIZE];
+ DWORD hresult;
+ int minLatencyMsec = 0;
+
+ /* Let user determine minimal latency by setting environment variable. */
+ hresult = GetEnvironmentVariable( PA_LATENCY_ENV_NAME, envbuf, PA_ENV_BUF_SIZE );
+ if( (hresult > 0) && (hresult < PA_ENV_BUF_SIZE) )
+ {
+ minLatencyMsec = atoi( envbuf );
+ }
+ else
+ {
+ minLatencyMsec = PaWinDS_GetMinSystemLatency();
+#if PA_USE_HIGH_LATENCY
+ PRINT(("PA - Minimum Latency set to %d msec!\n", minLatencyMsec ));
+#endif
+
+ }
+
+ return (int) (minLatencyMsec * sampleRate * SECONDS_PER_MSEC);
+}
+
+#ifndef NDEBUG
+#define EZ = 0
+#else
+#define EZ
+#endif
+
+/***********************************************************************************/
+/* see pa_hostapi.h for a list of validity guarantees made about OpenStream parameters */
+
+static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
+ PaStream** s,
+ const PaStreamParameters *inputParameters,
+ const PaStreamParameters *outputParameters,
+ double sampleRate,
+ unsigned long framesPerBuffer,
+ PaStreamFlags streamFlags,
+ PaStreamCallback *streamCallback,
+ void *userData )
+{
+ PaError result = paNoError;
+ PaWinDsHostApiRepresentation *winDsHostApi = (PaWinDsHostApiRepresentation*)hostApi;
+ PaWinDsStream *stream = 0;
+ int inputChannelCount, outputChannelCount;
+ PaSampleFormat inputSampleFormat EZ, outputSampleFormat EZ;
+ PaSampleFormat hostInputSampleFormat EZ, hostOutputSampleFormat EZ;
+ unsigned long suggestedInputLatencyFrames, suggestedOutputLatencyFrames;
+
+ if( inputParameters )
+ {
+ inputChannelCount = inputParameters->channelCount;
+ inputSampleFormat = inputParameters->sampleFormat;
+ suggestedInputLatencyFrames = (unsigned long)(inputParameters->suggestedLatency * sampleRate);
+
+ /* IDEA: the following 3 checks could be performed by default by pa_front
+ unless some flag indicated otherwise */
+
+ /* unless alternate device specification is supported, reject the use of
+ paUseHostApiSpecificDeviceSpecification */
+ if( inputParameters->device == paUseHostApiSpecificDeviceSpecification )
+ return paInvalidDevice;
+
+ /* check that input device can support inputChannelCount */
+ if( inputChannelCount > hostApi->deviceInfos[ inputParameters->device ]->maxInputChannels )
+ return paInvalidChannelCount;
+
+ /* validate hostApiSpecificStreamInfo */
+ if( inputParameters->hostApiSpecificStreamInfo )
+ return paIncompatibleHostApiSpecificStreamInfo; /* this implementation doesn't use custom stream info */
+ }
+ else
+ {
+ inputChannelCount = 0;
+ suggestedInputLatencyFrames = 0;
+ }
+
+
+ if( outputParameters )
+ {
+ outputChannelCount = outputParameters->channelCount;
+ outputSampleFormat = outputParameters->sampleFormat;
+ suggestedOutputLatencyFrames = (unsigned long)(outputParameters->suggestedLatency * sampleRate);
+
+ /* unless alternate device specification is supported, reject the use of
+ paUseHostApiSpecificDeviceSpecification */
+ if( outputParameters->device == paUseHostApiSpecificDeviceSpecification )
+ return paInvalidDevice;
+
+ /* check that output device can support inputChannelCount */
+ if( outputChannelCount > hostApi->deviceInfos[ outputParameters->device ]->maxOutputChannels )
+ return paInvalidChannelCount;
+
+ /* validate hostApiSpecificStreamInfo */
+ if( outputParameters->hostApiSpecificStreamInfo )
+ return paIncompatibleHostApiSpecificStreamInfo; /* this implementation doesn't use custom stream info */
+ }
+ else
+ {
+ outputChannelCount = 0;
+ suggestedOutputLatencyFrames = 0;
+ }
+
+
+ /*
+ IMPLEMENT ME:
+
+ ( the following two checks are taken care of by PaUtil_InitializeBufferProcessor() )
+
+ - check that input device can support inputSampleFormat, or that
+ we have the capability to convert from outputSampleFormat to
+ a native format
+
+ - check that output device can support outputSampleFormat, or that
+ we have the capability to convert from outputSampleFormat to
+ a native format
+
+ - if a full duplex stream is requested, check that the combination
+ of input and output parameters is supported
+
+ - check that the device supports sampleRate
+
+ - alter sampleRate to a close allowable rate if possible / necessary
+
+ - validate suggestedInputLatency and suggestedOutputLatency parameters,
+ use default values where necessary
+ */
+
+
+ /* validate platform specific flags */
+ if( (streamFlags & paPlatformSpecificFlags) != 0 )
+ return paInvalidFlag; /* unexpected platform specific flag */
+
+
+ stream = (PaWinDsStream*)PaUtil_AllocateMemory( sizeof(PaWinDsStream) );
+ if( !stream )
+ {
+ result = paInsufficientMemory;
+ goto error;
+ }
+
+ if( streamCallback )
+ {
+ PaUtil_InitializeStreamRepresentation( &stream->streamRepresentation,
+ &winDsHostApi->callbackStreamInterface, streamCallback, userData );
+ }
+ else
+ {
+ PaUtil_InitializeStreamRepresentation( &stream->streamRepresentation,
+ &winDsHostApi->blockingStreamInterface, streamCallback, userData );
+ }
+
+ PaUtil_InitializeCpuLoadMeasurer( &stream->cpuLoadMeasurer, sampleRate );
+
+
+ if( inputParameters )
+ {
+ /* IMPLEMENT ME - establish which host formats are available */
+ hostInputSampleFormat =
+ PaUtil_SelectClosestAvailableFormat( paInt16 /* native formats */, inputParameters->sampleFormat );
+ }
+
+ if( outputParameters )
+ {
+ /* IMPLEMENT ME - establish which host formats are available */
+ hostOutputSampleFormat =
+ PaUtil_SelectClosestAvailableFormat( paInt16 /* native formats */, outputParameters->sampleFormat );
+ }
+
+ result = PaUtil_InitializeBufferProcessor( &stream->bufferProcessor,
+ inputChannelCount, inputSampleFormat, hostInputSampleFormat,
+ outputChannelCount, outputSampleFormat, hostOutputSampleFormat,
+ sampleRate, streamFlags, framesPerBuffer,
+ framesPerBuffer, /* ignored in paUtilVariableHostBufferSizePartialUsageAllowed mode. */
+ /* This next mode is required because DS can split the host buffer when it wraps around. */
+ paUtilVariableHostBufferSizePartialUsageAllowed,
+ streamCallback, userData );
+ if( result != paNoError )
+ goto error;
+
+
+ stream->streamRepresentation.streamInfo.inputLatency =
+ PaUtil_GetBufferProcessorInputLatency(&stream->bufferProcessor); /* FIXME: not initialised anywhere else */
+ stream->streamRepresentation.streamInfo.outputLatency =
+ PaUtil_GetBufferProcessorOutputLatency(&stream->bufferProcessor); /* FIXME: not initialised anywhere else */
+ stream->streamRepresentation.streamInfo.sampleRate = sampleRate;
+
+
+/* DirectSound specific initialization */
+ {
+ HRESULT hr;
+ int bytesPerDirectSoundBuffer;
+ DSoundWrapper *dsw;
+ int userLatencyFrames;
+ int minLatencyFrames;
+
+ stream->timerID = 0;
+ dsw = &stream->directSoundWrapper;
+ DSW_Init( dsw );
+
+ /* Get system minimum latency. */
+ minLatencyFrames = PaWinDs_GetMinLatencyFrames( sampleRate );
+
+ /* Let user override latency by passing latency parameter. */
+ userLatencyFrames = (suggestedInputLatencyFrames > suggestedOutputLatencyFrames)
+ ? suggestedInputLatencyFrames
+ : suggestedOutputLatencyFrames;
+ if( userLatencyFrames > 0 ) minLatencyFrames = userLatencyFrames;
+
+ /* Calculate stream->framesPerDSBuffer depending on framesPerBuffer */
+ if( framesPerBuffer == paFramesPerBufferUnspecified )
+ {
+ /* App support variable framesPerBuffer */
+ stream->framesPerDSBuffer = minLatencyFrames;
+
+ stream->streamRepresentation.streamInfo.outputLatency = (double)(minLatencyFrames - 1) / sampleRate;
+ }
+ else
+ {
+ /* Round up to number of buffers needed to guarantee that latency. */
+ int numUserBuffers = (minLatencyFrames + framesPerBuffer - 1) / framesPerBuffer;
+ if( numUserBuffers < 1 ) numUserBuffers = 1;
+ numUserBuffers += 1; /* So we have latency worth of buffers ahead of current buffer. */
+ stream->framesPerDSBuffer = framesPerBuffer * numUserBuffers;
+
+ stream->streamRepresentation.streamInfo.outputLatency = (double)(framesPerBuffer * (numUserBuffers-1)) / sampleRate;
+ }
+
+ {
+ /** @todo REVIEW: this calculation seems incorrect to me - rossb. */
+ int msecLatency = (int) ((stream->framesPerDSBuffer * MSEC_PER_SECOND) / sampleRate);
+ PRINT(("PortAudio on DirectSound - Latency = %d frames, %d msec\n", stream->framesPerDSBuffer, msecLatency ));
+ }
+
+
+ /* ------------------ OUTPUT */
+ if( outputParameters )
+ {
+ /*
+ PaDeviceInfo *deviceInfo = hostApi->deviceInfos[ outputParameters->device ];
+ DBUG(("PaHost_OpenStream: deviceID = 0x%x\n", outputParameters->device));
+ */
+
+ bytesPerDirectSoundBuffer = stream->framesPerDSBuffer * outputParameters->channelCount * sizeof(short);
+ if( bytesPerDirectSoundBuffer < DSBSIZE_MIN )
+ {
+ result = paBufferTooSmall;
+ goto error;
+ }
+ else if( bytesPerDirectSoundBuffer > DSBSIZE_MAX )
+ {
+ result = paBufferTooBig;
+ goto error;
+ }
+
+
+ hr = dswDSoundEntryPoints.DirectSoundCreate( winDsHostApi->winDsDeviceInfos[outputParameters->device].lpGUID,
+ &dsw->dsw_pDirectSound, NULL );
+ if( hr != DS_OK )
+ {
+ ERR_RPT(("PortAudio: DirectSoundCreate() failed!\n"));
+ result = paUnanticipatedHostError;
+ PA_DS_SET_LAST_DIRECTSOUND_ERROR( hr );
+ goto error;
+ }
+ hr = DSW_InitOutputBuffer( dsw,
+ (unsigned long) (sampleRate + 0.5),
+ (WORD)outputParameters->channelCount, bytesPerDirectSoundBuffer );
+ DBUG(("DSW_InitOutputBuffer() returns %x\n", hr));
+ if( hr != DS_OK )
+ {
+ result = paUnanticipatedHostError;
+ PA_DS_SET_LAST_DIRECTSOUND_ERROR( hr );
+ goto error;
+ }
+ /* Calculate value used in latency calculation to avoid real-time divides. */
+ stream->secondsPerHostByte = 1.0 /
+ (stream->bufferProcessor.bytesPerHostOutputSample *
+ outputChannelCount * sampleRate);
+ }
+
+ /* ------------------ INPUT */
+ if( inputParameters )
+ {
+ /*
+ PaDeviceInfo *deviceInfo = hostApi->deviceInfos[ inputParameters->device ];
+ DBUG(("PaHost_OpenStream: deviceID = 0x%x\n", inputParameters->device));
+ */
+
+ bytesPerDirectSoundBuffer = stream->framesPerDSBuffer * inputParameters->channelCount * sizeof(short);
+ if( bytesPerDirectSoundBuffer < DSBSIZE_MIN )
+ {
+ result = paBufferTooSmall;
+ goto error;
+ }
+ else if( bytesPerDirectSoundBuffer > DSBSIZE_MAX )
+ {
+ result = paBufferTooBig;
+ goto error;
+ }
+
+ hr = dswDSoundEntryPoints.DirectSoundCaptureCreate( winDsHostApi->winDsDeviceInfos[inputParameters->device].lpGUID,
+ &dsw->dsw_pDirectSoundCapture, NULL );
+ if( hr != DS_OK )
+ {
+ ERR_RPT(("PortAudio: DirectSoundCaptureCreate() failed!\n"));
+ result = paUnanticipatedHostError;
+ PA_DS_SET_LAST_DIRECTSOUND_ERROR( hr );
+ goto error;
+ }
+ hr = DSW_InitInputBuffer( dsw,
+ (unsigned long) (sampleRate + 0.5),
+ (WORD)inputParameters->channelCount, bytesPerDirectSoundBuffer );
+ DBUG(("DSW_InitInputBuffer() returns %x\n", hr));
+ if( hr != DS_OK )
+ {
+ ERR_RPT(("PortAudio: DSW_InitInputBuffer() returns %x\n", hr));
+ result = paUnanticipatedHostError;
+ PA_DS_SET_LAST_DIRECTSOUND_ERROR( hr );
+ goto error;
+ }
+ }
+
+ }
+
+ *s = (PaStream*)stream;
+
+ return result;
+
+error:
+ if( stream )
+ PaUtil_FreeMemory( stream );
+
+ return result;
+}
+
+
+/***********************************************************************************/
+static PaError Pa_TimeSlice( PaWinDsStream *stream )
+{
+ PaError result = 0; /* FIXME: this should be declared int and this function should also return that type (same as stream callback return type)*/
+ DSoundWrapper *dsw;
+ long numFrames = 0;
+ long bytesEmpty = 0;
+ long bytesFilled = 0;
+ long bytesToXfer = 0;
+ long framesToXfer = 0;
+ long numInFramesReady = 0;
+ long numOutFramesReady = 0;
+ long bytesProcessed;
+ HRESULT hresult;
+ double outputLatency = 0;
+ PaStreamCallbackTimeInfo timeInfo = {0,0,0}; /** @todo implement inputBufferAdcTime */
+
+/* Input */
+ LPBYTE lpInBuf1 = NULL;
+ LPBYTE lpInBuf2 = NULL;
+ DWORD dwInSize1 = 0;
+ DWORD dwInSize2 = 0;
+/* Output */
+ LPBYTE lpOutBuf1 = NULL;
+ LPBYTE lpOutBuf2 = NULL;
+ DWORD dwOutSize1 = 0;
+ DWORD dwOutSize2 = 0;
+
+ dsw = &stream->directSoundWrapper;
+
+ /* How much input data is available? */
+ if( stream->bufferProcessor.inputChannelCount > 0 )
+ {
+ DSW_QueryInputFilled( dsw, &bytesFilled );
+ framesToXfer = numInFramesReady = bytesFilled / dsw->dsw_BytesPerInputFrame;
+ outputLatency = ((double)bytesFilled) * stream->secondsPerHostByte;
+
+ /** @todo Check for overflow */
+ }
+
+ /* How much output room is available? */
+ if( stream->bufferProcessor.outputChannelCount > 0 )
+ {
+ UINT previousUnderflowCount = dsw->dsw_OutputUnderflows;
+ DSW_QueryOutputSpace( dsw, &bytesEmpty );
+ framesToXfer = numOutFramesReady = bytesEmpty / dsw->dsw_BytesPerOutputFrame;
+
+ /* Check for underflow */
+ if( dsw->dsw_OutputUnderflows != previousUnderflowCount )
+ stream->callbackFlags |= paOutputUnderflow;
+ }
+
+ if( (numInFramesReady > 0) && (numOutFramesReady > 0) )
+ {
+ framesToXfer = (numOutFramesReady < numInFramesReady) ? numOutFramesReady : numInFramesReady;
+ }
+
+ if( framesToXfer > 0 )
+ {
+
+ PaUtil_BeginCpuLoadMeasurement( &stream->cpuLoadMeasurer );
+
+ /* The outputBufferDacTime parameter should indicates the time at which
+ the first sample of the output buffer is heard at the DACs. */
+ timeInfo.currentTime = PaUtil_GetTime();
+ timeInfo.outputBufferDacTime = timeInfo.currentTime + outputLatency;
+
+
+ PaUtil_BeginBufferProcessing( &stream->bufferProcessor, &timeInfo, stream->callbackFlags );
+ stream->callbackFlags = 0;
+
+ /* Input */
+ if( stream->bufferProcessor.inputChannelCount > 0 )
+ {
+ bytesToXfer = framesToXfer * dsw->dsw_BytesPerInputFrame;
+ hresult = IDirectSoundCaptureBuffer_Lock ( dsw->dsw_InputBuffer,
+ dsw->dsw_ReadOffset, bytesToXfer,
+ (void **) &lpInBuf1, &dwInSize1,
+ (void **) &lpInBuf2, &dwInSize2, 0);
+ if (hresult != DS_OK)
+ {
+ ERR_RPT(("DirectSound IDirectSoundCaptureBuffer_Lock failed, hresult = 0x%x\n",hresult));
+ result = paUnanticipatedHostError;
+ PA_DS_SET_LAST_DIRECTSOUND_ERROR( hresult );
+ goto error2;
+ }
+
+ numFrames = dwInSize1 / dsw->dsw_BytesPerInputFrame;
+ PaUtil_SetInputFrameCount( &stream->bufferProcessor, numFrames );
+ PaUtil_SetInterleavedInputChannels( &stream->bufferProcessor, 0, lpInBuf1, 0 );
+ /* Is input split into two regions. */
+ if( dwInSize2 > 0 )
+ {
+ numFrames = dwInSize2 / dsw->dsw_BytesPerInputFrame;
+ PaUtil_Set2ndInputFrameCount( &stream->bufferProcessor, numFrames );
+ PaUtil_Set2ndInterleavedInputChannels( &stream->bufferProcessor, 0, lpInBuf2, 0 );
+ }
+ }
+
+ /* Output */
+ if( stream->bufferProcessor.outputChannelCount > 0 )
+ {
+ bytesToXfer = framesToXfer * dsw->dsw_BytesPerOutputFrame;
+ hresult = IDirectSoundBuffer_Lock ( dsw->dsw_OutputBuffer,
+ dsw->dsw_WriteOffset, bytesToXfer,
+ (void **) &lpOutBuf1, &dwOutSize1,
+ (void **) &lpOutBuf2, &dwOutSize2, 0);
+ if (hresult != DS_OK)
+ {
+ ERR_RPT(("DirectSound IDirectSoundBuffer_Lock failed, hresult = 0x%x\n",hresult));
+ result = paUnanticipatedHostError;
+ PA_DS_SET_LAST_DIRECTSOUND_ERROR( hresult );
+ goto error1;
+ }
+
+ numFrames = dwOutSize1 / dsw->dsw_BytesPerOutputFrame;
+ PaUtil_SetOutputFrameCount( &stream->bufferProcessor, numFrames );
+ PaUtil_SetInterleavedOutputChannels( &stream->bufferProcessor, 0, lpOutBuf1, 0 );
+
+ /* Is output split into two regions. */
+ if( dwOutSize2 > 0 )
+ {
+ numFrames = dwOutSize2 / dsw->dsw_BytesPerOutputFrame;
+ PaUtil_Set2ndOutputFrameCount( &stream->bufferProcessor, numFrames );
+ PaUtil_Set2ndInterleavedOutputChannels( &stream->bufferProcessor, 0, lpOutBuf2, 0 );
+ }
+ }
+
+ result = paContinue;
+ numFrames = PaUtil_EndBufferProcessing( &stream->bufferProcessor, &result );
+ stream->framesWritten += numFrames;
+
+ if( stream->bufferProcessor.outputChannelCount > 0 )
+ {
+ /* FIXME: an underflow could happen here */
+
+ /* Update our buffer offset and unlock sound buffer */
+ bytesProcessed = numFrames * dsw->dsw_BytesPerOutputFrame;
+ dsw->dsw_WriteOffset = (dsw->dsw_WriteOffset + bytesProcessed) % dsw->dsw_OutputSize;
+ IDirectSoundBuffer_Unlock( dsw->dsw_OutputBuffer, lpOutBuf1, dwOutSize1, lpOutBuf2, dwOutSize2);
+ dsw->dsw_FramesWritten += numFrames;
+ }
+
+error1:
+ if( stream->bufferProcessor.inputChannelCount > 0 )
+ {
+ /* FIXME: an overflow could happen here */
+
+ /* Update our buffer offset and unlock sound buffer */
+ bytesProcessed = numFrames * dsw->dsw_BytesPerInputFrame;
+ dsw->dsw_ReadOffset = (dsw->dsw_ReadOffset + bytesProcessed) % dsw->dsw_InputSize;
+ IDirectSoundCaptureBuffer_Unlock( dsw->dsw_InputBuffer, lpInBuf1, dwInSize1, lpInBuf2, dwInSize2);
+ }
+error2:
+
+ PaUtil_EndCpuLoadMeasurement( &stream->cpuLoadMeasurer, numFrames );
+
+ }
+
+ return result;
+}
+/*******************************************************************/
+static void CALLBACK Pa_TimerCallback(UINT uID, UINT uMsg, DWORD dwUser, DWORD dw1, DWORD dw2)
+{
+ PaWinDsStream *stream;
+
+ /* suppress unused variable warnings */
+ (void) uID;
+ (void) uMsg;
+ (void) dw1;
+ (void) dw2;
+
+ stream = (PaWinDsStream *) dwUser;
+ if( stream == NULL ) return;
+
+ if( stream->isActive )
+ {
+ if( stream->abortProcessing )
+ {
+ stream->isActive = 0;
+ }
+ else if( stream->stopProcessing )
+ {
+ DSoundWrapper *dsw = &stream->directSoundWrapper;
+ if( stream->bufferProcessor.outputChannelCount > 0 )
+ {
+ DSW_ZeroEmptySpace( dsw );
+ /* clear isActive when all sound played */
+ if( dsw->dsw_FramesPlayed >= stream->framesWritten )
+ {
+ stream->isActive = 0;
+ }
+ }
+ else
+ {
+ stream->isActive = 0;
+ }
+ }
+ else
+ {
+ if( Pa_TimeSlice( stream ) != 0) /* Call time slice independant of timing method. */
+ {
+ /* FIXME implement handling of paComplete and paAbort if possible */
+ stream->stopProcessing = 1;
+ }
+ }
+
+ if( !stream->isActive ){
+ if( stream->streamRepresentation.streamFinishedCallback != 0 )
+ stream->streamRepresentation.streamFinishedCallback( stream->streamRepresentation.userData );
+ }
+ }
+}
+
+/***********************************************************************************
+ When CloseStream() is called, the multi-api layer ensures that
+ the stream has already been stopped or aborted.
+*/
+static PaError CloseStream( PaStream* s )
+{
+ PaError result = paNoError;
+ PaWinDsStream *stream = (PaWinDsStream*)s;
+
+ DSW_Term( &stream->directSoundWrapper );
+
+ PaUtil_TerminateBufferProcessor( &stream->bufferProcessor );
+ PaUtil_TerminateStreamRepresentation( &stream->streamRepresentation );
+ PaUtil_FreeMemory( stream );
+
+ return result;
+}
+
+/***********************************************************************************/
+static PaError StartStream( PaStream *s )
+{
+ PaError result = paNoError;
+ PaWinDsStream *stream = (PaWinDsStream*)s;
+ HRESULT hr;
+
+ PaUtil_ResetBufferProcessor( &stream->bufferProcessor );
+
+ if( stream->bufferProcessor.inputChannelCount > 0 )
+ {
+ hr = DSW_StartInput( &stream->directSoundWrapper );
+ DBUG(("StartStream: DSW_StartInput returned = 0x%X.\n", hr));
+ if( hr != DS_OK )
+ {
+ result = paUnanticipatedHostError;
+ PA_DS_SET_LAST_DIRECTSOUND_ERROR( hr );
+ goto error;
+ }
+ }
+
+ stream->framesWritten = 0;
+ stream->callbackFlags = 0;
+
+ stream->abortProcessing = 0;
+ stream->stopProcessing = 0;
+ stream->isActive = 1;
+
+ if( stream->bufferProcessor.outputChannelCount > 0 )
+ {
+ /* Give user callback a chance to pre-fill buffer. REVIEW - i thought we weren't pre-filling, rb. */
+ result = Pa_TimeSlice( stream );
+ if( result != paNoError ) return result; // FIXME - what if finished?
+
+ hr = DSW_StartOutput( &stream->directSoundWrapper );
+ DBUG(("PaHost_StartOutput: DSW_StartOutput returned = 0x%X.\n", hr));
+ if( hr != DS_OK )
+ {
+ result = paUnanticipatedHostError;
+ PA_DS_SET_LAST_DIRECTSOUND_ERROR( hr );
+ goto error;
+ }
+ }
+
+
+ /* Create timer that will wake us up so we can fill the DSound buffer. */
+ {
+ int resolution;
+ int framesPerWakeup = stream->framesPerDSBuffer / 4;
+ int msecPerWakeup = MSEC_PER_SECOND * framesPerWakeup / (int) stream->streamRepresentation.streamInfo.sampleRate;
+ if( msecPerWakeup < 10 ) msecPerWakeup = 10;
+ else if( msecPerWakeup > 100 ) msecPerWakeup = 100;
+ resolution = msecPerWakeup/4;
+ stream->timerID = timeSetEvent( msecPerWakeup, resolution, (LPTIMECALLBACK) Pa_TimerCallback,
+ (DWORD) stream, TIME_PERIODIC );
+ }
+ if( stream->timerID == 0 )
+ {
+ stream->isActive = 0;
+ result = paUnanticipatedHostError;
+ PA_DS_SET_LAST_DIRECTSOUND_ERROR( hr );
+ goto error;
+ }
+
+ stream->isStarted = TRUE;
+
+error:
+ return result;
+}
+
+
+/***********************************************************************************/
+static PaError StopStream( PaStream *s )
+{
+ PaError result = paNoError;
+ PaWinDsStream *stream = (PaWinDsStream*)s;
+ HRESULT hr;
+ int timeoutMsec;
+
+ stream->stopProcessing = 1;
+ /* Set timeout at 20% beyond maximum time we might wait. */
+ timeoutMsec = (int) (1200.0 * stream->framesPerDSBuffer / stream->streamRepresentation.streamInfo.sampleRate);
+ while( stream->isActive && (timeoutMsec > 0) )
+ {
+ Sleep(10);
+ timeoutMsec -= 10;
+ }
+ if( stream->timerID != 0 )
+ {
+ timeKillEvent(stream->timerID); /* Stop callback timer. */
+ stream->timerID = 0;
+ }
+
+
+ if( stream->bufferProcessor.outputChannelCount > 0 )
+ {
+ hr = DSW_StopOutput( &stream->directSoundWrapper );
+ }
+
+ if( stream->bufferProcessor.inputChannelCount > 0 )
+ {
+ hr = DSW_StopInput( &stream->directSoundWrapper );
+ }
+
+ stream->isStarted = FALSE;
+
+ return result;
+}
+
+
+/***********************************************************************************/
+static PaError AbortStream( PaStream *s )
+{
+ PaWinDsStream *stream = (PaWinDsStream*)s;
+
+ stream->abortProcessing = 1;
+ return StopStream( s );
+}
+
+
+/***********************************************************************************/
+static PaError IsStreamStopped( PaStream *s )
+{
+ PaWinDsStream *stream = (PaWinDsStream*)s;
+
+ return !stream->isStarted;
+}
+
+
+/***********************************************************************************/
+static PaError IsStreamActive( PaStream *s )
+{
+ PaWinDsStream *stream = (PaWinDsStream*)s;
+
+ return stream->isActive;
+}
+
+/***********************************************************************************/
+static PaTime GetStreamTime( PaStream *s )
+{
+ /* suppress unused variable warnings */
+ (void) s;
+
+
+/*
+ new behavior for GetStreamTime is to return a stream based seconds clock
+ used for the outTime parameter to the callback.
+ FIXME: delete this comment when the other unnecessary related code has
+ been cleaned from this file.
+
+ PaWinDsStream *stream = (PaWinDsStream*)s;
+ DSoundWrapper *dsw;
+ dsw = &stream->directSoundWrapper;
+ return dsw->dsw_FramesPlayed;
+*/
+ return PaUtil_GetTime();
+}
+
+
+/***********************************************************************************/
+static double GetStreamCpuLoad( PaStream* s )
+{
+ PaWinDsStream *stream = (PaWinDsStream*)s;
+
+ return PaUtil_GetCpuLoad( &stream->cpuLoadMeasurer );
+}
+
+
+
+/***********************************************************************************
+ As separate stream interfaces are used for blocking and callback
+ streams, the following functions can be guaranteed to only be called
+ for blocking streams.
+*/
+
+static PaError ReadStream( PaStream* s,
+ void *buffer,
+ unsigned long frames )
+{
+ PaWinDsStream *stream = (PaWinDsStream*)s;
+
+ /* suppress unused variable warnings */
+ (void) buffer;
+ (void) frames;
+ (void) stream;
+
+ /* IMPLEMENT ME, see portaudio.h for required behavior*/
+
+ return paNoError;
+}
+
+
+/***********************************************************************************/
+static PaError WriteStream( PaStream* s,
+ const void *buffer,
+ unsigned long frames )
+{
+ PaWinDsStream *stream = (PaWinDsStream*)s;
+
+ /* suppress unused variable warnings */
+ (void) buffer;
+ (void) frames;
+ (void) stream;
+
+ /* IMPLEMENT ME, see portaudio.h for required behavior*/
+
+ return paNoError;
+}
+
+
+/***********************************************************************************/
+static signed long GetStreamReadAvailable( PaStream* s )
+{
+ PaWinDsStream *stream = (PaWinDsStream*)s;
+
+ /* suppress unused variable warnings */
+ (void) stream;
+
+ /* IMPLEMENT ME, see portaudio.h for required behavior*/
+
+ return 0;
+}
+
+
+/***********************************************************************************/
+static signed long GetStreamWriteAvailable( PaStream* s )
+{
+ PaWinDsStream *stream = (PaWinDsStream*)s;
+
+ /* suppress unused variable warnings */
+ (void) stream;
+
+ /* IMPLEMENT ME, see portaudio.h for required behavior*/
+
+ return 0;
+}
+
+
+
diff --git a/pjmedia/src/pjmedia/portaudio/pa_win_hostapis.c b/pjmedia/src/pjmedia/portaudio/pa_win_hostapis.c
index 4e07fee2..b95af514 100644
--- a/pjmedia/src/pjmedia/portaudio/pa_win_hostapis.c
+++ b/pjmedia/src/pjmedia/portaudio/pa_win_hostapis.c
@@ -1,86 +1,107 @@
-/*
- * $Id: pa_win_hostapis.c,v 1.1.2.10 2004/09/08 17:31:37 rossbencina Exp $
- * Portable Audio I/O Library Windows initialization table
- *
- * Based on the Open Source API proposed by Ross Bencina
- * Copyright (c) 1999-2002 Ross Bencina, Phil Burk
- *
- * Permission is hereby granted, free of charge, to any person obtaining
- * a copy of this software and associated documentation files
- * (the "Software"), to deal in the Software without restriction,
- * including without limitation the rights to use, copy, modify, merge,
- * publish, distribute, sublicense, and/or sell copies of the Software,
- * and to permit persons to whom the Software is furnished to do so,
- * subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be
- * included in all copies or substantial portions of the Software.
- *
- * Any person wishing to distribute modifications to the Software is
- * requested to send the modifications to the original developer so that
- * they can be incorporated into the canonical version.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
- * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
- * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
- * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
- * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
- */
-
-/** @file
- Win32 host API initialization function table.
-
- @todo Consider using PA_USE_WMME etc instead of PA_NO_WMME. This is what
- the Unix version does, we should consider being consistent.
-*/
-
-
-#include <pa_hostapi.h>
-
-#ifdef __cplusplus
-extern "C"
-{
-#endif /* __cplusplus */
-
-PaError PaSkeleton_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index );
-PaError PaWinMme_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index );
-PaError PaWinDs_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index );
-PaError PaAsio_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index );
-PaError PaWinWdm_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index );
-
-#ifdef __cplusplus
-}
-#endif /* __cplusplus */
-
-
-PaUtilHostApiInitializer *paHostApiInitializers[] =
- {
-
-#ifndef PA_NO_WMME
- PaWinMme_Initialize,
-#endif
-
-#ifndef PA_NO_DS
- PaWinDs_Initialize,
-#endif
-
-#ifndef PA_NO_ASIO
- PaAsio_Initialize,
-#endif
-
-/*
-#ifndef PA_NO_WDMKS
- PaWinWdm_Initialize,
-#endif
-*/
-
- PaSkeleton_Initialize, /* just for testing */
-
- 0 /* NULL terminated array */
- };
-
-
-int paDefaultHostApiIndex = 0;
-
+/*
+ * $Id: pa_win_hostapis.c,v 1.1.2.10 2004/09/08 17:31:37 rossbencina Exp $
+ * Portable Audio I/O Library Windows initialization table
+ *
+ * Based on the Open Source API proposed by Ross Bencina
+ * Copyright (c) 1999-2002 Ross Bencina, Phil Burk
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * Any person wishing to distribute modifications to the Software is
+ * requested to send the modifications to the original developer so that
+ * they can be incorporated into the canonical version.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+ * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+/*
+ * PJMEDIA - Multimedia over IP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/** @file
+ Win32 host API initialization function table.
+
+ @todo Consider using PA_USE_WMME etc instead of PA_NO_WMME. This is what
+ the Unix version does, we should consider being consistent.
+*/
+
+
+#include <pa_hostapi.h>
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif /* __cplusplus */
+
+PaError PaSkeleton_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index );
+PaError PaWinMme_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index );
+PaError PaWinDs_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index );
+PaError PaAsio_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index );
+PaError PaWinWdm_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index );
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+
+PaUtilHostApiInitializer *paHostApiInitializers[] =
+ {
+
+#ifndef PA_NO_WMME
+ PaWinMme_Initialize,
+#endif
+
+#ifndef PA_NO_DS
+ PaWinDs_Initialize,
+#endif
+
+#ifndef PA_NO_ASIO
+ PaAsio_Initialize,
+#endif
+
+/*
+#ifndef PA_NO_WDMKS
+ PaWinWdm_Initialize,
+#endif
+*/
+
+ PaSkeleton_Initialize, /* just for testing */
+
+ 0 /* NULL terminated array */
+ };
+
+
+int paDefaultHostApiIndex = 0;
+
diff --git a/pjmedia/src/pjmedia/portaudio/pa_win_util.c b/pjmedia/src/pjmedia/portaudio/pa_win_util.c
index 0395e5c8..9f7fd339 100644
--- a/pjmedia/src/pjmedia/portaudio/pa_win_util.c
+++ b/pjmedia/src/pjmedia/portaudio/pa_win_util.c
@@ -1,134 +1,155 @@
-/*
- * $Id: pa_win_util.c,v 1.1.2.7 2003/09/15 18:30:26 rossbencina Exp $
- * Portable Audio I/O Library
- * Win32 platform-specific support functions
- *
- * Based on the Open Source API proposed by Ross Bencina
- * Copyright (c) 1999-2000 Ross Bencina
- *
- * Permission is hereby granted, free of charge, to any person obtaining
- * a copy of this software and associated documentation files
- * (the "Software"), to deal in the Software without restriction,
- * including without limitation the rights to use, copy, modify, merge,
- * publish, distribute, sublicense, and/or sell copies of the Software,
- * and to permit persons to whom the Software is furnished to do so,
- * subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be
- * included in all copies or substantial portions of the Software.
- *
- * Any person wishing to distribute modifications to the Software is
- * requested to send the modifications to the original developer so that
- * they can be incorporated into the canonical version.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
- * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
- * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
- * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
- * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
- */
-
-/** @file
- Win32 platform-specific support functions.
-
- @todo Implement workaround for QueryPerformanceCounter() skipping forward
- bug. (see msdn kb Q274323).
-*/
-
-#include <windows.h>
-#include <mmsystem.h> /* for timeGetTime() */
-
-#include "pa_util.h"
-
-
-/*
- Track memory allocations to avoid leaks.
- */
-
-#if PA_TRACK_MEMORY
-static int numAllocations_ = 0;
-#endif
-
-
-void *PaUtil_AllocateMemory( long size )
-{
- void *result = GlobalAlloc( GPTR, size );
-
-#if PA_TRACK_MEMORY
- if( result != NULL ) numAllocations_ += 1;
-#endif
- return result;
-}
-
-
-void PaUtil_FreeMemory( void *block )
-{
- if( block != NULL )
- {
- GlobalFree( block );
-#if PA_TRACK_MEMORY
- numAllocations_ -= 1;
-#endif
-
- }
-}
-
-
-int PaUtil_CountCurrentlyAllocatedBlocks( void )
-{
-#if PA_TRACK_MEMORY
- return numAllocations_;
-#else
- return 0;
-#endif
-}
-
-
-void Pa_Sleep( long msec )
-{
- Sleep( msec );
-}
-
-static int usePerformanceCounter_;
-static double secondsPerTick_;
-
-void PaUtil_InitializeClock( void )
-{
- LARGE_INTEGER ticksPerSecond;
-
- if( QueryPerformanceFrequency( &ticksPerSecond ) != 0 )
- {
- usePerformanceCounter_ = 1;
- secondsPerTick_ = 1.0 / (double)ticksPerSecond.QuadPart;
- }
- else
- {
- usePerformanceCounter_ = 0;
- }
-}
-
-
-double PaUtil_GetTime( void )
-{
- LARGE_INTEGER time;
-
- if( usePerformanceCounter_ )
- {
- /* FIXME:
- according to this knowledge-base article, QueryPerformanceCounter
- can skip forward by seconds!
- http://support.microsoft.com/default.aspx?scid=KB;EN-US;Q274323&
-
- it may be better to use the rtdsc instruction using inline asm,
- however then a method is needed to calculate a ticks/seconds ratio.
- */
- QueryPerformanceCounter( &time );
- return time.QuadPart * secondsPerTick_;
- }
- else
- {
- return timeGetTime() * .001;
- }
-}
+/*
+ * $Id: pa_win_util.c,v 1.1.2.7 2003/09/15 18:30:26 rossbencina Exp $
+ * Portable Audio I/O Library
+ * Win32 platform-specific support functions
+ *
+ * Based on the Open Source API proposed by Ross Bencina
+ * Copyright (c) 1999-2000 Ross Bencina
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * Any person wishing to distribute modifications to the Software is
+ * requested to send the modifications to the original developer so that
+ * they can be incorporated into the canonical version.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+ * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+/*
+ * PJMEDIA - Multimedia over IP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/** @file
+ Win32 platform-specific support functions.
+
+ @todo Implement workaround for QueryPerformanceCounter() skipping forward
+ bug. (see msdn kb Q274323).
+*/
+
+#include <windows.h>
+#include <mmsystem.h> /* for timeGetTime() */
+
+#include "pa_util.h"
+
+
+/*
+ Track memory allocations to avoid leaks.
+ */
+
+#if PA_TRACK_MEMORY
+static int numAllocations_ = 0;
+#endif
+
+
+void *PaUtil_AllocateMemory( long size )
+{
+ void *result = GlobalAlloc( GPTR, size );
+
+#if PA_TRACK_MEMORY
+ if( result != NULL ) numAllocations_ += 1;
+#endif
+ return result;
+}
+
+
+void PaUtil_FreeMemory( void *block )
+{
+ if( block != NULL )
+ {
+ GlobalFree( block );
+#if PA_TRACK_MEMORY
+ numAllocations_ -= 1;
+#endif
+
+ }
+}
+
+
+int PaUtil_CountCurrentlyAllocatedBlocks( void )
+{
+#if PA_TRACK_MEMORY
+ return numAllocations_;
+#else
+ return 0;
+#endif
+}
+
+
+void Pa_Sleep( long msec )
+{
+ Sleep( msec );
+}
+
+static int usePerformanceCounter_;
+static double secondsPerTick_;
+
+void PaUtil_InitializeClock( void )
+{
+ LARGE_INTEGER ticksPerSecond;
+
+ if( QueryPerformanceFrequency( &ticksPerSecond ) != 0 )
+ {
+ usePerformanceCounter_ = 1;
+ secondsPerTick_ = 1.0 / (double)ticksPerSecond.QuadPart;
+ }
+ else
+ {
+ usePerformanceCounter_ = 0;
+ }
+}
+
+
+double PaUtil_GetTime( void )
+{
+ LARGE_INTEGER time;
+
+ if( usePerformanceCounter_ )
+ {
+ /* FIXME:
+ according to this knowledge-base article, QueryPerformanceCounter
+ can skip forward by seconds!
+ http://support.microsoft.com/default.aspx?scid=KB;EN-US;Q274323&
+
+ it may be better to use the rtdsc instruction using inline asm,
+ however then a method is needed to calculate a ticks/seconds ratio.
+ */
+ QueryPerformanceCounter( &time );
+ return time.QuadPart * secondsPerTick_;
+ }
+ else
+ {
+ return timeGetTime() * .001;
+ }
+}
diff --git a/pjmedia/src/pjmedia/portaudio/pa_win_wmme.c b/pjmedia/src/pjmedia/portaudio/pa_win_wmme.c
index c3d7fe6d..82c9078d 100644
--- a/pjmedia/src/pjmedia/portaudio/pa_win_wmme.c
+++ b/pjmedia/src/pjmedia/portaudio/pa_win_wmme.c
@@ -1,3623 +1,3644 @@
-/*
- * $Id: pa_win_wmme.c,v 1.6.2.86 2004/02/21 11:38:28 rossbencina Exp $
- * pa_win_wmme.c
- * Implementation of PortAudio for Windows MultiMedia Extensions (WMME)
- *
- * PortAudio Portable Real-Time Audio Library
- * Latest Version at: http://www.portaudio.com
- *
- * Authors: Ross Bencina and Phil Burk
- * Copyright (c) 1999-2000 Ross Bencina and Phil Burk
- *
- * Permission is hereby granted, free of charge, to any person obtaining
- * a copy of this software and associated documentation files
- * (the "Software"), to deal in the Software without restriction,
- * including without limitation the rights to use, copy, modify, merge,
- * publish, distribute, sublicense, and/or sell copies of the Software,
- * and to permit persons to whom the Software is furnished to do so,
- * subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be
- * included in all copies or substantial portions of the Software.
- *
- * Any person wishing to distribute modifications to the Software is
- * requested to send the modifications to the original developer so that
- * they can be incorporated into the canonical version.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
- * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
- * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
- * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
- * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
- *
- */
-
-/* Modification History:
- PLB = Phil Burk
- JM = Julien Maillard
- RDB = Ross Bencina
- PLB20010402 - sDevicePtrs now allocates based on sizeof(pointer)
- PLB20010413 - check for excessive numbers of channels
- PLB20010422 - apply Mike Berry's changes for CodeWarrior on PC
- including conditional inclusion of memory.h,
- and explicit typecasting on memory allocation
- PLB20010802 - use GlobalAlloc for sDevicesPtr instead of PaHost_AllocFastMemory
- PLB20010816 - pass process instead of thread to SetPriorityClass()
- PLB20010927 - use number of frames instead of real-time for CPULoad calculation.
- JM20020118 - prevent hung thread when buffers underflow.
- PLB20020321 - detect Win XP versus NT, 9x; fix DBUG typo; removed init of CurrentCount
- RDB20020411 - various renaming cleanups, factored streamData alloc and cpu usage init
- RDB20020417 - stopped counting WAVE_MAPPER when there were no real devices
- refactoring, renaming and fixed a few edge case bugs
- RDB20020531 - converted to V19 framework
- ** NOTE maintanance history is now stored in CVS **
-*/
-
-/** @file
-
- @todo Fix buffer catch up code, can sometimes get stuck (perhaps fixed now,
- needs to be reviewed and tested.)
-
- @todo implement paInputUnderflow, paOutputOverflow streamCallback statusFlags, paNeverDropInput.
-
- @todo BUG: PA_MME_SET_LAST_WAVEIN/OUT_ERROR is used in functions which may
- be called asynchronously from the callback thread. this is bad.
-
- @todo implement inputBufferAdcTime in callback thread
-
- @todo review/fix error recovery and cleanup in marked functions
-
- @todo implement timeInfo for stream priming
-
- @todo handle the case where the callback returns paAbort or paComplete during stream priming.
-
- @todo review input overflow and output underflow handling in ReadStream and WriteStream
-
-Non-critical stuff for the future:
-
- @todo Investigate supporting host buffer formats > 16 bits
-
- @todo define UNICODE and _UNICODE in the project settings and see what breaks
-
-*/
-
-/*
- How it works:
-
- For both callback and blocking read/write streams we open the MME devices
- in CALLBACK_EVENT mode. In this mode, MME signals an Event object whenever
- it has finished with a buffer (either filled it for input, or played it
- for output). Where necessary we block waiting for Event objects using
- WaitMultipleObjects().
-
- When implementing a PA callback stream, we set up a high priority thread
- which waits on the MME buffer Events and drains/fills the buffers when
- they are ready.
-
- When implementing a PA blocking read/write stream, we simply wait on these
- Events (when necessary) inside the ReadStream() and WriteStream() functions.
-*/
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <math.h>
-#include <windows.h>
-#include <mmsystem.h>
-#include <process.h>
-#include <assert.h>
-/* PLB20010422 - "memory.h" doesn't work on CodeWarrior for PC. Thanks Mike Berry for the mod. */
-#ifndef __MWERKS__
-#include <malloc.h>
-#include <memory.h>
-#endif /* __MWERKS__ */
-
-#include "portaudio.h"
-#include "pa_trace.h"
-#include "pa_util.h"
-#include "pa_allocation.h"
-#include "pa_hostapi.h"
-#include "pa_stream.h"
-#include "pa_cpuload.h"
-#include "pa_process.h"
-
-#include "pa_win_wmme.h"
-
-#if (defined(WIN32) && (defined(_MSC_VER) && (_MSC_VER >= 1200))) /* MSC version 6 and above */
-#pragma comment(lib, "winmm.lib")
-#endif
-
-/************************************************* Constants ********/
-
-#define PA_MME_USE_HIGH_DEFAULT_LATENCY_ (0) /* For debugging glitches. */
-
-#if PA_MME_USE_HIGH_DEFAULT_LATENCY_
- #define PA_MME_WIN_9X_DEFAULT_LATENCY_ (0.4)
- #define PA_MME_MIN_HOST_OUTPUT_BUFFER_COUNT_ (4)
- #define PA_MME_MIN_HOST_INPUT_BUFFER_COUNT_FULL_DUPLEX_ (4)
- #define PA_MME_MIN_HOST_INPUT_BUFFER_COUNT_HALF_DUPLEX_ (4)
- #define PA_MME_MIN_HOST_BUFFER_FRAMES_WHEN_UNSPECIFIED_ (16)
- #define PA_MME_MAX_HOST_BUFFER_SECS_ (0.3) /* Do not exceed unless user buffer exceeds */
- #define PA_MME_MAX_HOST_BUFFER_BYTES_ (32 * 1024) /* Has precedence over PA_MME_MAX_HOST_BUFFER_SECS_, some drivers are known to crash with buffer sizes > 32k */
-#else
- #define PA_MME_WIN_9X_DEFAULT_LATENCY_ (0.2)
- #define PA_MME_MIN_HOST_OUTPUT_BUFFER_COUNT_ (2)
- #define PA_MME_MIN_HOST_INPUT_BUFFER_COUNT_FULL_DUPLEX_ (3)
- #define PA_MME_MIN_HOST_INPUT_BUFFER_COUNT_HALF_DUPLEX_ (2)
- #define PA_MME_MIN_HOST_BUFFER_FRAMES_WHEN_UNSPECIFIED_ (16)
- #define PA_MME_MAX_HOST_BUFFER_SECS_ (0.1) /* Do not exceed unless user buffer exceeds */
- #define PA_MME_MAX_HOST_BUFFER_BYTES_ (32 * 1024) /* Has precedence over PA_MME_MAX_HOST_BUFFER_SECS_, some drivers are known to crash with buffer sizes > 32k */
-#endif
-
-/* Use higher latency for NT because it is even worse at real-time
- operation than Win9x.
-*/
-#define PA_MME_WIN_NT_DEFAULT_LATENCY_ (PA_MME_WIN_9X_DEFAULT_LATENCY_ * 2)
-#define PA_MME_WIN_WDM_DEFAULT_LATENCY_ (PA_MME_WIN_9X_DEFAULT_LATENCY_)
-
-
-#define PA_MME_MIN_TIMEOUT_MSEC_ (1000)
-
-static const char constInputMapperSuffix_[] = " - Input";
-static const char constOutputMapperSuffix_[] = " - Output";
-
-/********************************************************************/
-
-typedef struct PaWinMmeStream PaWinMmeStream; /* forward declaration */
-
-/* prototypes for functions declared in this file */
-
-#ifdef __cplusplus
-extern "C"
-{
-#endif /* __cplusplus */
-
-PaError PaWinMme_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index );
-
-#ifdef __cplusplus
-}
-#endif /* __cplusplus */
-
-static void Terminate( struct PaUtilHostApiRepresentation *hostApi );
-static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
- PaStream** stream,
- const PaStreamParameters *inputParameters,
- const PaStreamParameters *outputParameters,
- double sampleRate,
- unsigned long framesPerBuffer,
- PaStreamFlags streamFlags,
- PaStreamCallback *streamCallback,
- void *userData );
-static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
- const PaStreamParameters *inputParameters,
- const PaStreamParameters *outputParameters,
- double sampleRate );
-static PaError CloseStream( PaStream* stream );
-static PaError StartStream( PaStream *stream );
-static PaError StopStream( PaStream *stream );
-static PaError AbortStream( PaStream *stream );
-static PaError IsStreamStopped( PaStream *s );
-static PaError IsStreamActive( PaStream *stream );
-static PaTime GetStreamTime( PaStream *stream );
-static double GetStreamCpuLoad( PaStream* stream );
-static PaError ReadStream( PaStream* stream, void *buffer, unsigned long frames );
-static PaError WriteStream( PaStream* stream, const void *buffer, unsigned long frames );
-static signed long GetStreamReadAvailable( PaStream* stream );
-static signed long GetStreamWriteAvailable( PaStream* stream );
-
-
-/* macros for setting last host error information */
-
-#ifdef UNICODE
-
-#define PA_MME_SET_LAST_WAVEIN_ERROR( mmresult ) \
- { \
- wchar_t mmeErrorTextWide[ MAXERRORLENGTH ]; \
- char mmeErrorText[ MAXERRORLENGTH ]; \
- waveInGetErrorText( mmresult, mmeErrorTextWide, MAXERRORLENGTH ); \
- WideCharToMultiByte( CP_ACP, WC_COMPOSITECHECK | WC_DEFAULTCHAR,\
- mmeErrorTextWide, -1, mmeErrorText, MAXERRORLENGTH, NULL, NULL ); \
- PaUtil_SetLastHostErrorInfo( paMME, mmresult, mmeErrorText ); \
- }
-
-#define PA_MME_SET_LAST_WAVEOUT_ERROR( mmresult ) \
- { \
- wchar_t mmeErrorTextWide[ MAXERRORLENGTH ]; \
- char mmeErrorText[ MAXERRORLENGTH ]; \
- waveOutGetErrorText( mmresult, mmeErrorTextWide, MAXERRORLENGTH ); \
- WideCharToMultiByte( CP_ACP, WC_COMPOSITECHECK | WC_DEFAULTCHAR,\
- mmeErrorTextWide, -1, mmeErrorText, MAXERRORLENGTH, NULL, NULL ); \
- PaUtil_SetLastHostErrorInfo( paMME, mmresult, mmeErrorText ); \
- }
-
-#else /* !UNICODE */
-
-#define PA_MME_SET_LAST_WAVEIN_ERROR( mmresult ) \
- { \
- char mmeErrorText[ MAXERRORLENGTH ]; \
- waveInGetErrorText( mmresult, mmeErrorText, MAXERRORLENGTH ); \
- PaUtil_SetLastHostErrorInfo( paMME, mmresult, mmeErrorText ); \
- }
-
-#define PA_MME_SET_LAST_WAVEOUT_ERROR( mmresult ) \
- { \
- char mmeErrorText[ MAXERRORLENGTH ]; \
- waveOutGetErrorText( mmresult, mmeErrorText, MAXERRORLENGTH ); \
- PaUtil_SetLastHostErrorInfo( paMME, mmresult, mmeErrorText ); \
- }
-
-#endif /* UNICODE */
-
-
-static void PaMme_SetLastSystemError( DWORD errorCode )
-{
- char *lpMsgBuf;
- FormatMessage(
- FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
- NULL,
- errorCode,
- MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
- (LPTSTR) &lpMsgBuf,
- 0,
- NULL
- );
- PaUtil_SetLastHostErrorInfo( paMME, errorCode, lpMsgBuf );
- LocalFree( lpMsgBuf );
-}
-
-#define PA_MME_SET_LAST_SYSTEM_ERROR( errorCode ) \
- PaMme_SetLastSystemError( errorCode )
-
-
-/* PaError returning wrappers for some commonly used win32 functions
- note that we allow passing a null ptr to have no effect.
-*/
-
-static PaError CreateEventWithPaError( HANDLE *handle,
- LPSECURITY_ATTRIBUTES lpEventAttributes,
- BOOL bManualReset,
- BOOL bInitialState,
- LPCTSTR lpName )
-{
- PaError result = paNoError;
-
- *handle = NULL;
-
- *handle = CreateEvent( lpEventAttributes, bManualReset, bInitialState, lpName );
- if( *handle == NULL )
- {
- result = paUnanticipatedHostError;
- PA_MME_SET_LAST_SYSTEM_ERROR( GetLastError() );
- }
-
- return result;
-}
-
-
-static PaError ResetEventWithPaError( HANDLE handle )
-{
- PaError result = paNoError;
-
- if( handle )
- {
- if( ResetEvent( handle ) == 0 )
- {
- result = paUnanticipatedHostError;
- PA_MME_SET_LAST_SYSTEM_ERROR( GetLastError() );
- }
- }
-
- return result;
-}
-
-
-static PaError CloseHandleWithPaError( HANDLE handle )
-{
- PaError result = paNoError;
-
- if( handle )
- {
- if( CloseHandle( handle ) == 0 )
- {
- result = paUnanticipatedHostError;
- PA_MME_SET_LAST_SYSTEM_ERROR( GetLastError() );
- }
- }
-
- return result;
-}
-
-
-/* PaWinMmeHostApiRepresentation - host api datastructure specific to this implementation */
-
-typedef struct
-{
- PaUtilHostApiRepresentation inheritedHostApiRep;
- PaUtilStreamInterface callbackStreamInterface;
- PaUtilStreamInterface blockingStreamInterface;
-
- PaUtilAllocationGroup *allocations;
-
- int inputDeviceCount, outputDeviceCount;
-
- /** winMmeDeviceIds is an array of WinMme device ids.
- fields in the range [0, inputDeviceCount) are input device ids,
- and [inputDeviceCount, inputDeviceCount + outputDeviceCount) are output
- device ids.
- */
- UINT *winMmeDeviceIds;
-}
-PaWinMmeHostApiRepresentation;
-
-
-typedef struct
-{
- PaDeviceInfo inheritedDeviceInfo;
- DWORD dwFormats; /**<< standard formats bitmask from the WAVEINCAPS and WAVEOUTCAPS structures */
-}
-PaWinMmeDeviceInfo;
-
-
-/*************************************************************************
- * Returns recommended device ID.
- * On the PC, the recommended device can be specified by the user by
- * setting an environment variable. For example, to use device #1.
- *
- * set PA_RECOMMENDED_OUTPUT_DEVICE=1
- *
- * The user should first determine the available device ID by using
- * the supplied application "pa_devs".
- */
-#define PA_ENV_BUF_SIZE_ (32)
-#define PA_REC_IN_DEV_ENV_NAME_ ("PA_RECOMMENDED_INPUT_DEVICE")
-#define PA_REC_OUT_DEV_ENV_NAME_ ("PA_RECOMMENDED_OUTPUT_DEVICE")
-static PaDeviceIndex GetEnvDefaultDeviceID( char *envName )
-{
- PaDeviceIndex recommendedIndex = paNoDevice;
- DWORD hresult;
- char envbuf[PA_ENV_BUF_SIZE_];
-
-#ifndef WIN32_PLATFORM_PSPC /* no GetEnvironmentVariable on PocketPC */
-
- /* Let user determine default device by setting environment variable. */
- hresult = GetEnvironmentVariable( envName, envbuf, PA_ENV_BUF_SIZE_ );
- if( (hresult > 0) && (hresult < PA_ENV_BUF_SIZE_) )
- {
- recommendedIndex = atoi( envbuf );
- }
-#endif
-
- return recommendedIndex;
-}
-
-
-static void InitializeDefaultDeviceIdsFromEnv( PaWinMmeHostApiRepresentation *hostApi )
-{
- PaDeviceIndex device;
-
- /* input */
- device = GetEnvDefaultDeviceID( PA_REC_IN_DEV_ENV_NAME_ );
- if( device != paNoDevice &&
- ( device >= 0 && device < hostApi->inheritedHostApiRep.info.deviceCount ) &&
- hostApi->inheritedHostApiRep.deviceInfos[ device ]->maxInputChannels > 0 )
- {
- hostApi->inheritedHostApiRep.info.defaultInputDevice = device;
- }
-
- /* output */
- device = GetEnvDefaultDeviceID( PA_REC_OUT_DEV_ENV_NAME_ );
- if( device != paNoDevice &&
- ( device >= 0 && device < hostApi->inheritedHostApiRep.info.deviceCount ) &&
- hostApi->inheritedHostApiRep.deviceInfos[ device ]->maxOutputChannels > 0 )
- {
- hostApi->inheritedHostApiRep.info.defaultOutputDevice = device;
- }
-}
-
-
-/** Convert external PA ID to a windows multimedia device ID
-*/
-static UINT LocalDeviceIndexToWinMmeDeviceId( PaWinMmeHostApiRepresentation *hostApi, PaDeviceIndex device )
-{
- assert( device >= 0 && device < hostApi->inputDeviceCount + hostApi->outputDeviceCount );
-
- return hostApi->winMmeDeviceIds[ device ];
-}
-
-
-static PaError QueryInputWaveFormatEx( int deviceId, WAVEFORMATEX *waveFormatEx )
-{
- MMRESULT mmresult;
-
- switch( mmresult = waveInOpen( NULL, deviceId, waveFormatEx, 0, 0, WAVE_FORMAT_QUERY ) )
- {
- case MMSYSERR_NOERROR:
- return paNoError;
- case MMSYSERR_ALLOCATED: /* Specified resource is already allocated. */
- return paDeviceUnavailable;
- case MMSYSERR_NODRIVER: /* No device driver is present. */
- return paDeviceUnavailable;
- case MMSYSERR_NOMEM: /* Unable to allocate or lock memory. */
- return paInsufficientMemory;
- case WAVERR_BADFORMAT: /* Attempted to open with an unsupported waveform-audio format. */
- return paSampleFormatNotSupported;
-
- case MMSYSERR_BADDEVICEID: /* Specified device identifier is out of range. */
- /* falls through */
- default:
- PA_MME_SET_LAST_WAVEIN_ERROR( mmresult );
- return paUnanticipatedHostError;
- }
-}
-
-
-static PaError QueryOutputWaveFormatEx( int deviceId, WAVEFORMATEX *waveFormatEx )
-{
- MMRESULT mmresult;
-
- switch( mmresult = waveOutOpen( NULL, deviceId, waveFormatEx, 0, 0, WAVE_FORMAT_QUERY ) )
- {
- case MMSYSERR_NOERROR:
- return paNoError;
- case MMSYSERR_ALLOCATED: /* Specified resource is already allocated. */
- return paDeviceUnavailable;
- case MMSYSERR_NODRIVER: /* No device driver is present. */
- return paDeviceUnavailable;
- case MMSYSERR_NOMEM: /* Unable to allocate or lock memory. */
- return paInsufficientMemory;
- case WAVERR_BADFORMAT: /* Attempted to open with an unsupported waveform-audio format. */
- return paSampleFormatNotSupported;
-
- case MMSYSERR_BADDEVICEID: /* Specified device identifier is out of range. */
- /* falls through */
- default:
- PA_MME_SET_LAST_WAVEOUT_ERROR( mmresult );
- return paUnanticipatedHostError;
- }
-}
-
-
-static PaError QueryFormatSupported( PaDeviceInfo *deviceInfo,
- PaError (*waveFormatExQueryFunction)(int, WAVEFORMATEX*),
- int winMmeDeviceId, int channels, double sampleRate )
-{
- PaWinMmeDeviceInfo *winMmeDeviceInfo = (PaWinMmeDeviceInfo*)deviceInfo;
- WAVEFORMATEX waveFormatEx;
-
- if( sampleRate == 11025.0
- && ( (channels == 1 && (winMmeDeviceInfo->dwFormats & WAVE_FORMAT_1M16))
- || (channels == 2 && (winMmeDeviceInfo->dwFormats & WAVE_FORMAT_1S16)) ) ){
-
- return paNoError;
- }
-
- if( sampleRate == 22050.0
- && ( (channels == 1 && (winMmeDeviceInfo->dwFormats & WAVE_FORMAT_2M16))
- || (channels == 2 && (winMmeDeviceInfo->dwFormats & WAVE_FORMAT_2S16)) ) ){
-
- return paNoError;
- }
-
- if( sampleRate == 44100.0
- && ( (channels == 1 && (winMmeDeviceInfo->dwFormats & WAVE_FORMAT_4M16))
- || (channels == 2 && (winMmeDeviceInfo->dwFormats & WAVE_FORMAT_4S16)) ) ){
-
- return paNoError;
- }
-
- waveFormatEx.wFormatTag = WAVE_FORMAT_PCM;
- waveFormatEx.nChannels = (WORD)channels;
- waveFormatEx.nSamplesPerSec = (DWORD)sampleRate;
- waveFormatEx.nAvgBytesPerSec = waveFormatEx.nSamplesPerSec * channels * sizeof(short);
- waveFormatEx.nBlockAlign = (WORD)(channels * sizeof(short));
- waveFormatEx.wBitsPerSample = 16;
- waveFormatEx.cbSize = 0;
-
- return waveFormatExQueryFunction( winMmeDeviceId, &waveFormatEx );
-}
-
-
-#define PA_DEFAULTSAMPLERATESEARCHORDER_COUNT_ (13) /* must match array length below */
-static double defaultSampleRateSearchOrder_[] =
- { 44100.0, 48000.0, 32000.0, 24000.0, 22050.0, 88200.0, 96000.0, 192000.0,
- 16000.0, 12000.0, 11025.0, 9600.0, 8000.0 };
-
-static void DetectDefaultSampleRate( PaWinMmeDeviceInfo *winMmeDeviceInfo, int winMmeDeviceId,
- PaError (*waveFormatExQueryFunction)(int, WAVEFORMATEX*), int maxChannels )
-{
- PaDeviceInfo *deviceInfo = &winMmeDeviceInfo->inheritedDeviceInfo;
- int i;
-
- deviceInfo->defaultSampleRate = 0.;
-
- for( i=0; i < PA_DEFAULTSAMPLERATESEARCHORDER_COUNT_; ++i )
- {
- double sampleRate = defaultSampleRateSearchOrder_[ i ];
- PaError paerror = QueryFormatSupported( deviceInfo, waveFormatExQueryFunction, winMmeDeviceId, maxChannels, sampleRate );
- if( paerror == paNoError )
- {
- deviceInfo->defaultSampleRate = sampleRate;
- break;
- }
- }
-}
-
-
-static PaError InitializeInputDeviceInfo( PaWinMmeHostApiRepresentation *winMmeHostApi,
- PaWinMmeDeviceInfo *winMmeDeviceInfo, UINT winMmeInputDeviceId, int *success )
-{
- PaError result = paNoError;
- char *deviceName; /* non-const ptr */
- MMRESULT mmresult;
- WAVEINCAPS wic;
- PaDeviceInfo *deviceInfo = &winMmeDeviceInfo->inheritedDeviceInfo;
-
- *success = 0;
-
- mmresult = waveInGetDevCaps( winMmeInputDeviceId, &wic, sizeof( WAVEINCAPS ) );
- if( mmresult == MMSYSERR_NOMEM )
- {
- result = paInsufficientMemory;
- goto error;
- }
- else if( mmresult != MMSYSERR_NOERROR )
- {
- /* instead of returning paUnanticipatedHostError we return
- paNoError, but leave success set as 0. This allows
- Pa_Initialize to just ignore this device, without failing
- the entire initialisation process.
- */
- return paNoError;
- }
-
- if( winMmeInputDeviceId == WAVE_MAPPER )
- {
- /* Append I/O suffix to WAVE_MAPPER device. */
- deviceName = (char *)PaUtil_GroupAllocateMemory(
- winMmeHostApi->allocations, strlen( wic.szPname ) + 1 + sizeof(constInputMapperSuffix_) );
- if( !deviceName )
- {
- result = paInsufficientMemory;
- goto error;
- }
- strcpy( deviceName, wic.szPname );
- strcat( deviceName, constInputMapperSuffix_ );
- }
- else
- {
- deviceName = (char*)PaUtil_GroupAllocateMemory(
- winMmeHostApi->allocations, strlen( wic.szPname ) + 1 );
- if( !deviceName )
- {
- result = paInsufficientMemory;
- goto error;
- }
- strcpy( deviceName, wic.szPname );
- }
- deviceInfo->name = deviceName;
-
- deviceInfo->maxInputChannels = wic.wChannels;
- /* Sometimes a device can return a rediculously large number of channels.
- * This happened with an SBLive card on a Windows ME box.
- * If that happens, then force it to 2 channels. PLB20010413
- */
- if( (deviceInfo->maxInputChannels < 1) || (deviceInfo->maxInputChannels > 256) )
- {
- PA_DEBUG(("Pa_GetDeviceInfo: Num input channels reported as %d! Changed to 2.\n", deviceInfo->maxInputChannels ));
- deviceInfo->maxInputChannels = 2;
- }
-
- winMmeDeviceInfo->dwFormats = wic.dwFormats;
-
- DetectDefaultSampleRate( winMmeDeviceInfo, winMmeInputDeviceId,
- QueryInputWaveFormatEx, deviceInfo->maxInputChannels );
-
- *success = 1;
-
-error:
- return result;
-}
-
-
-static PaError InitializeOutputDeviceInfo( PaWinMmeHostApiRepresentation *winMmeHostApi,
- PaWinMmeDeviceInfo *winMmeDeviceInfo, UINT winMmeOutputDeviceId, int *success )
-{
- PaError result = paNoError;
- char *deviceName; /* non-const ptr */
- MMRESULT mmresult;
- WAVEOUTCAPS woc;
- PaDeviceInfo *deviceInfo = &winMmeDeviceInfo->inheritedDeviceInfo;
-
- *success = 0;
-
- mmresult = waveOutGetDevCaps( winMmeOutputDeviceId, &woc, sizeof( WAVEOUTCAPS ) );
- if( mmresult == MMSYSERR_NOMEM )
- {
- result = paInsufficientMemory;
- goto error;
- }
- else if( mmresult != MMSYSERR_NOERROR )
- {
- /* instead of returning paUnanticipatedHostError we return
- paNoError, but leave success set as 0. This allows
- Pa_Initialize to just ignore this device, without failing
- the entire initialisation process.
- */
- return paNoError;
- }
-
- if( winMmeOutputDeviceId == WAVE_MAPPER )
- {
- /* Append I/O suffix to WAVE_MAPPER device. */
- deviceName = (char *)PaUtil_GroupAllocateMemory(
- winMmeHostApi->allocations, strlen( woc.szPname ) + 1 + sizeof(constOutputMapperSuffix_) );
- if( !deviceName )
- {
- result = paInsufficientMemory;
- goto error;
- }
- strcpy( deviceName, woc.szPname );
- strcat( deviceName, constOutputMapperSuffix_ );
- }
- else
- {
- deviceName = (char*)PaUtil_GroupAllocateMemory(
- winMmeHostApi->allocations, strlen( woc.szPname ) + 1 );
- if( !deviceName )
- {
- result = paInsufficientMemory;
- goto error;
- }
- strcpy( deviceName, woc.szPname );
- }
- deviceInfo->name = deviceName;
-
- deviceInfo->maxOutputChannels = woc.wChannels;
- /* Sometimes a device can return a rediculously large number of channels.
- * This happened with an SBLive card on a Windows ME box.
- * It also happens on Win XP!
- */
- if( (deviceInfo->maxOutputChannels < 1) || (deviceInfo->maxOutputChannels > 256) )
- {
- PA_DEBUG(("Pa_GetDeviceInfo: Num output channels reported as %d! Changed to 2.\n", deviceInfo->maxOutputChannels ));
- deviceInfo->maxOutputChannels = 2;
- }
-
- winMmeDeviceInfo->dwFormats = woc.dwFormats;
-
- DetectDefaultSampleRate( winMmeDeviceInfo, winMmeOutputDeviceId,
- QueryOutputWaveFormatEx, deviceInfo->maxOutputChannels );
-
- *success = 1;
-
-error:
- return result;
-}
-
-
-static void GetDefaultLatencies( PaTime *defaultLowLatency, PaTime *defaultHighLatency )
-{
- OSVERSIONINFO osvi;
- osvi.dwOSVersionInfoSize = sizeof( osvi );
- GetVersionEx( &osvi );
-
- /* Check for NT */
- if( (osvi.dwMajorVersion == 4) && (osvi.dwPlatformId == 2) )
- {
- *defaultLowLatency = PA_MME_WIN_NT_DEFAULT_LATENCY_;
- }
- else if(osvi.dwMajorVersion >= 5)
- {
- *defaultLowLatency = PA_MME_WIN_WDM_DEFAULT_LATENCY_;
- }
- else
- {
- *defaultLowLatency = PA_MME_WIN_9X_DEFAULT_LATENCY_;
- }
-
- *defaultHighLatency = *defaultLowLatency * 2;
-}
-
-
-PaError PaWinMme_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex hostApiIndex )
-{
- PaError result = paNoError;
- int i;
- PaWinMmeHostApiRepresentation *winMmeHostApi;
- int inputDeviceCount, outputDeviceCount, maximumPossibleDeviceCount;
- PaWinMmeDeviceInfo *deviceInfoArray;
- int deviceInfoInitializationSucceeded;
- PaTime defaultLowLatency, defaultHighLatency;
-
- winMmeHostApi = (PaWinMmeHostApiRepresentation*)PaUtil_AllocateMemory( sizeof(PaWinMmeHostApiRepresentation) );
- if( !winMmeHostApi )
- {
- result = paInsufficientMemory;
- goto error;
- }
-
- winMmeHostApi->allocations = PaUtil_CreateAllocationGroup();
- if( !winMmeHostApi->allocations )
- {
- result = paInsufficientMemory;
- goto error;
- }
-
- *hostApi = &winMmeHostApi->inheritedHostApiRep;
- (*hostApi)->info.structVersion = 1;
- (*hostApi)->info.type = paMME;
- (*hostApi)->info.name = "MME";
-
-
- /* initialise device counts and default devices under the assumption that
- there are no devices. These values are incremented below if and when
- devices are successfully initialized.
- */
- (*hostApi)->info.deviceCount = 0;
- (*hostApi)->info.defaultInputDevice = paNoDevice;
- (*hostApi)->info.defaultOutputDevice = paNoDevice;
- winMmeHostApi->inputDeviceCount = 0;
- winMmeHostApi->outputDeviceCount = 0;
-
-
- maximumPossibleDeviceCount = 0;
-
- inputDeviceCount = waveInGetNumDevs();
- if( inputDeviceCount > 0 )
- maximumPossibleDeviceCount += inputDeviceCount + 1; /* assume there is a WAVE_MAPPER */
-
- outputDeviceCount = waveOutGetNumDevs();
- if( outputDeviceCount > 0 )
- maximumPossibleDeviceCount += outputDeviceCount + 1; /* assume there is a WAVE_MAPPER */
-
-
- if( maximumPossibleDeviceCount > 0 ){
-
- (*hostApi)->deviceInfos = (PaDeviceInfo**)PaUtil_GroupAllocateMemory(
- winMmeHostApi->allocations, sizeof(PaDeviceInfo*) * maximumPossibleDeviceCount );
- if( !(*hostApi)->deviceInfos )
- {
- result = paInsufficientMemory;
- goto error;
- }
-
- /* allocate all device info structs in a contiguous block */
- deviceInfoArray = (PaWinMmeDeviceInfo*)PaUtil_GroupAllocateMemory(
- winMmeHostApi->allocations, sizeof(PaWinMmeDeviceInfo) * maximumPossibleDeviceCount );
- if( !deviceInfoArray )
- {
- result = paInsufficientMemory;
- goto error;
- }
-
- winMmeHostApi->winMmeDeviceIds = (UINT*)PaUtil_GroupAllocateMemory(
- winMmeHostApi->allocations, sizeof(int) * maximumPossibleDeviceCount );
- if( !winMmeHostApi->winMmeDeviceIds )
- {
- result = paInsufficientMemory;
- goto error;
- }
-
- GetDefaultLatencies( &defaultLowLatency, &defaultHighLatency );
-
- if( inputDeviceCount > 0 ){
- /* -1 is the WAVE_MAPPER */
- for( i = -1; i < inputDeviceCount; ++i ){
- UINT winMmeDeviceId = (UINT)((i==-1) ? WAVE_MAPPER : i);
- PaWinMmeDeviceInfo *wmmeDeviceInfo = &deviceInfoArray[ (*hostApi)->info.deviceCount ];
- PaDeviceInfo *deviceInfo = &wmmeDeviceInfo->inheritedDeviceInfo;
- deviceInfo->structVersion = 2;
- deviceInfo->hostApi = hostApiIndex;
-
- deviceInfo->maxInputChannels = 0;
- deviceInfo->maxOutputChannels = 0;
-
- deviceInfo->defaultLowInputLatency = defaultLowLatency;
- deviceInfo->defaultLowOutputLatency = defaultLowLatency;
- deviceInfo->defaultHighInputLatency = defaultHighLatency;
- deviceInfo->defaultHighOutputLatency = defaultHighLatency;
-
- result = InitializeInputDeviceInfo( winMmeHostApi, wmmeDeviceInfo,
- winMmeDeviceId, &deviceInfoInitializationSucceeded );
- if( result != paNoError )
- goto error;
-
- if( deviceInfoInitializationSucceeded ){
- if( (*hostApi)->info.defaultInputDevice == paNoDevice )
- (*hostApi)->info.defaultInputDevice = (*hostApi)->info.deviceCount;
-
- winMmeHostApi->winMmeDeviceIds[ (*hostApi)->info.deviceCount ] = winMmeDeviceId;
- (*hostApi)->deviceInfos[ (*hostApi)->info.deviceCount ] = deviceInfo;
-
- winMmeHostApi->inputDeviceCount++;
- (*hostApi)->info.deviceCount++;
- }
- }
- }
-
- if( outputDeviceCount > 0 ){
- /* -1 is the WAVE_MAPPER */
- for( i = -1; i < outputDeviceCount; ++i ){
- UINT winMmeDeviceId = (UINT)((i==-1) ? WAVE_MAPPER : i);
- PaWinMmeDeviceInfo *wmmeDeviceInfo = &deviceInfoArray[ (*hostApi)->info.deviceCount ];
- PaDeviceInfo *deviceInfo = &wmmeDeviceInfo->inheritedDeviceInfo;
- deviceInfo->structVersion = 2;
- deviceInfo->hostApi = hostApiIndex;
-
- deviceInfo->maxInputChannels = 0;
- deviceInfo->maxOutputChannels = 0;
-
- deviceInfo->defaultLowInputLatency = defaultLowLatency;
- deviceInfo->defaultLowOutputLatency = defaultLowLatency;
- deviceInfo->defaultHighInputLatency = defaultHighLatency;
- deviceInfo->defaultHighOutputLatency = defaultHighLatency;
-
- result = InitializeOutputDeviceInfo( winMmeHostApi, wmmeDeviceInfo,
- winMmeDeviceId, &deviceInfoInitializationSucceeded );
- if( result != paNoError )
- goto error;
-
- if( deviceInfoInitializationSucceeded ){
- if( (*hostApi)->info.defaultOutputDevice == paNoDevice )
- (*hostApi)->info.defaultOutputDevice = (*hostApi)->info.deviceCount;
-
- winMmeHostApi->winMmeDeviceIds[ (*hostApi)->info.deviceCount ] = winMmeDeviceId;
- (*hostApi)->deviceInfos[ (*hostApi)->info.deviceCount ] = deviceInfo;
-
- winMmeHostApi->outputDeviceCount++;
- (*hostApi)->info.deviceCount++;
- }
- }
- }
- }
-
-
- InitializeDefaultDeviceIdsFromEnv( winMmeHostApi );
-
- (*hostApi)->Terminate = Terminate;
- (*hostApi)->OpenStream = OpenStream;
- (*hostApi)->IsFormatSupported = IsFormatSupported;
-
- PaUtil_InitializeStreamInterface( &winMmeHostApi->callbackStreamInterface, CloseStream, StartStream,
- StopStream, AbortStream, IsStreamStopped, IsStreamActive,
- GetStreamTime, GetStreamCpuLoad,
- PaUtil_DummyRead, PaUtil_DummyWrite,
- PaUtil_DummyGetReadAvailable, PaUtil_DummyGetWriteAvailable );
-
- PaUtil_InitializeStreamInterface( &winMmeHostApi->blockingStreamInterface, CloseStream, StartStream,
- StopStream, AbortStream, IsStreamStopped, IsStreamActive,
- GetStreamTime, PaUtil_DummyGetCpuLoad,
- ReadStream, WriteStream, GetStreamReadAvailable, GetStreamWriteAvailable );
-
- return result;
-
-error:
- if( winMmeHostApi )
- {
- if( winMmeHostApi->allocations )
- {
- PaUtil_FreeAllAllocations( winMmeHostApi->allocations );
- PaUtil_DestroyAllocationGroup( winMmeHostApi->allocations );
- }
-
- PaUtil_FreeMemory( winMmeHostApi );
- }
-
- return result;
-}
-
-
-static void Terminate( struct PaUtilHostApiRepresentation *hostApi )
-{
- PaWinMmeHostApiRepresentation *winMmeHostApi = (PaWinMmeHostApiRepresentation*)hostApi;
-
- if( winMmeHostApi->allocations )
- {
- PaUtil_FreeAllAllocations( winMmeHostApi->allocations );
- PaUtil_DestroyAllocationGroup( winMmeHostApi->allocations );
- }
-
- PaUtil_FreeMemory( winMmeHostApi );
-}
-
-
-static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
- const PaStreamParameters *inputParameters,
- const PaStreamParameters *outputParameters,
- double sampleRate )
-{
- PaWinMmeHostApiRepresentation *winMmeHostApi = (PaWinMmeHostApiRepresentation*)hostApi;
- PaDeviceInfo *inputDeviceInfo, *outputDeviceInfo;
- int inputChannelCount, outputChannelCount;
- int inputMultipleDeviceChannelCount, outputMultipleDeviceChannelCount;
- PaSampleFormat inputSampleFormat, outputSampleFormat;
- PaWinMmeStreamInfo *inputStreamInfo, *outputStreamInfo;
- UINT winMmeInputDeviceId, winMmeOutputDeviceId;
- unsigned int i;
- PaError paerror;
-
- /* The calls to QueryFormatSupported below are intended to detect invalid
- sample rates. If we assume that the channel count and format are OK,
- then the only thing that could fail is the sample rate. This isn't
- strictly true, but I can't think of a better way to test that the
- sample rate is valid.
- */
-
- if( inputParameters )
- {
- inputChannelCount = inputParameters->channelCount;
- inputSampleFormat = inputParameters->sampleFormat;
- inputStreamInfo = inputParameters->hostApiSpecificStreamInfo;
-
- /* all standard sample formats are supported by the buffer adapter,
- this implementation doesn't support any custom sample formats */
- if( inputSampleFormat & paCustomFormat )
- return paSampleFormatNotSupported;
-
- if( inputParameters->device == paUseHostApiSpecificDeviceSpecification
- && inputStreamInfo && (inputStreamInfo->flags & paWinMmeUseMultipleDevices) )
- {
- inputMultipleDeviceChannelCount = 0;
- for( i=0; i< inputStreamInfo->deviceCount; ++i )
- {
- inputMultipleDeviceChannelCount += inputStreamInfo->devices[i].channelCount;
-
- inputDeviceInfo = hostApi->deviceInfos[ inputStreamInfo->devices[i].device ];
-
- /* check that input device can support inputChannelCount */
- if( inputStreamInfo->devices[i].channelCount <= 0
- || inputStreamInfo->devices[i].channelCount > inputDeviceInfo->maxInputChannels )
- return paInvalidChannelCount;
-
- /* test for valid sample rate, see comment above */
- winMmeInputDeviceId = LocalDeviceIndexToWinMmeDeviceId( winMmeHostApi, inputStreamInfo->devices[i].device );
- paerror = QueryFormatSupported( inputDeviceInfo, QueryInputWaveFormatEx, winMmeInputDeviceId, inputStreamInfo->devices[i].channelCount, sampleRate );
- if( paerror != paNoError )
- return paInvalidSampleRate;
- }
-
- if( inputMultipleDeviceChannelCount != inputChannelCount )
- return paIncompatibleHostApiSpecificStreamInfo;
- }
- else
- {
- if( inputStreamInfo && (inputStreamInfo->flags & paWinMmeUseMultipleDevices) )
- return paIncompatibleHostApiSpecificStreamInfo; /* paUseHostApiSpecificDeviceSpecification was not supplied as the input device */
-
- inputDeviceInfo = hostApi->deviceInfos[ inputParameters->device ];
-
- /* check that input device can support inputChannelCount */
- if( inputChannelCount > inputDeviceInfo->maxInputChannels )
- return paInvalidChannelCount;
-
- /* test for valid sample rate, see comment above */
- winMmeInputDeviceId = LocalDeviceIndexToWinMmeDeviceId( winMmeHostApi, inputParameters->device );
- paerror = QueryFormatSupported( inputDeviceInfo, QueryInputWaveFormatEx, winMmeInputDeviceId, inputChannelCount, sampleRate );
- if( paerror != paNoError )
- return paInvalidSampleRate;
- }
- }
-
- if( outputParameters )
- {
- outputChannelCount = outputParameters->channelCount;
- outputSampleFormat = outputParameters->sampleFormat;
- outputStreamInfo = outputParameters->hostApiSpecificStreamInfo;
-
- /* all standard sample formats are supported by the buffer adapter,
- this implementation doesn't support any custom sample formats */
- if( outputSampleFormat & paCustomFormat )
- return paSampleFormatNotSupported;
-
- if( outputParameters->device == paUseHostApiSpecificDeviceSpecification
- && outputStreamInfo && (outputStreamInfo->flags & paWinMmeUseMultipleDevices) )
- {
- outputMultipleDeviceChannelCount = 0;
- for( i=0; i< outputStreamInfo->deviceCount; ++i )
- {
- outputMultipleDeviceChannelCount += outputStreamInfo->devices[i].channelCount;
-
- outputDeviceInfo = hostApi->deviceInfos[ outputStreamInfo->devices[i].device ];
-
- /* check that output device can support outputChannelCount */
- if( outputStreamInfo->devices[i].channelCount <= 0
- || outputStreamInfo->devices[i].channelCount > outputDeviceInfo->maxOutputChannels )
- return paInvalidChannelCount;
-
- /* test for valid sample rate, see comment above */
- winMmeOutputDeviceId = LocalDeviceIndexToWinMmeDeviceId( winMmeHostApi, outputStreamInfo->devices[i].device );
- paerror = QueryFormatSupported( outputDeviceInfo, QueryOutputWaveFormatEx, winMmeOutputDeviceId, outputStreamInfo->devices[i].channelCount, sampleRate );
- if( paerror != paNoError )
- return paInvalidSampleRate;
- }
-
- if( outputMultipleDeviceChannelCount != outputChannelCount )
- return paIncompatibleHostApiSpecificStreamInfo;
- }
- else
- {
- if( outputStreamInfo && (outputStreamInfo->flags & paWinMmeUseMultipleDevices) )
- return paIncompatibleHostApiSpecificStreamInfo; /* paUseHostApiSpecificDeviceSpecification was not supplied as the output device */
-
- outputDeviceInfo = hostApi->deviceInfos[ outputParameters->device ];
-
- /* check that output device can support outputChannelCount */
- if( outputChannelCount > outputDeviceInfo->maxOutputChannels )
- return paInvalidChannelCount;
-
- /* test for valid sample rate, see comment above */
- winMmeOutputDeviceId = LocalDeviceIndexToWinMmeDeviceId( winMmeHostApi, outputParameters->device );
- paerror = QueryFormatSupported( outputDeviceInfo, QueryOutputWaveFormatEx, winMmeOutputDeviceId, outputChannelCount, sampleRate );
- if( paerror != paNoError )
- return paInvalidSampleRate;
- }
- }
-
- /*
- - if a full duplex stream is requested, check that the combination
- of input and output parameters is supported
-
- - check that the device supports sampleRate
-
- for mme all we can do is test that the input and output devices
- support the requested sample rate and number of channels. we
- cannot test for full duplex compatibility.
- */
-
- return paFormatIsSupported;
-}
-
-
-
-static void SelectBufferSizeAndCount( unsigned long baseBufferSize,
- unsigned long requestedLatency,
- unsigned long baseBufferCount, unsigned long minimumBufferCount,
- unsigned long maximumBufferSize, unsigned long *hostBufferSize,
- unsigned long *hostBufferCount )
-{
- unsigned long sizeMultiplier, bufferCount, latency;
- unsigned long nextLatency, nextBufferSize;
- int baseBufferSizeIsPowerOfTwo;
-
- sizeMultiplier = 1;
- bufferCount = baseBufferCount;
-
- /* count-1 below because latency is always determined by one less
- than the total number of buffers.
- */
- latency = (baseBufferSize * sizeMultiplier) * (bufferCount-1);
-
- if( latency > requestedLatency )
- {
-
- /* reduce number of buffers without falling below suggested latency */
-
- nextLatency = (baseBufferSize * sizeMultiplier) * (bufferCount-2);
- while( bufferCount > minimumBufferCount && nextLatency >= requestedLatency )
- {
- --bufferCount;
- nextLatency = (baseBufferSize * sizeMultiplier) * (bufferCount-2);
- }
-
- }else if( latency < requestedLatency ){
-
- baseBufferSizeIsPowerOfTwo = (! (baseBufferSize & (baseBufferSize - 1)));
- if( baseBufferSizeIsPowerOfTwo ){
-
- /* double size of buffers without exceeding requestedLatency */
-
- nextBufferSize = (baseBufferSize * (sizeMultiplier*2));
- nextLatency = nextBufferSize * (bufferCount-1);
- while( nextBufferSize <= maximumBufferSize
- && nextLatency < requestedLatency )
- {
- sizeMultiplier *= 2;
- nextBufferSize = (baseBufferSize * (sizeMultiplier*2));
- nextLatency = nextBufferSize * (bufferCount-1);
- }
-
- }else{
-
- /* increase size of buffers upto first excess of requestedLatency */
-
- nextBufferSize = (baseBufferSize * (sizeMultiplier+1));
- nextLatency = nextBufferSize * (bufferCount-1);
- while( nextBufferSize <= maximumBufferSize
- && nextLatency < requestedLatency )
- {
- ++sizeMultiplier;
- nextBufferSize = (baseBufferSize * (sizeMultiplier+1));
- nextLatency = nextBufferSize * (bufferCount-1);
- }
-
- if( nextLatency < requestedLatency )
- ++sizeMultiplier;
- }
-
- /* increase number of buffers until requestedLatency is reached */
-
- latency = (baseBufferSize * sizeMultiplier) * (bufferCount-1);
- while( latency < requestedLatency )
- {
- ++bufferCount;
- latency = (baseBufferSize * sizeMultiplier) * (bufferCount-1);
- }
- }
-
- *hostBufferSize = baseBufferSize * sizeMultiplier;
- *hostBufferCount = bufferCount;
-}
-
-
-static void ReselectBufferCount( unsigned long bufferSize,
- unsigned long requestedLatency,
- unsigned long baseBufferCount, unsigned long minimumBufferCount,
- unsigned long *hostBufferCount )
-{
- unsigned long bufferCount, latency;
- unsigned long nextLatency;
-
- bufferCount = baseBufferCount;
-
- /* count-1 below because latency is always determined by one less
- than the total number of buffers.
- */
- latency = bufferSize * (bufferCount-1);
-
- if( latency > requestedLatency )
- {
- /* reduce number of buffers without falling below suggested latency */
-
- nextLatency = bufferSize * (bufferCount-2);
- while( bufferCount > minimumBufferCount && nextLatency >= requestedLatency )
- {
- --bufferCount;
- nextLatency = bufferSize * (bufferCount-2);
- }
-
- }else if( latency < requestedLatency ){
-
- /* increase number of buffers until requestedLatency is reached */
-
- latency = bufferSize * (bufferCount-1);
- while( latency < requestedLatency )
- {
- ++bufferCount;
- latency = bufferSize * (bufferCount-1);
- }
- }
-
- *hostBufferCount = bufferCount;
-}
-
-
-/* CalculateBufferSettings() fills the framesPerHostInputBuffer, hostInputBufferCount,
- framesPerHostOutputBuffer and hostOutputBufferCount parameters based on the values
- of the other parameters.
-*/
-
-static PaError CalculateBufferSettings(
- unsigned long *framesPerHostInputBuffer, unsigned long *hostInputBufferCount,
- unsigned long *framesPerHostOutputBuffer, unsigned long *hostOutputBufferCount,
- int inputChannelCount, PaSampleFormat hostInputSampleFormat,
- PaTime suggestedInputLatency, PaWinMmeStreamInfo *inputStreamInfo,
- int outputChannelCount, PaSampleFormat hostOutputSampleFormat,
- PaTime suggestedOutputLatency, PaWinMmeStreamInfo *outputStreamInfo,
- double sampleRate, unsigned long framesPerBuffer )
-{
- PaError result = paNoError;
- int effectiveInputChannelCount, effectiveOutputChannelCount;
- int hostInputFrameSize = 0;
- unsigned int i;
-
- if( inputChannelCount > 0 )
- {
- int hostInputSampleSize = Pa_GetSampleSize( hostInputSampleFormat );
- if( hostInputSampleSize < 0 )
- {
- result = hostInputSampleSize;
- goto error;
- }
-
- if( inputStreamInfo
- && ( inputStreamInfo->flags & paWinMmeUseMultipleDevices ) )
- {
- /* set effectiveInputChannelCount to the largest number of
- channels on any one device.
- */
- effectiveInputChannelCount = 0;
- for( i=0; i< inputStreamInfo->deviceCount; ++i )
- {
- if( inputStreamInfo->devices[i].channelCount > effectiveInputChannelCount )
- effectiveInputChannelCount = inputStreamInfo->devices[i].channelCount;
- }
- }
- else
- {
- effectiveInputChannelCount = inputChannelCount;
- }
-
- hostInputFrameSize = hostInputSampleSize * effectiveInputChannelCount;
-
- if( inputStreamInfo
- && ( inputStreamInfo->flags & paWinMmeUseLowLevelLatencyParameters ) )
- {
- if( inputStreamInfo->bufferCount <= 0
- || inputStreamInfo->framesPerBuffer <= 0 )
- {
- result = paIncompatibleHostApiSpecificStreamInfo;
- goto error;
- }
-
- *framesPerHostInputBuffer = inputStreamInfo->framesPerBuffer;
- *hostInputBufferCount = inputStreamInfo->bufferCount;
- }
- else
- {
- unsigned long hostBufferSizeBytes, hostBufferCount;
- unsigned long minimumBufferCount = (outputChannelCount > 0)
- ? PA_MME_MIN_HOST_INPUT_BUFFER_COUNT_FULL_DUPLEX_
- : PA_MME_MIN_HOST_INPUT_BUFFER_COUNT_HALF_DUPLEX_;
-
- unsigned long maximumBufferSize = (long) ((PA_MME_MAX_HOST_BUFFER_SECS_ * sampleRate) * hostInputFrameSize);
- if( maximumBufferSize > PA_MME_MAX_HOST_BUFFER_BYTES_ )
- maximumBufferSize = PA_MME_MAX_HOST_BUFFER_BYTES_;
-
- /* compute the following in bytes, then convert back to frames */
-
- SelectBufferSizeAndCount(
- ((framesPerBuffer == paFramesPerBufferUnspecified)
- ? PA_MME_MIN_HOST_BUFFER_FRAMES_WHEN_UNSPECIFIED_
- : framesPerBuffer ) * hostInputFrameSize, /* baseBufferSize */
- ((unsigned long)(suggestedInputLatency * sampleRate)) * hostInputFrameSize, /* suggestedLatency */
- 4, /* baseBufferCount */
- minimumBufferCount, maximumBufferSize,
- &hostBufferSizeBytes, &hostBufferCount );
-
- *framesPerHostInputBuffer = hostBufferSizeBytes / hostInputFrameSize;
- *hostInputBufferCount = hostBufferCount;
- }
- }
- else
- {
- *framesPerHostInputBuffer = 0;
- *hostInputBufferCount = 0;
- }
-
- if( outputChannelCount > 0 )
- {
- if( outputStreamInfo
- && ( outputStreamInfo->flags & paWinMmeUseLowLevelLatencyParameters ) )
- {
- if( outputStreamInfo->bufferCount <= 0
- || outputStreamInfo->framesPerBuffer <= 0 )
- {
- result = paIncompatibleHostApiSpecificStreamInfo;
- goto error;
- }
-
- *framesPerHostOutputBuffer = outputStreamInfo->framesPerBuffer;
- *hostOutputBufferCount = outputStreamInfo->bufferCount;
-
-
- if( inputChannelCount > 0 ) /* full duplex */
- {
- if( *framesPerHostInputBuffer != *framesPerHostOutputBuffer )
- {
- if( inputStreamInfo
- && ( inputStreamInfo->flags & paWinMmeUseLowLevelLatencyParameters ) )
- {
- /* a custom StreamInfo was used for specifying both input
- and output buffer sizes, the larger buffer size
- must be a multiple of the smaller buffer size */
-
- if( *framesPerHostInputBuffer < *framesPerHostOutputBuffer )
- {
- if( *framesPerHostOutputBuffer % *framesPerHostInputBuffer != 0 )
- {
- result = paIncompatibleHostApiSpecificStreamInfo;
- goto error;
- }
- }
- else
- {
- assert( *framesPerHostInputBuffer > *framesPerHostOutputBuffer );
- if( *framesPerHostInputBuffer % *framesPerHostOutputBuffer != 0 )
- {
- result = paIncompatibleHostApiSpecificStreamInfo;
- goto error;
- }
- }
- }
- else
- {
- /* a custom StreamInfo was not used for specifying the input buffer size,
- so use the output buffer size, and approximately the same latency. */
-
- *framesPerHostInputBuffer = *framesPerHostOutputBuffer;
- *hostInputBufferCount = (((unsigned long)(suggestedInputLatency * sampleRate)) / *framesPerHostInputBuffer) + 1;
-
- if( *hostInputBufferCount < PA_MME_MIN_HOST_INPUT_BUFFER_COUNT_FULL_DUPLEX_ )
- *hostInputBufferCount = PA_MME_MIN_HOST_INPUT_BUFFER_COUNT_FULL_DUPLEX_;
- }
- }
- }
- }
- else
- {
- unsigned long hostBufferSizeBytes, hostBufferCount;
- unsigned long minimumBufferCount = PA_MME_MIN_HOST_OUTPUT_BUFFER_COUNT_;
- unsigned long maximumBufferSize;
- int hostOutputFrameSize;
- int hostOutputSampleSize;
-
- hostOutputSampleSize = Pa_GetSampleSize( hostOutputSampleFormat );
- if( hostOutputSampleSize < 0 )
- {
- result = hostOutputSampleSize;
- goto error;
- }
-
- if( outputStreamInfo
- && ( outputStreamInfo->flags & paWinMmeUseMultipleDevices ) )
- {
- /* set effectiveOutputChannelCount to the largest number of
- channels on any one device.
- */
- effectiveOutputChannelCount = 0;
- for( i=0; i< outputStreamInfo->deviceCount; ++i )
- {
- if( outputStreamInfo->devices[i].channelCount > effectiveOutputChannelCount )
- effectiveOutputChannelCount = outputStreamInfo->devices[i].channelCount;
- }
- }
- else
- {
- effectiveOutputChannelCount = outputChannelCount;
- }
-
- hostOutputFrameSize = hostOutputSampleSize * effectiveOutputChannelCount;
-
- maximumBufferSize = (long) ((PA_MME_MAX_HOST_BUFFER_SECS_ * sampleRate) * hostOutputFrameSize);
- if( maximumBufferSize > PA_MME_MAX_HOST_BUFFER_BYTES_ )
- maximumBufferSize = PA_MME_MAX_HOST_BUFFER_BYTES_;
-
-
- /* compute the following in bytes, then convert back to frames */
-
- SelectBufferSizeAndCount(
- ((framesPerBuffer == paFramesPerBufferUnspecified)
- ? PA_MME_MIN_HOST_BUFFER_FRAMES_WHEN_UNSPECIFIED_
- : framesPerBuffer ) * hostOutputFrameSize, /* baseBufferSize */
- ((unsigned long)(suggestedOutputLatency * sampleRate)) * hostOutputFrameSize, /* suggestedLatency */
- 4, /* baseBufferCount */
- minimumBufferCount,
- maximumBufferSize,
- &hostBufferSizeBytes, &hostBufferCount );
-
- *framesPerHostOutputBuffer = hostBufferSizeBytes / hostOutputFrameSize;
- *hostOutputBufferCount = hostBufferCount;
-
-
- if( inputChannelCount > 0 )
- {
- /* ensure that both input and output buffer sizes are the same.
- if they don't match at this stage, choose the smallest one
- and use that for input and output
- */
-
- if( *framesPerHostOutputBuffer != *framesPerHostInputBuffer )
- {
- if( framesPerHostInputBuffer < framesPerHostOutputBuffer )
- {
- unsigned long framesPerHostBuffer = *framesPerHostInputBuffer;
-
- minimumBufferCount = PA_MME_MIN_HOST_OUTPUT_BUFFER_COUNT_;
- ReselectBufferCount(
- framesPerHostBuffer * hostOutputFrameSize, /* bufferSize */
- ((unsigned long)(suggestedOutputLatency * sampleRate)) * hostOutputFrameSize, /* suggestedLatency */
- 4, /* baseBufferCount */
- minimumBufferCount,
- &hostBufferCount );
-
- *framesPerHostOutputBuffer = framesPerHostBuffer;
- *hostOutputBufferCount = hostBufferCount;
- }
- else
- {
- unsigned long framesPerHostBuffer = *framesPerHostOutputBuffer;
-
- minimumBufferCount = PA_MME_MIN_HOST_INPUT_BUFFER_COUNT_FULL_DUPLEX_;
- ReselectBufferCount(
- framesPerHostBuffer * hostInputFrameSize, /* bufferSize */
- ((unsigned long)(suggestedInputLatency * sampleRate)) * hostInputFrameSize, /* suggestedLatency */
- 4, /* baseBufferCount */
- minimumBufferCount,
- &hostBufferCount );
-
- *framesPerHostInputBuffer = framesPerHostBuffer;
- *hostInputBufferCount = hostBufferCount;
- }
- }
- }
- }
- }
- else
- {
- *framesPerHostOutputBuffer = 0;
- *hostOutputBufferCount = 0;
- }
-
-error:
- return result;
-}
-
-
-typedef struct
-{
- HANDLE bufferEvent;
- void *waveHandles;
- unsigned int deviceCount;
- /* unsigned int channelCount; */
- WAVEHDR **waveHeaders; /* waveHeaders[device][buffer] */
- unsigned int bufferCount;
- unsigned int currentBufferIndex;
- unsigned int framesPerBuffer;
- unsigned int framesUsedInCurrentBuffer;
-}PaWinMmeSingleDirectionHandlesAndBuffers;
-
-/* prototypes for functions operating on PaWinMmeSingleDirectionHandlesAndBuffers */
-
-static void InitializeSingleDirectionHandlesAndBuffers( PaWinMmeSingleDirectionHandlesAndBuffers *handlesAndBuffers );
-static PaError InitializeWaveHandles( PaWinMmeHostApiRepresentation *winMmeHostApi,
- PaWinMmeSingleDirectionHandlesAndBuffers *handlesAndBuffers,
- unsigned long bytesPerHostSample,
- double sampleRate, PaWinMmeDeviceAndChannelCount *devices,
- unsigned int deviceCount, int isInput );
-static PaError TerminateWaveHandles( PaWinMmeSingleDirectionHandlesAndBuffers *handlesAndBuffers, int isInput, int currentlyProcessingAnError );
-static PaError InitializeWaveHeaders( PaWinMmeSingleDirectionHandlesAndBuffers *handlesAndBuffers,
- unsigned long hostBufferCount,
- PaSampleFormat hostSampleFormat,
- unsigned long framesPerHostBuffer,
- PaWinMmeDeviceAndChannelCount *devices,
- int isInput );
-static void TerminateWaveHeaders( PaWinMmeSingleDirectionHandlesAndBuffers *handlesAndBuffers, int isInput );
-
-
-static void InitializeSingleDirectionHandlesAndBuffers( PaWinMmeSingleDirectionHandlesAndBuffers *handlesAndBuffers )
-{
- handlesAndBuffers->bufferEvent = 0;
- handlesAndBuffers->waveHandles = 0;
- handlesAndBuffers->deviceCount = 0;
- handlesAndBuffers->waveHeaders = 0;
- handlesAndBuffers->bufferCount = 0;
-}
-
-static PaError InitializeWaveHandles( PaWinMmeHostApiRepresentation *winMmeHostApi,
- PaWinMmeSingleDirectionHandlesAndBuffers *handlesAndBuffers,
- unsigned long bytesPerHostSample,
- double sampleRate, PaWinMmeDeviceAndChannelCount *devices,
- unsigned int deviceCount, int isInput )
-{
- PaError result;
- MMRESULT mmresult;
- unsigned long bytesPerFrame;
- WAVEFORMATEX wfx;
- signed int i;
-
- /* for error cleanup we expect that InitializeSingleDirectionHandlesAndBuffers()
- has already been called to zero some fields */
-
- result = CreateEventWithPaError( &handlesAndBuffers->bufferEvent, NULL, FALSE, FALSE, NULL );
- if( result != paNoError ) goto error;
-
- if( isInput )
- handlesAndBuffers->waveHandles = (void*)PaUtil_AllocateMemory( sizeof(HWAVEIN) * deviceCount );
- else
- handlesAndBuffers->waveHandles = (void*)PaUtil_AllocateMemory( sizeof(HWAVEOUT) * deviceCount );
- if( !handlesAndBuffers->waveHandles )
- {
- result = paInsufficientMemory;
- goto error;
- }
-
- handlesAndBuffers->deviceCount = deviceCount;
-
- for( i = 0; i < (signed int)deviceCount; ++i )
- {
- if( isInput )
- ((HWAVEIN*)handlesAndBuffers->waveHandles)[i] = 0;
- else
- ((HWAVEOUT*)handlesAndBuffers->waveHandles)[i] = 0;
- }
-
- wfx.wFormatTag = WAVE_FORMAT_PCM;
- wfx.nSamplesPerSec = (DWORD) sampleRate;
- wfx.cbSize = 0;
-
- for( i = 0; i < (signed int)deviceCount; ++i )
- {
- UINT winMmeDeviceId;
-
- winMmeDeviceId = LocalDeviceIndexToWinMmeDeviceId( winMmeHostApi, devices[i].device );
- wfx.nChannels = (WORD)devices[i].channelCount;
-
- bytesPerFrame = wfx.nChannels * bytesPerHostSample;
-
- wfx.nAvgBytesPerSec = (DWORD)(bytesPerFrame * sampleRate);
- wfx.nBlockAlign = (WORD)bytesPerFrame;
- wfx.wBitsPerSample = (WORD)((bytesPerFrame/wfx.nChannels) * 8);
-
- /* REVIEW: consider not firing an event for input when a full duplex
- stream is being used. this would probably depend on the
- neverDropInput flag. */
-
- if( isInput )
- mmresult = waveInOpen( &((HWAVEIN*)handlesAndBuffers->waveHandles)[i], winMmeDeviceId, &wfx,
- (DWORD)handlesAndBuffers->bufferEvent, (DWORD)0, CALLBACK_EVENT );
- else
- mmresult = waveOutOpen( &((HWAVEOUT*)handlesAndBuffers->waveHandles)[i], winMmeDeviceId, &wfx,
- (DWORD)handlesAndBuffers->bufferEvent, (DWORD)0, CALLBACK_EVENT );
-
- if( mmresult != MMSYSERR_NOERROR )
- {
- switch( mmresult )
- {
- case MMSYSERR_ALLOCATED: /* Specified resource is already allocated. */
- result = paDeviceUnavailable;
- break;
- case MMSYSERR_NODRIVER: /* No device driver is present. */
- result = paDeviceUnavailable;
- break;
- case MMSYSERR_NOMEM: /* Unable to allocate or lock memory. */
- result = paInsufficientMemory;
- break;
-
- case MMSYSERR_BADDEVICEID: /* Specified device identifier is out of range. */
- /* falls through */
- case WAVERR_BADFORMAT: /* Attempted to open with an unsupported waveform-audio format. */
- /* falls through */
- default:
- result = paUnanticipatedHostError;
- if( isInput )
- {
- PA_MME_SET_LAST_WAVEIN_ERROR( mmresult );
- }
- else
- {
- PA_MME_SET_LAST_WAVEOUT_ERROR( mmresult );
- }
- }
- goto error;
- }
- }
-
- return result;
-
-error:
- TerminateWaveHandles( handlesAndBuffers, isInput, 1 /* currentlyProcessingAnError */ );
-
- return result;
-}
-
-
-static PaError TerminateWaveHandles( PaWinMmeSingleDirectionHandlesAndBuffers *handlesAndBuffers, int isInput, int currentlyProcessingAnError )
-{
- PaError result = paNoError;
- MMRESULT mmresult;
- signed int i;
-
- if( handlesAndBuffers->waveHandles )
- {
- for( i = handlesAndBuffers->deviceCount-1; i >= 0; --i )
- {
- if( isInput )
- {
- if( ((HWAVEIN*)handlesAndBuffers->waveHandles)[i] )
- mmresult = waveInClose( ((HWAVEIN*)handlesAndBuffers->waveHandles)[i] );
- }
- else
- {
- if( ((HWAVEOUT*)handlesAndBuffers->waveHandles)[i] )
- mmresult = waveOutClose( ((HWAVEOUT*)handlesAndBuffers->waveHandles)[i] );
- }
-
- if( mmresult != MMSYSERR_NOERROR &&
- !currentlyProcessingAnError ) /* don't update the error state if we're already processing an error */
- {
- result = paUnanticipatedHostError;
- if( isInput )
- {
- PA_MME_SET_LAST_WAVEIN_ERROR( mmresult );
- }
- else
- {
- PA_MME_SET_LAST_WAVEOUT_ERROR( mmresult );
- }
- /* note that we don't break here, we try to continue closing devices */
- }
- }
-
- PaUtil_FreeMemory( handlesAndBuffers->waveHandles );
- handlesAndBuffers->waveHandles = 0;
- }
-
- if( handlesAndBuffers->bufferEvent )
- {
- result = CloseHandleWithPaError( handlesAndBuffers->bufferEvent );
- handlesAndBuffers->bufferEvent = 0;
- }
-
- return result;
-}
-
-
-static PaError InitializeWaveHeaders( PaWinMmeSingleDirectionHandlesAndBuffers *handlesAndBuffers,
- unsigned long hostBufferCount,
- PaSampleFormat hostSampleFormat,
- unsigned long framesPerHostBuffer,
- PaWinMmeDeviceAndChannelCount *devices,
- int isInput )
-{
- PaError result = paNoError;
- MMRESULT mmresult;
- WAVEHDR *deviceWaveHeaders;
- signed int i, j;
-
- /* for error cleanup we expect that InitializeSingleDirectionHandlesAndBuffers()
- has already been called to zero some fields */
-
-
- /* allocate an array of pointers to arrays of wave headers, one array of
- wave headers per device */
- handlesAndBuffers->waveHeaders = (WAVEHDR**)PaUtil_AllocateMemory( sizeof(WAVEHDR*) * handlesAndBuffers->deviceCount );
- if( !handlesAndBuffers->waveHeaders )
- {
- result = paInsufficientMemory;
- goto error;
- }
-
- for( i = 0; i < (signed int)handlesAndBuffers->deviceCount; ++i )
- handlesAndBuffers->waveHeaders[i] = 0;
-
- handlesAndBuffers->bufferCount = hostBufferCount;
-
- for( i = 0; i < (signed int)handlesAndBuffers->deviceCount; ++i )
- {
- int bufferBytes = Pa_GetSampleSize( hostSampleFormat ) *
- framesPerHostBuffer * devices[i].channelCount;
- if( bufferBytes < 0 )
- {
- result = paInternalError;
- goto error;
- }
-
- /* Allocate an array of wave headers for device i */
- deviceWaveHeaders = (WAVEHDR *) PaUtil_AllocateMemory( sizeof(WAVEHDR)*hostBufferCount );
- if( !deviceWaveHeaders )
- {
- result = paInsufficientMemory;
- goto error;
- }
-
- for( j=0; j < (signed int)hostBufferCount; ++j )
- deviceWaveHeaders[j].lpData = 0;
-
- handlesAndBuffers->waveHeaders[i] = deviceWaveHeaders;
-
- /* Allocate a buffer for each wave header */
- for( j=0; j < (signed int)hostBufferCount; ++j )
- {
- deviceWaveHeaders[j].lpData = (char *)PaUtil_AllocateMemory( bufferBytes );
- if( !deviceWaveHeaders[j].lpData )
- {
- result = paInsufficientMemory;
- goto error;
- }
- deviceWaveHeaders[j].dwBufferLength = bufferBytes;
- deviceWaveHeaders[j].dwUser = 0xFFFFFFFF; /* indicates that *PrepareHeader() has not yet been called, for error clean up code */
-
- if( isInput )
- {
- mmresult = waveInPrepareHeader( ((HWAVEIN*)handlesAndBuffers->waveHandles)[i], &deviceWaveHeaders[j], sizeof(WAVEHDR) );
- if( mmresult != MMSYSERR_NOERROR )
- {
- result = paUnanticipatedHostError;
- PA_MME_SET_LAST_WAVEIN_ERROR( mmresult );
- goto error;
- }
- }
- else /* output */
- {
- mmresult = waveOutPrepareHeader( ((HWAVEOUT*)handlesAndBuffers->waveHandles)[i], &deviceWaveHeaders[j], sizeof(WAVEHDR) );
- if( mmresult != MMSYSERR_NOERROR )
- {
- result = paUnanticipatedHostError;
- PA_MME_SET_LAST_WAVEIN_ERROR( mmresult );
- goto error;
- }
- }
- deviceWaveHeaders[j].dwUser = devices[i].channelCount;
- }
- }
-
- return result;
-
-error:
- TerminateWaveHeaders( handlesAndBuffers, isInput );
-
- return result;
-}
-
-
-static void TerminateWaveHeaders( PaWinMmeSingleDirectionHandlesAndBuffers *handlesAndBuffers, int isInput )
-{
- signed int i, j;
- WAVEHDR *deviceWaveHeaders;
-
- if( handlesAndBuffers->waveHeaders )
- {
- for( i = handlesAndBuffers->deviceCount-1; i >= 0 ; --i )
- {
- deviceWaveHeaders = handlesAndBuffers->waveHeaders[i]; /* wave headers for device i */
- if( deviceWaveHeaders )
- {
- for( j = handlesAndBuffers->bufferCount-1; j >= 0; --j )
- {
- if( deviceWaveHeaders[j].lpData )
- {
- if( deviceWaveHeaders[j].dwUser != 0xFFFFFFFF )
- {
- if( isInput )
- waveInUnprepareHeader( ((HWAVEIN*)handlesAndBuffers->waveHandles)[i], &deviceWaveHeaders[j], sizeof(WAVEHDR) );
- else
- waveOutUnprepareHeader( ((HWAVEOUT*)handlesAndBuffers->waveHandles)[i], &deviceWaveHeaders[j], sizeof(WAVEHDR) );
- }
-
- PaUtil_FreeMemory( deviceWaveHeaders[j].lpData );
- }
- }
-
- PaUtil_FreeMemory( deviceWaveHeaders );
- }
- }
-
- PaUtil_FreeMemory( handlesAndBuffers->waveHeaders );
- handlesAndBuffers->waveHeaders = 0;
- }
-}
-
-
-
-/* PaWinMmeStream - a stream data structure specifically for this implementation */
-/* note that struct PaWinMmeStream is typedeffed to PaWinMmeStream above. */
-struct PaWinMmeStream
-{
- PaUtilStreamRepresentation streamRepresentation;
- PaUtilCpuLoadMeasurer cpuLoadMeasurer;
- PaUtilBufferProcessor bufferProcessor;
-
- int primeStreamUsingCallback;
-
- PaWinMmeSingleDirectionHandlesAndBuffers input;
- PaWinMmeSingleDirectionHandlesAndBuffers output;
-
- /* Processing thread management -------------- */
- HANDLE abortEvent;
- HANDLE processingThread;
- DWORD processingThreadId;
-
- char throttleProcessingThreadOnOverload; /* 0 -> don't throtte, non-0 -> throttle */
- int processingThreadPriority;
- int highThreadPriority;
- int throttledThreadPriority;
- unsigned long throttledSleepMsecs;
-
- int isStopped;
- volatile int isActive;
- volatile int stopProcessing; /* stop thread once existing buffers have been returned */
- volatile int abortProcessing; /* stop thread immediately */
-
- DWORD allBuffersDurationMs; /* used to calculate timeouts */
-};
-
-/* updates deviceCount if PaWinMmeUseMultipleDevices is used */
-
-static PaError ValidateWinMmeSpecificStreamInfo(
- const PaStreamParameters *streamParameters,
- const PaWinMmeStreamInfo *streamInfo,
- char *throttleProcessingThreadOnOverload,
- unsigned long *deviceCount )
-{
- if( streamInfo )
- {
- if( streamInfo->size != sizeof( PaWinMmeStreamInfo )
- || streamInfo->version != 1 )
- {
- return paIncompatibleHostApiSpecificStreamInfo;
- }
-
- if( streamInfo->flags & paWinMmeDontThrottleOverloadedProcessingThread )
- *throttleProcessingThreadOnOverload = 0;
-
- if( streamInfo->flags & paWinMmeUseMultipleDevices )
- {
- if( streamParameters->device != paUseHostApiSpecificDeviceSpecification )
- return paInvalidDevice;
-
- *deviceCount = streamInfo->deviceCount;
- }
- }
-
- return paNoError;
-}
-
-static PaError RetrieveDevicesFromStreamParameters(
- struct PaUtilHostApiRepresentation *hostApi,
- const PaStreamParameters *streamParameters,
- const PaWinMmeStreamInfo *streamInfo,
- PaWinMmeDeviceAndChannelCount *devices,
- unsigned long deviceCount )
-{
- PaError result = paNoError;
- unsigned int i;
- int totalChannelCount;
- PaDeviceIndex hostApiDevice;
-
- if( streamInfo && streamInfo->flags & paWinMmeUseMultipleDevices )
- {
- totalChannelCount = 0;
- for( i=0; i < deviceCount; ++i )
- {
- /* validate that the device number is within range */
- result = PaUtil_DeviceIndexToHostApiDeviceIndex( &hostApiDevice,
- streamInfo->devices[i].device, hostApi );
- if( result != paNoError )
- return result;
-
- devices[i].device = hostApiDevice;
- devices[i].channelCount = streamInfo->devices[i].channelCount;
-
- totalChannelCount += devices[i].channelCount;
- }
-
- if( totalChannelCount != streamParameters->channelCount )
- {
- /* channelCount must match total channels specified by multiple devices */
- return paInvalidChannelCount; /* REVIEW use of this error code */
- }
- }
- else
- {
- devices[0].device = streamParameters->device;
- devices[0].channelCount = streamParameters->channelCount;
- }
-
- return result;
-}
-
-static PaError ValidateInputChannelCounts(
- struct PaUtilHostApiRepresentation *hostApi,
- PaWinMmeDeviceAndChannelCount *devices,
- unsigned long deviceCount )
-{
- unsigned int i;
-
- for( i=0; i < deviceCount; ++i )
- {
- if( devices[i].channelCount < 1 || devices[i].channelCount
- > hostApi->deviceInfos[ devices[i].device ]->maxInputChannels )
- return paInvalidChannelCount;
- }
-
- return paNoError;
-}
-
-static PaError ValidateOutputChannelCounts(
- struct PaUtilHostApiRepresentation *hostApi,
- PaWinMmeDeviceAndChannelCount *devices,
- unsigned long deviceCount )
-{
- unsigned int i;
-
- for( i=0; i < deviceCount; ++i )
- {
- if( devices[i].channelCount < 1 || devices[i].channelCount
- > hostApi->deviceInfos[ devices[i].device ]->maxOutputChannels )
- return paInvalidChannelCount;
- }
-
- return paNoError;
-}
-
-
-/* the following macros are intended to improve the readability of the following code */
-#define PA_IS_INPUT_STREAM_( stream ) ( stream ->input.waveHandles )
-#define PA_IS_OUTPUT_STREAM_( stream ) ( stream ->output.waveHandles )
-#define PA_IS_FULL_DUPLEX_STREAM_( stream ) ( stream ->input.waveHandles && stream ->output.waveHandles )
-#define PA_IS_HALF_DUPLEX_STREAM_( stream ) ( !(stream ->input.waveHandles && stream ->output.waveHandles) )
-
-static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
- PaStream** s,
- const PaStreamParameters *inputParameters,
- const PaStreamParameters *outputParameters,
- double sampleRate,
- unsigned long framesPerBuffer,
- PaStreamFlags streamFlags,
- PaStreamCallback *streamCallback,
- void *userData )
-{
- PaError result;
- PaWinMmeHostApiRepresentation *winMmeHostApi = (PaWinMmeHostApiRepresentation*)hostApi;
- PaWinMmeStream *stream = 0;
- int bufferProcessorIsInitialized = 0;
- int streamRepresentationIsInitialized = 0;
- PaSampleFormat hostInputSampleFormat, hostOutputSampleFormat;
- int inputChannelCount, outputChannelCount;
- PaSampleFormat inputSampleFormat, outputSampleFormat;
- double suggestedInputLatency, suggestedOutputLatency;
- PaWinMmeStreamInfo *inputStreamInfo, *outputStreamInfo;
- unsigned long framesPerHostInputBuffer;
- unsigned long hostInputBufferCount;
- unsigned long framesPerHostOutputBuffer;
- unsigned long hostOutputBufferCount;
- unsigned long framesPerBufferProcessorCall;
- PaWinMmeDeviceAndChannelCount *inputDevices = 0; /* contains all devices and channel counts as local host api ids, even when PaWinMmeUseMultipleDevices is not used */
- unsigned long inputDeviceCount = 0;
- PaWinMmeDeviceAndChannelCount *outputDevices = 0;
- unsigned long outputDeviceCount = 0; /* contains all devices and channel counts as local host api ids, even when PaWinMmeUseMultipleDevices is not used */
- char throttleProcessingThreadOnOverload = 1;
-
-
- if( inputParameters )
- {
- inputChannelCount = inputParameters->channelCount;
- inputSampleFormat = inputParameters->sampleFormat;
- suggestedInputLatency = inputParameters->suggestedLatency;
-
- inputDeviceCount = 1;
-
- /* validate input hostApiSpecificStreamInfo */
- inputStreamInfo = (PaWinMmeStreamInfo*)inputParameters->hostApiSpecificStreamInfo;
- result = ValidateWinMmeSpecificStreamInfo( inputParameters, inputStreamInfo,
- &throttleProcessingThreadOnOverload,
- &inputDeviceCount );
- if( result != paNoError ) return result;
-
- inputDevices = (PaWinMmeDeviceAndChannelCount*)alloca( sizeof(PaWinMmeDeviceAndChannelCount) * inputDeviceCount );
- if( !inputDevices ) return paInsufficientMemory;
-
- result = RetrieveDevicesFromStreamParameters( hostApi, inputParameters, inputStreamInfo, inputDevices, inputDeviceCount );
- if( result != paNoError ) return result;
-
- result = ValidateInputChannelCounts( hostApi, inputDevices, inputDeviceCount );
- if( result != paNoError ) return result;
-
- hostInputSampleFormat =
- PaUtil_SelectClosestAvailableFormat( paInt16 /* native formats */, inputSampleFormat );
- }
- else
- {
- inputChannelCount = 0;
- inputSampleFormat = 0;
- suggestedInputLatency = 0.;
- inputStreamInfo = 0;
- hostInputSampleFormat = 0;
- }
-
-
- if( outputParameters )
- {
- outputChannelCount = outputParameters->channelCount;
- outputSampleFormat = outputParameters->sampleFormat;
- suggestedOutputLatency = outputParameters->suggestedLatency;
-
- outputDeviceCount = 1;
-
- /* validate output hostApiSpecificStreamInfo */
- outputStreamInfo = (PaWinMmeStreamInfo*)outputParameters->hostApiSpecificStreamInfo;
- result = ValidateWinMmeSpecificStreamInfo( outputParameters, outputStreamInfo,
- &throttleProcessingThreadOnOverload,
- &outputDeviceCount );
- if( result != paNoError ) return result;
-
- outputDevices = (PaWinMmeDeviceAndChannelCount*)alloca( sizeof(PaWinMmeDeviceAndChannelCount) * outputDeviceCount );
- if( !outputDevices ) return paInsufficientMemory;
-
- result = RetrieveDevicesFromStreamParameters( hostApi, outputParameters, outputStreamInfo, outputDevices, outputDeviceCount );
- if( result != paNoError ) return result;
-
- result = ValidateOutputChannelCounts( hostApi, outputDevices, outputDeviceCount );
- if( result != paNoError ) return result;
-
- hostOutputSampleFormat =
- PaUtil_SelectClosestAvailableFormat( paInt16 /* native formats */, outputSampleFormat );
- }
- else
- {
- outputChannelCount = 0;
- outputSampleFormat = 0;
- outputStreamInfo = 0;
- hostOutputSampleFormat = 0;
- suggestedOutputLatency = 0.;
- }
-
-
- /*
- IMPLEMENT ME:
- - alter sampleRate to a close allowable rate if possible / necessary
- */
-
-
- /* validate platform specific flags */
- if( (streamFlags & paPlatformSpecificFlags) != 0 )
- return paInvalidFlag; /* unexpected platform specific flag */
-
-
- result = CalculateBufferSettings( &framesPerHostInputBuffer, &hostInputBufferCount,
- &framesPerHostOutputBuffer, &hostOutputBufferCount,
- inputChannelCount, hostInputSampleFormat, suggestedInputLatency, inputStreamInfo,
- outputChannelCount, hostOutputSampleFormat, suggestedOutputLatency, outputStreamInfo,
- sampleRate, framesPerBuffer );
- if( result != paNoError ) goto error;
-
-
- stream = (PaWinMmeStream*)PaUtil_AllocateMemory( sizeof(PaWinMmeStream) );
- if( !stream )
- {
- result = paInsufficientMemory;
- goto error;
- }
-
- InitializeSingleDirectionHandlesAndBuffers( &stream->input );
- InitializeSingleDirectionHandlesAndBuffers( &stream->output );
-
- stream->abortEvent = 0;
- stream->processingThread = 0;
-
- stream->throttleProcessingThreadOnOverload = throttleProcessingThreadOnOverload;
-
- PaUtil_InitializeStreamRepresentation( &stream->streamRepresentation,
- ( (streamCallback)
- ? &winMmeHostApi->callbackStreamInterface
- : &winMmeHostApi->blockingStreamInterface ),
- streamCallback, userData );
- streamRepresentationIsInitialized = 1;
-
- PaUtil_InitializeCpuLoadMeasurer( &stream->cpuLoadMeasurer, sampleRate );
-
-
- if( inputParameters && outputParameters ) /* full duplex */
- {
- if( framesPerHostInputBuffer < framesPerHostOutputBuffer )
- {
- assert( (framesPerHostOutputBuffer % framesPerHostInputBuffer) == 0 ); /* CalculateBufferSettings() should guarantee this condition */
-
- framesPerBufferProcessorCall = framesPerHostInputBuffer;
- }
- else
- {
- assert( (framesPerHostInputBuffer % framesPerHostOutputBuffer) == 0 ); /* CalculateBufferSettings() should guarantee this condition */
-
- framesPerBufferProcessorCall = framesPerHostOutputBuffer;
- }
- }
- else if( inputParameters )
- {
- framesPerBufferProcessorCall = framesPerHostInputBuffer;
- }
- else if( outputParameters )
- {
- framesPerBufferProcessorCall = framesPerHostOutputBuffer;
- }
-
- stream->input.framesPerBuffer = framesPerHostInputBuffer;
- stream->output.framesPerBuffer = framesPerHostOutputBuffer;
-
- result = PaUtil_InitializeBufferProcessor( &stream->bufferProcessor,
- inputChannelCount, inputSampleFormat, hostInputSampleFormat,
- outputChannelCount, outputSampleFormat, hostOutputSampleFormat,
- sampleRate, streamFlags, framesPerBuffer,
- framesPerBufferProcessorCall, paUtilFixedHostBufferSize,
- streamCallback, userData );
- if( result != paNoError ) goto error;
-
- bufferProcessorIsInitialized = 1;
-
- stream->streamRepresentation.streamInfo.inputLatency =
- (double)(PaUtil_GetBufferProcessorInputLatency(&stream->bufferProcessor)
- +(framesPerHostInputBuffer * (hostInputBufferCount-1))) / sampleRate;
- stream->streamRepresentation.streamInfo.outputLatency =
- (double)(PaUtil_GetBufferProcessorOutputLatency(&stream->bufferProcessor)
- +(framesPerHostOutputBuffer * (hostOutputBufferCount-1))) / sampleRate;
- stream->streamRepresentation.streamInfo.sampleRate = sampleRate;
-
- stream->primeStreamUsingCallback = ( (streamFlags&paPrimeOutputBuffersUsingStreamCallback) && streamCallback ) ? 1 : 0;
-
- /* time to sleep when throttling due to >100% cpu usage.
- -a quater of a buffer's duration */
- stream->throttledSleepMsecs =
- (unsigned long)(stream->bufferProcessor.framesPerHostBuffer *
- stream->bufferProcessor.samplePeriod * .25 * 1000);
-
- stream->isStopped = 1;
- stream->isActive = 0;
-
-
- /* for maximum compatibility with multi-device multichannel drivers,
- we first open all devices, then we prepare all buffers, finally
- we start all devices ( in StartStream() ). teardown in reverse order.
- */
-
- if( inputParameters )
- {
- result = InitializeWaveHandles( winMmeHostApi, &stream->input,
- stream->bufferProcessor.bytesPerHostInputSample, sampleRate,
- inputDevices, inputDeviceCount, 1 /* isInput */ );
- if( result != paNoError ) goto error;
- }
-
- if( outputParameters )
- {
- result = InitializeWaveHandles( winMmeHostApi, &stream->output,
- stream->bufferProcessor.bytesPerHostOutputSample, sampleRate,
- outputDevices, outputDeviceCount, 0 /* isInput */ );
- if( result != paNoError ) goto error;
- }
-
- if( inputParameters )
- {
- result = InitializeWaveHeaders( &stream->input, hostInputBufferCount,
- hostInputSampleFormat, framesPerHostInputBuffer, inputDevices, 1 /* isInput */ );
- if( result != paNoError ) goto error;
- }
-
- if( outputParameters )
- {
- result = InitializeWaveHeaders( &stream->output, hostOutputBufferCount,
- hostOutputSampleFormat, framesPerHostOutputBuffer, outputDevices, 0 /* not isInput */ );
- if( result != paNoError ) goto error;
-
- stream->allBuffersDurationMs = (DWORD) (1000.0 * (framesPerHostOutputBuffer * stream->output.bufferCount) / sampleRate);
- }
- else
- {
- stream->allBuffersDurationMs = (DWORD) (1000.0 * (framesPerHostInputBuffer * stream->input.bufferCount) / sampleRate);
- }
-
-
- if( streamCallback )
- {
- /* abort event is only needed for callback streams */
- result = CreateEventWithPaError( &stream->abortEvent, NULL, TRUE, FALSE, NULL );
- if( result != paNoError ) goto error;
- }
-
- *s = (PaStream*)stream;
-
- return result;
-
-error:
-
- if( stream )
- {
- if( stream->abortEvent )
- CloseHandle( stream->abortEvent );
-
- TerminateWaveHeaders( &stream->output, 0 /* not isInput */ );
- TerminateWaveHeaders( &stream->input, 1 /* isInput */ );
-
- TerminateWaveHandles( &stream->output, 0 /* not isInput */, 1 /* currentlyProcessingAnError */ );
- TerminateWaveHandles( &stream->input, 1 /* isInput */, 1 /* currentlyProcessingAnError */ );
-
- if( bufferProcessorIsInitialized )
- PaUtil_TerminateBufferProcessor( &stream->bufferProcessor );
-
- if( streamRepresentationIsInitialized )
- PaUtil_TerminateStreamRepresentation( &stream->streamRepresentation );
-
- PaUtil_FreeMemory( stream );
- }
-
- return result;
-}
-
-
-/* return non-zero if all current buffers are done */
-static int BuffersAreDone( WAVEHDR **waveHeaders, unsigned int deviceCount, int bufferIndex )
-{
- unsigned int i;
-
- for( i=0; i < deviceCount; ++i )
- {
- if( !(waveHeaders[i][ bufferIndex ].dwFlags & WHDR_DONE) )
- {
- return 0;
- }
- }
-
- return 1;
-}
-
-static int CurrentInputBuffersAreDone( PaWinMmeStream *stream )
-{
- return BuffersAreDone( stream->input.waveHeaders, stream->input.deviceCount, stream->input.currentBufferIndex );
-}
-
-static int CurrentOutputBuffersAreDone( PaWinMmeStream *stream )
-{
- return BuffersAreDone( stream->output.waveHeaders, stream->output.deviceCount, stream->output.currentBufferIndex );
-}
-
-
-/* return non-zero if any buffers are queued */
-static int NoBuffersAreQueued( PaWinMmeSingleDirectionHandlesAndBuffers *handlesAndBuffers )
-{
- unsigned int i, j;
-
- if( handlesAndBuffers->waveHandles )
- {
- for( i=0; i < handlesAndBuffers->bufferCount; ++i )
- {
- for( j=0; j < handlesAndBuffers->deviceCount; ++j )
- {
- if( !( handlesAndBuffers->waveHeaders[ j ][ i ].dwFlags & WHDR_DONE) )
- {
- return 0;
- }
- }
- }
- }
-
- return 1;
-}
-
-
-#define PA_CIRCULAR_INCREMENT_( current, max )\
- ( (((current) + 1) >= (max)) ? (0) : (current+1) )
-
-#define PA_CIRCULAR_DECREMENT_( current, max )\
- ( ((current) == 0) ? ((max)-1) : (current-1) )
-
-
-static signed long GetAvailableFrames( PaWinMmeSingleDirectionHandlesAndBuffers *handlesAndBuffers )
-{
- signed long result = 0;
- unsigned int i;
-
- if( BuffersAreDone( handlesAndBuffers->waveHeaders, handlesAndBuffers->deviceCount, handlesAndBuffers->currentBufferIndex ) )
- {
- /* we could calculate the following in O(1) if we kept track of the
- last done buffer */
- result = handlesAndBuffers->framesPerBuffer - handlesAndBuffers->framesUsedInCurrentBuffer;
-
- i = PA_CIRCULAR_INCREMENT_( handlesAndBuffers->currentBufferIndex, handlesAndBuffers->bufferCount );
- while( i != handlesAndBuffers->currentBufferIndex )
- {
- if( BuffersAreDone( handlesAndBuffers->waveHeaders, handlesAndBuffers->deviceCount, i ) )
- {
- result += handlesAndBuffers->framesPerBuffer;
- i = PA_CIRCULAR_INCREMENT_( i, handlesAndBuffers->bufferCount );
- }
- else
- break;
- }
- }
-
- return result;
-}
-
-
-static PaError AdvanceToNextInputBuffer( PaWinMmeStream *stream )
-{
- PaError result = paNoError;
- MMRESULT mmresult;
- unsigned int i;
-
- for( i=0; i < stream->input.deviceCount; ++i )
- {
- mmresult = waveInAddBuffer( ((HWAVEIN*)stream->input.waveHandles)[i],
- &stream->input.waveHeaders[i][ stream->input.currentBufferIndex ],
- sizeof(WAVEHDR) );
- if( mmresult != MMSYSERR_NOERROR )
- {
- result = paUnanticipatedHostError;
- PA_MME_SET_LAST_WAVEIN_ERROR( mmresult );
- }
- }
-
- stream->input.currentBufferIndex =
- PA_CIRCULAR_INCREMENT_( stream->input.currentBufferIndex, stream->input.bufferCount );
-
- stream->input.framesUsedInCurrentBuffer = 0;
-
- return result;
-}
-
-
-static PaError AdvanceToNextOutputBuffer( PaWinMmeStream *stream )
-{
- PaError result = paNoError;
- MMRESULT mmresult;
- unsigned int i;
-
- for( i=0; i < stream->output.deviceCount; ++i )
- {
- mmresult = waveOutWrite( ((HWAVEOUT*)stream->output.waveHandles)[i],
- &stream->output.waveHeaders[i][ stream->output.currentBufferIndex ],
- sizeof(WAVEHDR) );
- if( mmresult != MMSYSERR_NOERROR )
- {
- result = paUnanticipatedHostError;
- PA_MME_SET_LAST_WAVEOUT_ERROR( mmresult );
- }
- }
-
- stream->output.currentBufferIndex =
- PA_CIRCULAR_INCREMENT_( stream->output.currentBufferIndex, stream->output.bufferCount );
-
- stream->output.framesUsedInCurrentBuffer = 0;
-
- return result;
-}
-
-
-/* requeue all but the most recent input with the driver. Used for catching
- up after a total input buffer underrun */
-static PaError CatchUpInputBuffers( PaWinMmeStream *stream )
-{
- PaError result = paNoError;
- unsigned int i;
-
- for( i=0; i < stream->input.bufferCount - 1; ++i )
- {
- result = AdvanceToNextInputBuffer( stream );
- if( result != paNoError )
- break;
- }
-
- return result;
-}
-
-
-/* take the most recent output and duplicate it to all other output buffers
- and requeue them. Used for catching up after a total output buffer underrun.
-*/
-static PaError CatchUpOutputBuffers( PaWinMmeStream *stream )
-{
- PaError result = paNoError;
- unsigned int i, j;
- unsigned int previousBufferIndex =
- PA_CIRCULAR_DECREMENT_( stream->output.currentBufferIndex, stream->output.bufferCount );
-
- for( i=0; i < stream->output.bufferCount - 1; ++i )
- {
- for( j=0; j < stream->output.deviceCount; ++j )
- {
- if( stream->output.waveHeaders[j][ stream->output.currentBufferIndex ].lpData
- != stream->output.waveHeaders[j][ previousBufferIndex ].lpData )
- {
- CopyMemory( stream->output.waveHeaders[j][ stream->output.currentBufferIndex ].lpData,
- stream->output.waveHeaders[j][ previousBufferIndex ].lpData,
- stream->output.waveHeaders[j][ stream->output.currentBufferIndex ].dwBufferLength );
- }
- }
-
- result = AdvanceToNextOutputBuffer( stream );
- if( result != paNoError )
- break;
- }
-
- return result;
-}
-
-
-static DWORD WINAPI ProcessingThreadProc( void *pArg )
-{
- PaWinMmeStream *stream = (PaWinMmeStream *)pArg;
- HANDLE events[3];
- int eventCount = 0;
- DWORD result = paNoError;
- DWORD waitResult;
- DWORD timeout = (unsigned long)(stream->allBuffersDurationMs * 0.5);
- int hostBuffersAvailable;
- signed int hostInputBufferIndex, hostOutputBufferIndex;
- PaStreamCallbackFlags statusFlags;
- int callbackResult;
- int done = 0;
- unsigned int channel, i;
- unsigned long framesProcessed;
-
- /* prepare event array for call to WaitForMultipleObjects() */
- if( stream->input.bufferEvent )
- events[eventCount++] = stream->input.bufferEvent;
- if( stream->output.bufferEvent )
- events[eventCount++] = stream->output.bufferEvent;
- events[eventCount++] = stream->abortEvent;
-
- statusFlags = 0; /** @todo support paInputUnderflow, paOutputOverflow and paNeverDropInput */
-
- /* loop until something causes us to stop */
- do{
- /* wait for MME to signal that a buffer is available, or for
- the PA abort event to be signaled.
-
- When this indicates that one or more buffers are available
- NoBuffersAreQueued() and Current*BuffersAreDone are used below to
- poll for additional done buffers. NoBuffersAreQueued() will fail
- to identify an underrun/overflow if the driver doesn't mark all done
- buffers prior to signalling the event. Some drivers do this
- (eg RME Digi96, and others don't eg VIA PC 97 input). This isn't a
- huge problem, it just means that we won't always be able to detect
- underflow/overflow.
- */
- waitResult = WaitForMultipleObjects( eventCount, events, FALSE /* wait all = FALSE */, timeout );
- if( waitResult == WAIT_FAILED )
- {
- result = paUnanticipatedHostError;
- /** @todo FIXME/REVIEW: can't return host error info from an asyncronous thread */
- done = 1;
- }
- else if( waitResult == WAIT_TIMEOUT )
- {
- /* if a timeout is encountered, continue */
- }
-
- if( stream->abortProcessing )
- {
- /* Pa_AbortStream() has been called, stop processing immediately */
- done = 1;
- }
- else if( stream->stopProcessing )
- {
- /* Pa_StopStream() has been called or the user callback returned
- non-zero, processing will continue until all output buffers
- are marked as done. The stream will stop immediately if it
- is input-only.
- */
-
- if( PA_IS_OUTPUT_STREAM_(stream) )
- {
- if( NoBuffersAreQueued( &stream->output ) )
- done = 1; /* Will cause thread to return. */
- }
- else
- {
- /* input only stream */
- done = 1; /* Will cause thread to return. */
- }
- }
- else
- {
- hostBuffersAvailable = 1;
-
- /* process all available host buffers */
- do
- {
- hostInputBufferIndex = -1;
- hostOutputBufferIndex = -1;
-
- if( PA_IS_INPUT_STREAM_(stream) )
- {
- if( CurrentInputBuffersAreDone( stream ) )
- {
- if( NoBuffersAreQueued( &stream->input ) )
- {
- /** @todo
- if all of the other buffers are also ready then
- we discard all but the most recent. This is an
- input buffer overflow. FIXME: these buffers should
- be passed to the callback in a paNeverDropInput
- stream.
-
- note that it is also possible for an input overflow
- to happen while the callback is processing a buffer.
- that is handled further down.
- */
- result = CatchUpInputBuffers( stream );
- if( result != paNoError )
- done = 1;
-
- statusFlags |= paInputOverflow;
- }
-
- hostInputBufferIndex = stream->input.currentBufferIndex;
- }
- }
-
- if( PA_IS_OUTPUT_STREAM_(stream) )
- {
- if( CurrentOutputBuffersAreDone( stream ) )
- {
- /* ok, we have an output buffer */
-
- if( NoBuffersAreQueued( &stream->output ) )
- {
- /*
- if all of the other buffers are also ready, catch up by copying
- the most recently generated buffer into all but one of the output
- buffers.
-
- note that this catch up code only handles the case where all
- buffers have been played out due to this thread not having
- woken up at all. a more common case occurs when this thread
- is woken up, processes one buffer, but takes too long, and as
- a result all the other buffers have become un-queued. that
- case is handled further down.
- */
-
- result = CatchUpOutputBuffers( stream );
- if( result != paNoError )
- done = 1;
-
- statusFlags |= paOutputUnderflow;
- }
-
- hostOutputBufferIndex = stream->output.currentBufferIndex;
- }
- }
-
-
- if( (PA_IS_FULL_DUPLEX_STREAM_(stream) && hostInputBufferIndex != -1 && hostOutputBufferIndex != -1) ||
- (PA_IS_HALF_DUPLEX_STREAM_(stream) && ( hostInputBufferIndex != -1 || hostOutputBufferIndex != -1 ) ) )
- {
- PaStreamCallbackTimeInfo timeInfo = {0,0,0}; /** @todo implement inputBufferAdcTime */
-
-
- if( PA_IS_OUTPUT_STREAM_(stream) )
- {
- /* set timeInfo.currentTime and calculate timeInfo.outputBufferDacTime
- from the current wave out position */
- MMTIME mmtime;
- double timeBeforeGetPosition, timeAfterGetPosition;
- double time;
- long framesInBufferRing;
- long writePosition;
- long playbackPosition;
- HWAVEOUT firstWaveOutDevice = ((HWAVEOUT*)stream->output.waveHandles)[0];
-
- mmtime.wType = TIME_SAMPLES;
- timeBeforeGetPosition = PaUtil_GetTime();
- waveOutGetPosition( firstWaveOutDevice, &mmtime, sizeof(MMTIME) );
- timeAfterGetPosition = PaUtil_GetTime();
-
- timeInfo.currentTime = timeAfterGetPosition;
-
- /* approximate time at which wave out position was measured
- as half way between timeBeforeGetPosition and timeAfterGetPosition */
- time = timeBeforeGetPosition + (timeAfterGetPosition - timeBeforeGetPosition) * .5;
-
- framesInBufferRing = stream->output.bufferCount * stream->bufferProcessor.framesPerHostBuffer;
- playbackPosition = mmtime.u.sample % framesInBufferRing;
-
- writePosition = stream->output.currentBufferIndex * stream->bufferProcessor.framesPerHostBuffer
- + stream->output.framesUsedInCurrentBuffer;
-
- if( playbackPosition >= writePosition ){
- timeInfo.outputBufferDacTime =
- time + ((double)( writePosition + (framesInBufferRing - playbackPosition) ) * stream->bufferProcessor.samplePeriod );
- }else{
- timeInfo.outputBufferDacTime =
- time + ((double)( writePosition - playbackPosition ) * stream->bufferProcessor.samplePeriod );
- }
- }
-
-
- PaUtil_BeginCpuLoadMeasurement( &stream->cpuLoadMeasurer );
-
- PaUtil_BeginBufferProcessing( &stream->bufferProcessor, &timeInfo, statusFlags );
-
- /* reset status flags once they have been passed to the buffer processor */
- statusFlags = 0;
-
- if( PA_IS_INPUT_STREAM_(stream) )
- {
- PaUtil_SetInputFrameCount( &stream->bufferProcessor, 0 /* default to host buffer size */ );
-
- channel = 0;
- for( i=0; i<stream->input.deviceCount; ++i )
- {
- /* we have stored the number of channels in the buffer in dwUser */
- int channelCount = stream->input.waveHeaders[i][ hostInputBufferIndex ].dwUser;
-
- PaUtil_SetInterleavedInputChannels( &stream->bufferProcessor, channel,
- stream->input.waveHeaders[i][ hostInputBufferIndex ].lpData +
- stream->input.framesUsedInCurrentBuffer * channelCount *
- stream->bufferProcessor.bytesPerHostInputSample,
- channelCount );
-
-
- channel += channelCount;
- }
- }
-
- if( PA_IS_OUTPUT_STREAM_(stream) )
- {
- PaUtil_SetOutputFrameCount( &stream->bufferProcessor, 0 /* default to host buffer size */ );
-
- channel = 0;
- for( i=0; i<stream->output.deviceCount; ++i )
- {
- /* we have stored the number of channels in the buffer in dwUser */
- int channelCount = stream->output.waveHeaders[i][ hostOutputBufferIndex ].dwUser;
-
- PaUtil_SetInterleavedOutputChannels( &stream->bufferProcessor, channel,
- stream->output.waveHeaders[i][ hostOutputBufferIndex ].lpData +
- stream->output.framesUsedInCurrentBuffer * channelCount *
- stream->bufferProcessor.bytesPerHostOutputSample,
- channelCount );
-
- channel += channelCount;
- }
- }
-
- callbackResult = paContinue;
- framesProcessed = PaUtil_EndBufferProcessing( &stream->bufferProcessor, &callbackResult );
-
- stream->input.framesUsedInCurrentBuffer += framesProcessed;
- stream->output.framesUsedInCurrentBuffer += framesProcessed;
-
- PaUtil_EndCpuLoadMeasurement( &stream->cpuLoadMeasurer, framesProcessed );
-
- if( callbackResult == paContinue )
- {
- /* nothing special to do */
- }
- else if( callbackResult == paAbort )
- {
- stream->abortProcessing = 1;
- done = 1;
- /** @todo FIXME: should probably reset the output device immediately once the callback returns paAbort */
- result = paNoError;
- }
- else
- {
- /* User callback has asked us to stop with paComplete or other non-zero value */
- stream->stopProcessing = 1; /* stop once currently queued audio has finished */
- result = paNoError;
- }
-
-
- if( PA_IS_INPUT_STREAM_(stream)
- && stream->stopProcessing == 0 && stream->abortProcessing == 0
- && stream->input.framesUsedInCurrentBuffer == stream->input.framesPerBuffer )
- {
- if( NoBuffersAreQueued( &stream->input ) )
- {
- /** @todo need to handle PaNeverDropInput here where necessary */
- result = CatchUpInputBuffers( stream );
- if( result != paNoError )
- done = 1;
-
- statusFlags |= paInputOverflow;
- }
-
- result = AdvanceToNextInputBuffer( stream );
- if( result != paNoError )
- done = 1;
- }
-
-
- if( PA_IS_OUTPUT_STREAM_(stream) && !stream->abortProcessing )
- {
- if( stream->stopProcessing &&
- stream->output.framesUsedInCurrentBuffer < stream->output.framesPerBuffer )
- {
- /* zero remaining samples in output output buffer and flush */
-
- stream->output.framesUsedInCurrentBuffer += PaUtil_ZeroOutput( &stream->bufferProcessor,
- stream->output.framesPerBuffer - stream->output.framesUsedInCurrentBuffer );
-
- /* we send the entire buffer to the output devices, but we could
- just send a partial buffer, rather than zeroing the unused
- samples.
- */
- }
-
- if( stream->output.framesUsedInCurrentBuffer == stream->output.framesPerBuffer )
- {
- /* check for underflow before enquing the just-generated buffer,
- but recover from underflow after enquing it. This ensures
- that the most recent audio segment is repeated */
- int outputUnderflow = NoBuffersAreQueued( &stream->output );
-
- result = AdvanceToNextOutputBuffer( stream );
- if( result != paNoError )
- done = 1;
-
- if( outputUnderflow && !done && !stream->stopProcessing )
- {
- /* Recover from underflow in the case where the
- underflow occured while processing the buffer
- we just finished */
-
- result = CatchUpOutputBuffers( stream );
- if( result != paNoError )
- done = 1;
-
- statusFlags |= paOutputUnderflow;
- }
- }
- }
-
- if( stream->throttleProcessingThreadOnOverload != 0 )
- {
- if( stream->stopProcessing || stream->abortProcessing )
- {
- if( stream->processingThreadPriority != stream->highThreadPriority )
- {
- SetThreadPriority( stream->processingThread, stream->highThreadPriority );
- stream->processingThreadPriority = stream->highThreadPriority;
- }
- }
- else if( PaUtil_GetCpuLoad( &stream->cpuLoadMeasurer ) > 1. )
- {
- if( stream->processingThreadPriority != stream->throttledThreadPriority )
- {
- SetThreadPriority( stream->processingThread, stream->throttledThreadPriority );
- stream->processingThreadPriority = stream->throttledThreadPriority;
- }
-
- /* sleep to give other processes a go */
- Sleep( stream->throttledSleepMsecs );
- }
- else
- {
- if( stream->processingThreadPriority != stream->highThreadPriority )
- {
- SetThreadPriority( stream->processingThread, stream->highThreadPriority );
- stream->processingThreadPriority = stream->highThreadPriority;
- }
- }
- }
- }
- else
- {
- hostBuffersAvailable = 0;
- }
- }
- while( hostBuffersAvailable &&
- stream->stopProcessing == 0 &&
- stream->abortProcessing == 0 &&
- !done );
- }
- }
- while( !done );
-
- stream->isActive = 0;
-
- if( stream->streamRepresentation.streamFinishedCallback != 0 )
- stream->streamRepresentation.streamFinishedCallback( stream->streamRepresentation.userData );
-
- PaUtil_ResetCpuLoadMeasurer( &stream->cpuLoadMeasurer );
-
- return result;
-}
-
-
-/*
- When CloseStream() is called, the multi-api layer ensures that
- the stream has already been stopped or aborted.
-*/
-static PaError CloseStream( PaStream* s )
-{
- PaError result;
- PaWinMmeStream *stream = (PaWinMmeStream*)s;
-
- result = CloseHandleWithPaError( stream->abortEvent );
- if( result != paNoError ) goto error;
-
- TerminateWaveHeaders( &stream->output, 0 /* not isInput */ );
- TerminateWaveHeaders( &stream->input, 1 /* isInput */ );
-
- TerminateWaveHandles( &stream->output, 0 /* not isInput */, 0 /* not currentlyProcessingAnError */ );
- TerminateWaveHandles( &stream->input, 1 /* isInput */, 0 /* not currentlyProcessingAnError */ );
-
- PaUtil_TerminateBufferProcessor( &stream->bufferProcessor );
- PaUtil_TerminateStreamRepresentation( &stream->streamRepresentation );
- PaUtil_FreeMemory( stream );
-
-error:
- /** @todo REVIEW: what is the best way to clean up a stream if an error is detected? */
- return result;
-}
-
-
-static PaError StartStream( PaStream *s )
-{
- PaError result;
- PaWinMmeStream *stream = (PaWinMmeStream*)s;
- MMRESULT mmresult;
- unsigned int i, j;
- int callbackResult;
- unsigned int channel;
- unsigned long framesProcessed;
- PaStreamCallbackTimeInfo timeInfo = {0,0,0}; /** @todo implement this for stream priming */
-
- PaUtil_ResetBufferProcessor( &stream->bufferProcessor );
-
- if( PA_IS_INPUT_STREAM_(stream) )
- {
- for( i=0; i<stream->input.bufferCount; ++i )
- {
- for( j=0; j<stream->input.deviceCount; ++j )
- {
- mmresult = waveInAddBuffer( ((HWAVEIN*)stream->input.waveHandles)[j], &stream->input.waveHeaders[j][i], sizeof(WAVEHDR) );
- if( mmresult != MMSYSERR_NOERROR )
- {
- result = paUnanticipatedHostError;
- PA_MME_SET_LAST_WAVEIN_ERROR( mmresult );
- goto error;
- }
- }
- }
- stream->input.currentBufferIndex = 0;
- stream->input.framesUsedInCurrentBuffer = 0;
- }
-
- if( PA_IS_OUTPUT_STREAM_(stream) )
- {
- for( i=0; i<stream->output.deviceCount; ++i )
- {
- if( (mmresult = waveOutPause( ((HWAVEOUT*)stream->output.waveHandles)[i] )) != MMSYSERR_NOERROR )
- {
- result = paUnanticipatedHostError;
- PA_MME_SET_LAST_WAVEOUT_ERROR( mmresult );
- goto error;
- }
- }
-
- for( i=0; i<stream->output.bufferCount; ++i )
- {
- if( stream->primeStreamUsingCallback )
- {
-
- stream->output.framesUsedInCurrentBuffer = 0;
- do{
-
- PaUtil_BeginBufferProcessing( &stream->bufferProcessor,
- &timeInfo,
- paPrimingOutput | ((stream->input.bufferCount > 0 ) ? paInputUnderflow : 0));
-
- if( stream->input.bufferCount > 0 )
- PaUtil_SetNoInput( &stream->bufferProcessor );
-
- PaUtil_SetOutputFrameCount( &stream->bufferProcessor, 0 /* default to host buffer size */ );
-
- channel = 0;
- for( j=0; j<stream->output.deviceCount; ++j )
- {
- /* we have stored the number of channels in the buffer in dwUser */
- int channelCount = stream->output.waveHeaders[j][i].dwUser;
-
- PaUtil_SetInterleavedOutputChannels( &stream->bufferProcessor, channel,
- stream->output.waveHeaders[j][i].lpData +
- stream->output.framesUsedInCurrentBuffer * channelCount *
- stream->bufferProcessor.bytesPerHostOutputSample,
- channelCount );
-
- /* we have stored the number of channels in the buffer in dwUser */
- channel += channelCount;
- }
-
- callbackResult = paContinue;
- framesProcessed = PaUtil_EndBufferProcessing( &stream->bufferProcessor, &callbackResult );
- stream->output.framesUsedInCurrentBuffer += framesProcessed;
-
- if( callbackResult != paContinue )
- {
- /** @todo fix this, what do we do if callback result is non-zero during stream
- priming?
-
- for complete: play out primed waveHeaders as usual
- for abort: clean up immediately.
- */
- }
-
- }while( stream->output.framesUsedInCurrentBuffer != stream->output.framesPerBuffer );
-
- }
- else
- {
- for( j=0; j<stream->output.deviceCount; ++j )
- {
- ZeroMemory( stream->output.waveHeaders[j][i].lpData, stream->output.waveHeaders[j][i].dwBufferLength );
- }
- }
-
- /* we queue all channels of a single buffer frame (accross all
- devices, because some multidevice multichannel drivers work
- better this way */
- for( j=0; j<stream->output.deviceCount; ++j )
- {
- mmresult = waveOutWrite( ((HWAVEOUT*)stream->output.waveHandles)[j], &stream->output.waveHeaders[j][i], sizeof(WAVEHDR) );
- if( mmresult != MMSYSERR_NOERROR )
- {
- result = paUnanticipatedHostError;
- PA_MME_SET_LAST_WAVEOUT_ERROR( mmresult );
- goto error;
- }
- }
- }
- stream->output.currentBufferIndex = 0;
- stream->output.framesUsedInCurrentBuffer = 0;
- }
-
-
- stream->isStopped = 0;
- stream->isActive = 1;
- stream->stopProcessing = 0;
- stream->abortProcessing = 0;
-
- result = ResetEventWithPaError( stream->input.bufferEvent );
- if( result != paNoError ) goto error;
-
- result = ResetEventWithPaError( stream->output.bufferEvent );
- if( result != paNoError ) goto error;
-
-
- if( stream->streamRepresentation.streamCallback )
- {
- /* callback stream */
-
- result = ResetEventWithPaError( stream->abortEvent );
- if( result != paNoError ) goto error;
-
- /* Create thread that waits for audio buffers to be ready for processing. */
- stream->processingThread = CreateThread( 0, 0, ProcessingThreadProc, stream, 0, &stream->processingThreadId );
- if( !stream->processingThread )
- {
- result = paUnanticipatedHostError;
- PA_MME_SET_LAST_SYSTEM_ERROR( GetLastError() );
- goto error;
- }
-
- /** @todo could have mme specific stream parameters to allow the user
- to set the callback thread priorities */
- stream->highThreadPriority = THREAD_PRIORITY_TIME_CRITICAL;
- stream->throttledThreadPriority = THREAD_PRIORITY_NORMAL;
-
- if( !SetThreadPriority( stream->processingThread, stream->highThreadPriority ) )
- {
- result = paUnanticipatedHostError;
- PA_MME_SET_LAST_SYSTEM_ERROR( GetLastError() );
- goto error;
- }
- stream->processingThreadPriority = stream->highThreadPriority;
- }
- else
- {
- /* blocking read/write stream */
-
- }
-
- if( PA_IS_INPUT_STREAM_(stream) )
- {
- for( i=0; i < stream->input.deviceCount; ++i )
- {
- mmresult = waveInStart( ((HWAVEIN*)stream->input.waveHandles)[i] );
- PA_DEBUG(("Pa_StartStream: waveInStart returned = 0x%X.\n", mmresult));
- if( mmresult != MMSYSERR_NOERROR )
- {
- result = paUnanticipatedHostError;
- PA_MME_SET_LAST_WAVEIN_ERROR( mmresult );
- goto error;
- }
- }
- }
-
- if( PA_IS_OUTPUT_STREAM_(stream) )
- {
- for( i=0; i < stream->output.deviceCount; ++i )
- {
- if( (mmresult = waveOutRestart( ((HWAVEOUT*)stream->output.waveHandles)[i] )) != MMSYSERR_NOERROR )
- {
- result = paUnanticipatedHostError;
- PA_MME_SET_LAST_WAVEOUT_ERROR( mmresult );
- goto error;
- }
- }
- }
-
- return result;
-
-error:
- /** @todo FIXME: implement recovery as best we can
- This should involve rolling back to a state as-if this function had never been called
- */
- return result;
-}
-
-
-static PaError StopStream( PaStream *s )
-{
- PaError result = paNoError;
- PaWinMmeStream *stream = (PaWinMmeStream*)s;
- int timeout;
- DWORD waitResult;
- MMRESULT mmresult;
- signed int hostOutputBufferIndex;
- unsigned int channel, waitCount, i;
-
- /** @todo
- REVIEW: the error checking in this function needs review. the basic
- idea is to return from this function in a known state - for example
- there is no point avoiding calling waveInReset just because
- the thread times out.
- */
-
- if( stream->processingThread )
- {
- /* callback stream */
-
- /* Tell processing thread to stop generating more data and to let current data play out. */
- stream->stopProcessing = 1;
-
- /* Calculate timeOut longer than longest time it could take to return all buffers. */
- timeout = (int)(stream->allBuffersDurationMs * 1.5);
- if( timeout < PA_MME_MIN_TIMEOUT_MSEC_ )
- timeout = PA_MME_MIN_TIMEOUT_MSEC_;
-
- PA_DEBUG(("WinMME StopStream: waiting for background thread.\n"));
-
- waitResult = WaitForSingleObject( stream->processingThread, timeout );
- if( waitResult == WAIT_TIMEOUT )
- {
- /* try to abort */
- stream->abortProcessing = 1;
- SetEvent( stream->abortEvent );
- waitResult = WaitForSingleObject( stream->processingThread, timeout );
- if( waitResult == WAIT_TIMEOUT )
- {
- PA_DEBUG(("WinMME StopStream: timed out while waiting for background thread to finish.\n"));
- result = paTimedOut;
- }
- }
-
- CloseHandle( stream->processingThread );
- stream->processingThread = NULL;
- }
- else
- {
- /* blocking read / write stream */
-
- if( PA_IS_OUTPUT_STREAM_(stream) )
- {
- if( stream->output.framesUsedInCurrentBuffer > 0 )
- {
- /* there are still unqueued frames in the current buffer, so flush them */
-
- hostOutputBufferIndex = stream->output.currentBufferIndex;
-
- PaUtil_SetOutputFrameCount( &stream->bufferProcessor,
- stream->output.framesPerBuffer - stream->output.framesUsedInCurrentBuffer );
-
- channel = 0;
- for( i=0; i<stream->output.deviceCount; ++i )
- {
- /* we have stored the number of channels in the buffer in dwUser */
- int channelCount = stream->output.waveHeaders[i][ hostOutputBufferIndex ].dwUser;
-
- PaUtil_SetInterleavedOutputChannels( &stream->bufferProcessor, channel,
- stream->output.waveHeaders[i][ hostOutputBufferIndex ].lpData +
- stream->output.framesUsedInCurrentBuffer * channelCount *
- stream->bufferProcessor.bytesPerHostOutputSample,
- channelCount );
-
- channel += channelCount;
- }
-
- PaUtil_ZeroOutput( &stream->bufferProcessor,
- stream->output.framesPerBuffer - stream->output.framesUsedInCurrentBuffer );
-
- /* we send the entire buffer to the output devices, but we could
- just send a partial buffer, rather than zeroing the unused
- samples.
- */
- AdvanceToNextOutputBuffer( stream );
- }
-
-
- timeout = (stream->allBuffersDurationMs / stream->output.bufferCount) + 1;
- if( timeout < PA_MME_MIN_TIMEOUT_MSEC_ )
- timeout = PA_MME_MIN_TIMEOUT_MSEC_;
-
- waitCount = 0;
- while( !NoBuffersAreQueued( &stream->output ) && waitCount <= stream->output.bufferCount )
- {
- /* wait for MME to signal that a buffer is available */
- waitResult = WaitForSingleObject( stream->output.bufferEvent, timeout );
- if( waitResult == WAIT_FAILED )
- {
- break;
- }
- else if( waitResult == WAIT_TIMEOUT )
- {
- /* keep waiting */
- }
-
- ++waitCount;
- }
- }
- }
-
- if( PA_IS_OUTPUT_STREAM_(stream) )
- {
- for( i =0; i < stream->output.deviceCount; ++i )
- {
- mmresult = waveOutReset( ((HWAVEOUT*)stream->output.waveHandles)[i] );
- if( mmresult != MMSYSERR_NOERROR )
- {
- result = paUnanticipatedHostError;
- PA_MME_SET_LAST_WAVEOUT_ERROR( mmresult );
- }
- }
- }
-
- if( PA_IS_INPUT_STREAM_(stream) )
- {
- for( i=0; i < stream->input.deviceCount; ++i )
- {
- mmresult = waveInReset( ((HWAVEIN*)stream->input.waveHandles)[i] );
- if( mmresult != MMSYSERR_NOERROR )
- {
- result = paUnanticipatedHostError;
- PA_MME_SET_LAST_WAVEIN_ERROR( mmresult );
- }
- }
- }
-
- stream->isStopped = 1;
- stream->isActive = 0;
-
- return result;
-}
-
-
-static PaError AbortStream( PaStream *s )
-{
- PaError result = paNoError;
- PaWinMmeStream *stream = (PaWinMmeStream*)s;
- int timeout;
- DWORD waitResult;
- MMRESULT mmresult;
- unsigned int i;
-
- /** @todo
- REVIEW: the error checking in this function needs review. the basic
- idea is to return from this function in a known state - for example
- there is no point avoiding calling waveInReset just because
- the thread times out.
- */
-
- if( stream->processingThread )
- {
- /* callback stream */
-
- /* Tell processing thread to abort immediately */
- stream->abortProcessing = 1;
- SetEvent( stream->abortEvent );
- }
-
-
- if( PA_IS_OUTPUT_STREAM_(stream) )
- {
- for( i =0; i < stream->output.deviceCount; ++i )
- {
- mmresult = waveOutReset( ((HWAVEOUT*)stream->output.waveHandles)[i] );
- if( mmresult != MMSYSERR_NOERROR )
- {
- PA_MME_SET_LAST_WAVEOUT_ERROR( mmresult );
- return paUnanticipatedHostError;
- }
- }
- }
-
- if( PA_IS_INPUT_STREAM_(stream) )
- {
- for( i=0; i < stream->input.deviceCount; ++i )
- {
- mmresult = waveInReset( ((HWAVEIN*)stream->input.waveHandles)[i] );
- if( mmresult != MMSYSERR_NOERROR )
- {
- PA_MME_SET_LAST_WAVEIN_ERROR( mmresult );
- return paUnanticipatedHostError;
- }
- }
- }
-
-
- if( stream->processingThread )
- {
- /* callback stream */
-
- PA_DEBUG(("WinMME AbortStream: waiting for background thread.\n"));
-
- /* Calculate timeOut longer than longest time it could take to return all buffers. */
- timeout = (int)(stream->allBuffersDurationMs * 1.5);
- if( timeout < PA_MME_MIN_TIMEOUT_MSEC_ )
- timeout = PA_MME_MIN_TIMEOUT_MSEC_;
-
- waitResult = WaitForSingleObject( stream->processingThread, timeout );
- if( waitResult == WAIT_TIMEOUT )
- {
- PA_DEBUG(("WinMME AbortStream: timed out while waiting for background thread to finish.\n"));
- return paTimedOut;
- }
-
- CloseHandle( stream->processingThread );
- stream->processingThread = NULL;
- }
-
- stream->isStopped = 1;
- stream->isActive = 0;
-
- return result;
-}
-
-
-static PaError IsStreamStopped( PaStream *s )
-{
- PaWinMmeStream *stream = (PaWinMmeStream*)s;
-
- return stream->isStopped;
-}
-
-
-static PaError IsStreamActive( PaStream *s )
-{
- PaWinMmeStream *stream = (PaWinMmeStream*)s;
-
- return stream->isActive;
-}
-
-
-static PaTime GetStreamTime( PaStream *s )
-{
- (void) s; /* unused parameter */
-
- return PaUtil_GetTime();
-}
-
-
-static double GetStreamCpuLoad( PaStream* s )
-{
- PaWinMmeStream *stream = (PaWinMmeStream*)s;
-
- return PaUtil_GetCpuLoad( &stream->cpuLoadMeasurer );
-}
-
-
-/*
- As separate stream interfaces are used for blocking and callback
- streams, the following functions can be guaranteed to only be called
- for blocking streams.
-*/
-
-static PaError ReadStream( PaStream* s,
- void *buffer,
- unsigned long frames )
-{
- PaError result = paNoError;
- PaWinMmeStream *stream = (PaWinMmeStream*)s;
- void *userBuffer;
- unsigned long framesRead = 0;
- unsigned long framesProcessed;
- signed int hostInputBufferIndex;
- DWORD waitResult;
- DWORD timeout = (unsigned long)(stream->allBuffersDurationMs * 0.5);
- unsigned int channel, i;
-
- if( PA_IS_INPUT_STREAM_(stream) )
- {
- /* make a local copy of the user buffer pointer(s). this is necessary
- because PaUtil_CopyInput() advances these pointers every time
- it is called.
- */
- if( stream->bufferProcessor.userInputIsInterleaved )
- {
- userBuffer = buffer;
- }
- else
- {
- userBuffer = alloca( sizeof(void*) * stream->bufferProcessor.inputChannelCount );
- if( !userBuffer )
- return paInsufficientMemory;
- for( i = 0; i<stream->bufferProcessor.inputChannelCount; ++i )
- ((void**)userBuffer)[i] = ((void**)buffer)[i];
- }
-
- do{
- if( CurrentInputBuffersAreDone( stream ) )
- {
- if( NoBuffersAreQueued( &stream->input ) )
- {
- /** @todo REVIEW: consider what to do if the input overflows.
- do we requeue all of the buffers? should we be running
- a thread to make sure they are always queued? */
-
- result = paInputOverflowed;
- }
-
- hostInputBufferIndex = stream->input.currentBufferIndex;
-
- PaUtil_SetInputFrameCount( &stream->bufferProcessor,
- stream->input.framesPerBuffer - stream->input.framesUsedInCurrentBuffer );
-
- channel = 0;
- for( i=0; i<stream->input.deviceCount; ++i )
- {
- /* we have stored the number of channels in the buffer in dwUser */
- int channelCount = stream->input.waveHeaders[i][ hostInputBufferIndex ].dwUser;
-
- PaUtil_SetInterleavedInputChannels( &stream->bufferProcessor, channel,
- stream->input.waveHeaders[i][ hostInputBufferIndex ].lpData +
- stream->input.framesUsedInCurrentBuffer * channelCount *
- stream->bufferProcessor.bytesPerHostInputSample,
- channelCount );
-
- channel += channelCount;
- }
-
- framesProcessed = PaUtil_CopyInput( &stream->bufferProcessor, &userBuffer, frames - framesRead );
-
- stream->input.framesUsedInCurrentBuffer += framesProcessed;
- if( stream->input.framesUsedInCurrentBuffer == stream->input.framesPerBuffer )
- {
- result = AdvanceToNextInputBuffer( stream );
- if( result != paNoError )
- break;
- }
-
- framesRead += framesProcessed;
-
- }else{
- /* wait for MME to signal that a buffer is available */
- waitResult = WaitForSingleObject( stream->input.bufferEvent, timeout );
- if( waitResult == WAIT_FAILED )
- {
- result = paUnanticipatedHostError;
- break;
- }
- else if( waitResult == WAIT_TIMEOUT )
- {
- /* if a timeout is encountered, continue,
- perhaps we should give up eventually
- */
- }
- }
- }while( framesRead < frames );
- }
- else
- {
- result = paCanNotReadFromAnOutputOnlyStream;
- }
-
- return result;
-}
-
-
-static PaError WriteStream( PaStream* s,
- const void *buffer,
- unsigned long frames )
-{
- PaError result = paNoError;
- PaWinMmeStream *stream = (PaWinMmeStream*)s;
- const void *userBuffer;
- unsigned long framesWritten = 0;
- unsigned long framesProcessed;
- signed int hostOutputBufferIndex;
- DWORD waitResult;
- DWORD timeout = (unsigned long)(stream->allBuffersDurationMs * 0.5);
- unsigned int channel, i;
-
-
- if( PA_IS_OUTPUT_STREAM_(stream) )
- {
- /* make a local copy of the user buffer pointer(s). this is necessary
- because PaUtil_CopyOutput() advances these pointers every time
- it is called.
- */
- if( stream->bufferProcessor.userOutputIsInterleaved )
- {
- userBuffer = buffer;
- }
- else
- {
- userBuffer = alloca( sizeof(void*) * stream->bufferProcessor.outputChannelCount );
- if( !userBuffer )
- return paInsufficientMemory;
- for( i = 0; i<stream->bufferProcessor.outputChannelCount; ++i )
- ((const void**)userBuffer)[i] = ((const void**)buffer)[i];
- }
-
- do{
- if( CurrentOutputBuffersAreDone( stream ) )
- {
- if( NoBuffersAreQueued( &stream->output ) )
- {
- /** @todo REVIEW: consider what to do if the output
- underflows. do we requeue all the existing buffers with
- zeros? should we run a separate thread to keep the buffers
- enqueued at all times? */
-
- result = paOutputUnderflowed;
- }
-
- hostOutputBufferIndex = stream->output.currentBufferIndex;
-
- PaUtil_SetOutputFrameCount( &stream->bufferProcessor,
- stream->output.framesPerBuffer - stream->output.framesUsedInCurrentBuffer );
-
- channel = 0;
- for( i=0; i<stream->output.deviceCount; ++i )
- {
- /* we have stored the number of channels in the buffer in dwUser */
- int channelCount = stream->output.waveHeaders[i][ hostOutputBufferIndex ].dwUser;
-
- PaUtil_SetInterleavedOutputChannels( &stream->bufferProcessor, channel,
- stream->output.waveHeaders[i][ hostOutputBufferIndex ].lpData +
- stream->output.framesUsedInCurrentBuffer * channelCount *
- stream->bufferProcessor.bytesPerHostOutputSample,
- channelCount );
-
- channel += channelCount;
- }
-
- framesProcessed = PaUtil_CopyOutput( &stream->bufferProcessor, &userBuffer, frames - framesWritten );
-
- stream->output.framesUsedInCurrentBuffer += framesProcessed;
- if( stream->output.framesUsedInCurrentBuffer == stream->output.framesPerBuffer )
- {
- result = AdvanceToNextOutputBuffer( stream );
- if( result != paNoError )
- break;
- }
-
- framesWritten += framesProcessed;
- }
- else
- {
- /* wait for MME to signal that a buffer is available */
- waitResult = WaitForSingleObject( stream->output.bufferEvent, timeout );
- if( waitResult == WAIT_FAILED )
- {
- result = paUnanticipatedHostError;
- break;
- }
- else if( waitResult == WAIT_TIMEOUT )
- {
- /* if a timeout is encountered, continue,
- perhaps we should give up eventually
- */
- }
- }
- }while( framesWritten < frames );
- }
- else
- {
- result = paCanNotWriteToAnInputOnlyStream;
- }
-
- return result;
-}
-
-
-static signed long GetStreamReadAvailable( PaStream* s )
-{
- PaWinMmeStream *stream = (PaWinMmeStream*)s;
-
- if( PA_IS_INPUT_STREAM_(stream) )
- return GetAvailableFrames( &stream->input );
- else
- return paCanNotReadFromAnOutputOnlyStream;
-}
-
-
-static signed long GetStreamWriteAvailable( PaStream* s )
-{
- PaWinMmeStream *stream = (PaWinMmeStream*)s;
-
- if( PA_IS_OUTPUT_STREAM_(stream) )
- return GetAvailableFrames( &stream->output );
- else
- return paCanNotWriteToAnInputOnlyStream;
-}
-
-
-/* NOTE: the following functions are MME-stream specific, and are called directly
- by client code. We need to check for many more error conditions here because
- we don't have the benefit of pa_front.c's parameter checking.
-*/
-
-static PaError GetWinMMEStreamPointer( PaWinMmeStream **stream, PaStream *s )
-{
- PaError result;
- PaUtilHostApiRepresentation *hostApi;
- PaWinMmeHostApiRepresentation *winMmeHostApi;
-
- result = PaUtil_ValidateStreamPointer( s );
- if( result != paNoError )
- return result;
-
- result = PaUtil_GetHostApiRepresentation( &hostApi, paMME );
- if( result != paNoError )
- return result;
-
- winMmeHostApi = (PaWinMmeHostApiRepresentation*)hostApi;
-
- /* note, the following would be easier if there was a generic way of testing
- that a stream belongs to a specific host API */
-
- if( PA_STREAM_REP( s )->streamInterface == &winMmeHostApi->callbackStreamInterface
- || PA_STREAM_REP( s )->streamInterface == &winMmeHostApi->blockingStreamInterface )
- {
- /* s is a WinMME stream */
- *stream = (PaWinMmeStream *)s;
- return paNoError;
- }
- else
- {
- return paIncompatibleStreamHostApi;
- }
-}
-
-
-int PaWinMME_GetStreamInputHandleCount( PaStream* s )
-{
- PaWinMmeStream *stream;
- PaError result = GetWinMMEStreamPointer( &stream, s );
-
- if( result == paNoError )
- return (PA_IS_INPUT_STREAM_(stream)) ? stream->input.deviceCount : 0;
- else
- return result;
-}
-
-
-HWAVEIN PaWinMME_GetStreamInputHandle( PaStream* s, int handleIndex )
-{
- PaWinMmeStream *stream;
- PaError result = GetWinMMEStreamPointer( &stream, s );
-
- if( result == paNoError
- && PA_IS_INPUT_STREAM_(stream)
- && handleIndex >= 0
- && (unsigned int)handleIndex < stream->input.deviceCount )
- return ((HWAVEIN*)stream->input.waveHandles)[handleIndex];
- else
- return 0;
-}
-
-
-int PaWinMME_GetStreamOutputHandleCount( PaStream* s)
-{
- PaWinMmeStream *stream;
- PaError result = GetWinMMEStreamPointer( &stream, s );
-
- if( result == paNoError )
- return (PA_IS_OUTPUT_STREAM_(stream)) ? stream->output.deviceCount : 0;
- else
- return result;
-}
-
-
-HWAVEOUT PaWinMME_GetStreamOutputHandle( PaStream* s, int handleIndex )
-{
- PaWinMmeStream *stream;
- PaError result = GetWinMMEStreamPointer( &stream, s );
-
- if( result == paNoError
- && PA_IS_OUTPUT_STREAM_(stream)
- && handleIndex >= 0
- && (unsigned int)handleIndex < stream->output.deviceCount )
- return ((HWAVEOUT*)stream->output.waveHandles)[handleIndex];
- else
- return 0;
-}
-
-
-
-
-
+/*
+ * $Id: pa_win_wmme.c,v 1.6.2.86 2004/02/21 11:38:28 rossbencina Exp $
+ * pa_win_wmme.c
+ * Implementation of PortAudio for Windows MultiMedia Extensions (WMME)
+ *
+ * PortAudio Portable Real-Time Audio Library
+ * Latest Version at: http://www.portaudio.com
+ *
+ * Authors: Ross Bencina and Phil Burk
+ * Copyright (c) 1999-2000 Ross Bencina and Phil Burk
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * Any person wishing to distribute modifications to the Software is
+ * requested to send the modifications to the original developer so that
+ * they can be incorporated into the canonical version.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+ * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+/*
+ * PJMEDIA - Multimedia over IP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/* Modification History:
+ PLB = Phil Burk
+ JM = Julien Maillard
+ RDB = Ross Bencina
+ PLB20010402 - sDevicePtrs now allocates based on sizeof(pointer)
+ PLB20010413 - check for excessive numbers of channels
+ PLB20010422 - apply Mike Berry's changes for CodeWarrior on PC
+ including conditional inclusion of memory.h,
+ and explicit typecasting on memory allocation
+ PLB20010802 - use GlobalAlloc for sDevicesPtr instead of PaHost_AllocFastMemory
+ PLB20010816 - pass process instead of thread to SetPriorityClass()
+ PLB20010927 - use number of frames instead of real-time for CPULoad calculation.
+ JM20020118 - prevent hung thread when buffers underflow.
+ PLB20020321 - detect Win XP versus NT, 9x; fix DBUG typo; removed init of CurrentCount
+ RDB20020411 - various renaming cleanups, factored streamData alloc and cpu usage init
+ RDB20020417 - stopped counting WAVE_MAPPER when there were no real devices
+ refactoring, renaming and fixed a few edge case bugs
+ RDB20020531 - converted to V19 framework
+ ** NOTE maintanance history is now stored in CVS **
+*/
+
+/** @file
+
+ @todo Fix buffer catch up code, can sometimes get stuck (perhaps fixed now,
+ needs to be reviewed and tested.)
+
+ @todo implement paInputUnderflow, paOutputOverflow streamCallback statusFlags, paNeverDropInput.
+
+ @todo BUG: PA_MME_SET_LAST_WAVEIN/OUT_ERROR is used in functions which may
+ be called asynchronously from the callback thread. this is bad.
+
+ @todo implement inputBufferAdcTime in callback thread
+
+ @todo review/fix error recovery and cleanup in marked functions
+
+ @todo implement timeInfo for stream priming
+
+ @todo handle the case where the callback returns paAbort or paComplete during stream priming.
+
+ @todo review input overflow and output underflow handling in ReadStream and WriteStream
+
+Non-critical stuff for the future:
+
+ @todo Investigate supporting host buffer formats > 16 bits
+
+ @todo define UNICODE and _UNICODE in the project settings and see what breaks
+
+*/
+
+/*
+ How it works:
+
+ For both callback and blocking read/write streams we open the MME devices
+ in CALLBACK_EVENT mode. In this mode, MME signals an Event object whenever
+ it has finished with a buffer (either filled it for input, or played it
+ for output). Where necessary we block waiting for Event objects using
+ WaitMultipleObjects().
+
+ When implementing a PA callback stream, we set up a high priority thread
+ which waits on the MME buffer Events and drains/fills the buffers when
+ they are ready.
+
+ When implementing a PA blocking read/write stream, we simply wait on these
+ Events (when necessary) inside the ReadStream() and WriteStream() functions.
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include <windows.h>
+#include <mmsystem.h>
+#include <process.h>
+#include <assert.h>
+/* PLB20010422 - "memory.h" doesn't work on CodeWarrior for PC. Thanks Mike Berry for the mod. */
+#ifndef __MWERKS__
+#include <malloc.h>
+#include <memory.h>
+#endif /* __MWERKS__ */
+
+#include "portaudio.h"
+#include "pa_trace.h"
+#include "pa_util.h"
+#include "pa_allocation.h"
+#include "pa_hostapi.h"
+#include "pa_stream.h"
+#include "pa_cpuload.h"
+#include "pa_process.h"
+
+#include "pa_win_wmme.h"
+
+#if (defined(WIN32) && (defined(_MSC_VER) && (_MSC_VER >= 1200))) /* MSC version 6 and above */
+#pragma comment(lib, "winmm.lib")
+#endif
+
+/************************************************* Constants ********/
+
+#define PA_MME_USE_HIGH_DEFAULT_LATENCY_ (0) /* For debugging glitches. */
+
+#if PA_MME_USE_HIGH_DEFAULT_LATENCY_
+ #define PA_MME_WIN_9X_DEFAULT_LATENCY_ (0.4)
+ #define PA_MME_MIN_HOST_OUTPUT_BUFFER_COUNT_ (4)
+ #define PA_MME_MIN_HOST_INPUT_BUFFER_COUNT_FULL_DUPLEX_ (4)
+ #define PA_MME_MIN_HOST_INPUT_BUFFER_COUNT_HALF_DUPLEX_ (4)
+ #define PA_MME_MIN_HOST_BUFFER_FRAMES_WHEN_UNSPECIFIED_ (16)
+ #define PA_MME_MAX_HOST_BUFFER_SECS_ (0.3) /* Do not exceed unless user buffer exceeds */
+ #define PA_MME_MAX_HOST_BUFFER_BYTES_ (32 * 1024) /* Has precedence over PA_MME_MAX_HOST_BUFFER_SECS_, some drivers are known to crash with buffer sizes > 32k */
+#else
+ #define PA_MME_WIN_9X_DEFAULT_LATENCY_ (0.2)
+ #define PA_MME_MIN_HOST_OUTPUT_BUFFER_COUNT_ (2)
+ #define PA_MME_MIN_HOST_INPUT_BUFFER_COUNT_FULL_DUPLEX_ (3)
+ #define PA_MME_MIN_HOST_INPUT_BUFFER_COUNT_HALF_DUPLEX_ (2)
+ #define PA_MME_MIN_HOST_BUFFER_FRAMES_WHEN_UNSPECIFIED_ (16)
+ #define PA_MME_MAX_HOST_BUFFER_SECS_ (0.1) /* Do not exceed unless user buffer exceeds */
+ #define PA_MME_MAX_HOST_BUFFER_BYTES_ (32 * 1024) /* Has precedence over PA_MME_MAX_HOST_BUFFER_SECS_, some drivers are known to crash with buffer sizes > 32k */
+#endif
+
+/* Use higher latency for NT because it is even worse at real-time
+ operation than Win9x.
+*/
+#define PA_MME_WIN_NT_DEFAULT_LATENCY_ (PA_MME_WIN_9X_DEFAULT_LATENCY_ * 2)
+#define PA_MME_WIN_WDM_DEFAULT_LATENCY_ (PA_MME_WIN_9X_DEFAULT_LATENCY_)
+
+
+#define PA_MME_MIN_TIMEOUT_MSEC_ (1000)
+
+static const char constInputMapperSuffix_[] = " - Input";
+static const char constOutputMapperSuffix_[] = " - Output";
+
+/********************************************************************/
+
+typedef struct PaWinMmeStream PaWinMmeStream; /* forward declaration */
+
+/* prototypes for functions declared in this file */
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif /* __cplusplus */
+
+PaError PaWinMme_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index );
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+static void Terminate( struct PaUtilHostApiRepresentation *hostApi );
+static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
+ PaStream** stream,
+ const PaStreamParameters *inputParameters,
+ const PaStreamParameters *outputParameters,
+ double sampleRate,
+ unsigned long framesPerBuffer,
+ PaStreamFlags streamFlags,
+ PaStreamCallback *streamCallback,
+ void *userData );
+static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
+ const PaStreamParameters *inputParameters,
+ const PaStreamParameters *outputParameters,
+ double sampleRate );
+static PaError CloseStream( PaStream* stream );
+static PaError StartStream( PaStream *stream );
+static PaError StopStream( PaStream *stream );
+static PaError AbortStream( PaStream *stream );
+static PaError IsStreamStopped( PaStream *s );
+static PaError IsStreamActive( PaStream *stream );
+static PaTime GetStreamTime( PaStream *stream );
+static double GetStreamCpuLoad( PaStream* stream );
+static PaError ReadStream( PaStream* stream, void *buffer, unsigned long frames );
+static PaError WriteStream( PaStream* stream, const void *buffer, unsigned long frames );
+static signed long GetStreamReadAvailable( PaStream* stream );
+static signed long GetStreamWriteAvailable( PaStream* stream );
+
+
+/* macros for setting last host error information */
+
+#ifdef UNICODE
+
+#define PA_MME_SET_LAST_WAVEIN_ERROR( mmresult ) \
+ { \
+ wchar_t mmeErrorTextWide[ MAXERRORLENGTH ]; \
+ char mmeErrorText[ MAXERRORLENGTH ]; \
+ waveInGetErrorText( mmresult, mmeErrorTextWide, MAXERRORLENGTH ); \
+ WideCharToMultiByte( CP_ACP, WC_COMPOSITECHECK | WC_DEFAULTCHAR,\
+ mmeErrorTextWide, -1, mmeErrorText, MAXERRORLENGTH, NULL, NULL ); \
+ PaUtil_SetLastHostErrorInfo( paMME, mmresult, mmeErrorText ); \
+ }
+
+#define PA_MME_SET_LAST_WAVEOUT_ERROR( mmresult ) \
+ { \
+ wchar_t mmeErrorTextWide[ MAXERRORLENGTH ]; \
+ char mmeErrorText[ MAXERRORLENGTH ]; \
+ waveOutGetErrorText( mmresult, mmeErrorTextWide, MAXERRORLENGTH ); \
+ WideCharToMultiByte( CP_ACP, WC_COMPOSITECHECK | WC_DEFAULTCHAR,\
+ mmeErrorTextWide, -1, mmeErrorText, MAXERRORLENGTH, NULL, NULL ); \
+ PaUtil_SetLastHostErrorInfo( paMME, mmresult, mmeErrorText ); \
+ }
+
+#else /* !UNICODE */
+
+#define PA_MME_SET_LAST_WAVEIN_ERROR( mmresult ) \
+ { \
+ char mmeErrorText[ MAXERRORLENGTH ]; \
+ waveInGetErrorText( mmresult, mmeErrorText, MAXERRORLENGTH ); \
+ PaUtil_SetLastHostErrorInfo( paMME, mmresult, mmeErrorText ); \
+ }
+
+#define PA_MME_SET_LAST_WAVEOUT_ERROR( mmresult ) \
+ { \
+ char mmeErrorText[ MAXERRORLENGTH ]; \
+ waveOutGetErrorText( mmresult, mmeErrorText, MAXERRORLENGTH ); \
+ PaUtil_SetLastHostErrorInfo( paMME, mmresult, mmeErrorText ); \
+ }
+
+#endif /* UNICODE */
+
+
+static void PaMme_SetLastSystemError( DWORD errorCode )
+{
+ char *lpMsgBuf;
+ FormatMessage(
+ FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
+ NULL,
+ errorCode,
+ MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
+ (LPTSTR) &lpMsgBuf,
+ 0,
+ NULL
+ );
+ PaUtil_SetLastHostErrorInfo( paMME, errorCode, lpMsgBuf );
+ LocalFree( lpMsgBuf );
+}
+
+#define PA_MME_SET_LAST_SYSTEM_ERROR( errorCode ) \
+ PaMme_SetLastSystemError( errorCode )
+
+
+/* PaError returning wrappers for some commonly used win32 functions
+ note that we allow passing a null ptr to have no effect.
+*/
+
+static PaError CreateEventWithPaError( HANDLE *handle,
+ LPSECURITY_ATTRIBUTES lpEventAttributes,
+ BOOL bManualReset,
+ BOOL bInitialState,
+ LPCTSTR lpName )
+{
+ PaError result = paNoError;
+
+ *handle = NULL;
+
+ *handle = CreateEvent( lpEventAttributes, bManualReset, bInitialState, lpName );
+ if( *handle == NULL )
+ {
+ result = paUnanticipatedHostError;
+ PA_MME_SET_LAST_SYSTEM_ERROR( GetLastError() );
+ }
+
+ return result;
+}
+
+
+static PaError ResetEventWithPaError( HANDLE handle )
+{
+ PaError result = paNoError;
+
+ if( handle )
+ {
+ if( ResetEvent( handle ) == 0 )
+ {
+ result = paUnanticipatedHostError;
+ PA_MME_SET_LAST_SYSTEM_ERROR( GetLastError() );
+ }
+ }
+
+ return result;
+}
+
+
+static PaError CloseHandleWithPaError( HANDLE handle )
+{
+ PaError result = paNoError;
+
+ if( handle )
+ {
+ if( CloseHandle( handle ) == 0 )
+ {
+ result = paUnanticipatedHostError;
+ PA_MME_SET_LAST_SYSTEM_ERROR( GetLastError() );
+ }
+ }
+
+ return result;
+}
+
+
+/* PaWinMmeHostApiRepresentation - host api datastructure specific to this implementation */
+
+typedef struct
+{
+ PaUtilHostApiRepresentation inheritedHostApiRep;
+ PaUtilStreamInterface callbackStreamInterface;
+ PaUtilStreamInterface blockingStreamInterface;
+
+ PaUtilAllocationGroup *allocations;
+
+ int inputDeviceCount, outputDeviceCount;
+
+ /** winMmeDeviceIds is an array of WinMme device ids.
+ fields in the range [0, inputDeviceCount) are input device ids,
+ and [inputDeviceCount, inputDeviceCount + outputDeviceCount) are output
+ device ids.
+ */
+ UINT *winMmeDeviceIds;
+}
+PaWinMmeHostApiRepresentation;
+
+
+typedef struct
+{
+ PaDeviceInfo inheritedDeviceInfo;
+ DWORD dwFormats; /**<< standard formats bitmask from the WAVEINCAPS and WAVEOUTCAPS structures */
+}
+PaWinMmeDeviceInfo;
+
+
+/*************************************************************************
+ * Returns recommended device ID.
+ * On the PC, the recommended device can be specified by the user by
+ * setting an environment variable. For example, to use device #1.
+ *
+ * set PA_RECOMMENDED_OUTPUT_DEVICE=1
+ *
+ * The user should first determine the available device ID by using
+ * the supplied application "pa_devs".
+ */
+#define PA_ENV_BUF_SIZE_ (32)
+#define PA_REC_IN_DEV_ENV_NAME_ ("PA_RECOMMENDED_INPUT_DEVICE")
+#define PA_REC_OUT_DEV_ENV_NAME_ ("PA_RECOMMENDED_OUTPUT_DEVICE")
+static PaDeviceIndex GetEnvDefaultDeviceID( char *envName )
+{
+ PaDeviceIndex recommendedIndex = paNoDevice;
+ DWORD hresult;
+ char envbuf[PA_ENV_BUF_SIZE_];
+
+#ifndef WIN32_PLATFORM_PSPC /* no GetEnvironmentVariable on PocketPC */
+
+ /* Let user determine default device by setting environment variable. */
+ hresult = GetEnvironmentVariable( envName, envbuf, PA_ENV_BUF_SIZE_ );
+ if( (hresult > 0) && (hresult < PA_ENV_BUF_SIZE_) )
+ {
+ recommendedIndex = atoi( envbuf );
+ }
+#endif
+
+ return recommendedIndex;
+}
+
+
+static void InitializeDefaultDeviceIdsFromEnv( PaWinMmeHostApiRepresentation *hostApi )
+{
+ PaDeviceIndex device;
+
+ /* input */
+ device = GetEnvDefaultDeviceID( PA_REC_IN_DEV_ENV_NAME_ );
+ if( device != paNoDevice &&
+ ( device >= 0 && device < hostApi->inheritedHostApiRep.info.deviceCount ) &&
+ hostApi->inheritedHostApiRep.deviceInfos[ device ]->maxInputChannels > 0 )
+ {
+ hostApi->inheritedHostApiRep.info.defaultInputDevice = device;
+ }
+
+ /* output */
+ device = GetEnvDefaultDeviceID( PA_REC_OUT_DEV_ENV_NAME_ );
+ if( device != paNoDevice &&
+ ( device >= 0 && device < hostApi->inheritedHostApiRep.info.deviceCount ) &&
+ hostApi->inheritedHostApiRep.deviceInfos[ device ]->maxOutputChannels > 0 )
+ {
+ hostApi->inheritedHostApiRep.info.defaultOutputDevice = device;
+ }
+}
+
+
+/** Convert external PA ID to a windows multimedia device ID
+*/
+static UINT LocalDeviceIndexToWinMmeDeviceId( PaWinMmeHostApiRepresentation *hostApi, PaDeviceIndex device )
+{
+ assert( device >= 0 && device < hostApi->inputDeviceCount + hostApi->outputDeviceCount );
+
+ return hostApi->winMmeDeviceIds[ device ];
+}
+
+
+static PaError QueryInputWaveFormatEx( int deviceId, WAVEFORMATEX *waveFormatEx )
+{
+ MMRESULT mmresult;
+
+ switch( mmresult = waveInOpen( NULL, deviceId, waveFormatEx, 0, 0, WAVE_FORMAT_QUERY ) )
+ {
+ case MMSYSERR_NOERROR:
+ return paNoError;
+ case MMSYSERR_ALLOCATED: /* Specified resource is already allocated. */
+ return paDeviceUnavailable;
+ case MMSYSERR_NODRIVER: /* No device driver is present. */
+ return paDeviceUnavailable;
+ case MMSYSERR_NOMEM: /* Unable to allocate or lock memory. */
+ return paInsufficientMemory;
+ case WAVERR_BADFORMAT: /* Attempted to open with an unsupported waveform-audio format. */
+ return paSampleFormatNotSupported;
+
+ case MMSYSERR_BADDEVICEID: /* Specified device identifier is out of range. */
+ /* falls through */
+ default:
+ PA_MME_SET_LAST_WAVEIN_ERROR( mmresult );
+ return paUnanticipatedHostError;
+ }
+}
+
+
+static PaError QueryOutputWaveFormatEx( int deviceId, WAVEFORMATEX *waveFormatEx )
+{
+ MMRESULT mmresult;
+
+ switch( mmresult = waveOutOpen( NULL, deviceId, waveFormatEx, 0, 0, WAVE_FORMAT_QUERY ) )
+ {
+ case MMSYSERR_NOERROR:
+ return paNoError;
+ case MMSYSERR_ALLOCATED: /* Specified resource is already allocated. */
+ return paDeviceUnavailable;
+ case MMSYSERR_NODRIVER: /* No device driver is present. */
+ return paDeviceUnavailable;
+ case MMSYSERR_NOMEM: /* Unable to allocate or lock memory. */
+ return paInsufficientMemory;
+ case WAVERR_BADFORMAT: /* Attempted to open with an unsupported waveform-audio format. */
+ return paSampleFormatNotSupported;
+
+ case MMSYSERR_BADDEVICEID: /* Specified device identifier is out of range. */
+ /* falls through */
+ default:
+ PA_MME_SET_LAST_WAVEOUT_ERROR( mmresult );
+ return paUnanticipatedHostError;
+ }
+}
+
+
+static PaError QueryFormatSupported( PaDeviceInfo *deviceInfo,
+ PaError (*waveFormatExQueryFunction)(int, WAVEFORMATEX*),
+ int winMmeDeviceId, int channels, double sampleRate )
+{
+ PaWinMmeDeviceInfo *winMmeDeviceInfo = (PaWinMmeDeviceInfo*)deviceInfo;
+ WAVEFORMATEX waveFormatEx;
+
+ if( sampleRate == 11025.0
+ && ( (channels == 1 && (winMmeDeviceInfo->dwFormats & WAVE_FORMAT_1M16))
+ || (channels == 2 && (winMmeDeviceInfo->dwFormats & WAVE_FORMAT_1S16)) ) ){
+
+ return paNoError;
+ }
+
+ if( sampleRate == 22050.0
+ && ( (channels == 1 && (winMmeDeviceInfo->dwFormats & WAVE_FORMAT_2M16))
+ || (channels == 2 && (winMmeDeviceInfo->dwFormats & WAVE_FORMAT_2S16)) ) ){
+
+ return paNoError;
+ }
+
+ if( sampleRate == 44100.0
+ && ( (channels == 1 && (winMmeDeviceInfo->dwFormats & WAVE_FORMAT_4M16))
+ || (channels == 2 && (winMmeDeviceInfo->dwFormats & WAVE_FORMAT_4S16)) ) ){
+
+ return paNoError;
+ }
+
+ waveFormatEx.wFormatTag = WAVE_FORMAT_PCM;
+ waveFormatEx.nChannels = (WORD)channels;
+ waveFormatEx.nSamplesPerSec = (DWORD)sampleRate;
+ waveFormatEx.nAvgBytesPerSec = waveFormatEx.nSamplesPerSec * channels * sizeof(short);
+ waveFormatEx.nBlockAlign = (WORD)(channels * sizeof(short));
+ waveFormatEx.wBitsPerSample = 16;
+ waveFormatEx.cbSize = 0;
+
+ return waveFormatExQueryFunction( winMmeDeviceId, &waveFormatEx );
+}
+
+
+#define PA_DEFAULTSAMPLERATESEARCHORDER_COUNT_ (13) /* must match array length below */
+static double defaultSampleRateSearchOrder_[] =
+ { 44100.0, 48000.0, 32000.0, 24000.0, 22050.0, 88200.0, 96000.0, 192000.0,
+ 16000.0, 12000.0, 11025.0, 9600.0, 8000.0 };
+
+static void DetectDefaultSampleRate( PaWinMmeDeviceInfo *winMmeDeviceInfo, int winMmeDeviceId,
+ PaError (*waveFormatExQueryFunction)(int, WAVEFORMATEX*), int maxChannels )
+{
+ PaDeviceInfo *deviceInfo = &winMmeDeviceInfo->inheritedDeviceInfo;
+ int i;
+
+ deviceInfo->defaultSampleRate = 0.;
+
+ for( i=0; i < PA_DEFAULTSAMPLERATESEARCHORDER_COUNT_; ++i )
+ {
+ double sampleRate = defaultSampleRateSearchOrder_[ i ];
+ PaError paerror = QueryFormatSupported( deviceInfo, waveFormatExQueryFunction, winMmeDeviceId, maxChannels, sampleRate );
+ if( paerror == paNoError )
+ {
+ deviceInfo->defaultSampleRate = sampleRate;
+ break;
+ }
+ }
+}
+
+
+static PaError InitializeInputDeviceInfo( PaWinMmeHostApiRepresentation *winMmeHostApi,
+ PaWinMmeDeviceInfo *winMmeDeviceInfo, UINT winMmeInputDeviceId, int *success )
+{
+ PaError result = paNoError;
+ char *deviceName; /* non-const ptr */
+ MMRESULT mmresult;
+ WAVEINCAPS wic;
+ PaDeviceInfo *deviceInfo = &winMmeDeviceInfo->inheritedDeviceInfo;
+
+ *success = 0;
+
+ mmresult = waveInGetDevCaps( winMmeInputDeviceId, &wic, sizeof( WAVEINCAPS ) );
+ if( mmresult == MMSYSERR_NOMEM )
+ {
+ result = paInsufficientMemory;
+ goto error;
+ }
+ else if( mmresult != MMSYSERR_NOERROR )
+ {
+ /* instead of returning paUnanticipatedHostError we return
+ paNoError, but leave success set as 0. This allows
+ Pa_Initialize to just ignore this device, without failing
+ the entire initialisation process.
+ */
+ return paNoError;
+ }
+
+ if( winMmeInputDeviceId == WAVE_MAPPER )
+ {
+ /* Append I/O suffix to WAVE_MAPPER device. */
+ deviceName = (char *)PaUtil_GroupAllocateMemory(
+ winMmeHostApi->allocations, strlen( wic.szPname ) + 1 + sizeof(constInputMapperSuffix_) );
+ if( !deviceName )
+ {
+ result = paInsufficientMemory;
+ goto error;
+ }
+ strcpy( deviceName, wic.szPname );
+ strcat( deviceName, constInputMapperSuffix_ );
+ }
+ else
+ {
+ deviceName = (char*)PaUtil_GroupAllocateMemory(
+ winMmeHostApi->allocations, strlen( wic.szPname ) + 1 );
+ if( !deviceName )
+ {
+ result = paInsufficientMemory;
+ goto error;
+ }
+ strcpy( deviceName, wic.szPname );
+ }
+ deviceInfo->name = deviceName;
+
+ deviceInfo->maxInputChannels = wic.wChannels;
+ /* Sometimes a device can return a rediculously large number of channels.
+ * This happened with an SBLive card on a Windows ME box.
+ * If that happens, then force it to 2 channels. PLB20010413
+ */
+ if( (deviceInfo->maxInputChannels < 1) || (deviceInfo->maxInputChannels > 256) )
+ {
+ PA_DEBUG(("Pa_GetDeviceInfo: Num input channels reported as %d! Changed to 2.\n", deviceInfo->maxInputChannels ));
+ deviceInfo->maxInputChannels = 2;
+ }
+
+ winMmeDeviceInfo->dwFormats = wic.dwFormats;
+
+ DetectDefaultSampleRate( winMmeDeviceInfo, winMmeInputDeviceId,
+ QueryInputWaveFormatEx, deviceInfo->maxInputChannels );
+
+ *success = 1;
+
+error:
+ return result;
+}
+
+
+static PaError InitializeOutputDeviceInfo( PaWinMmeHostApiRepresentation *winMmeHostApi,
+ PaWinMmeDeviceInfo *winMmeDeviceInfo, UINT winMmeOutputDeviceId, int *success )
+{
+ PaError result = paNoError;
+ char *deviceName; /* non-const ptr */
+ MMRESULT mmresult;
+ WAVEOUTCAPS woc;
+ PaDeviceInfo *deviceInfo = &winMmeDeviceInfo->inheritedDeviceInfo;
+
+ *success = 0;
+
+ mmresult = waveOutGetDevCaps( winMmeOutputDeviceId, &woc, sizeof( WAVEOUTCAPS ) );
+ if( mmresult == MMSYSERR_NOMEM )
+ {
+ result = paInsufficientMemory;
+ goto error;
+ }
+ else if( mmresult != MMSYSERR_NOERROR )
+ {
+ /* instead of returning paUnanticipatedHostError we return
+ paNoError, but leave success set as 0. This allows
+ Pa_Initialize to just ignore this device, without failing
+ the entire initialisation process.
+ */
+ return paNoError;
+ }
+
+ if( winMmeOutputDeviceId == WAVE_MAPPER )
+ {
+ /* Append I/O suffix to WAVE_MAPPER device. */
+ deviceName = (char *)PaUtil_GroupAllocateMemory(
+ winMmeHostApi->allocations, strlen( woc.szPname ) + 1 + sizeof(constOutputMapperSuffix_) );
+ if( !deviceName )
+ {
+ result = paInsufficientMemory;
+ goto error;
+ }
+ strcpy( deviceName, woc.szPname );
+ strcat( deviceName, constOutputMapperSuffix_ );
+ }
+ else
+ {
+ deviceName = (char*)PaUtil_GroupAllocateMemory(
+ winMmeHostApi->allocations, strlen( woc.szPname ) + 1 );
+ if( !deviceName )
+ {
+ result = paInsufficientMemory;
+ goto error;
+ }
+ strcpy( deviceName, woc.szPname );
+ }
+ deviceInfo->name = deviceName;
+
+ deviceInfo->maxOutputChannels = woc.wChannels;
+ /* Sometimes a device can return a rediculously large number of channels.
+ * This happened with an SBLive card on a Windows ME box.
+ * It also happens on Win XP!
+ */
+ if( (deviceInfo->maxOutputChannels < 1) || (deviceInfo->maxOutputChannels > 256) )
+ {
+ PA_DEBUG(("Pa_GetDeviceInfo: Num output channels reported as %d! Changed to 2.\n", deviceInfo->maxOutputChannels ));
+ deviceInfo->maxOutputChannels = 2;
+ }
+
+ winMmeDeviceInfo->dwFormats = woc.dwFormats;
+
+ DetectDefaultSampleRate( winMmeDeviceInfo, winMmeOutputDeviceId,
+ QueryOutputWaveFormatEx, deviceInfo->maxOutputChannels );
+
+ *success = 1;
+
+error:
+ return result;
+}
+
+
+static void GetDefaultLatencies( PaTime *defaultLowLatency, PaTime *defaultHighLatency )
+{
+ OSVERSIONINFO osvi;
+ osvi.dwOSVersionInfoSize = sizeof( osvi );
+ GetVersionEx( &osvi );
+
+ /* Check for NT */
+ if( (osvi.dwMajorVersion == 4) && (osvi.dwPlatformId == 2) )
+ {
+ *defaultLowLatency = PA_MME_WIN_NT_DEFAULT_LATENCY_;
+ }
+ else if(osvi.dwMajorVersion >= 5)
+ {
+ *defaultLowLatency = PA_MME_WIN_WDM_DEFAULT_LATENCY_;
+ }
+ else
+ {
+ *defaultLowLatency = PA_MME_WIN_9X_DEFAULT_LATENCY_;
+ }
+
+ *defaultHighLatency = *defaultLowLatency * 2;
+}
+
+
+PaError PaWinMme_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex hostApiIndex )
+{
+ PaError result = paNoError;
+ int i;
+ PaWinMmeHostApiRepresentation *winMmeHostApi;
+ int inputDeviceCount, outputDeviceCount, maximumPossibleDeviceCount;
+ PaWinMmeDeviceInfo *deviceInfoArray;
+ int deviceInfoInitializationSucceeded;
+ PaTime defaultLowLatency, defaultHighLatency;
+
+ winMmeHostApi = (PaWinMmeHostApiRepresentation*)PaUtil_AllocateMemory( sizeof(PaWinMmeHostApiRepresentation) );
+ if( !winMmeHostApi )
+ {
+ result = paInsufficientMemory;
+ goto error;
+ }
+
+ winMmeHostApi->allocations = PaUtil_CreateAllocationGroup();
+ if( !winMmeHostApi->allocations )
+ {
+ result = paInsufficientMemory;
+ goto error;
+ }
+
+ *hostApi = &winMmeHostApi->inheritedHostApiRep;
+ (*hostApi)->info.structVersion = 1;
+ (*hostApi)->info.type = paMME;
+ (*hostApi)->info.name = "MME";
+
+
+ /* initialise device counts and default devices under the assumption that
+ there are no devices. These values are incremented below if and when
+ devices are successfully initialized.
+ */
+ (*hostApi)->info.deviceCount = 0;
+ (*hostApi)->info.defaultInputDevice = paNoDevice;
+ (*hostApi)->info.defaultOutputDevice = paNoDevice;
+ winMmeHostApi->inputDeviceCount = 0;
+ winMmeHostApi->outputDeviceCount = 0;
+
+
+ maximumPossibleDeviceCount = 0;
+
+ inputDeviceCount = waveInGetNumDevs();
+ if( inputDeviceCount > 0 )
+ maximumPossibleDeviceCount += inputDeviceCount + 1; /* assume there is a WAVE_MAPPER */
+
+ outputDeviceCount = waveOutGetNumDevs();
+ if( outputDeviceCount > 0 )
+ maximumPossibleDeviceCount += outputDeviceCount + 1; /* assume there is a WAVE_MAPPER */
+
+
+ if( maximumPossibleDeviceCount > 0 ){
+
+ (*hostApi)->deviceInfos = (PaDeviceInfo**)PaUtil_GroupAllocateMemory(
+ winMmeHostApi->allocations, sizeof(PaDeviceInfo*) * maximumPossibleDeviceCount );
+ if( !(*hostApi)->deviceInfos )
+ {
+ result = paInsufficientMemory;
+ goto error;
+ }
+
+ /* allocate all device info structs in a contiguous block */
+ deviceInfoArray = (PaWinMmeDeviceInfo*)PaUtil_GroupAllocateMemory(
+ winMmeHostApi->allocations, sizeof(PaWinMmeDeviceInfo) * maximumPossibleDeviceCount );
+ if( !deviceInfoArray )
+ {
+ result = paInsufficientMemory;
+ goto error;
+ }
+
+ winMmeHostApi->winMmeDeviceIds = (UINT*)PaUtil_GroupAllocateMemory(
+ winMmeHostApi->allocations, sizeof(int) * maximumPossibleDeviceCount );
+ if( !winMmeHostApi->winMmeDeviceIds )
+ {
+ result = paInsufficientMemory;
+ goto error;
+ }
+
+ GetDefaultLatencies( &defaultLowLatency, &defaultHighLatency );
+
+ if( inputDeviceCount > 0 ){
+ /* -1 is the WAVE_MAPPER */
+ for( i = -1; i < inputDeviceCount; ++i ){
+ UINT winMmeDeviceId = (UINT)((i==-1) ? WAVE_MAPPER : i);
+ PaWinMmeDeviceInfo *wmmeDeviceInfo = &deviceInfoArray[ (*hostApi)->info.deviceCount ];
+ PaDeviceInfo *deviceInfo = &wmmeDeviceInfo->inheritedDeviceInfo;
+ deviceInfo->structVersion = 2;
+ deviceInfo->hostApi = hostApiIndex;
+
+ deviceInfo->maxInputChannels = 0;
+ deviceInfo->maxOutputChannels = 0;
+
+ deviceInfo->defaultLowInputLatency = defaultLowLatency;
+ deviceInfo->defaultLowOutputLatency = defaultLowLatency;
+ deviceInfo->defaultHighInputLatency = defaultHighLatency;
+ deviceInfo->defaultHighOutputLatency = defaultHighLatency;
+
+ result = InitializeInputDeviceInfo( winMmeHostApi, wmmeDeviceInfo,
+ winMmeDeviceId, &deviceInfoInitializationSucceeded );
+ if( result != paNoError )
+ goto error;
+
+ if( deviceInfoInitializationSucceeded ){
+ if( (*hostApi)->info.defaultInputDevice == paNoDevice )
+ (*hostApi)->info.defaultInputDevice = (*hostApi)->info.deviceCount;
+
+ winMmeHostApi->winMmeDeviceIds[ (*hostApi)->info.deviceCount ] = winMmeDeviceId;
+ (*hostApi)->deviceInfos[ (*hostApi)->info.deviceCount ] = deviceInfo;
+
+ winMmeHostApi->inputDeviceCount++;
+ (*hostApi)->info.deviceCount++;
+ }
+ }
+ }
+
+ if( outputDeviceCount > 0 ){
+ /* -1 is the WAVE_MAPPER */
+ for( i = -1; i < outputDeviceCount; ++i ){
+ UINT winMmeDeviceId = (UINT)((i==-1) ? WAVE_MAPPER : i);
+ PaWinMmeDeviceInfo *wmmeDeviceInfo = &deviceInfoArray[ (*hostApi)->info.deviceCount ];
+ PaDeviceInfo *deviceInfo = &wmmeDeviceInfo->inheritedDeviceInfo;
+ deviceInfo->structVersion = 2;
+ deviceInfo->hostApi = hostApiIndex;
+
+ deviceInfo->maxInputChannels = 0;
+ deviceInfo->maxOutputChannels = 0;
+
+ deviceInfo->defaultLowInputLatency = defaultLowLatency;
+ deviceInfo->defaultLowOutputLatency = defaultLowLatency;
+ deviceInfo->defaultHighInputLatency = defaultHighLatency;
+ deviceInfo->defaultHighOutputLatency = defaultHighLatency;
+
+ result = InitializeOutputDeviceInfo( winMmeHostApi, wmmeDeviceInfo,
+ winMmeDeviceId, &deviceInfoInitializationSucceeded );
+ if( result != paNoError )
+ goto error;
+
+ if( deviceInfoInitializationSucceeded ){
+ if( (*hostApi)->info.defaultOutputDevice == paNoDevice )
+ (*hostApi)->info.defaultOutputDevice = (*hostApi)->info.deviceCount;
+
+ winMmeHostApi->winMmeDeviceIds[ (*hostApi)->info.deviceCount ] = winMmeDeviceId;
+ (*hostApi)->deviceInfos[ (*hostApi)->info.deviceCount ] = deviceInfo;
+
+ winMmeHostApi->outputDeviceCount++;
+ (*hostApi)->info.deviceCount++;
+ }
+ }
+ }
+ }
+
+
+ InitializeDefaultDeviceIdsFromEnv( winMmeHostApi );
+
+ (*hostApi)->Terminate = Terminate;
+ (*hostApi)->OpenStream = OpenStream;
+ (*hostApi)->IsFormatSupported = IsFormatSupported;
+
+ PaUtil_InitializeStreamInterface( &winMmeHostApi->callbackStreamInterface, CloseStream, StartStream,
+ StopStream, AbortStream, IsStreamStopped, IsStreamActive,
+ GetStreamTime, GetStreamCpuLoad,
+ PaUtil_DummyRead, PaUtil_DummyWrite,
+ PaUtil_DummyGetReadAvailable, PaUtil_DummyGetWriteAvailable );
+
+ PaUtil_InitializeStreamInterface( &winMmeHostApi->blockingStreamInterface, CloseStream, StartStream,
+ StopStream, AbortStream, IsStreamStopped, IsStreamActive,
+ GetStreamTime, PaUtil_DummyGetCpuLoad,
+ ReadStream, WriteStream, GetStreamReadAvailable, GetStreamWriteAvailable );
+
+ return result;
+
+error:
+ if( winMmeHostApi )
+ {
+ if( winMmeHostApi->allocations )
+ {
+ PaUtil_FreeAllAllocations( winMmeHostApi->allocations );
+ PaUtil_DestroyAllocationGroup( winMmeHostApi->allocations );
+ }
+
+ PaUtil_FreeMemory( winMmeHostApi );
+ }
+
+ return result;
+}
+
+
+static void Terminate( struct PaUtilHostApiRepresentation *hostApi )
+{
+ PaWinMmeHostApiRepresentation *winMmeHostApi = (PaWinMmeHostApiRepresentation*)hostApi;
+
+ if( winMmeHostApi->allocations )
+ {
+ PaUtil_FreeAllAllocations( winMmeHostApi->allocations );
+ PaUtil_DestroyAllocationGroup( winMmeHostApi->allocations );
+ }
+
+ PaUtil_FreeMemory( winMmeHostApi );
+}
+
+
+static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
+ const PaStreamParameters *inputParameters,
+ const PaStreamParameters *outputParameters,
+ double sampleRate )
+{
+ PaWinMmeHostApiRepresentation *winMmeHostApi = (PaWinMmeHostApiRepresentation*)hostApi;
+ PaDeviceInfo *inputDeviceInfo, *outputDeviceInfo;
+ int inputChannelCount, outputChannelCount;
+ int inputMultipleDeviceChannelCount, outputMultipleDeviceChannelCount;
+ PaSampleFormat inputSampleFormat, outputSampleFormat;
+ PaWinMmeStreamInfo *inputStreamInfo, *outputStreamInfo;
+ UINT winMmeInputDeviceId, winMmeOutputDeviceId;
+ unsigned int i;
+ PaError paerror;
+
+ /* The calls to QueryFormatSupported below are intended to detect invalid
+ sample rates. If we assume that the channel count and format are OK,
+ then the only thing that could fail is the sample rate. This isn't
+ strictly true, but I can't think of a better way to test that the
+ sample rate is valid.
+ */
+
+ if( inputParameters )
+ {
+ inputChannelCount = inputParameters->channelCount;
+ inputSampleFormat = inputParameters->sampleFormat;
+ inputStreamInfo = inputParameters->hostApiSpecificStreamInfo;
+
+ /* all standard sample formats are supported by the buffer adapter,
+ this implementation doesn't support any custom sample formats */
+ if( inputSampleFormat & paCustomFormat )
+ return paSampleFormatNotSupported;
+
+ if( inputParameters->device == paUseHostApiSpecificDeviceSpecification
+ && inputStreamInfo && (inputStreamInfo->flags & paWinMmeUseMultipleDevices) )
+ {
+ inputMultipleDeviceChannelCount = 0;
+ for( i=0; i< inputStreamInfo->deviceCount; ++i )
+ {
+ inputMultipleDeviceChannelCount += inputStreamInfo->devices[i].channelCount;
+
+ inputDeviceInfo = hostApi->deviceInfos[ inputStreamInfo->devices[i].device ];
+
+ /* check that input device can support inputChannelCount */
+ if( inputStreamInfo->devices[i].channelCount <= 0
+ || inputStreamInfo->devices[i].channelCount > inputDeviceInfo->maxInputChannels )
+ return paInvalidChannelCount;
+
+ /* test for valid sample rate, see comment above */
+ winMmeInputDeviceId = LocalDeviceIndexToWinMmeDeviceId( winMmeHostApi, inputStreamInfo->devices[i].device );
+ paerror = QueryFormatSupported( inputDeviceInfo, QueryInputWaveFormatEx, winMmeInputDeviceId, inputStreamInfo->devices[i].channelCount, sampleRate );
+ if( paerror != paNoError )
+ return paInvalidSampleRate;
+ }
+
+ if( inputMultipleDeviceChannelCount != inputChannelCount )
+ return paIncompatibleHostApiSpecificStreamInfo;
+ }
+ else
+ {
+ if( inputStreamInfo && (inputStreamInfo->flags & paWinMmeUseMultipleDevices) )
+ return paIncompatibleHostApiSpecificStreamInfo; /* paUseHostApiSpecificDeviceSpecification was not supplied as the input device */
+
+ inputDeviceInfo = hostApi->deviceInfos[ inputParameters->device ];
+
+ /* check that input device can support inputChannelCount */
+ if( inputChannelCount > inputDeviceInfo->maxInputChannels )
+ return paInvalidChannelCount;
+
+ /* test for valid sample rate, see comment above */
+ winMmeInputDeviceId = LocalDeviceIndexToWinMmeDeviceId( winMmeHostApi, inputParameters->device );
+ paerror = QueryFormatSupported( inputDeviceInfo, QueryInputWaveFormatEx, winMmeInputDeviceId, inputChannelCount, sampleRate );
+ if( paerror != paNoError )
+ return paInvalidSampleRate;
+ }
+ }
+
+ if( outputParameters )
+ {
+ outputChannelCount = outputParameters->channelCount;
+ outputSampleFormat = outputParameters->sampleFormat;
+ outputStreamInfo = outputParameters->hostApiSpecificStreamInfo;
+
+ /* all standard sample formats are supported by the buffer adapter,
+ this implementation doesn't support any custom sample formats */
+ if( outputSampleFormat & paCustomFormat )
+ return paSampleFormatNotSupported;
+
+ if( outputParameters->device == paUseHostApiSpecificDeviceSpecification
+ && outputStreamInfo && (outputStreamInfo->flags & paWinMmeUseMultipleDevices) )
+ {
+ outputMultipleDeviceChannelCount = 0;
+ for( i=0; i< outputStreamInfo->deviceCount; ++i )
+ {
+ outputMultipleDeviceChannelCount += outputStreamInfo->devices[i].channelCount;
+
+ outputDeviceInfo = hostApi->deviceInfos[ outputStreamInfo->devices[i].device ];
+
+ /* check that output device can support outputChannelCount */
+ if( outputStreamInfo->devices[i].channelCount <= 0
+ || outputStreamInfo->devices[i].channelCount > outputDeviceInfo->maxOutputChannels )
+ return paInvalidChannelCount;
+
+ /* test for valid sample rate, see comment above */
+ winMmeOutputDeviceId = LocalDeviceIndexToWinMmeDeviceId( winMmeHostApi, outputStreamInfo->devices[i].device );
+ paerror = QueryFormatSupported( outputDeviceInfo, QueryOutputWaveFormatEx, winMmeOutputDeviceId, outputStreamInfo->devices[i].channelCount, sampleRate );
+ if( paerror != paNoError )
+ return paInvalidSampleRate;
+ }
+
+ if( outputMultipleDeviceChannelCount != outputChannelCount )
+ return paIncompatibleHostApiSpecificStreamInfo;
+ }
+ else
+ {
+ if( outputStreamInfo && (outputStreamInfo->flags & paWinMmeUseMultipleDevices) )
+ return paIncompatibleHostApiSpecificStreamInfo; /* paUseHostApiSpecificDeviceSpecification was not supplied as the output device */
+
+ outputDeviceInfo = hostApi->deviceInfos[ outputParameters->device ];
+
+ /* check that output device can support outputChannelCount */
+ if( outputChannelCount > outputDeviceInfo->maxOutputChannels )
+ return paInvalidChannelCount;
+
+ /* test for valid sample rate, see comment above */
+ winMmeOutputDeviceId = LocalDeviceIndexToWinMmeDeviceId( winMmeHostApi, outputParameters->device );
+ paerror = QueryFormatSupported( outputDeviceInfo, QueryOutputWaveFormatEx, winMmeOutputDeviceId, outputChannelCount, sampleRate );
+ if( paerror != paNoError )
+ return paInvalidSampleRate;
+ }
+ }
+
+ /*
+ - if a full duplex stream is requested, check that the combination
+ of input and output parameters is supported
+
+ - check that the device supports sampleRate
+
+ for mme all we can do is test that the input and output devices
+ support the requested sample rate and number of channels. we
+ cannot test for full duplex compatibility.
+ */
+
+ return paFormatIsSupported;
+}
+
+
+
+static void SelectBufferSizeAndCount( unsigned long baseBufferSize,
+ unsigned long requestedLatency,
+ unsigned long baseBufferCount, unsigned long minimumBufferCount,
+ unsigned long maximumBufferSize, unsigned long *hostBufferSize,
+ unsigned long *hostBufferCount )
+{
+ unsigned long sizeMultiplier, bufferCount, latency;
+ unsigned long nextLatency, nextBufferSize;
+ int baseBufferSizeIsPowerOfTwo;
+
+ sizeMultiplier = 1;
+ bufferCount = baseBufferCount;
+
+ /* count-1 below because latency is always determined by one less
+ than the total number of buffers.
+ */
+ latency = (baseBufferSize * sizeMultiplier) * (bufferCount-1);
+
+ if( latency > requestedLatency )
+ {
+
+ /* reduce number of buffers without falling below suggested latency */
+
+ nextLatency = (baseBufferSize * sizeMultiplier) * (bufferCount-2);
+ while( bufferCount > minimumBufferCount && nextLatency >= requestedLatency )
+ {
+ --bufferCount;
+ nextLatency = (baseBufferSize * sizeMultiplier) * (bufferCount-2);
+ }
+
+ }else if( latency < requestedLatency ){
+
+ baseBufferSizeIsPowerOfTwo = (! (baseBufferSize & (baseBufferSize - 1)));
+ if( baseBufferSizeIsPowerOfTwo ){
+
+ /* double size of buffers without exceeding requestedLatency */
+
+ nextBufferSize = (baseBufferSize * (sizeMultiplier*2));
+ nextLatency = nextBufferSize * (bufferCount-1);
+ while( nextBufferSize <= maximumBufferSize
+ && nextLatency < requestedLatency )
+ {
+ sizeMultiplier *= 2;
+ nextBufferSize = (baseBufferSize * (sizeMultiplier*2));
+ nextLatency = nextBufferSize * (bufferCount-1);
+ }
+
+ }else{
+
+ /* increase size of buffers upto first excess of requestedLatency */
+
+ nextBufferSize = (baseBufferSize * (sizeMultiplier+1));
+ nextLatency = nextBufferSize * (bufferCount-1);
+ while( nextBufferSize <= maximumBufferSize
+ && nextLatency < requestedLatency )
+ {
+ ++sizeMultiplier;
+ nextBufferSize = (baseBufferSize * (sizeMultiplier+1));
+ nextLatency = nextBufferSize * (bufferCount-1);
+ }
+
+ if( nextLatency < requestedLatency )
+ ++sizeMultiplier;
+ }
+
+ /* increase number of buffers until requestedLatency is reached */
+
+ latency = (baseBufferSize * sizeMultiplier) * (bufferCount-1);
+ while( latency < requestedLatency )
+ {
+ ++bufferCount;
+ latency = (baseBufferSize * sizeMultiplier) * (bufferCount-1);
+ }
+ }
+
+ *hostBufferSize = baseBufferSize * sizeMultiplier;
+ *hostBufferCount = bufferCount;
+}
+
+
+static void ReselectBufferCount( unsigned long bufferSize,
+ unsigned long requestedLatency,
+ unsigned long baseBufferCount, unsigned long minimumBufferCount,
+ unsigned long *hostBufferCount )
+{
+ unsigned long bufferCount, latency;
+ unsigned long nextLatency;
+
+ bufferCount = baseBufferCount;
+
+ /* count-1 below because latency is always determined by one less
+ than the total number of buffers.
+ */
+ latency = bufferSize * (bufferCount-1);
+
+ if( latency > requestedLatency )
+ {
+ /* reduce number of buffers without falling below suggested latency */
+
+ nextLatency = bufferSize * (bufferCount-2);
+ while( bufferCount > minimumBufferCount && nextLatency >= requestedLatency )
+ {
+ --bufferCount;
+ nextLatency = bufferSize * (bufferCount-2);
+ }
+
+ }else if( latency < requestedLatency ){
+
+ /* increase number of buffers until requestedLatency is reached */
+
+ latency = bufferSize * (bufferCount-1);
+ while( latency < requestedLatency )
+ {
+ ++bufferCount;
+ latency = bufferSize * (bufferCount-1);
+ }
+ }
+
+ *hostBufferCount = bufferCount;
+}
+
+
+/* CalculateBufferSettings() fills the framesPerHostInputBuffer, hostInputBufferCount,
+ framesPerHostOutputBuffer and hostOutputBufferCount parameters based on the values
+ of the other parameters.
+*/
+
+static PaError CalculateBufferSettings(
+ unsigned long *framesPerHostInputBuffer, unsigned long *hostInputBufferCount,
+ unsigned long *framesPerHostOutputBuffer, unsigned long *hostOutputBufferCount,
+ int inputChannelCount, PaSampleFormat hostInputSampleFormat,
+ PaTime suggestedInputLatency, PaWinMmeStreamInfo *inputStreamInfo,
+ int outputChannelCount, PaSampleFormat hostOutputSampleFormat,
+ PaTime suggestedOutputLatency, PaWinMmeStreamInfo *outputStreamInfo,
+ double sampleRate, unsigned long framesPerBuffer )
+{
+ PaError result = paNoError;
+ int effectiveInputChannelCount, effectiveOutputChannelCount;
+ int hostInputFrameSize = 0;
+ unsigned int i;
+
+ if( inputChannelCount > 0 )
+ {
+ int hostInputSampleSize = Pa_GetSampleSize( hostInputSampleFormat );
+ if( hostInputSampleSize < 0 )
+ {
+ result = hostInputSampleSize;
+ goto error;
+ }
+
+ if( inputStreamInfo
+ && ( inputStreamInfo->flags & paWinMmeUseMultipleDevices ) )
+ {
+ /* set effectiveInputChannelCount to the largest number of
+ channels on any one device.
+ */
+ effectiveInputChannelCount = 0;
+ for( i=0; i< inputStreamInfo->deviceCount; ++i )
+ {
+ if( inputStreamInfo->devices[i].channelCount > effectiveInputChannelCount )
+ effectiveInputChannelCount = inputStreamInfo->devices[i].channelCount;
+ }
+ }
+ else
+ {
+ effectiveInputChannelCount = inputChannelCount;
+ }
+
+ hostInputFrameSize = hostInputSampleSize * effectiveInputChannelCount;
+
+ if( inputStreamInfo
+ && ( inputStreamInfo->flags & paWinMmeUseLowLevelLatencyParameters ) )
+ {
+ if( inputStreamInfo->bufferCount <= 0
+ || inputStreamInfo->framesPerBuffer <= 0 )
+ {
+ result = paIncompatibleHostApiSpecificStreamInfo;
+ goto error;
+ }
+
+ *framesPerHostInputBuffer = inputStreamInfo->framesPerBuffer;
+ *hostInputBufferCount = inputStreamInfo->bufferCount;
+ }
+ else
+ {
+ unsigned long hostBufferSizeBytes, hostBufferCount;
+ unsigned long minimumBufferCount = (outputChannelCount > 0)
+ ? PA_MME_MIN_HOST_INPUT_BUFFER_COUNT_FULL_DUPLEX_
+ : PA_MME_MIN_HOST_INPUT_BUFFER_COUNT_HALF_DUPLEX_;
+
+ unsigned long maximumBufferSize = (long) ((PA_MME_MAX_HOST_BUFFER_SECS_ * sampleRate) * hostInputFrameSize);
+ if( maximumBufferSize > PA_MME_MAX_HOST_BUFFER_BYTES_ )
+ maximumBufferSize = PA_MME_MAX_HOST_BUFFER_BYTES_;
+
+ /* compute the following in bytes, then convert back to frames */
+
+ SelectBufferSizeAndCount(
+ ((framesPerBuffer == paFramesPerBufferUnspecified)
+ ? PA_MME_MIN_HOST_BUFFER_FRAMES_WHEN_UNSPECIFIED_
+ : framesPerBuffer ) * hostInputFrameSize, /* baseBufferSize */
+ ((unsigned long)(suggestedInputLatency * sampleRate)) * hostInputFrameSize, /* suggestedLatency */
+ 4, /* baseBufferCount */
+ minimumBufferCount, maximumBufferSize,
+ &hostBufferSizeBytes, &hostBufferCount );
+
+ *framesPerHostInputBuffer = hostBufferSizeBytes / hostInputFrameSize;
+ *hostInputBufferCount = hostBufferCount;
+ }
+ }
+ else
+ {
+ *framesPerHostInputBuffer = 0;
+ *hostInputBufferCount = 0;
+ }
+
+ if( outputChannelCount > 0 )
+ {
+ if( outputStreamInfo
+ && ( outputStreamInfo->flags & paWinMmeUseLowLevelLatencyParameters ) )
+ {
+ if( outputStreamInfo->bufferCount <= 0
+ || outputStreamInfo->framesPerBuffer <= 0 )
+ {
+ result = paIncompatibleHostApiSpecificStreamInfo;
+ goto error;
+ }
+
+ *framesPerHostOutputBuffer = outputStreamInfo->framesPerBuffer;
+ *hostOutputBufferCount = outputStreamInfo->bufferCount;
+
+
+ if( inputChannelCount > 0 ) /* full duplex */
+ {
+ if( *framesPerHostInputBuffer != *framesPerHostOutputBuffer )
+ {
+ if( inputStreamInfo
+ && ( inputStreamInfo->flags & paWinMmeUseLowLevelLatencyParameters ) )
+ {
+ /* a custom StreamInfo was used for specifying both input
+ and output buffer sizes, the larger buffer size
+ must be a multiple of the smaller buffer size */
+
+ if( *framesPerHostInputBuffer < *framesPerHostOutputBuffer )
+ {
+ if( *framesPerHostOutputBuffer % *framesPerHostInputBuffer != 0 )
+ {
+ result = paIncompatibleHostApiSpecificStreamInfo;
+ goto error;
+ }
+ }
+ else
+ {
+ assert( *framesPerHostInputBuffer > *framesPerHostOutputBuffer );
+ if( *framesPerHostInputBuffer % *framesPerHostOutputBuffer != 0 )
+ {
+ result = paIncompatibleHostApiSpecificStreamInfo;
+ goto error;
+ }
+ }
+ }
+ else
+ {
+ /* a custom StreamInfo was not used for specifying the input buffer size,
+ so use the output buffer size, and approximately the same latency. */
+
+ *framesPerHostInputBuffer = *framesPerHostOutputBuffer;
+ *hostInputBufferCount = (((unsigned long)(suggestedInputLatency * sampleRate)) / *framesPerHostInputBuffer) + 1;
+
+ if( *hostInputBufferCount < PA_MME_MIN_HOST_INPUT_BUFFER_COUNT_FULL_DUPLEX_ )
+ *hostInputBufferCount = PA_MME_MIN_HOST_INPUT_BUFFER_COUNT_FULL_DUPLEX_;
+ }
+ }
+ }
+ }
+ else
+ {
+ unsigned long hostBufferSizeBytes, hostBufferCount;
+ unsigned long minimumBufferCount = PA_MME_MIN_HOST_OUTPUT_BUFFER_COUNT_;
+ unsigned long maximumBufferSize;
+ int hostOutputFrameSize;
+ int hostOutputSampleSize;
+
+ hostOutputSampleSize = Pa_GetSampleSize( hostOutputSampleFormat );
+ if( hostOutputSampleSize < 0 )
+ {
+ result = hostOutputSampleSize;
+ goto error;
+ }
+
+ if( outputStreamInfo
+ && ( outputStreamInfo->flags & paWinMmeUseMultipleDevices ) )
+ {
+ /* set effectiveOutputChannelCount to the largest number of
+ channels on any one device.
+ */
+ effectiveOutputChannelCount = 0;
+ for( i=0; i< outputStreamInfo->deviceCount; ++i )
+ {
+ if( outputStreamInfo->devices[i].channelCount > effectiveOutputChannelCount )
+ effectiveOutputChannelCount = outputStreamInfo->devices[i].channelCount;
+ }
+ }
+ else
+ {
+ effectiveOutputChannelCount = outputChannelCount;
+ }
+
+ hostOutputFrameSize = hostOutputSampleSize * effectiveOutputChannelCount;
+
+ maximumBufferSize = (long) ((PA_MME_MAX_HOST_BUFFER_SECS_ * sampleRate) * hostOutputFrameSize);
+ if( maximumBufferSize > PA_MME_MAX_HOST_BUFFER_BYTES_ )
+ maximumBufferSize = PA_MME_MAX_HOST_BUFFER_BYTES_;
+
+
+ /* compute the following in bytes, then convert back to frames */
+
+ SelectBufferSizeAndCount(
+ ((framesPerBuffer == paFramesPerBufferUnspecified)
+ ? PA_MME_MIN_HOST_BUFFER_FRAMES_WHEN_UNSPECIFIED_
+ : framesPerBuffer ) * hostOutputFrameSize, /* baseBufferSize */
+ ((unsigned long)(suggestedOutputLatency * sampleRate)) * hostOutputFrameSize, /* suggestedLatency */
+ 4, /* baseBufferCount */
+ minimumBufferCount,
+ maximumBufferSize,
+ &hostBufferSizeBytes, &hostBufferCount );
+
+ *framesPerHostOutputBuffer = hostBufferSizeBytes / hostOutputFrameSize;
+ *hostOutputBufferCount = hostBufferCount;
+
+
+ if( inputChannelCount > 0 )
+ {
+ /* ensure that both input and output buffer sizes are the same.
+ if they don't match at this stage, choose the smallest one
+ and use that for input and output
+ */
+
+ if( *framesPerHostOutputBuffer != *framesPerHostInputBuffer )
+ {
+ if( framesPerHostInputBuffer < framesPerHostOutputBuffer )
+ {
+ unsigned long framesPerHostBuffer = *framesPerHostInputBuffer;
+
+ minimumBufferCount = PA_MME_MIN_HOST_OUTPUT_BUFFER_COUNT_;
+ ReselectBufferCount(
+ framesPerHostBuffer * hostOutputFrameSize, /* bufferSize */
+ ((unsigned long)(suggestedOutputLatency * sampleRate)) * hostOutputFrameSize, /* suggestedLatency */
+ 4, /* baseBufferCount */
+ minimumBufferCount,
+ &hostBufferCount );
+
+ *framesPerHostOutputBuffer = framesPerHostBuffer;
+ *hostOutputBufferCount = hostBufferCount;
+ }
+ else
+ {
+ unsigned long framesPerHostBuffer = *framesPerHostOutputBuffer;
+
+ minimumBufferCount = PA_MME_MIN_HOST_INPUT_BUFFER_COUNT_FULL_DUPLEX_;
+ ReselectBufferCount(
+ framesPerHostBuffer * hostInputFrameSize, /* bufferSize */
+ ((unsigned long)(suggestedInputLatency * sampleRate)) * hostInputFrameSize, /* suggestedLatency */
+ 4, /* baseBufferCount */
+ minimumBufferCount,
+ &hostBufferCount );
+
+ *framesPerHostInputBuffer = framesPerHostBuffer;
+ *hostInputBufferCount = hostBufferCount;
+ }
+ }
+ }
+ }
+ }
+ else
+ {
+ *framesPerHostOutputBuffer = 0;
+ *hostOutputBufferCount = 0;
+ }
+
+error:
+ return result;
+}
+
+
+typedef struct
+{
+ HANDLE bufferEvent;
+ void *waveHandles;
+ unsigned int deviceCount;
+ /* unsigned int channelCount; */
+ WAVEHDR **waveHeaders; /* waveHeaders[device][buffer] */
+ unsigned int bufferCount;
+ unsigned int currentBufferIndex;
+ unsigned int framesPerBuffer;
+ unsigned int framesUsedInCurrentBuffer;
+}PaWinMmeSingleDirectionHandlesAndBuffers;
+
+/* prototypes for functions operating on PaWinMmeSingleDirectionHandlesAndBuffers */
+
+static void InitializeSingleDirectionHandlesAndBuffers( PaWinMmeSingleDirectionHandlesAndBuffers *handlesAndBuffers );
+static PaError InitializeWaveHandles( PaWinMmeHostApiRepresentation *winMmeHostApi,
+ PaWinMmeSingleDirectionHandlesAndBuffers *handlesAndBuffers,
+ unsigned long bytesPerHostSample,
+ double sampleRate, PaWinMmeDeviceAndChannelCount *devices,
+ unsigned int deviceCount, int isInput );
+static PaError TerminateWaveHandles( PaWinMmeSingleDirectionHandlesAndBuffers *handlesAndBuffers, int isInput, int currentlyProcessingAnError );
+static PaError InitializeWaveHeaders( PaWinMmeSingleDirectionHandlesAndBuffers *handlesAndBuffers,
+ unsigned long hostBufferCount,
+ PaSampleFormat hostSampleFormat,
+ unsigned long framesPerHostBuffer,
+ PaWinMmeDeviceAndChannelCount *devices,
+ int isInput );
+static void TerminateWaveHeaders( PaWinMmeSingleDirectionHandlesAndBuffers *handlesAndBuffers, int isInput );
+
+
+static void InitializeSingleDirectionHandlesAndBuffers( PaWinMmeSingleDirectionHandlesAndBuffers *handlesAndBuffers )
+{
+ handlesAndBuffers->bufferEvent = 0;
+ handlesAndBuffers->waveHandles = 0;
+ handlesAndBuffers->deviceCount = 0;
+ handlesAndBuffers->waveHeaders = 0;
+ handlesAndBuffers->bufferCount = 0;
+}
+
+static PaError InitializeWaveHandles( PaWinMmeHostApiRepresentation *winMmeHostApi,
+ PaWinMmeSingleDirectionHandlesAndBuffers *handlesAndBuffers,
+ unsigned long bytesPerHostSample,
+ double sampleRate, PaWinMmeDeviceAndChannelCount *devices,
+ unsigned int deviceCount, int isInput )
+{
+ PaError result;
+ MMRESULT mmresult;
+ unsigned long bytesPerFrame;
+ WAVEFORMATEX wfx;
+ signed int i;
+
+ /* for error cleanup we expect that InitializeSingleDirectionHandlesAndBuffers()
+ has already been called to zero some fields */
+
+ result = CreateEventWithPaError( &handlesAndBuffers->bufferEvent, NULL, FALSE, FALSE, NULL );
+ if( result != paNoError ) goto error;
+
+ if( isInput )
+ handlesAndBuffers->waveHandles = (void*)PaUtil_AllocateMemory( sizeof(HWAVEIN) * deviceCount );
+ else
+ handlesAndBuffers->waveHandles = (void*)PaUtil_AllocateMemory( sizeof(HWAVEOUT) * deviceCount );
+ if( !handlesAndBuffers->waveHandles )
+ {
+ result = paInsufficientMemory;
+ goto error;
+ }
+
+ handlesAndBuffers->deviceCount = deviceCount;
+
+ for( i = 0; i < (signed int)deviceCount; ++i )
+ {
+ if( isInput )
+ ((HWAVEIN*)handlesAndBuffers->waveHandles)[i] = 0;
+ else
+ ((HWAVEOUT*)handlesAndBuffers->waveHandles)[i] = 0;
+ }
+
+ wfx.wFormatTag = WAVE_FORMAT_PCM;
+ wfx.nSamplesPerSec = (DWORD) sampleRate;
+ wfx.cbSize = 0;
+
+ for( i = 0; i < (signed int)deviceCount; ++i )
+ {
+ UINT winMmeDeviceId;
+
+ winMmeDeviceId = LocalDeviceIndexToWinMmeDeviceId( winMmeHostApi, devices[i].device );
+ wfx.nChannels = (WORD)devices[i].channelCount;
+
+ bytesPerFrame = wfx.nChannels * bytesPerHostSample;
+
+ wfx.nAvgBytesPerSec = (DWORD)(bytesPerFrame * sampleRate);
+ wfx.nBlockAlign = (WORD)bytesPerFrame;
+ wfx.wBitsPerSample = (WORD)((bytesPerFrame/wfx.nChannels) * 8);
+
+ /* REVIEW: consider not firing an event for input when a full duplex
+ stream is being used. this would probably depend on the
+ neverDropInput flag. */
+
+ if( isInput )
+ mmresult = waveInOpen( &((HWAVEIN*)handlesAndBuffers->waveHandles)[i], winMmeDeviceId, &wfx,
+ (DWORD)handlesAndBuffers->bufferEvent, (DWORD)0, CALLBACK_EVENT );
+ else
+ mmresult = waveOutOpen( &((HWAVEOUT*)handlesAndBuffers->waveHandles)[i], winMmeDeviceId, &wfx,
+ (DWORD)handlesAndBuffers->bufferEvent, (DWORD)0, CALLBACK_EVENT );
+
+ if( mmresult != MMSYSERR_NOERROR )
+ {
+ switch( mmresult )
+ {
+ case MMSYSERR_ALLOCATED: /* Specified resource is already allocated. */
+ result = paDeviceUnavailable;
+ break;
+ case MMSYSERR_NODRIVER: /* No device driver is present. */
+ result = paDeviceUnavailable;
+ break;
+ case MMSYSERR_NOMEM: /* Unable to allocate or lock memory. */
+ result = paInsufficientMemory;
+ break;
+
+ case MMSYSERR_BADDEVICEID: /* Specified device identifier is out of range. */
+ /* falls through */
+ case WAVERR_BADFORMAT: /* Attempted to open with an unsupported waveform-audio format. */
+ /* falls through */
+ default:
+ result = paUnanticipatedHostError;
+ if( isInput )
+ {
+ PA_MME_SET_LAST_WAVEIN_ERROR( mmresult );
+ }
+ else
+ {
+ PA_MME_SET_LAST_WAVEOUT_ERROR( mmresult );
+ }
+ }
+ goto error;
+ }
+ }
+
+ return result;
+
+error:
+ TerminateWaveHandles( handlesAndBuffers, isInput, 1 /* currentlyProcessingAnError */ );
+
+ return result;
+}
+
+
+static PaError TerminateWaveHandles( PaWinMmeSingleDirectionHandlesAndBuffers *handlesAndBuffers, int isInput, int currentlyProcessingAnError )
+{
+ PaError result = paNoError;
+ MMRESULT mmresult;
+ signed int i;
+
+ if( handlesAndBuffers->waveHandles )
+ {
+ for( i = handlesAndBuffers->deviceCount-1; i >= 0; --i )
+ {
+ if( isInput )
+ {
+ if( ((HWAVEIN*)handlesAndBuffers->waveHandles)[i] )
+ mmresult = waveInClose( ((HWAVEIN*)handlesAndBuffers->waveHandles)[i] );
+ }
+ else
+ {
+ if( ((HWAVEOUT*)handlesAndBuffers->waveHandles)[i] )
+ mmresult = waveOutClose( ((HWAVEOUT*)handlesAndBuffers->waveHandles)[i] );
+ }
+
+ if( mmresult != MMSYSERR_NOERROR &&
+ !currentlyProcessingAnError ) /* don't update the error state if we're already processing an error */
+ {
+ result = paUnanticipatedHostError;
+ if( isInput )
+ {
+ PA_MME_SET_LAST_WAVEIN_ERROR( mmresult );
+ }
+ else
+ {
+ PA_MME_SET_LAST_WAVEOUT_ERROR( mmresult );
+ }
+ /* note that we don't break here, we try to continue closing devices */
+ }
+ }
+
+ PaUtil_FreeMemory( handlesAndBuffers->waveHandles );
+ handlesAndBuffers->waveHandles = 0;
+ }
+
+ if( handlesAndBuffers->bufferEvent )
+ {
+ result = CloseHandleWithPaError( handlesAndBuffers->bufferEvent );
+ handlesAndBuffers->bufferEvent = 0;
+ }
+
+ return result;
+}
+
+
+static PaError InitializeWaveHeaders( PaWinMmeSingleDirectionHandlesAndBuffers *handlesAndBuffers,
+ unsigned long hostBufferCount,
+ PaSampleFormat hostSampleFormat,
+ unsigned long framesPerHostBuffer,
+ PaWinMmeDeviceAndChannelCount *devices,
+ int isInput )
+{
+ PaError result = paNoError;
+ MMRESULT mmresult;
+ WAVEHDR *deviceWaveHeaders;
+ signed int i, j;
+
+ /* for error cleanup we expect that InitializeSingleDirectionHandlesAndBuffers()
+ has already been called to zero some fields */
+
+
+ /* allocate an array of pointers to arrays of wave headers, one array of
+ wave headers per device */
+ handlesAndBuffers->waveHeaders = (WAVEHDR**)PaUtil_AllocateMemory( sizeof(WAVEHDR*) * handlesAndBuffers->deviceCount );
+ if( !handlesAndBuffers->waveHeaders )
+ {
+ result = paInsufficientMemory;
+ goto error;
+ }
+
+ for( i = 0; i < (signed int)handlesAndBuffers->deviceCount; ++i )
+ handlesAndBuffers->waveHeaders[i] = 0;
+
+ handlesAndBuffers->bufferCount = hostBufferCount;
+
+ for( i = 0; i < (signed int)handlesAndBuffers->deviceCount; ++i )
+ {
+ int bufferBytes = Pa_GetSampleSize( hostSampleFormat ) *
+ framesPerHostBuffer * devices[i].channelCount;
+ if( bufferBytes < 0 )
+ {
+ result = paInternalError;
+ goto error;
+ }
+
+ /* Allocate an array of wave headers for device i */
+ deviceWaveHeaders = (WAVEHDR *) PaUtil_AllocateMemory( sizeof(WAVEHDR)*hostBufferCount );
+ if( !deviceWaveHeaders )
+ {
+ result = paInsufficientMemory;
+ goto error;
+ }
+
+ for( j=0; j < (signed int)hostBufferCount; ++j )
+ deviceWaveHeaders[j].lpData = 0;
+
+ handlesAndBuffers->waveHeaders[i] = deviceWaveHeaders;
+
+ /* Allocate a buffer for each wave header */
+ for( j=0; j < (signed int)hostBufferCount; ++j )
+ {
+ deviceWaveHeaders[j].lpData = (char *)PaUtil_AllocateMemory( bufferBytes );
+ if( !deviceWaveHeaders[j].lpData )
+ {
+ result = paInsufficientMemory;
+ goto error;
+ }
+ deviceWaveHeaders[j].dwBufferLength = bufferBytes;
+ deviceWaveHeaders[j].dwUser = 0xFFFFFFFF; /* indicates that *PrepareHeader() has not yet been called, for error clean up code */
+
+ if( isInput )
+ {
+ mmresult = waveInPrepareHeader( ((HWAVEIN*)handlesAndBuffers->waveHandles)[i], &deviceWaveHeaders[j], sizeof(WAVEHDR) );
+ if( mmresult != MMSYSERR_NOERROR )
+ {
+ result = paUnanticipatedHostError;
+ PA_MME_SET_LAST_WAVEIN_ERROR( mmresult );
+ goto error;
+ }
+ }
+ else /* output */
+ {
+ mmresult = waveOutPrepareHeader( ((HWAVEOUT*)handlesAndBuffers->waveHandles)[i], &deviceWaveHeaders[j], sizeof(WAVEHDR) );
+ if( mmresult != MMSYSERR_NOERROR )
+ {
+ result = paUnanticipatedHostError;
+ PA_MME_SET_LAST_WAVEIN_ERROR( mmresult );
+ goto error;
+ }
+ }
+ deviceWaveHeaders[j].dwUser = devices[i].channelCount;
+ }
+ }
+
+ return result;
+
+error:
+ TerminateWaveHeaders( handlesAndBuffers, isInput );
+
+ return result;
+}
+
+
+static void TerminateWaveHeaders( PaWinMmeSingleDirectionHandlesAndBuffers *handlesAndBuffers, int isInput )
+{
+ signed int i, j;
+ WAVEHDR *deviceWaveHeaders;
+
+ if( handlesAndBuffers->waveHeaders )
+ {
+ for( i = handlesAndBuffers->deviceCount-1; i >= 0 ; --i )
+ {
+ deviceWaveHeaders = handlesAndBuffers->waveHeaders[i]; /* wave headers for device i */
+ if( deviceWaveHeaders )
+ {
+ for( j = handlesAndBuffers->bufferCount-1; j >= 0; --j )
+ {
+ if( deviceWaveHeaders[j].lpData )
+ {
+ if( deviceWaveHeaders[j].dwUser != 0xFFFFFFFF )
+ {
+ if( isInput )
+ waveInUnprepareHeader( ((HWAVEIN*)handlesAndBuffers->waveHandles)[i], &deviceWaveHeaders[j], sizeof(WAVEHDR) );
+ else
+ waveOutUnprepareHeader( ((HWAVEOUT*)handlesAndBuffers->waveHandles)[i], &deviceWaveHeaders[j], sizeof(WAVEHDR) );
+ }
+
+ PaUtil_FreeMemory( deviceWaveHeaders[j].lpData );
+ }
+ }
+
+ PaUtil_FreeMemory( deviceWaveHeaders );
+ }
+ }
+
+ PaUtil_FreeMemory( handlesAndBuffers->waveHeaders );
+ handlesAndBuffers->waveHeaders = 0;
+ }
+}
+
+
+
+/* PaWinMmeStream - a stream data structure specifically for this implementation */
+/* note that struct PaWinMmeStream is typedeffed to PaWinMmeStream above. */
+struct PaWinMmeStream
+{
+ PaUtilStreamRepresentation streamRepresentation;
+ PaUtilCpuLoadMeasurer cpuLoadMeasurer;
+ PaUtilBufferProcessor bufferProcessor;
+
+ int primeStreamUsingCallback;
+
+ PaWinMmeSingleDirectionHandlesAndBuffers input;
+ PaWinMmeSingleDirectionHandlesAndBuffers output;
+
+ /* Processing thread management -------------- */
+ HANDLE abortEvent;
+ HANDLE processingThread;
+ DWORD processingThreadId;
+
+ char throttleProcessingThreadOnOverload; /* 0 -> don't throtte, non-0 -> throttle */
+ int processingThreadPriority;
+ int highThreadPriority;
+ int throttledThreadPriority;
+ unsigned long throttledSleepMsecs;
+
+ int isStopped;
+ volatile int isActive;
+ volatile int stopProcessing; /* stop thread once existing buffers have been returned */
+ volatile int abortProcessing; /* stop thread immediately */
+
+ DWORD allBuffersDurationMs; /* used to calculate timeouts */
+};
+
+/* updates deviceCount if PaWinMmeUseMultipleDevices is used */
+
+static PaError ValidateWinMmeSpecificStreamInfo(
+ const PaStreamParameters *streamParameters,
+ const PaWinMmeStreamInfo *streamInfo,
+ char *throttleProcessingThreadOnOverload,
+ unsigned long *deviceCount )
+{
+ if( streamInfo )
+ {
+ if( streamInfo->size != sizeof( PaWinMmeStreamInfo )
+ || streamInfo->version != 1 )
+ {
+ return paIncompatibleHostApiSpecificStreamInfo;
+ }
+
+ if( streamInfo->flags & paWinMmeDontThrottleOverloadedProcessingThread )
+ *throttleProcessingThreadOnOverload = 0;
+
+ if( streamInfo->flags & paWinMmeUseMultipleDevices )
+ {
+ if( streamParameters->device != paUseHostApiSpecificDeviceSpecification )
+ return paInvalidDevice;
+
+ *deviceCount = streamInfo->deviceCount;
+ }
+ }
+
+ return paNoError;
+}
+
+static PaError RetrieveDevicesFromStreamParameters(
+ struct PaUtilHostApiRepresentation *hostApi,
+ const PaStreamParameters *streamParameters,
+ const PaWinMmeStreamInfo *streamInfo,
+ PaWinMmeDeviceAndChannelCount *devices,
+ unsigned long deviceCount )
+{
+ PaError result = paNoError;
+ unsigned int i;
+ int totalChannelCount;
+ PaDeviceIndex hostApiDevice;
+
+ if( streamInfo && streamInfo->flags & paWinMmeUseMultipleDevices )
+ {
+ totalChannelCount = 0;
+ for( i=0; i < deviceCount; ++i )
+ {
+ /* validate that the device number is within range */
+ result = PaUtil_DeviceIndexToHostApiDeviceIndex( &hostApiDevice,
+ streamInfo->devices[i].device, hostApi );
+ if( result != paNoError )
+ return result;
+
+ devices[i].device = hostApiDevice;
+ devices[i].channelCount = streamInfo->devices[i].channelCount;
+
+ totalChannelCount += devices[i].channelCount;
+ }
+
+ if( totalChannelCount != streamParameters->channelCount )
+ {
+ /* channelCount must match total channels specified by multiple devices */
+ return paInvalidChannelCount; /* REVIEW use of this error code */
+ }
+ }
+ else
+ {
+ devices[0].device = streamParameters->device;
+ devices[0].channelCount = streamParameters->channelCount;
+ }
+
+ return result;
+}
+
+static PaError ValidateInputChannelCounts(
+ struct PaUtilHostApiRepresentation *hostApi,
+ PaWinMmeDeviceAndChannelCount *devices,
+ unsigned long deviceCount )
+{
+ unsigned int i;
+
+ for( i=0; i < deviceCount; ++i )
+ {
+ if( devices[i].channelCount < 1 || devices[i].channelCount
+ > hostApi->deviceInfos[ devices[i].device ]->maxInputChannels )
+ return paInvalidChannelCount;
+ }
+
+ return paNoError;
+}
+
+static PaError ValidateOutputChannelCounts(
+ struct PaUtilHostApiRepresentation *hostApi,
+ PaWinMmeDeviceAndChannelCount *devices,
+ unsigned long deviceCount )
+{
+ unsigned int i;
+
+ for( i=0; i < deviceCount; ++i )
+ {
+ if( devices[i].channelCount < 1 || devices[i].channelCount
+ > hostApi->deviceInfos[ devices[i].device ]->maxOutputChannels )
+ return paInvalidChannelCount;
+ }
+
+ return paNoError;
+}
+
+
+/* the following macros are intended to improve the readability of the following code */
+#define PA_IS_INPUT_STREAM_( stream ) ( stream ->input.waveHandles )
+#define PA_IS_OUTPUT_STREAM_( stream ) ( stream ->output.waveHandles )
+#define PA_IS_FULL_DUPLEX_STREAM_( stream ) ( stream ->input.waveHandles && stream ->output.waveHandles )
+#define PA_IS_HALF_DUPLEX_STREAM_( stream ) ( !(stream ->input.waveHandles && stream ->output.waveHandles) )
+
+static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
+ PaStream** s,
+ const PaStreamParameters *inputParameters,
+ const PaStreamParameters *outputParameters,
+ double sampleRate,
+ unsigned long framesPerBuffer,
+ PaStreamFlags streamFlags,
+ PaStreamCallback *streamCallback,
+ void *userData )
+{
+ PaError result;
+ PaWinMmeHostApiRepresentation *winMmeHostApi = (PaWinMmeHostApiRepresentation*)hostApi;
+ PaWinMmeStream *stream = 0;
+ int bufferProcessorIsInitialized = 0;
+ int streamRepresentationIsInitialized = 0;
+ PaSampleFormat hostInputSampleFormat, hostOutputSampleFormat;
+ int inputChannelCount, outputChannelCount;
+ PaSampleFormat inputSampleFormat, outputSampleFormat;
+ double suggestedInputLatency, suggestedOutputLatency;
+ PaWinMmeStreamInfo *inputStreamInfo, *outputStreamInfo;
+ unsigned long framesPerHostInputBuffer;
+ unsigned long hostInputBufferCount;
+ unsigned long framesPerHostOutputBuffer;
+ unsigned long hostOutputBufferCount;
+ unsigned long framesPerBufferProcessorCall;
+ PaWinMmeDeviceAndChannelCount *inputDevices = 0; /* contains all devices and channel counts as local host api ids, even when PaWinMmeUseMultipleDevices is not used */
+ unsigned long inputDeviceCount = 0;
+ PaWinMmeDeviceAndChannelCount *outputDevices = 0;
+ unsigned long outputDeviceCount = 0; /* contains all devices and channel counts as local host api ids, even when PaWinMmeUseMultipleDevices is not used */
+ char throttleProcessingThreadOnOverload = 1;
+
+
+ if( inputParameters )
+ {
+ inputChannelCount = inputParameters->channelCount;
+ inputSampleFormat = inputParameters->sampleFormat;
+ suggestedInputLatency = inputParameters->suggestedLatency;
+
+ inputDeviceCount = 1;
+
+ /* validate input hostApiSpecificStreamInfo */
+ inputStreamInfo = (PaWinMmeStreamInfo*)inputParameters->hostApiSpecificStreamInfo;
+ result = ValidateWinMmeSpecificStreamInfo( inputParameters, inputStreamInfo,
+ &throttleProcessingThreadOnOverload,
+ &inputDeviceCount );
+ if( result != paNoError ) return result;
+
+ inputDevices = (PaWinMmeDeviceAndChannelCount*)alloca( sizeof(PaWinMmeDeviceAndChannelCount) * inputDeviceCount );
+ if( !inputDevices ) return paInsufficientMemory;
+
+ result = RetrieveDevicesFromStreamParameters( hostApi, inputParameters, inputStreamInfo, inputDevices, inputDeviceCount );
+ if( result != paNoError ) return result;
+
+ result = ValidateInputChannelCounts( hostApi, inputDevices, inputDeviceCount );
+ if( result != paNoError ) return result;
+
+ hostInputSampleFormat =
+ PaUtil_SelectClosestAvailableFormat( paInt16 /* native formats */, inputSampleFormat );
+ }
+ else
+ {
+ inputChannelCount = 0;
+ inputSampleFormat = 0;
+ suggestedInputLatency = 0.;
+ inputStreamInfo = 0;
+ hostInputSampleFormat = 0;
+ }
+
+
+ if( outputParameters )
+ {
+ outputChannelCount = outputParameters->channelCount;
+ outputSampleFormat = outputParameters->sampleFormat;
+ suggestedOutputLatency = outputParameters->suggestedLatency;
+
+ outputDeviceCount = 1;
+
+ /* validate output hostApiSpecificStreamInfo */
+ outputStreamInfo = (PaWinMmeStreamInfo*)outputParameters->hostApiSpecificStreamInfo;
+ result = ValidateWinMmeSpecificStreamInfo( outputParameters, outputStreamInfo,
+ &throttleProcessingThreadOnOverload,
+ &outputDeviceCount );
+ if( result != paNoError ) return result;
+
+ outputDevices = (PaWinMmeDeviceAndChannelCount*)alloca( sizeof(PaWinMmeDeviceAndChannelCount) * outputDeviceCount );
+ if( !outputDevices ) return paInsufficientMemory;
+
+ result = RetrieveDevicesFromStreamParameters( hostApi, outputParameters, outputStreamInfo, outputDevices, outputDeviceCount );
+ if( result != paNoError ) return result;
+
+ result = ValidateOutputChannelCounts( hostApi, outputDevices, outputDeviceCount );
+ if( result != paNoError ) return result;
+
+ hostOutputSampleFormat =
+ PaUtil_SelectClosestAvailableFormat( paInt16 /* native formats */, outputSampleFormat );
+ }
+ else
+ {
+ outputChannelCount = 0;
+ outputSampleFormat = 0;
+ outputStreamInfo = 0;
+ hostOutputSampleFormat = 0;
+ suggestedOutputLatency = 0.;
+ }
+
+
+ /*
+ IMPLEMENT ME:
+ - alter sampleRate to a close allowable rate if possible / necessary
+ */
+
+
+ /* validate platform specific flags */
+ if( (streamFlags & paPlatformSpecificFlags) != 0 )
+ return paInvalidFlag; /* unexpected platform specific flag */
+
+
+ result = CalculateBufferSettings( &framesPerHostInputBuffer, &hostInputBufferCount,
+ &framesPerHostOutputBuffer, &hostOutputBufferCount,
+ inputChannelCount, hostInputSampleFormat, suggestedInputLatency, inputStreamInfo,
+ outputChannelCount, hostOutputSampleFormat, suggestedOutputLatency, outputStreamInfo,
+ sampleRate, framesPerBuffer );
+ if( result != paNoError ) goto error;
+
+
+ stream = (PaWinMmeStream*)PaUtil_AllocateMemory( sizeof(PaWinMmeStream) );
+ if( !stream )
+ {
+ result = paInsufficientMemory;
+ goto error;
+ }
+
+ InitializeSingleDirectionHandlesAndBuffers( &stream->input );
+ InitializeSingleDirectionHandlesAndBuffers( &stream->output );
+
+ stream->abortEvent = 0;
+ stream->processingThread = 0;
+
+ stream->throttleProcessingThreadOnOverload = throttleProcessingThreadOnOverload;
+
+ PaUtil_InitializeStreamRepresentation( &stream->streamRepresentation,
+ ( (streamCallback)
+ ? &winMmeHostApi->callbackStreamInterface
+ : &winMmeHostApi->blockingStreamInterface ),
+ streamCallback, userData );
+ streamRepresentationIsInitialized = 1;
+
+ PaUtil_InitializeCpuLoadMeasurer( &stream->cpuLoadMeasurer, sampleRate );
+
+
+ if( inputParameters && outputParameters ) /* full duplex */
+ {
+ if( framesPerHostInputBuffer < framesPerHostOutputBuffer )
+ {
+ assert( (framesPerHostOutputBuffer % framesPerHostInputBuffer) == 0 ); /* CalculateBufferSettings() should guarantee this condition */
+
+ framesPerBufferProcessorCall = framesPerHostInputBuffer;
+ }
+ else
+ {
+ assert( (framesPerHostInputBuffer % framesPerHostOutputBuffer) == 0 ); /* CalculateBufferSettings() should guarantee this condition */
+
+ framesPerBufferProcessorCall = framesPerHostOutputBuffer;
+ }
+ }
+ else if( inputParameters )
+ {
+ framesPerBufferProcessorCall = framesPerHostInputBuffer;
+ }
+ else if( outputParameters )
+ {
+ framesPerBufferProcessorCall = framesPerHostOutputBuffer;
+ }
+
+ stream->input.framesPerBuffer = framesPerHostInputBuffer;
+ stream->output.framesPerBuffer = framesPerHostOutputBuffer;
+
+ result = PaUtil_InitializeBufferProcessor( &stream->bufferProcessor,
+ inputChannelCount, inputSampleFormat, hostInputSampleFormat,
+ outputChannelCount, outputSampleFormat, hostOutputSampleFormat,
+ sampleRate, streamFlags, framesPerBuffer,
+ framesPerBufferProcessorCall, paUtilFixedHostBufferSize,
+ streamCallback, userData );
+ if( result != paNoError ) goto error;
+
+ bufferProcessorIsInitialized = 1;
+
+ stream->streamRepresentation.streamInfo.inputLatency =
+ (double)(PaUtil_GetBufferProcessorInputLatency(&stream->bufferProcessor)
+ +(framesPerHostInputBuffer * (hostInputBufferCount-1))) / sampleRate;
+ stream->streamRepresentation.streamInfo.outputLatency =
+ (double)(PaUtil_GetBufferProcessorOutputLatency(&stream->bufferProcessor)
+ +(framesPerHostOutputBuffer * (hostOutputBufferCount-1))) / sampleRate;
+ stream->streamRepresentation.streamInfo.sampleRate = sampleRate;
+
+ stream->primeStreamUsingCallback = ( (streamFlags&paPrimeOutputBuffersUsingStreamCallback) && streamCallback ) ? 1 : 0;
+
+ /* time to sleep when throttling due to >100% cpu usage.
+ -a quater of a buffer's duration */
+ stream->throttledSleepMsecs =
+ (unsigned long)(stream->bufferProcessor.framesPerHostBuffer *
+ stream->bufferProcessor.samplePeriod * .25 * 1000);
+
+ stream->isStopped = 1;
+ stream->isActive = 0;
+
+
+ /* for maximum compatibility with multi-device multichannel drivers,
+ we first open all devices, then we prepare all buffers, finally
+ we start all devices ( in StartStream() ). teardown in reverse order.
+ */
+
+ if( inputParameters )
+ {
+ result = InitializeWaveHandles( winMmeHostApi, &stream->input,
+ stream->bufferProcessor.bytesPerHostInputSample, sampleRate,
+ inputDevices, inputDeviceCount, 1 /* isInput */ );
+ if( result != paNoError ) goto error;
+ }
+
+ if( outputParameters )
+ {
+ result = InitializeWaveHandles( winMmeHostApi, &stream->output,
+ stream->bufferProcessor.bytesPerHostOutputSample, sampleRate,
+ outputDevices, outputDeviceCount, 0 /* isInput */ );
+ if( result != paNoError ) goto error;
+ }
+
+ if( inputParameters )
+ {
+ result = InitializeWaveHeaders( &stream->input, hostInputBufferCount,
+ hostInputSampleFormat, framesPerHostInputBuffer, inputDevices, 1 /* isInput */ );
+ if( result != paNoError ) goto error;
+ }
+
+ if( outputParameters )
+ {
+ result = InitializeWaveHeaders( &stream->output, hostOutputBufferCount,
+ hostOutputSampleFormat, framesPerHostOutputBuffer, outputDevices, 0 /* not isInput */ );
+ if( result != paNoError ) goto error;
+
+ stream->allBuffersDurationMs = (DWORD) (1000.0 * (framesPerHostOutputBuffer * stream->output.bufferCount) / sampleRate);
+ }
+ else
+ {
+ stream->allBuffersDurationMs = (DWORD) (1000.0 * (framesPerHostInputBuffer * stream->input.bufferCount) / sampleRate);
+ }
+
+
+ if( streamCallback )
+ {
+ /* abort event is only needed for callback streams */
+ result = CreateEventWithPaError( &stream->abortEvent, NULL, TRUE, FALSE, NULL );
+ if( result != paNoError ) goto error;
+ }
+
+ *s = (PaStream*)stream;
+
+ return result;
+
+error:
+
+ if( stream )
+ {
+ if( stream->abortEvent )
+ CloseHandle( stream->abortEvent );
+
+ TerminateWaveHeaders( &stream->output, 0 /* not isInput */ );
+ TerminateWaveHeaders( &stream->input, 1 /* isInput */ );
+
+ TerminateWaveHandles( &stream->output, 0 /* not isInput */, 1 /* currentlyProcessingAnError */ );
+ TerminateWaveHandles( &stream->input, 1 /* isInput */, 1 /* currentlyProcessingAnError */ );
+
+ if( bufferProcessorIsInitialized )
+ PaUtil_TerminateBufferProcessor( &stream->bufferProcessor );
+
+ if( streamRepresentationIsInitialized )
+ PaUtil_TerminateStreamRepresentation( &stream->streamRepresentation );
+
+ PaUtil_FreeMemory( stream );
+ }
+
+ return result;
+}
+
+
+/* return non-zero if all current buffers are done */
+static int BuffersAreDone( WAVEHDR **waveHeaders, unsigned int deviceCount, int bufferIndex )
+{
+ unsigned int i;
+
+ for( i=0; i < deviceCount; ++i )
+ {
+ if( !(waveHeaders[i][ bufferIndex ].dwFlags & WHDR_DONE) )
+ {
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+static int CurrentInputBuffersAreDone( PaWinMmeStream *stream )
+{
+ return BuffersAreDone( stream->input.waveHeaders, stream->input.deviceCount, stream->input.currentBufferIndex );
+}
+
+static int CurrentOutputBuffersAreDone( PaWinMmeStream *stream )
+{
+ return BuffersAreDone( stream->output.waveHeaders, stream->output.deviceCount, stream->output.currentBufferIndex );
+}
+
+
+/* return non-zero if any buffers are queued */
+static int NoBuffersAreQueued( PaWinMmeSingleDirectionHandlesAndBuffers *handlesAndBuffers )
+{
+ unsigned int i, j;
+
+ if( handlesAndBuffers->waveHandles )
+ {
+ for( i=0; i < handlesAndBuffers->bufferCount; ++i )
+ {
+ for( j=0; j < handlesAndBuffers->deviceCount; ++j )
+ {
+ if( !( handlesAndBuffers->waveHeaders[ j ][ i ].dwFlags & WHDR_DONE) )
+ {
+ return 0;
+ }
+ }
+ }
+ }
+
+ return 1;
+}
+
+
+#define PA_CIRCULAR_INCREMENT_( current, max )\
+ ( (((current) + 1) >= (max)) ? (0) : (current+1) )
+
+#define PA_CIRCULAR_DECREMENT_( current, max )\
+ ( ((current) == 0) ? ((max)-1) : (current-1) )
+
+
+static signed long GetAvailableFrames( PaWinMmeSingleDirectionHandlesAndBuffers *handlesAndBuffers )
+{
+ signed long result = 0;
+ unsigned int i;
+
+ if( BuffersAreDone( handlesAndBuffers->waveHeaders, handlesAndBuffers->deviceCount, handlesAndBuffers->currentBufferIndex ) )
+ {
+ /* we could calculate the following in O(1) if we kept track of the
+ last done buffer */
+ result = handlesAndBuffers->framesPerBuffer - handlesAndBuffers->framesUsedInCurrentBuffer;
+
+ i = PA_CIRCULAR_INCREMENT_( handlesAndBuffers->currentBufferIndex, handlesAndBuffers->bufferCount );
+ while( i != handlesAndBuffers->currentBufferIndex )
+ {
+ if( BuffersAreDone( handlesAndBuffers->waveHeaders, handlesAndBuffers->deviceCount, i ) )
+ {
+ result += handlesAndBuffers->framesPerBuffer;
+ i = PA_CIRCULAR_INCREMENT_( i, handlesAndBuffers->bufferCount );
+ }
+ else
+ break;
+ }
+ }
+
+ return result;
+}
+
+
+static PaError AdvanceToNextInputBuffer( PaWinMmeStream *stream )
+{
+ PaError result = paNoError;
+ MMRESULT mmresult;
+ unsigned int i;
+
+ for( i=0; i < stream->input.deviceCount; ++i )
+ {
+ mmresult = waveInAddBuffer( ((HWAVEIN*)stream->input.waveHandles)[i],
+ &stream->input.waveHeaders[i][ stream->input.currentBufferIndex ],
+ sizeof(WAVEHDR) );
+ if( mmresult != MMSYSERR_NOERROR )
+ {
+ result = paUnanticipatedHostError;
+ PA_MME_SET_LAST_WAVEIN_ERROR( mmresult );
+ }
+ }
+
+ stream->input.currentBufferIndex =
+ PA_CIRCULAR_INCREMENT_( stream->input.currentBufferIndex, stream->input.bufferCount );
+
+ stream->input.framesUsedInCurrentBuffer = 0;
+
+ return result;
+}
+
+
+static PaError AdvanceToNextOutputBuffer( PaWinMmeStream *stream )
+{
+ PaError result = paNoError;
+ MMRESULT mmresult;
+ unsigned int i;
+
+ for( i=0; i < stream->output.deviceCount; ++i )
+ {
+ mmresult = waveOutWrite( ((HWAVEOUT*)stream->output.waveHandles)[i],
+ &stream->output.waveHeaders[i][ stream->output.currentBufferIndex ],
+ sizeof(WAVEHDR) );
+ if( mmresult != MMSYSERR_NOERROR )
+ {
+ result = paUnanticipatedHostError;
+ PA_MME_SET_LAST_WAVEOUT_ERROR( mmresult );
+ }
+ }
+
+ stream->output.currentBufferIndex =
+ PA_CIRCULAR_INCREMENT_( stream->output.currentBufferIndex, stream->output.bufferCount );
+
+ stream->output.framesUsedInCurrentBuffer = 0;
+
+ return result;
+}
+
+
+/* requeue all but the most recent input with the driver. Used for catching
+ up after a total input buffer underrun */
+static PaError CatchUpInputBuffers( PaWinMmeStream *stream )
+{
+ PaError result = paNoError;
+ unsigned int i;
+
+ for( i=0; i < stream->input.bufferCount - 1; ++i )
+ {
+ result = AdvanceToNextInputBuffer( stream );
+ if( result != paNoError )
+ break;
+ }
+
+ return result;
+}
+
+
+/* take the most recent output and duplicate it to all other output buffers
+ and requeue them. Used for catching up after a total output buffer underrun.
+*/
+static PaError CatchUpOutputBuffers( PaWinMmeStream *stream )
+{
+ PaError result = paNoError;
+ unsigned int i, j;
+ unsigned int previousBufferIndex =
+ PA_CIRCULAR_DECREMENT_( stream->output.currentBufferIndex, stream->output.bufferCount );
+
+ for( i=0; i < stream->output.bufferCount - 1; ++i )
+ {
+ for( j=0; j < stream->output.deviceCount; ++j )
+ {
+ if( stream->output.waveHeaders[j][ stream->output.currentBufferIndex ].lpData
+ != stream->output.waveHeaders[j][ previousBufferIndex ].lpData )
+ {
+ CopyMemory( stream->output.waveHeaders[j][ stream->output.currentBufferIndex ].lpData,
+ stream->output.waveHeaders[j][ previousBufferIndex ].lpData,
+ stream->output.waveHeaders[j][ stream->output.currentBufferIndex ].dwBufferLength );
+ }
+ }
+
+ result = AdvanceToNextOutputBuffer( stream );
+ if( result != paNoError )
+ break;
+ }
+
+ return result;
+}
+
+
+static DWORD WINAPI ProcessingThreadProc( void *pArg )
+{
+ PaWinMmeStream *stream = (PaWinMmeStream *)pArg;
+ HANDLE events[3];
+ int eventCount = 0;
+ DWORD result = paNoError;
+ DWORD waitResult;
+ DWORD timeout = (unsigned long)(stream->allBuffersDurationMs * 0.5);
+ int hostBuffersAvailable;
+ signed int hostInputBufferIndex, hostOutputBufferIndex;
+ PaStreamCallbackFlags statusFlags;
+ int callbackResult;
+ int done = 0;
+ unsigned int channel, i;
+ unsigned long framesProcessed;
+
+ /* prepare event array for call to WaitForMultipleObjects() */
+ if( stream->input.bufferEvent )
+ events[eventCount++] = stream->input.bufferEvent;
+ if( stream->output.bufferEvent )
+ events[eventCount++] = stream->output.bufferEvent;
+ events[eventCount++] = stream->abortEvent;
+
+ statusFlags = 0; /** @todo support paInputUnderflow, paOutputOverflow and paNeverDropInput */
+
+ /* loop until something causes us to stop */
+ do{
+ /* wait for MME to signal that a buffer is available, or for
+ the PA abort event to be signaled.
+
+ When this indicates that one or more buffers are available
+ NoBuffersAreQueued() and Current*BuffersAreDone are used below to
+ poll for additional done buffers. NoBuffersAreQueued() will fail
+ to identify an underrun/overflow if the driver doesn't mark all done
+ buffers prior to signalling the event. Some drivers do this
+ (eg RME Digi96, and others don't eg VIA PC 97 input). This isn't a
+ huge problem, it just means that we won't always be able to detect
+ underflow/overflow.
+ */
+ waitResult = WaitForMultipleObjects( eventCount, events, FALSE /* wait all = FALSE */, timeout );
+ if( waitResult == WAIT_FAILED )
+ {
+ result = paUnanticipatedHostError;
+ /** @todo FIXME/REVIEW: can't return host error info from an asyncronous thread */
+ done = 1;
+ }
+ else if( waitResult == WAIT_TIMEOUT )
+ {
+ /* if a timeout is encountered, continue */
+ }
+
+ if( stream->abortProcessing )
+ {
+ /* Pa_AbortStream() has been called, stop processing immediately */
+ done = 1;
+ }
+ else if( stream->stopProcessing )
+ {
+ /* Pa_StopStream() has been called or the user callback returned
+ non-zero, processing will continue until all output buffers
+ are marked as done. The stream will stop immediately if it
+ is input-only.
+ */
+
+ if( PA_IS_OUTPUT_STREAM_(stream) )
+ {
+ if( NoBuffersAreQueued( &stream->output ) )
+ done = 1; /* Will cause thread to return. */
+ }
+ else
+ {
+ /* input only stream */
+ done = 1; /* Will cause thread to return. */
+ }
+ }
+ else
+ {
+ hostBuffersAvailable = 1;
+
+ /* process all available host buffers */
+ do
+ {
+ hostInputBufferIndex = -1;
+ hostOutputBufferIndex = -1;
+
+ if( PA_IS_INPUT_STREAM_(stream) )
+ {
+ if( CurrentInputBuffersAreDone( stream ) )
+ {
+ if( NoBuffersAreQueued( &stream->input ) )
+ {
+ /** @todo
+ if all of the other buffers are also ready then
+ we discard all but the most recent. This is an
+ input buffer overflow. FIXME: these buffers should
+ be passed to the callback in a paNeverDropInput
+ stream.
+
+ note that it is also possible for an input overflow
+ to happen while the callback is processing a buffer.
+ that is handled further down.
+ */
+ result = CatchUpInputBuffers( stream );
+ if( result != paNoError )
+ done = 1;
+
+ statusFlags |= paInputOverflow;
+ }
+
+ hostInputBufferIndex = stream->input.currentBufferIndex;
+ }
+ }
+
+ if( PA_IS_OUTPUT_STREAM_(stream) )
+ {
+ if( CurrentOutputBuffersAreDone( stream ) )
+ {
+ /* ok, we have an output buffer */
+
+ if( NoBuffersAreQueued( &stream->output ) )
+ {
+ /*
+ if all of the other buffers are also ready, catch up by copying
+ the most recently generated buffer into all but one of the output
+ buffers.
+
+ note that this catch up code only handles the case where all
+ buffers have been played out due to this thread not having
+ woken up at all. a more common case occurs when this thread
+ is woken up, processes one buffer, but takes too long, and as
+ a result all the other buffers have become un-queued. that
+ case is handled further down.
+ */
+
+ result = CatchUpOutputBuffers( stream );
+ if( result != paNoError )
+ done = 1;
+
+ statusFlags |= paOutputUnderflow;
+ }
+
+ hostOutputBufferIndex = stream->output.currentBufferIndex;
+ }
+ }
+
+
+ if( (PA_IS_FULL_DUPLEX_STREAM_(stream) && hostInputBufferIndex != -1 && hostOutputBufferIndex != -1) ||
+ (PA_IS_HALF_DUPLEX_STREAM_(stream) && ( hostInputBufferIndex != -1 || hostOutputBufferIndex != -1 ) ) )
+ {
+ PaStreamCallbackTimeInfo timeInfo = {0,0,0}; /** @todo implement inputBufferAdcTime */
+
+
+ if( PA_IS_OUTPUT_STREAM_(stream) )
+ {
+ /* set timeInfo.currentTime and calculate timeInfo.outputBufferDacTime
+ from the current wave out position */
+ MMTIME mmtime;
+ double timeBeforeGetPosition, timeAfterGetPosition;
+ double time;
+ long framesInBufferRing;
+ long writePosition;
+ long playbackPosition;
+ HWAVEOUT firstWaveOutDevice = ((HWAVEOUT*)stream->output.waveHandles)[0];
+
+ mmtime.wType = TIME_SAMPLES;
+ timeBeforeGetPosition = PaUtil_GetTime();
+ waveOutGetPosition( firstWaveOutDevice, &mmtime, sizeof(MMTIME) );
+ timeAfterGetPosition = PaUtil_GetTime();
+
+ timeInfo.currentTime = timeAfterGetPosition;
+
+ /* approximate time at which wave out position was measured
+ as half way between timeBeforeGetPosition and timeAfterGetPosition */
+ time = timeBeforeGetPosition + (timeAfterGetPosition - timeBeforeGetPosition) * .5;
+
+ framesInBufferRing = stream->output.bufferCount * stream->bufferProcessor.framesPerHostBuffer;
+ playbackPosition = mmtime.u.sample % framesInBufferRing;
+
+ writePosition = stream->output.currentBufferIndex * stream->bufferProcessor.framesPerHostBuffer
+ + stream->output.framesUsedInCurrentBuffer;
+
+ if( playbackPosition >= writePosition ){
+ timeInfo.outputBufferDacTime =
+ time + ((double)( writePosition + (framesInBufferRing - playbackPosition) ) * stream->bufferProcessor.samplePeriod );
+ }else{
+ timeInfo.outputBufferDacTime =
+ time + ((double)( writePosition - playbackPosition ) * stream->bufferProcessor.samplePeriod );
+ }
+ }
+
+
+ PaUtil_BeginCpuLoadMeasurement( &stream->cpuLoadMeasurer );
+
+ PaUtil_BeginBufferProcessing( &stream->bufferProcessor, &timeInfo, statusFlags );
+
+ /* reset status flags once they have been passed to the buffer processor */
+ statusFlags = 0;
+
+ if( PA_IS_INPUT_STREAM_(stream) )
+ {
+ PaUtil_SetInputFrameCount( &stream->bufferProcessor, 0 /* default to host buffer size */ );
+
+ channel = 0;
+ for( i=0; i<stream->input.deviceCount; ++i )
+ {
+ /* we have stored the number of channels in the buffer in dwUser */
+ int channelCount = stream->input.waveHeaders[i][ hostInputBufferIndex ].dwUser;
+
+ PaUtil_SetInterleavedInputChannels( &stream->bufferProcessor, channel,
+ stream->input.waveHeaders[i][ hostInputBufferIndex ].lpData +
+ stream->input.framesUsedInCurrentBuffer * channelCount *
+ stream->bufferProcessor.bytesPerHostInputSample,
+ channelCount );
+
+
+ channel += channelCount;
+ }
+ }
+
+ if( PA_IS_OUTPUT_STREAM_(stream) )
+ {
+ PaUtil_SetOutputFrameCount( &stream->bufferProcessor, 0 /* default to host buffer size */ );
+
+ channel = 0;
+ for( i=0; i<stream->output.deviceCount; ++i )
+ {
+ /* we have stored the number of channels in the buffer in dwUser */
+ int channelCount = stream->output.waveHeaders[i][ hostOutputBufferIndex ].dwUser;
+
+ PaUtil_SetInterleavedOutputChannels( &stream->bufferProcessor, channel,
+ stream->output.waveHeaders[i][ hostOutputBufferIndex ].lpData +
+ stream->output.framesUsedInCurrentBuffer * channelCount *
+ stream->bufferProcessor.bytesPerHostOutputSample,
+ channelCount );
+
+ channel += channelCount;
+ }
+ }
+
+ callbackResult = paContinue;
+ framesProcessed = PaUtil_EndBufferProcessing( &stream->bufferProcessor, &callbackResult );
+
+ stream->input.framesUsedInCurrentBuffer += framesProcessed;
+ stream->output.framesUsedInCurrentBuffer += framesProcessed;
+
+ PaUtil_EndCpuLoadMeasurement( &stream->cpuLoadMeasurer, framesProcessed );
+
+ if( callbackResult == paContinue )
+ {
+ /* nothing special to do */
+ }
+ else if( callbackResult == paAbort )
+ {
+ stream->abortProcessing = 1;
+ done = 1;
+ /** @todo FIXME: should probably reset the output device immediately once the callback returns paAbort */
+ result = paNoError;
+ }
+ else
+ {
+ /* User callback has asked us to stop with paComplete or other non-zero value */
+ stream->stopProcessing = 1; /* stop once currently queued audio has finished */
+ result = paNoError;
+ }
+
+
+ if( PA_IS_INPUT_STREAM_(stream)
+ && stream->stopProcessing == 0 && stream->abortProcessing == 0
+ && stream->input.framesUsedInCurrentBuffer == stream->input.framesPerBuffer )
+ {
+ if( NoBuffersAreQueued( &stream->input ) )
+ {
+ /** @todo need to handle PaNeverDropInput here where necessary */
+ result = CatchUpInputBuffers( stream );
+ if( result != paNoError )
+ done = 1;
+
+ statusFlags |= paInputOverflow;
+ }
+
+ result = AdvanceToNextInputBuffer( stream );
+ if( result != paNoError )
+ done = 1;
+ }
+
+
+ if( PA_IS_OUTPUT_STREAM_(stream) && !stream->abortProcessing )
+ {
+ if( stream->stopProcessing &&
+ stream->output.framesUsedInCurrentBuffer < stream->output.framesPerBuffer )
+ {
+ /* zero remaining samples in output output buffer and flush */
+
+ stream->output.framesUsedInCurrentBuffer += PaUtil_ZeroOutput( &stream->bufferProcessor,
+ stream->output.framesPerBuffer - stream->output.framesUsedInCurrentBuffer );
+
+ /* we send the entire buffer to the output devices, but we could
+ just send a partial buffer, rather than zeroing the unused
+ samples.
+ */
+ }
+
+ if( stream->output.framesUsedInCurrentBuffer == stream->output.framesPerBuffer )
+ {
+ /* check for underflow before enquing the just-generated buffer,
+ but recover from underflow after enquing it. This ensures
+ that the most recent audio segment is repeated */
+ int outputUnderflow = NoBuffersAreQueued( &stream->output );
+
+ result = AdvanceToNextOutputBuffer( stream );
+ if( result != paNoError )
+ done = 1;
+
+ if( outputUnderflow && !done && !stream->stopProcessing )
+ {
+ /* Recover from underflow in the case where the
+ underflow occured while processing the buffer
+ we just finished */
+
+ result = CatchUpOutputBuffers( stream );
+ if( result != paNoError )
+ done = 1;
+
+ statusFlags |= paOutputUnderflow;
+ }
+ }
+ }
+
+ if( stream->throttleProcessingThreadOnOverload != 0 )
+ {
+ if( stream->stopProcessing || stream->abortProcessing )
+ {
+ if( stream->processingThreadPriority != stream->highThreadPriority )
+ {
+ SetThreadPriority( stream->processingThread, stream->highThreadPriority );
+ stream->processingThreadPriority = stream->highThreadPriority;
+ }
+ }
+ else if( PaUtil_GetCpuLoad( &stream->cpuLoadMeasurer ) > 1. )
+ {
+ if( stream->processingThreadPriority != stream->throttledThreadPriority )
+ {
+ SetThreadPriority( stream->processingThread, stream->throttledThreadPriority );
+ stream->processingThreadPriority = stream->throttledThreadPriority;
+ }
+
+ /* sleep to give other processes a go */
+ Sleep( stream->throttledSleepMsecs );
+ }
+ else
+ {
+ if( stream->processingThreadPriority != stream->highThreadPriority )
+ {
+ SetThreadPriority( stream->processingThread, stream->highThreadPriority );
+ stream->processingThreadPriority = stream->highThreadPriority;
+ }
+ }
+ }
+ }
+ else
+ {
+ hostBuffersAvailable = 0;
+ }
+ }
+ while( hostBuffersAvailable &&
+ stream->stopProcessing == 0 &&
+ stream->abortProcessing == 0 &&
+ !done );
+ }
+ }
+ while( !done );
+
+ stream->isActive = 0;
+
+ if( stream->streamRepresentation.streamFinishedCallback != 0 )
+ stream->streamRepresentation.streamFinishedCallback( stream->streamRepresentation.userData );
+
+ PaUtil_ResetCpuLoadMeasurer( &stream->cpuLoadMeasurer );
+
+ return result;
+}
+
+
+/*
+ When CloseStream() is called, the multi-api layer ensures that
+ the stream has already been stopped or aborted.
+*/
+static PaError CloseStream( PaStream* s )
+{
+ PaError result;
+ PaWinMmeStream *stream = (PaWinMmeStream*)s;
+
+ result = CloseHandleWithPaError( stream->abortEvent );
+ if( result != paNoError ) goto error;
+
+ TerminateWaveHeaders( &stream->output, 0 /* not isInput */ );
+ TerminateWaveHeaders( &stream->input, 1 /* isInput */ );
+
+ TerminateWaveHandles( &stream->output, 0 /* not isInput */, 0 /* not currentlyProcessingAnError */ );
+ TerminateWaveHandles( &stream->input, 1 /* isInput */, 0 /* not currentlyProcessingAnError */ );
+
+ PaUtil_TerminateBufferProcessor( &stream->bufferProcessor );
+ PaUtil_TerminateStreamRepresentation( &stream->streamRepresentation );
+ PaUtil_FreeMemory( stream );
+
+error:
+ /** @todo REVIEW: what is the best way to clean up a stream if an error is detected? */
+ return result;
+}
+
+
+static PaError StartStream( PaStream *s )
+{
+ PaError result;
+ PaWinMmeStream *stream = (PaWinMmeStream*)s;
+ MMRESULT mmresult;
+ unsigned int i, j;
+ int callbackResult;
+ unsigned int channel;
+ unsigned long framesProcessed;
+ PaStreamCallbackTimeInfo timeInfo = {0,0,0}; /** @todo implement this for stream priming */
+
+ PaUtil_ResetBufferProcessor( &stream->bufferProcessor );
+
+ if( PA_IS_INPUT_STREAM_(stream) )
+ {
+ for( i=0; i<stream->input.bufferCount; ++i )
+ {
+ for( j=0; j<stream->input.deviceCount; ++j )
+ {
+ mmresult = waveInAddBuffer( ((HWAVEIN*)stream->input.waveHandles)[j], &stream->input.waveHeaders[j][i], sizeof(WAVEHDR) );
+ if( mmresult != MMSYSERR_NOERROR )
+ {
+ result = paUnanticipatedHostError;
+ PA_MME_SET_LAST_WAVEIN_ERROR( mmresult );
+ goto error;
+ }
+ }
+ }
+ stream->input.currentBufferIndex = 0;
+ stream->input.framesUsedInCurrentBuffer = 0;
+ }
+
+ if( PA_IS_OUTPUT_STREAM_(stream) )
+ {
+ for( i=0; i<stream->output.deviceCount; ++i )
+ {
+ if( (mmresult = waveOutPause( ((HWAVEOUT*)stream->output.waveHandles)[i] )) != MMSYSERR_NOERROR )
+ {
+ result = paUnanticipatedHostError;
+ PA_MME_SET_LAST_WAVEOUT_ERROR( mmresult );
+ goto error;
+ }
+ }
+
+ for( i=0; i<stream->output.bufferCount; ++i )
+ {
+ if( stream->primeStreamUsingCallback )
+ {
+
+ stream->output.framesUsedInCurrentBuffer = 0;
+ do{
+
+ PaUtil_BeginBufferProcessing( &stream->bufferProcessor,
+ &timeInfo,
+ paPrimingOutput | ((stream->input.bufferCount > 0 ) ? paInputUnderflow : 0));
+
+ if( stream->input.bufferCount > 0 )
+ PaUtil_SetNoInput( &stream->bufferProcessor );
+
+ PaUtil_SetOutputFrameCount( &stream->bufferProcessor, 0 /* default to host buffer size */ );
+
+ channel = 0;
+ for( j=0; j<stream->output.deviceCount; ++j )
+ {
+ /* we have stored the number of channels in the buffer in dwUser */
+ int channelCount = stream->output.waveHeaders[j][i].dwUser;
+
+ PaUtil_SetInterleavedOutputChannels( &stream->bufferProcessor, channel,
+ stream->output.waveHeaders[j][i].lpData +
+ stream->output.framesUsedInCurrentBuffer * channelCount *
+ stream->bufferProcessor.bytesPerHostOutputSample,
+ channelCount );
+
+ /* we have stored the number of channels in the buffer in dwUser */
+ channel += channelCount;
+ }
+
+ callbackResult = paContinue;
+ framesProcessed = PaUtil_EndBufferProcessing( &stream->bufferProcessor, &callbackResult );
+ stream->output.framesUsedInCurrentBuffer += framesProcessed;
+
+ if( callbackResult != paContinue )
+ {
+ /** @todo fix this, what do we do if callback result is non-zero during stream
+ priming?
+
+ for complete: play out primed waveHeaders as usual
+ for abort: clean up immediately.
+ */
+ }
+
+ }while( stream->output.framesUsedInCurrentBuffer != stream->output.framesPerBuffer );
+
+ }
+ else
+ {
+ for( j=0; j<stream->output.deviceCount; ++j )
+ {
+ ZeroMemory( stream->output.waveHeaders[j][i].lpData, stream->output.waveHeaders[j][i].dwBufferLength );
+ }
+ }
+
+ /* we queue all channels of a single buffer frame (accross all
+ devices, because some multidevice multichannel drivers work
+ better this way */
+ for( j=0; j<stream->output.deviceCount; ++j )
+ {
+ mmresult = waveOutWrite( ((HWAVEOUT*)stream->output.waveHandles)[j], &stream->output.waveHeaders[j][i], sizeof(WAVEHDR) );
+ if( mmresult != MMSYSERR_NOERROR )
+ {
+ result = paUnanticipatedHostError;
+ PA_MME_SET_LAST_WAVEOUT_ERROR( mmresult );
+ goto error;
+ }
+ }
+ }
+ stream->output.currentBufferIndex = 0;
+ stream->output.framesUsedInCurrentBuffer = 0;
+ }
+
+
+ stream->isStopped = 0;
+ stream->isActive = 1;
+ stream->stopProcessing = 0;
+ stream->abortProcessing = 0;
+
+ result = ResetEventWithPaError( stream->input.bufferEvent );
+ if( result != paNoError ) goto error;
+
+ result = ResetEventWithPaError( stream->output.bufferEvent );
+ if( result != paNoError ) goto error;
+
+
+ if( stream->streamRepresentation.streamCallback )
+ {
+ /* callback stream */
+
+ result = ResetEventWithPaError( stream->abortEvent );
+ if( result != paNoError ) goto error;
+
+ /* Create thread that waits for audio buffers to be ready for processing. */
+ stream->processingThread = CreateThread( 0, 0, ProcessingThreadProc, stream, 0, &stream->processingThreadId );
+ if( !stream->processingThread )
+ {
+ result = paUnanticipatedHostError;
+ PA_MME_SET_LAST_SYSTEM_ERROR( GetLastError() );
+ goto error;
+ }
+
+ /** @todo could have mme specific stream parameters to allow the user
+ to set the callback thread priorities */
+ stream->highThreadPriority = THREAD_PRIORITY_TIME_CRITICAL;
+ stream->throttledThreadPriority = THREAD_PRIORITY_NORMAL;
+
+ if( !SetThreadPriority( stream->processingThread, stream->highThreadPriority ) )
+ {
+ result = paUnanticipatedHostError;
+ PA_MME_SET_LAST_SYSTEM_ERROR( GetLastError() );
+ goto error;
+ }
+ stream->processingThreadPriority = stream->highThreadPriority;
+ }
+ else
+ {
+ /* blocking read/write stream */
+
+ }
+
+ if( PA_IS_INPUT_STREAM_(stream) )
+ {
+ for( i=0; i < stream->input.deviceCount; ++i )
+ {
+ mmresult = waveInStart( ((HWAVEIN*)stream->input.waveHandles)[i] );
+ PA_DEBUG(("Pa_StartStream: waveInStart returned = 0x%X.\n", mmresult));
+ if( mmresult != MMSYSERR_NOERROR )
+ {
+ result = paUnanticipatedHostError;
+ PA_MME_SET_LAST_WAVEIN_ERROR( mmresult );
+ goto error;
+ }
+ }
+ }
+
+ if( PA_IS_OUTPUT_STREAM_(stream) )
+ {
+ for( i=0; i < stream->output.deviceCount; ++i )
+ {
+ if( (mmresult = waveOutRestart( ((HWAVEOUT*)stream->output.waveHandles)[i] )) != MMSYSERR_NOERROR )
+ {
+ result = paUnanticipatedHostError;
+ PA_MME_SET_LAST_WAVEOUT_ERROR( mmresult );
+ goto error;
+ }
+ }
+ }
+
+ return result;
+
+error:
+ /** @todo FIXME: implement recovery as best we can
+ This should involve rolling back to a state as-if this function had never been called
+ */
+ return result;
+}
+
+
+static PaError StopStream( PaStream *s )
+{
+ PaError result = paNoError;
+ PaWinMmeStream *stream = (PaWinMmeStream*)s;
+ int timeout;
+ DWORD waitResult;
+ MMRESULT mmresult;
+ signed int hostOutputBufferIndex;
+ unsigned int channel, waitCount, i;
+
+ /** @todo
+ REVIEW: the error checking in this function needs review. the basic
+ idea is to return from this function in a known state - for example
+ there is no point avoiding calling waveInReset just because
+ the thread times out.
+ */
+
+ if( stream->processingThread )
+ {
+ /* callback stream */
+
+ /* Tell processing thread to stop generating more data and to let current data play out. */
+ stream->stopProcessing = 1;
+
+ /* Calculate timeOut longer than longest time it could take to return all buffers. */
+ timeout = (int)(stream->allBuffersDurationMs * 1.5);
+ if( timeout < PA_MME_MIN_TIMEOUT_MSEC_ )
+ timeout = PA_MME_MIN_TIMEOUT_MSEC_;
+
+ PA_DEBUG(("WinMME StopStream: waiting for background thread.\n"));
+
+ waitResult = WaitForSingleObject( stream->processingThread, timeout );
+ if( waitResult == WAIT_TIMEOUT )
+ {
+ /* try to abort */
+ stream->abortProcessing = 1;
+ SetEvent( stream->abortEvent );
+ waitResult = WaitForSingleObject( stream->processingThread, timeout );
+ if( waitResult == WAIT_TIMEOUT )
+ {
+ PA_DEBUG(("WinMME StopStream: timed out while waiting for background thread to finish.\n"));
+ result = paTimedOut;
+ }
+ }
+
+ CloseHandle( stream->processingThread );
+ stream->processingThread = NULL;
+ }
+ else
+ {
+ /* blocking read / write stream */
+
+ if( PA_IS_OUTPUT_STREAM_(stream) )
+ {
+ if( stream->output.framesUsedInCurrentBuffer > 0 )
+ {
+ /* there are still unqueued frames in the current buffer, so flush them */
+
+ hostOutputBufferIndex = stream->output.currentBufferIndex;
+
+ PaUtil_SetOutputFrameCount( &stream->bufferProcessor,
+ stream->output.framesPerBuffer - stream->output.framesUsedInCurrentBuffer );
+
+ channel = 0;
+ for( i=0; i<stream->output.deviceCount; ++i )
+ {
+ /* we have stored the number of channels in the buffer in dwUser */
+ int channelCount = stream->output.waveHeaders[i][ hostOutputBufferIndex ].dwUser;
+
+ PaUtil_SetInterleavedOutputChannels( &stream->bufferProcessor, channel,
+ stream->output.waveHeaders[i][ hostOutputBufferIndex ].lpData +
+ stream->output.framesUsedInCurrentBuffer * channelCount *
+ stream->bufferProcessor.bytesPerHostOutputSample,
+ channelCount );
+
+ channel += channelCount;
+ }
+
+ PaUtil_ZeroOutput( &stream->bufferProcessor,
+ stream->output.framesPerBuffer - stream->output.framesUsedInCurrentBuffer );
+
+ /* we send the entire buffer to the output devices, but we could
+ just send a partial buffer, rather than zeroing the unused
+ samples.
+ */
+ AdvanceToNextOutputBuffer( stream );
+ }
+
+
+ timeout = (stream->allBuffersDurationMs / stream->output.bufferCount) + 1;
+ if( timeout < PA_MME_MIN_TIMEOUT_MSEC_ )
+ timeout = PA_MME_MIN_TIMEOUT_MSEC_;
+
+ waitCount = 0;
+ while( !NoBuffersAreQueued( &stream->output ) && waitCount <= stream->output.bufferCount )
+ {
+ /* wait for MME to signal that a buffer is available */
+ waitResult = WaitForSingleObject( stream->output.bufferEvent, timeout );
+ if( waitResult == WAIT_FAILED )
+ {
+ break;
+ }
+ else if( waitResult == WAIT_TIMEOUT )
+ {
+ /* keep waiting */
+ }
+
+ ++waitCount;
+ }
+ }
+ }
+
+ if( PA_IS_OUTPUT_STREAM_(stream) )
+ {
+ for( i =0; i < stream->output.deviceCount; ++i )
+ {
+ mmresult = waveOutReset( ((HWAVEOUT*)stream->output.waveHandles)[i] );
+ if( mmresult != MMSYSERR_NOERROR )
+ {
+ result = paUnanticipatedHostError;
+ PA_MME_SET_LAST_WAVEOUT_ERROR( mmresult );
+ }
+ }
+ }
+
+ if( PA_IS_INPUT_STREAM_(stream) )
+ {
+ for( i=0; i < stream->input.deviceCount; ++i )
+ {
+ mmresult = waveInReset( ((HWAVEIN*)stream->input.waveHandles)[i] );
+ if( mmresult != MMSYSERR_NOERROR )
+ {
+ result = paUnanticipatedHostError;
+ PA_MME_SET_LAST_WAVEIN_ERROR( mmresult );
+ }
+ }
+ }
+
+ stream->isStopped = 1;
+ stream->isActive = 0;
+
+ return result;
+}
+
+
+static PaError AbortStream( PaStream *s )
+{
+ PaError result = paNoError;
+ PaWinMmeStream *stream = (PaWinMmeStream*)s;
+ int timeout;
+ DWORD waitResult;
+ MMRESULT mmresult;
+ unsigned int i;
+
+ /** @todo
+ REVIEW: the error checking in this function needs review. the basic
+ idea is to return from this function in a known state - for example
+ there is no point avoiding calling waveInReset just because
+ the thread times out.
+ */
+
+ if( stream->processingThread )
+ {
+ /* callback stream */
+
+ /* Tell processing thread to abort immediately */
+ stream->abortProcessing = 1;
+ SetEvent( stream->abortEvent );
+ }
+
+
+ if( PA_IS_OUTPUT_STREAM_(stream) )
+ {
+ for( i =0; i < stream->output.deviceCount; ++i )
+ {
+ mmresult = waveOutReset( ((HWAVEOUT*)stream->output.waveHandles)[i] );
+ if( mmresult != MMSYSERR_NOERROR )
+ {
+ PA_MME_SET_LAST_WAVEOUT_ERROR( mmresult );
+ return paUnanticipatedHostError;
+ }
+ }
+ }
+
+ if( PA_IS_INPUT_STREAM_(stream) )
+ {
+ for( i=0; i < stream->input.deviceCount; ++i )
+ {
+ mmresult = waveInReset( ((HWAVEIN*)stream->input.waveHandles)[i] );
+ if( mmresult != MMSYSERR_NOERROR )
+ {
+ PA_MME_SET_LAST_WAVEIN_ERROR( mmresult );
+ return paUnanticipatedHostError;
+ }
+ }
+ }
+
+
+ if( stream->processingThread )
+ {
+ /* callback stream */
+
+ PA_DEBUG(("WinMME AbortStream: waiting for background thread.\n"));
+
+ /* Calculate timeOut longer than longest time it could take to return all buffers. */
+ timeout = (int)(stream->allBuffersDurationMs * 1.5);
+ if( timeout < PA_MME_MIN_TIMEOUT_MSEC_ )
+ timeout = PA_MME_MIN_TIMEOUT_MSEC_;
+
+ waitResult = WaitForSingleObject( stream->processingThread, timeout );
+ if( waitResult == WAIT_TIMEOUT )
+ {
+ PA_DEBUG(("WinMME AbortStream: timed out while waiting for background thread to finish.\n"));
+ return paTimedOut;
+ }
+
+ CloseHandle( stream->processingThread );
+ stream->processingThread = NULL;
+ }
+
+ stream->isStopped = 1;
+ stream->isActive = 0;
+
+ return result;
+}
+
+
+static PaError IsStreamStopped( PaStream *s )
+{
+ PaWinMmeStream *stream = (PaWinMmeStream*)s;
+
+ return stream->isStopped;
+}
+
+
+static PaError IsStreamActive( PaStream *s )
+{
+ PaWinMmeStream *stream = (PaWinMmeStream*)s;
+
+ return stream->isActive;
+}
+
+
+static PaTime GetStreamTime( PaStream *s )
+{
+ (void) s; /* unused parameter */
+
+ return PaUtil_GetTime();
+}
+
+
+static double GetStreamCpuLoad( PaStream* s )
+{
+ PaWinMmeStream *stream = (PaWinMmeStream*)s;
+
+ return PaUtil_GetCpuLoad( &stream->cpuLoadMeasurer );
+}
+
+
+/*
+ As separate stream interfaces are used for blocking and callback
+ streams, the following functions can be guaranteed to only be called
+ for blocking streams.
+*/
+
+static PaError ReadStream( PaStream* s,
+ void *buffer,
+ unsigned long frames )
+{
+ PaError result = paNoError;
+ PaWinMmeStream *stream = (PaWinMmeStream*)s;
+ void *userBuffer;
+ unsigned long framesRead = 0;
+ unsigned long framesProcessed;
+ signed int hostInputBufferIndex;
+ DWORD waitResult;
+ DWORD timeout = (unsigned long)(stream->allBuffersDurationMs * 0.5);
+ unsigned int channel, i;
+
+ if( PA_IS_INPUT_STREAM_(stream) )
+ {
+ /* make a local copy of the user buffer pointer(s). this is necessary
+ because PaUtil_CopyInput() advances these pointers every time
+ it is called.
+ */
+ if( stream->bufferProcessor.userInputIsInterleaved )
+ {
+ userBuffer = buffer;
+ }
+ else
+ {
+ userBuffer = alloca( sizeof(void*) * stream->bufferProcessor.inputChannelCount );
+ if( !userBuffer )
+ return paInsufficientMemory;
+ for( i = 0; i<stream->bufferProcessor.inputChannelCount; ++i )
+ ((void**)userBuffer)[i] = ((void**)buffer)[i];
+ }
+
+ do{
+ if( CurrentInputBuffersAreDone( stream ) )
+ {
+ if( NoBuffersAreQueued( &stream->input ) )
+ {
+ /** @todo REVIEW: consider what to do if the input overflows.
+ do we requeue all of the buffers? should we be running
+ a thread to make sure they are always queued? */
+
+ result = paInputOverflowed;
+ }
+
+ hostInputBufferIndex = stream->input.currentBufferIndex;
+
+ PaUtil_SetInputFrameCount( &stream->bufferProcessor,
+ stream->input.framesPerBuffer - stream->input.framesUsedInCurrentBuffer );
+
+ channel = 0;
+ for( i=0; i<stream->input.deviceCount; ++i )
+ {
+ /* we have stored the number of channels in the buffer in dwUser */
+ int channelCount = stream->input.waveHeaders[i][ hostInputBufferIndex ].dwUser;
+
+ PaUtil_SetInterleavedInputChannels( &stream->bufferProcessor, channel,
+ stream->input.waveHeaders[i][ hostInputBufferIndex ].lpData +
+ stream->input.framesUsedInCurrentBuffer * channelCount *
+ stream->bufferProcessor.bytesPerHostInputSample,
+ channelCount );
+
+ channel += channelCount;
+ }
+
+ framesProcessed = PaUtil_CopyInput( &stream->bufferProcessor, &userBuffer, frames - framesRead );
+
+ stream->input.framesUsedInCurrentBuffer += framesProcessed;
+ if( stream->input.framesUsedInCurrentBuffer == stream->input.framesPerBuffer )
+ {
+ result = AdvanceToNextInputBuffer( stream );
+ if( result != paNoError )
+ break;
+ }
+
+ framesRead += framesProcessed;
+
+ }else{
+ /* wait for MME to signal that a buffer is available */
+ waitResult = WaitForSingleObject( stream->input.bufferEvent, timeout );
+ if( waitResult == WAIT_FAILED )
+ {
+ result = paUnanticipatedHostError;
+ break;
+ }
+ else if( waitResult == WAIT_TIMEOUT )
+ {
+ /* if a timeout is encountered, continue,
+ perhaps we should give up eventually
+ */
+ }
+ }
+ }while( framesRead < frames );
+ }
+ else
+ {
+ result = paCanNotReadFromAnOutputOnlyStream;
+ }
+
+ return result;
+}
+
+
+static PaError WriteStream( PaStream* s,
+ const void *buffer,
+ unsigned long frames )
+{
+ PaError result = paNoError;
+ PaWinMmeStream *stream = (PaWinMmeStream*)s;
+ const void *userBuffer;
+ unsigned long framesWritten = 0;
+ unsigned long framesProcessed;
+ signed int hostOutputBufferIndex;
+ DWORD waitResult;
+ DWORD timeout = (unsigned long)(stream->allBuffersDurationMs * 0.5);
+ unsigned int channel, i;
+
+
+ if( PA_IS_OUTPUT_STREAM_(stream) )
+ {
+ /* make a local copy of the user buffer pointer(s). this is necessary
+ because PaUtil_CopyOutput() advances these pointers every time
+ it is called.
+ */
+ if( stream->bufferProcessor.userOutputIsInterleaved )
+ {
+ userBuffer = buffer;
+ }
+ else
+ {
+ userBuffer = alloca( sizeof(void*) * stream->bufferProcessor.outputChannelCount );
+ if( !userBuffer )
+ return paInsufficientMemory;
+ for( i = 0; i<stream->bufferProcessor.outputChannelCount; ++i )
+ ((const void**)userBuffer)[i] = ((const void**)buffer)[i];
+ }
+
+ do{
+ if( CurrentOutputBuffersAreDone( stream ) )
+ {
+ if( NoBuffersAreQueued( &stream->output ) )
+ {
+ /** @todo REVIEW: consider what to do if the output
+ underflows. do we requeue all the existing buffers with
+ zeros? should we run a separate thread to keep the buffers
+ enqueued at all times? */
+
+ result = paOutputUnderflowed;
+ }
+
+ hostOutputBufferIndex = stream->output.currentBufferIndex;
+
+ PaUtil_SetOutputFrameCount( &stream->bufferProcessor,
+ stream->output.framesPerBuffer - stream->output.framesUsedInCurrentBuffer );
+
+ channel = 0;
+ for( i=0; i<stream->output.deviceCount; ++i )
+ {
+ /* we have stored the number of channels in the buffer in dwUser */
+ int channelCount = stream->output.waveHeaders[i][ hostOutputBufferIndex ].dwUser;
+
+ PaUtil_SetInterleavedOutputChannels( &stream->bufferProcessor, channel,
+ stream->output.waveHeaders[i][ hostOutputBufferIndex ].lpData +
+ stream->output.framesUsedInCurrentBuffer * channelCount *
+ stream->bufferProcessor.bytesPerHostOutputSample,
+ channelCount );
+
+ channel += channelCount;
+ }
+
+ framesProcessed = PaUtil_CopyOutput( &stream->bufferProcessor, &userBuffer, frames - framesWritten );
+
+ stream->output.framesUsedInCurrentBuffer += framesProcessed;
+ if( stream->output.framesUsedInCurrentBuffer == stream->output.framesPerBuffer )
+ {
+ result = AdvanceToNextOutputBuffer( stream );
+ if( result != paNoError )
+ break;
+ }
+
+ framesWritten += framesProcessed;
+ }
+ else
+ {
+ /* wait for MME to signal that a buffer is available */
+ waitResult = WaitForSingleObject( stream->output.bufferEvent, timeout );
+ if( waitResult == WAIT_FAILED )
+ {
+ result = paUnanticipatedHostError;
+ break;
+ }
+ else if( waitResult == WAIT_TIMEOUT )
+ {
+ /* if a timeout is encountered, continue,
+ perhaps we should give up eventually
+ */
+ }
+ }
+ }while( framesWritten < frames );
+ }
+ else
+ {
+ result = paCanNotWriteToAnInputOnlyStream;
+ }
+
+ return result;
+}
+
+
+static signed long GetStreamReadAvailable( PaStream* s )
+{
+ PaWinMmeStream *stream = (PaWinMmeStream*)s;
+
+ if( PA_IS_INPUT_STREAM_(stream) )
+ return GetAvailableFrames( &stream->input );
+ else
+ return paCanNotReadFromAnOutputOnlyStream;
+}
+
+
+static signed long GetStreamWriteAvailable( PaStream* s )
+{
+ PaWinMmeStream *stream = (PaWinMmeStream*)s;
+
+ if( PA_IS_OUTPUT_STREAM_(stream) )
+ return GetAvailableFrames( &stream->output );
+ else
+ return paCanNotWriteToAnInputOnlyStream;
+}
+
+
+/* NOTE: the following functions are MME-stream specific, and are called directly
+ by client code. We need to check for many more error conditions here because
+ we don't have the benefit of pa_front.c's parameter checking.
+*/
+
+static PaError GetWinMMEStreamPointer( PaWinMmeStream **stream, PaStream *s )
+{
+ PaError result;
+ PaUtilHostApiRepresentation *hostApi;
+ PaWinMmeHostApiRepresentation *winMmeHostApi;
+
+ result = PaUtil_ValidateStreamPointer( s );
+ if( result != paNoError )
+ return result;
+
+ result = PaUtil_GetHostApiRepresentation( &hostApi, paMME );
+ if( result != paNoError )
+ return result;
+
+ winMmeHostApi = (PaWinMmeHostApiRepresentation*)hostApi;
+
+ /* note, the following would be easier if there was a generic way of testing
+ that a stream belongs to a specific host API */
+
+ if( PA_STREAM_REP( s )->streamInterface == &winMmeHostApi->callbackStreamInterface
+ || PA_STREAM_REP( s )->streamInterface == &winMmeHostApi->blockingStreamInterface )
+ {
+ /* s is a WinMME stream */
+ *stream = (PaWinMmeStream *)s;
+ return paNoError;
+ }
+ else
+ {
+ return paIncompatibleStreamHostApi;
+ }
+}
+
+
+int PaWinMME_GetStreamInputHandleCount( PaStream* s )
+{
+ PaWinMmeStream *stream;
+ PaError result = GetWinMMEStreamPointer( &stream, s );
+
+ if( result == paNoError )
+ return (PA_IS_INPUT_STREAM_(stream)) ? stream->input.deviceCount : 0;
+ else
+ return result;
+}
+
+
+HWAVEIN PaWinMME_GetStreamInputHandle( PaStream* s, int handleIndex )
+{
+ PaWinMmeStream *stream;
+ PaError result = GetWinMMEStreamPointer( &stream, s );
+
+ if( result == paNoError
+ && PA_IS_INPUT_STREAM_(stream)
+ && handleIndex >= 0
+ && (unsigned int)handleIndex < stream->input.deviceCount )
+ return ((HWAVEIN*)stream->input.waveHandles)[handleIndex];
+ else
+ return 0;
+}
+
+
+int PaWinMME_GetStreamOutputHandleCount( PaStream* s)
+{
+ PaWinMmeStream *stream;
+ PaError result = GetWinMMEStreamPointer( &stream, s );
+
+ if( result == paNoError )
+ return (PA_IS_OUTPUT_STREAM_(stream)) ? stream->output.deviceCount : 0;
+ else
+ return result;
+}
+
+
+HWAVEOUT PaWinMME_GetStreamOutputHandle( PaStream* s, int handleIndex )
+{
+ PaWinMmeStream *stream;
+ PaError result = GetWinMMEStreamPointer( &stream, s );
+
+ if( result == paNoError
+ && PA_IS_OUTPUT_STREAM_(stream)
+ && handleIndex >= 0
+ && (unsigned int)handleIndex < stream->output.deviceCount )
+ return ((HWAVEOUT*)stream->output.waveHandles)[handleIndex];
+ else
+ return 0;
+}
+
+
+
+
+
diff --git a/pjmedia/src/pjmedia/portaudio/pa_win_wmme.h b/pjmedia/src/pjmedia/portaudio/pa_win_wmme.h
index 1a71633a..ae807e68 100644
--- a/pjmedia/src/pjmedia/portaudio/pa_win_wmme.h
+++ b/pjmedia/src/pjmedia/portaudio/pa_win_wmme.h
@@ -1,160 +1,181 @@
-#ifndef PA_WIN_WMME_H
-#define PA_WIN_WMME_H
-/*
- * $Id: pa_win_wmme.h,v 1.1.2.14 2004/02/20 14:16:53 rossbencina Exp $
- * PortAudio Portable Real-Time Audio Library
- * MME specific extensions
- *
- * Copyright (c) 1999-2000 Ross Bencina and Phil Burk
- *
- * Permission is hereby granted, free of charge, to any person obtaining
- * a copy of this software and associated documentation files
- * (the "Software"), to deal in the Software without restriction,
- * including without limitation the rights to use, copy, modify, merge,
- * publish, distribute, sublicense, and/or sell copies of the Software,
- * and to permit persons to whom the Software is furnished to do so,
- * subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be
- * included in all copies or substantial portions of the Software.
- *
- * Any person wishing to distribute modifications to the Software is
- * requested to send the modifications to the original developer so that
- * they can be incorporated into the canonical version.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
- * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
- * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
- * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
- * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
- *
- */
-
-/** @file
- @brief WMME-specific PortAudio API extension header file.
-*/
-
-
-#include "portaudio.h"
-
-#ifdef __cplusplus
-extern "C"
-{
-#endif /* __cplusplus */
-
-
-#define paWinMmeUseLowLevelLatencyParameters (0x01)
-#define paWinMmeUseMultipleDevices (0x02) /* use mme specific multiple device feature */
-
-
-/* By default, the mme implementation drops the processing thread's priority
- to THREAD_PRIORITY_NORMAL and sleeps the thread if the CPU load exceeds 100%
- This flag disables any priority throttling. The processing thread will always
- run at THREAD_PRIORITY_TIME_CRITICAL.
-*/
-#define paWinMmeDontThrottleOverloadedProcessingThread (0x08)
-
-
-typedef struct PaWinMmeDeviceAndChannelCount{
- PaDeviceIndex device;
- int channelCount;
-}PaWinMmeDeviceAndChannelCount;
-
-
-typedef struct PaWinMmeStreamInfo{
- unsigned long size; /**< sizeof(PaWinMmeStreamInfo) */
- PaHostApiTypeId hostApiType; /**< paMME */
- unsigned long version; /**< 1 */
-
- unsigned long flags;
-
- /* low-level latency setting support
- These settings control the number and size of host buffers in order
- to set latency. They will be used instead of the generic parameters
- to Pa_OpenStream() if flags contains the PaWinMmeUseLowLevelLatencyParameters
- flag.
-
- If PaWinMmeStreamInfo structures with PaWinMmeUseLowLevelLatencyParameters
- are supplied for both input and output in a full duplex stream, then the
- input and output framesPerBuffer must be the same, or the larger of the
- two must be a multiple of the smaller, otherwise a
- paIncompatibleHostApiSpecificStreamInfo error will be returned from
- Pa_OpenStream().
- */
- unsigned long framesPerBuffer;
- unsigned long bufferCount; /* formerly numBuffers */
-
- /* multiple devices per direction support
- If flags contains the PaWinMmeUseMultipleDevices flag,
- this functionality will be used, otherwise the device parameter to
- Pa_OpenStream() will be used instead.
- If devices are specified here, the corresponding device parameter
- to Pa_OpenStream() should be set to paUseHostApiSpecificDeviceSpecification,
- otherwise an paInvalidDevice error will result.
- The total number of channels accross all specified devices
- must agree with the corresponding channelCount parameter to
- Pa_OpenStream() otherwise a paInvalidChannelCount error will result.
- */
- PaWinMmeDeviceAndChannelCount *devices;
- unsigned long deviceCount;
-
-}PaWinMmeStreamInfo;
-
-
-/** Retrieve the number of wave in handles used by a PortAudio WinMME stream.
- Returns zero if the stream is output only.
-
- @return A non-negative value indicating the number of wave in handles
- or, a PaErrorCode (which are always negative) if PortAudio is not initialized
- or an error is encountered.
-
- @see PaWinMME_GetStreamInputHandle
-*/
-int PaWinMME_GetStreamInputHandleCount( PaStream* stream );
-
-
-/** Retrieve a wave in handle used by a PortAudio WinMME stream.
-
- @param stream The stream to query.
- @param handleIndex The zero based index of the wave in handle to retrieve. This
- should be in the range [0, PaWinMME_GetStreamInputHandle(stream)-1].
-
- @return A valid wave in handle, or NULL if an error occurred.
-
- @see PaWinMME_GetStreamInputHandle
-*/
-HWAVEIN PaWinMME_GetStreamInputHandle( PaStream* stream, int handleIndex );
-
-
-/** Retrieve the number of wave out handles used by a PortAudio WinMME stream.
- Returns zero if the stream is input only.
-
- @return A non-negative value indicating the number of wave out handles
- or, a PaErrorCode (which are always negative) if PortAudio is not initialized
- or an error is encountered.
-
- @see PaWinMME_GetStreamOutputHandle
-*/
-int PaWinMME_GetStreamOutputHandleCount( PaStream* stream );
-
-
-/** Retrieve a wave out handle used by a PortAudio WinMME stream.
-
- @param stream The stream to query.
- @param handleIndex The zero based index of the wave out handle to retrieve.
- This should be in the range [0, PaWinMME_GetStreamOutputHandleCount(stream)-1].
-
- @return A valid wave out handle, or NULL if an error occurred.
-
- @see PaWinMME_GetStreamOutputHandleCount
-*/
-HWAVEOUT PaWinMME_GetStreamOutputHandle( PaStream* stream, int handleIndex );
-
-
-#ifdef __cplusplus
-}
-#endif /* __cplusplus */
-
-#endif /* PA_WIN_WMME_H */
+/*
+ * PJMEDIA - Multimedia over IP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef PA_WIN_WMME_H
+#define PA_WIN_WMME_H
+/*
+ * $Id: pa_win_wmme.h,v 1.1.2.14 2004/02/20 14:16:53 rossbencina Exp $
+ * PortAudio Portable Real-Time Audio Library
+ * MME specific extensions
+ *
+ * Copyright (c) 1999-2000 Ross Bencina and Phil Burk
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * Any person wishing to distribute modifications to the Software is
+ * requested to send the modifications to the original developer so that
+ * they can be incorporated into the canonical version.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+ * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+/** @file
+ @brief WMME-specific PortAudio API extension header file.
+*/
+
+
+#include "portaudio.h"
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif /* __cplusplus */
+
+
+#define paWinMmeUseLowLevelLatencyParameters (0x01)
+#define paWinMmeUseMultipleDevices (0x02) /* use mme specific multiple device feature */
+
+
+/* By default, the mme implementation drops the processing thread's priority
+ to THREAD_PRIORITY_NORMAL and sleeps the thread if the CPU load exceeds 100%
+ This flag disables any priority throttling. The processing thread will always
+ run at THREAD_PRIORITY_TIME_CRITICAL.
+*/
+#define paWinMmeDontThrottleOverloadedProcessingThread (0x08)
+
+
+typedef struct PaWinMmeDeviceAndChannelCount{
+ PaDeviceIndex device;
+ int channelCount;
+}PaWinMmeDeviceAndChannelCount;
+
+
+typedef struct PaWinMmeStreamInfo{
+ unsigned long size; /**< sizeof(PaWinMmeStreamInfo) */
+ PaHostApiTypeId hostApiType; /**< paMME */
+ unsigned long version; /**< 1 */
+
+ unsigned long flags;
+
+ /* low-level latency setting support
+ These settings control the number and size of host buffers in order
+ to set latency. They will be used instead of the generic parameters
+ to Pa_OpenStream() if flags contains the PaWinMmeUseLowLevelLatencyParameters
+ flag.
+
+ If PaWinMmeStreamInfo structures with PaWinMmeUseLowLevelLatencyParameters
+ are supplied for both input and output in a full duplex stream, then the
+ input and output framesPerBuffer must be the same, or the larger of the
+ two must be a multiple of the smaller, otherwise a
+ paIncompatibleHostApiSpecificStreamInfo error will be returned from
+ Pa_OpenStream().
+ */
+ unsigned long framesPerBuffer;
+ unsigned long bufferCount; /* formerly numBuffers */
+
+ /* multiple devices per direction support
+ If flags contains the PaWinMmeUseMultipleDevices flag,
+ this functionality will be used, otherwise the device parameter to
+ Pa_OpenStream() will be used instead.
+ If devices are specified here, the corresponding device parameter
+ to Pa_OpenStream() should be set to paUseHostApiSpecificDeviceSpecification,
+ otherwise an paInvalidDevice error will result.
+ The total number of channels accross all specified devices
+ must agree with the corresponding channelCount parameter to
+ Pa_OpenStream() otherwise a paInvalidChannelCount error will result.
+ */
+ PaWinMmeDeviceAndChannelCount *devices;
+ unsigned long deviceCount;
+
+}PaWinMmeStreamInfo;
+
+
+/** Retrieve the number of wave in handles used by a PortAudio WinMME stream.
+ Returns zero if the stream is output only.
+
+ @return A non-negative value indicating the number of wave in handles
+ or, a PaErrorCode (which are always negative) if PortAudio is not initialized
+ or an error is encountered.
+
+ @see PaWinMME_GetStreamInputHandle
+*/
+int PaWinMME_GetStreamInputHandleCount( PaStream* stream );
+
+
+/** Retrieve a wave in handle used by a PortAudio WinMME stream.
+
+ @param stream The stream to query.
+ @param handleIndex The zero based index of the wave in handle to retrieve. This
+ should be in the range [0, PaWinMME_GetStreamInputHandle(stream)-1].
+
+ @return A valid wave in handle, or NULL if an error occurred.
+
+ @see PaWinMME_GetStreamInputHandle
+*/
+HWAVEIN PaWinMME_GetStreamInputHandle( PaStream* stream, int handleIndex );
+
+
+/** Retrieve the number of wave out handles used by a PortAudio WinMME stream.
+ Returns zero if the stream is input only.
+
+ @return A non-negative value indicating the number of wave out handles
+ or, a PaErrorCode (which are always negative) if PortAudio is not initialized
+ or an error is encountered.
+
+ @see PaWinMME_GetStreamOutputHandle
+*/
+int PaWinMME_GetStreamOutputHandleCount( PaStream* stream );
+
+
+/** Retrieve a wave out handle used by a PortAudio WinMME stream.
+
+ @param stream The stream to query.
+ @param handleIndex The zero based index of the wave out handle to retrieve.
+ This should be in the range [0, PaWinMME_GetStreamOutputHandleCount(stream)-1].
+
+ @return A valid wave out handle, or NULL if an error occurred.
+
+ @see PaWinMME_GetStreamOutputHandleCount
+*/
+HWAVEOUT PaWinMME_GetStreamOutputHandle( PaStream* stream, int handleIndex );
+
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* PA_WIN_WMME_H */
diff --git a/pjmedia/src/pjmedia/portaudio/pa_x86_plain_converters.c b/pjmedia/src/pjmedia/portaudio/pa_x86_plain_converters.c
index 3a33540b..cbd441fe 100644
--- a/pjmedia/src/pjmedia/portaudio/pa_x86_plain_converters.c
+++ b/pjmedia/src/pjmedia/portaudio/pa_x86_plain_converters.c
@@ -1,1167 +1,1188 @@
-#include "pa_x86_plain_converters.h"
-
-#include "pa_converters.h"
-#include "pa_dither.h"
-
-/*
- plain intel assemby versions of standard pa converter functions.
-
- the main reason these versions are faster than the equivalent C versions
- is that float -> int casting is expensive in C on x86 because the rounding
- mode needs to be changed for every cast. these versions only set
- the rounding mode once outside the loop.
-
- small additional speed gains are made by the way that clamping is
- implemented.
-
-TODO:
- o- inline dither code
- o- implement Dither only (no-clip) versions
- o- implement int8 and uint8 versions
- o- test thouroughly
-
- o- the packed 24 bit functions could benefit from unrolling and avoiding
- byte and word sized register access.
-*/
-
-/* -------------------------------------------------------------------------- */
-
-/*
-#define PA_CLIP_( val, min, max )\
- { val = ((val) < (min)) ? (min) : (((val) > (max)) ? (max) : (val)); }
-*/
-
-/*
- the following notes were used to determine whether a floating point
- value should be saturated (ie >1 or <-1) by loading it into an integer
- register. these should be rewritten so that they make sense.
-
- an ieee floating point value
-
- 1.xxxxxxxxxxxxxxxxxxxx?
-
-
- is less than or equal to 1 and greater than or equal to -1 either:
-
- if the mantissa is 0 and the unbiased exponent is 0
-
- OR
-
- if the unbiased exponent < 0
-
- this translates to:
-
- if the mantissa is 0 and the biased exponent is 7F
-
- or
-
- if the biased exponent is less than 7F
-
-
- therefore the value is greater than 1 or less than -1 if
-
- the mantissa is not 0 and the biased exponent is 7F
-
- or
-
- if the biased exponent is greater than 7F
-
-
- in other words, if we mask out the sign bit, the value is
- greater than 1 or less than -1 if its integer representation is greater than:
-
- 0 01111111 0000 0000 0000 0000 0000 000
-
- 0011 1111 1000 0000 0000 0000 0000 0000 => 0x3F800000
-*/
-
-/* -------------------------------------------------------------------------- */
-
-static const short fpuControlWord_ = 0x033F; /*round to nearest, 64 bit precision, all exceptions masked*/
-static const double int32Scaler_ = 0x7FFFFFFF;
-static const double ditheredInt32Scaler_ = 0x7FFFFFFE;
-static const double int24Scaler_ = 0x7FFFFF;
-static const double ditheredInt24Scaler_ = 0x7FFFFE;
-static const double int16Scaler_ = 0x7FFF;
-static const double ditheredInt16Scaler_ = 0x7FFE;
-
-#define PA_DITHER_BITS_ (15)
-/* Multiply by PA_FLOAT_DITHER_SCALE_ to get a float between -2.0 and +1.99999 */
-#define PA_FLOAT_DITHER_SCALE_ (1.0 / ((1<<PA_DITHER_BITS_)-1))
-static const float const_float_dither_scale_ = (float) PA_FLOAT_DITHER_SCALE_;
-#define PA_DITHER_SHIFT_ ((32 - PA_DITHER_BITS_) + 1)
-
-/* -------------------------------------------------------------------------- */
-
-static void Float32_To_Int32(
- void *destinationBuffer, signed int destinationStride,
- void *sourceBuffer, signed int sourceStride,
- unsigned int count, PaUtilTriangularDitherGenerator *ditherGenerator )
-{
-/*
- float *src = (float*)sourceBuffer;
- signed long *dest = (signed long*)destinationBuffer;
- (void)ditherGenerator; // unused parameter
-
- while( count-- )
- {
- // REVIEW
- double scaled = *src * 0x7FFFFFFF;
- *dest = (signed long) scaled;
-
- src += sourceStride;
- dest += destinationStride;
- }
-*/
-
- short savedFpuControlWord;
-
- (void) ditherGenerator; /* unused parameter */
-
-
- __asm{
- // esi -> source ptr
- // eax -> source byte stride
- // edi -> destination ptr
- // ebx -> destination byte stride
- // ecx -> source end ptr
- // edx -> temp
-
- mov esi, sourceBuffer
-
- mov edx, 4 // sizeof float32 and int32
- mov eax, sourceStride
- imul eax, edx
-
- mov ecx, count
- imul ecx, eax
- add ecx, esi
-
- mov edi, destinationBuffer
-
- mov ebx, destinationStride
- imul ebx, edx
-
- fwait
- fstcw savedFpuControlWord
- fldcw fpuControlWord_
-
- fld int32Scaler_ // stack: (int)0x7FFFFFFF
-
- Float32_To_Int32_loop:
-
- // load unscaled value into st(0)
- fld dword ptr [esi] // stack: value, (int)0x7FFFFFFF
- add esi, eax // increment source ptr
- //lea esi, [esi+eax]
- fmul st(0), st(1) // st(0) *= st(1), stack: value*0x7FFFFFFF, (int)0x7FFFFFFF
- /*
- note: we could store to a temporary qword here which would cause
- wraparound distortion instead of int indefinite 0x10. that would
- be more work, and given that not enabling clipping is only advisable
- when you know that your signal isn't going to clip it isn't worth it.
- */
- fistp dword ptr [edi] // pop st(0) into dest, stack: (int)0x7FFFFFFF
-
- add edi, ebx // increment destination ptr
- //lea edi, [edi+ebx]
-
- cmp esi, ecx // has src ptr reached end?
- jne Float32_To_Int32_loop
-
- ffree st(0)
- fincstp
-
- fwait
- fnclex
- fldcw savedFpuControlWord
- }
-}
-
-/* -------------------------------------------------------------------------- */
-
-static void Float32_To_Int32_Clip(
- void *destinationBuffer, signed int destinationStride,
- void *sourceBuffer, signed int sourceStride,
- unsigned int count, PaUtilTriangularDitherGenerator *ditherGenerator )
-{
-/*
- float *src = (float*)sourceBuffer;
- signed long *dest = (signed long*)destinationBuffer;
- (void) ditherGenerator; // unused parameter
-
- while( count-- )
- {
- // REVIEW
- double scaled = *src * 0x7FFFFFFF;
- PA_CLIP_( scaled, -2147483648., 2147483647. );
- *dest = (signed long) scaled;
-
- src += sourceStride;
- dest += destinationStride;
- }
-*/
-
- short savedFpuControlWord;
-
- (void) ditherGenerator; /* unused parameter */
-
- __asm{
- // esi -> source ptr
- // eax -> source byte stride
- // edi -> destination ptr
- // ebx -> destination byte stride
- // ecx -> source end ptr
- // edx -> temp
-
- mov esi, sourceBuffer
-
- mov edx, 4 // sizeof float32 and int32
- mov eax, sourceStride
- imul eax, edx
-
- mov ecx, count
- imul ecx, eax
- add ecx, esi
-
- mov edi, destinationBuffer
-
- mov ebx, destinationStride
- imul ebx, edx
-
- fwait
- fstcw savedFpuControlWord
- fldcw fpuControlWord_
-
- fld int32Scaler_ // stack: (int)0x7FFFFFFF
-
- Float32_To_Int32_Clip_loop:
-
- mov edx, dword ptr [esi] // load floating point value into integer register
-
- and edx, 0x7FFFFFFF // mask off sign
- cmp edx, 0x3F800000 // greater than 1.0 or less than -1.0
-
- jg Float32_To_Int32_Clip_clamp
-
- // load unscaled value into st(0)
- fld dword ptr [esi] // stack: value, (int)0x7FFFFFFF
- add esi, eax // increment source ptr
- //lea esi, [esi+eax]
- fmul st(0), st(1) // st(0) *= st(1), stack: value*0x7FFFFFFF, (int)0x7FFFFFFF
- fistp dword ptr [edi] // pop st(0) into dest, stack: (int)0x7FFFFFFF
- jmp Float32_To_Int32_Clip_stored
-
- Float32_To_Int32_Clip_clamp:
- mov edx, dword ptr [esi] // load floating point value into integer register
- shr edx, 31 // move sign bit into bit 0
- add esi, eax // increment source ptr
- //lea esi, [esi+eax]
- add edx, 0x7FFFFFFF // convert to maximum range integers
- mov dword ptr [edi], edx
-
- Float32_To_Int32_Clip_stored:
-
- //add edi, ebx // increment destination ptr
- lea edi, [edi+ebx]
-
- cmp esi, ecx // has src ptr reached end?
- jne Float32_To_Int32_Clip_loop
-
- ffree st(0)
- fincstp
-
- fwait
- fnclex
- fldcw savedFpuControlWord
- }
-}
-
-/* -------------------------------------------------------------------------- */
-
-static void Float32_To_Int32_DitherClip(
- void *destinationBuffer, signed int destinationStride,
- void *sourceBuffer, signed int sourceStride,
- unsigned int count, PaUtilTriangularDitherGenerator *ditherGenerator )
-{
- /*
- float *src = (float*)sourceBuffer;
- signed long *dest = (signed long*)destinationBuffer;
-
- while( count-- )
- {
- // REVIEW
- double dither = PaUtil_GenerateFloatTriangularDither( ditherGenerator );
- // use smaller scaler to prevent overflow when we add the dither
- double dithered = ((double)*src * (2147483646.0)) + dither;
- PA_CLIP_( dithered, -2147483648., 2147483647. );
- *dest = (signed long) dithered;
-
-
- src += sourceStride;
- dest += destinationStride;
- }
- */
-
- short savedFpuControlWord;
-
- // spill storage:
- signed long sourceByteStride;
- signed long highpassedDither;
-
- // dither state:
- unsigned long ditherPrevious = ditherGenerator->previous;
- unsigned long ditherRandSeed1 = ditherGenerator->randSeed1;
- unsigned long ditherRandSeed2 = ditherGenerator->randSeed2;
-
- __asm{
- // esi -> source ptr
- // eax -> source byte stride
- // edi -> destination ptr
- // ebx -> destination byte stride
- // ecx -> source end ptr
- // edx -> temp
-
- mov esi, sourceBuffer
-
- mov edx, 4 // sizeof float32 and int32
- mov eax, sourceStride
- imul eax, edx
-
- mov ecx, count
- imul ecx, eax
- add ecx, esi
-
- mov edi, destinationBuffer
-
- mov ebx, destinationStride
- imul ebx, edx
-
- fwait
- fstcw savedFpuControlWord
- fldcw fpuControlWord_
-
- fld ditheredInt32Scaler_ // stack: int scaler
-
- Float32_To_Int32_DitherClip_loop:
-
- mov edx, dword ptr [esi] // load floating point value into integer register
-
- and edx, 0x7FFFFFFF // mask off sign
- cmp edx, 0x3F800000 // greater than 1.0 or less than -1.0
-
- jg Float32_To_Int32_DitherClip_clamp
-
- // load unscaled value into st(0)
- fld dword ptr [esi] // stack: value, int scaler
- add esi, eax // increment source ptr
- //lea esi, [esi+eax]
- fmul st(0), st(1) // st(0) *= st(1), stack: value*(int scaler), int scaler
-
- /*
- // call PaUtil_GenerateFloatTriangularDither with C calling convention
- mov sourceByteStride, eax // save eax
- mov sourceEnd, ecx // save ecx
- push ditherGenerator // pass ditherGenerator parameter on stack
- call PaUtil_GenerateFloatTriangularDither // stack: dither, value*(int scaler), int scaler
- pop edx // clear parameter off stack
- mov ecx, sourceEnd // restore ecx
- mov eax, sourceByteStride // restore eax
- */
-
- // generate dither
- mov sourceByteStride, eax // save eax
- mov edx, 196314165
- mov eax, ditherRandSeed1
- mul edx // eax:edx = eax * 196314165
- //add eax, 907633515
- lea eax, [eax+907633515]
- mov ditherRandSeed1, eax
- mov edx, 196314165
- mov eax, ditherRandSeed2
- mul edx // eax:edx = eax * 196314165
- //add eax, 907633515
- lea eax, [eax+907633515]
- mov edx, ditherRandSeed1
- shr edx, PA_DITHER_SHIFT_
- mov ditherRandSeed2, eax
- shr eax, PA_DITHER_SHIFT_
- //add eax, edx // eax -> current
- lea eax, [eax+edx]
- mov edx, ditherPrevious
- neg edx
- lea edx, [eax+edx] // highpass = current - previous
- mov highpassedDither, edx
- mov ditherPrevious, eax // previous = current
- mov eax, sourceByteStride // restore eax
- fild highpassedDither
- fmul const_float_dither_scale_
- // end generate dither, dither signal in st(0)
-
- faddp st(1), st(0) // stack: dither + value*(int scaler), int scaler
- fistp dword ptr [edi] // pop st(0) into dest, stack: int scaler
- jmp Float32_To_Int32_DitherClip_stored
-
- Float32_To_Int32_DitherClip_clamp:
- mov edx, dword ptr [esi] // load floating point value into integer register
- shr edx, 31 // move sign bit into bit 0
- add esi, eax // increment source ptr
- //lea esi, [esi+eax]
- add edx, 0x7FFFFFFF // convert to maximum range integers
- mov dword ptr [edi], edx
-
- Float32_To_Int32_DitherClip_stored:
-
- //add edi, ebx // increment destination ptr
- lea edi, [edi+ebx]
-
- cmp esi, ecx // has src ptr reached end?
- jne Float32_To_Int32_DitherClip_loop
-
- ffree st(0)
- fincstp
-
- fwait
- fnclex
- fldcw savedFpuControlWord
- }
-
- ditherGenerator->previous = ditherPrevious;
- ditherGenerator->randSeed1 = ditherRandSeed1;
- ditherGenerator->randSeed2 = ditherRandSeed2;
-}
-
-/* -------------------------------------------------------------------------- */
-
-static void Float32_To_Int24(
- void *destinationBuffer, signed int destinationStride,
- void *sourceBuffer, signed int sourceStride,
- unsigned int count, PaUtilTriangularDitherGenerator *ditherGenerator )
-{
-/*
- float *src = (float*)sourceBuffer;
- unsigned char *dest = (unsigned char*)destinationBuffer;
- signed long temp;
-
- (void) ditherGenerator; // unused parameter
-
- while( count-- )
- {
- // convert to 32 bit and drop the low 8 bits
- double scaled = *src * 0x7FFFFFFF;
- temp = (signed long) scaled;
-
- dest[0] = (unsigned char)(temp >> 8);
- dest[1] = (unsigned char)(temp >> 16);
- dest[2] = (unsigned char)(temp >> 24);
-
- src += sourceStride;
- dest += destinationStride * 3;
- }
-*/
-
- short savedFpuControlWord;
-
- signed long tempInt32;
-
- (void) ditherGenerator; /* unused parameter */
-
- __asm{
- // esi -> source ptr
- // eax -> source byte stride
- // edi -> destination ptr
- // ebx -> destination byte stride
- // ecx -> source end ptr
- // edx -> temp
-
- mov esi, sourceBuffer
-
- mov edx, 4 // sizeof float32
- mov eax, sourceStride
- imul eax, edx
-
- mov ecx, count
- imul ecx, eax
- add ecx, esi
-
- mov edi, destinationBuffer
-
- mov edx, 3 // sizeof int24
- mov ebx, destinationStride
- imul ebx, edx
-
- fwait
- fstcw savedFpuControlWord
- fldcw fpuControlWord_
-
- fld int24Scaler_ // stack: (int)0x7FFFFF
-
- Float32_To_Int24_loop:
-
- // load unscaled value into st(0)
- fld dword ptr [esi] // stack: value, (int)0x7FFFFF
- add esi, eax // increment source ptr
- //lea esi, [esi+eax]
- fmul st(0), st(1) // st(0) *= st(1), stack: value*0x7FFFFF, (int)0x7FFFFF
- fistp tempInt32 // pop st(0) into tempInt32, stack: (int)0x7FFFFF
- mov edx, tempInt32
-
- mov byte ptr [edi], DL
- shr edx, 8
- //mov byte ptr [edi+1], DL
- //mov byte ptr [edi+2], DH
- mov word ptr [edi+1], DX
-
- //add edi, ebx // increment destination ptr
- lea edi, [edi+ebx]
-
- cmp esi, ecx // has src ptr reached end?
- jne Float32_To_Int24_loop
-
- ffree st(0)
- fincstp
-
- fwait
- fnclex
- fldcw savedFpuControlWord
- }
-}
-
-/* -------------------------------------------------------------------------- */
-
-static void Float32_To_Int24_Clip(
- void *destinationBuffer, signed int destinationStride,
- void *sourceBuffer, signed int sourceStride,
- unsigned int count, PaUtilTriangularDitherGenerator *ditherGenerator )
-{
-/*
- float *src = (float*)sourceBuffer;
- unsigned char *dest = (unsigned char*)destinationBuffer;
- signed long temp;
-
- (void) ditherGenerator; // unused parameter
-
- while( count-- )
- {
- // convert to 32 bit and drop the low 8 bits
- double scaled = *src * 0x7FFFFFFF;
- PA_CLIP_( scaled, -2147483648., 2147483647. );
- temp = (signed long) scaled;
-
- dest[0] = (unsigned char)(temp >> 8);
- dest[1] = (unsigned char)(temp >> 16);
- dest[2] = (unsigned char)(temp >> 24);
-
- src += sourceStride;
- dest += destinationStride * 3;
- }
-*/
-
- short savedFpuControlWord;
-
- signed long tempInt32;
-
- (void) ditherGenerator; /* unused parameter */
-
- __asm{
- // esi -> source ptr
- // eax -> source byte stride
- // edi -> destination ptr
- // ebx -> destination byte stride
- // ecx -> source end ptr
- // edx -> temp
-
- mov esi, sourceBuffer
-
- mov edx, 4 // sizeof float32
- mov eax, sourceStride
- imul eax, edx
-
- mov ecx, count
- imul ecx, eax
- add ecx, esi
-
- mov edi, destinationBuffer
-
- mov edx, 3 // sizeof int24
- mov ebx, destinationStride
- imul ebx, edx
-
- fwait
- fstcw savedFpuControlWord
- fldcw fpuControlWord_
-
- fld int24Scaler_ // stack: (int)0x7FFFFF
-
- Float32_To_Int24_Clip_loop:
-
- mov edx, dword ptr [esi] // load floating point value into integer register
-
- and edx, 0x7FFFFFFF // mask off sign
- cmp edx, 0x3F800000 // greater than 1.0 or less than -1.0
-
- jg Float32_To_Int24_Clip_clamp
-
- // load unscaled value into st(0)
- fld dword ptr [esi] // stack: value, (int)0x7FFFFF
- add esi, eax // increment source ptr
- //lea esi, [esi+eax]
- fmul st(0), st(1) // st(0) *= st(1), stack: value*0x7FFFFF, (int)0x7FFFFF
- fistp tempInt32 // pop st(0) into tempInt32, stack: (int)0x7FFFFF
- mov edx, tempInt32
- jmp Float32_To_Int24_Clip_store
-
- Float32_To_Int24_Clip_clamp:
- mov edx, dword ptr [esi] // load floating point value into integer register
- shr edx, 31 // move sign bit into bit 0
- add esi, eax // increment source ptr
- //lea esi, [esi+eax]
- add edx, 0x7FFFFF // convert to maximum range integers
-
- Float32_To_Int24_Clip_store:
-
- mov byte ptr [edi], DL
- shr edx, 8
- //mov byte ptr [edi+1], DL
- //mov byte ptr [edi+2], DH
- mov word ptr [edi+1], DX
-
- //add edi, ebx // increment destination ptr
- lea edi, [edi+ebx]
-
- cmp esi, ecx // has src ptr reached end?
- jne Float32_To_Int24_Clip_loop
-
- ffree st(0)
- fincstp
-
- fwait
- fnclex
- fldcw savedFpuControlWord
- }
-}
-
-/* -------------------------------------------------------------------------- */
-
-static void Float32_To_Int24_DitherClip(
- void *destinationBuffer, signed int destinationStride,
- void *sourceBuffer, signed int sourceStride,
- unsigned int count, PaUtilTriangularDitherGenerator *ditherGenerator )
-{
-/*
- float *src = (float*)sourceBuffer;
- unsigned char *dest = (unsigned char*)destinationBuffer;
- signed long temp;
-
- while( count-- )
- {
- // convert to 32 bit and drop the low 8 bits
-
- // FIXME: the dither amplitude here appears to be too small by 8 bits
- double dither = PaUtil_GenerateFloatTriangularDither( ditherGenerator );
- // use smaller scaler to prevent overflow when we add the dither
- double dithered = ((double)*src * (2147483646.0)) + dither;
- PA_CLIP_( dithered, -2147483648., 2147483647. );
-
- temp = (signed long) dithered;
-
- dest[0] = (unsigned char)(temp >> 8);
- dest[1] = (unsigned char)(temp >> 16);
- dest[2] = (unsigned char)(temp >> 24);
-
- src += sourceStride;
- dest += destinationStride * 3;
- }
-*/
-
- short savedFpuControlWord;
-
- // spill storage:
- signed long sourceByteStride;
- signed long highpassedDither;
-
- // dither state:
- unsigned long ditherPrevious = ditherGenerator->previous;
- unsigned long ditherRandSeed1 = ditherGenerator->randSeed1;
- unsigned long ditherRandSeed2 = ditherGenerator->randSeed2;
-
- signed long tempInt32;
-
- __asm{
- // esi -> source ptr
- // eax -> source byte stride
- // edi -> destination ptr
- // ebx -> destination byte stride
- // ecx -> source end ptr
- // edx -> temp
-
- mov esi, sourceBuffer
-
- mov edx, 4 // sizeof float32
- mov eax, sourceStride
- imul eax, edx
-
- mov ecx, count
- imul ecx, eax
- add ecx, esi
-
- mov edi, destinationBuffer
-
- mov edx, 3 // sizeof int24
- mov ebx, destinationStride
- imul ebx, edx
-
- fwait
- fstcw savedFpuControlWord
- fldcw fpuControlWord_
-
- fld ditheredInt24Scaler_ // stack: int scaler
-
- Float32_To_Int24_DitherClip_loop:
-
- mov edx, dword ptr [esi] // load floating point value into integer register
-
- and edx, 0x7FFFFFFF // mask off sign
- cmp edx, 0x3F800000 // greater than 1.0 or less than -1.0
-
- jg Float32_To_Int24_DitherClip_clamp
-
- // load unscaled value into st(0)
- fld dword ptr [esi] // stack: value, int scaler
- add esi, eax // increment source ptr
- //lea esi, [esi+eax]
- fmul st(0), st(1) // st(0) *= st(1), stack: value*(int scaler), int scaler
-
- /*
- // call PaUtil_GenerateFloatTriangularDither with C calling convention
- mov sourceByteStride, eax // save eax
- mov sourceEnd, ecx // save ecx
- push ditherGenerator // pass ditherGenerator parameter on stack
- call PaUtil_GenerateFloatTriangularDither // stack: dither, value*(int scaler), int scaler
- pop edx // clear parameter off stack
- mov ecx, sourceEnd // restore ecx
- mov eax, sourceByteStride // restore eax
- */
-
- // generate dither
- mov sourceByteStride, eax // save eax
- mov edx, 196314165
- mov eax, ditherRandSeed1
- mul edx // eax:edx = eax * 196314165
- //add eax, 907633515
- lea eax, [eax+907633515]
- mov ditherRandSeed1, eax
- mov edx, 196314165
- mov eax, ditherRandSeed2
- mul edx // eax:edx = eax * 196314165
- //add eax, 907633515
- lea eax, [eax+907633515]
- mov edx, ditherRandSeed1
- shr edx, PA_DITHER_SHIFT_
- mov ditherRandSeed2, eax
- shr eax, PA_DITHER_SHIFT_
- //add eax, edx // eax -> current
- lea eax, [eax+edx]
- mov edx, ditherPrevious
- neg edx
- lea edx, [eax+edx] // highpass = current - previous
- mov highpassedDither, edx
- mov ditherPrevious, eax // previous = current
- mov eax, sourceByteStride // restore eax
- fild highpassedDither
- fmul const_float_dither_scale_
- // end generate dither, dither signal in st(0)
-
- faddp st(1), st(0) // stack: dither * value*(int scaler), int scaler
- fistp tempInt32 // pop st(0) into tempInt32, stack: int scaler
- mov edx, tempInt32
- jmp Float32_To_Int24_DitherClip_store
-
- Float32_To_Int24_DitherClip_clamp:
- mov edx, dword ptr [esi] // load floating point value into integer register
- shr edx, 31 // move sign bit into bit 0
- add esi, eax // increment source ptr
- //lea esi, [esi+eax]
- add edx, 0x7FFFFF // convert to maximum range integers
-
- Float32_To_Int24_DitherClip_store:
-
- mov byte ptr [edi], DL
- shr edx, 8
- //mov byte ptr [edi+1], DL
- //mov byte ptr [edi+2], DH
- mov word ptr [edi+1], DX
-
- //add edi, ebx // increment destination ptr
- lea edi, [edi+ebx]
-
- cmp esi, ecx // has src ptr reached end?
- jne Float32_To_Int24_DitherClip_loop
-
- ffree st(0)
- fincstp
-
- fwait
- fnclex
- fldcw savedFpuControlWord
- }
-
- ditherGenerator->previous = ditherPrevious;
- ditherGenerator->randSeed1 = ditherRandSeed1;
- ditherGenerator->randSeed2 = ditherRandSeed2;
-}
-
-/* -------------------------------------------------------------------------- */
-
-static void Float32_To_Int16(
- void *destinationBuffer, signed int destinationStride,
- void *sourceBuffer, signed int sourceStride,
- unsigned int count, PaUtilTriangularDitherGenerator *ditherGenerator )
-{
-/*
- float *src = (float*)sourceBuffer;
- signed short *dest = (signed short*)destinationBuffer;
- (void)ditherGenerator; // unused parameter
-
- while( count-- )
- {
-
- short samp = (short) (*src * (32767.0f));
- *dest = samp;
-
- src += sourceStride;
- dest += destinationStride;
- }
-*/
-
- short savedFpuControlWord;
-
- (void) ditherGenerator; /* unused parameter */
-
- __asm{
- // esi -> source ptr
- // eax -> source byte stride
- // edi -> destination ptr
- // ebx -> destination byte stride
- // ecx -> source end ptr
- // edx -> temp
-
- mov esi, sourceBuffer
-
- mov edx, 4 // sizeof float32
- mov eax, sourceStride
- imul eax, edx // source byte stride
-
- mov ecx, count
- imul ecx, eax
- add ecx, esi // source end ptr = count * source byte stride + source ptr
-
- mov edi, destinationBuffer
-
- mov edx, 2 // sizeof int16
- mov ebx, destinationStride
- imul ebx, edx // destination byte stride
-
- fwait
- fstcw savedFpuControlWord
- fldcw fpuControlWord_
-
- fld int16Scaler_ // stack: (int)0x7FFF
-
- Float32_To_Int16_loop:
-
- // load unscaled value into st(0)
- fld dword ptr [esi] // stack: value, (int)0x7FFF
- add esi, eax // increment source ptr
- //lea esi, [esi+eax]
- fmul st(0), st(1) // st(0) *= st(1), stack: value*0x7FFF, (int)0x7FFF
- fistp word ptr [edi] // store scaled int into dest, stack: (int)0x7FFF
-
- add edi, ebx // increment destination ptr
- //lea edi, [edi+ebx]
-
- cmp esi, ecx // has src ptr reached end?
- jne Float32_To_Int16_loop
-
- ffree st(0)
- fincstp
-
- fwait
- fnclex
- fldcw savedFpuControlWord
- }
-}
-
-/* -------------------------------------------------------------------------- */
-
-static void Float32_To_Int16_Clip(
- void *destinationBuffer, signed int destinationStride,
- void *sourceBuffer, signed int sourceStride,
- unsigned int count, PaUtilTriangularDitherGenerator *ditherGenerator )
-{
-/*
- float *src = (float*)sourceBuffer;
- signed short *dest = (signed short*)destinationBuffer;
- (void)ditherGenerator; // unused parameter
-
- while( count-- )
- {
- long samp = (signed long) (*src * (32767.0f));
- PA_CLIP_( samp, -0x8000, 0x7FFF );
- *dest = (signed short) samp;
-
- src += sourceStride;
- dest += destinationStride;
- }
-*/
-
- short savedFpuControlWord;
-
- (void) ditherGenerator; /* unused parameter */
-
- __asm{
- // esi -> source ptr
- // eax -> source byte stride
- // edi -> destination ptr
- // ebx -> destination byte stride
- // ecx -> source end ptr
- // edx -> temp
-
- mov esi, sourceBuffer
-
- mov edx, 4 // sizeof float32
- mov eax, sourceStride
- imul eax, edx // source byte stride
-
- mov ecx, count
- imul ecx, eax
- add ecx, esi // source end ptr = count * source byte stride + source ptr
-
- mov edi, destinationBuffer
-
- mov edx, 2 // sizeof int16
- mov ebx, destinationStride
- imul ebx, edx // destination byte stride
-
- fwait
- fstcw savedFpuControlWord
- fldcw fpuControlWord_
-
- fld int16Scaler_ // stack: (int)0x7FFF
-
- Float32_To_Int16_Clip_loop:
-
- mov edx, dword ptr [esi] // load floating point value into integer register
-
- and edx, 0x7FFFFFFF // mask off sign
- cmp edx, 0x3F800000 // greater than 1.0 or less than -1.0
-
- jg Float32_To_Int16_Clip_clamp
-
- // load unscaled value into st(0)
- fld dword ptr [esi] // stack: value, (int)0x7FFF
- add esi, eax // increment source ptr
- //lea esi, [esi+eax]
- fmul st(0), st(1) // st(0) *= st(1), stack: value*0x7FFF, (int)0x7FFF
- fistp word ptr [edi] // store scaled int into dest, stack: (int)0x7FFF
- jmp Float32_To_Int16_Clip_stored
-
- Float32_To_Int16_Clip_clamp:
- mov edx, dword ptr [esi] // load floating point value into integer register
- shr edx, 31 // move sign bit into bit 0
- add esi, eax // increment source ptr
- //lea esi, [esi+eax]
- add dx, 0x7FFF // convert to maximum range integers
- mov word ptr [edi], dx // store clamped into into dest
-
- Float32_To_Int16_Clip_stored:
-
- add edi, ebx // increment destination ptr
- //lea edi, [edi+ebx]
-
- cmp esi, ecx // has src ptr reached end?
- jne Float32_To_Int16_Clip_loop
-
- ffree st(0)
- fincstp
-
- fwait
- fnclex
- fldcw savedFpuControlWord
- }
-}
-
-/* -------------------------------------------------------------------------- */
-
-static void Float32_To_Int16_DitherClip(
- void *destinationBuffer, signed int destinationStride,
- void *sourceBuffer, signed int sourceStride,
- unsigned int count, PaUtilTriangularDitherGenerator *ditherGenerator )
-{
-/*
- float *src = (float*)sourceBuffer;
- signed short *dest = (signed short*)destinationBuffer;
- (void)ditherGenerator; // unused parameter
-
- while( count-- )
- {
-
- float dither = PaUtil_GenerateFloatTriangularDither( ditherGenerator );
- // use smaller scaler to prevent overflow when we add the dither
- float dithered = (*src * (32766.0f)) + dither;
- signed long samp = (signed long) dithered;
- PA_CLIP_( samp, -0x8000, 0x7FFF );
- *dest = (signed short) samp;
-
- src += sourceStride;
- dest += destinationStride;
- }
-*/
-
- short savedFpuControlWord;
-
- // spill storage:
- signed long sourceByteStride;
- signed long highpassedDither;
-
- // dither state:
- unsigned long ditherPrevious = ditherGenerator->previous;
- unsigned long ditherRandSeed1 = ditherGenerator->randSeed1;
- unsigned long ditherRandSeed2 = ditherGenerator->randSeed2;
-
- __asm{
- // esi -> source ptr
- // eax -> source byte stride
- // edi -> destination ptr
- // ebx -> destination byte stride
- // ecx -> source end ptr
- // edx -> temp
-
- mov esi, sourceBuffer
-
- mov edx, 4 // sizeof float32
- mov eax, sourceStride
- imul eax, edx // source byte stride
-
- mov ecx, count
- imul ecx, eax
- add ecx, esi // source end ptr = count * source byte stride + source ptr
-
- mov edi, destinationBuffer
-
- mov edx, 2 // sizeof int16
- mov ebx, destinationStride
- imul ebx, edx // destination byte stride
-
- fwait
- fstcw savedFpuControlWord
- fldcw fpuControlWord_
-
- fld ditheredInt16Scaler_ // stack: int scaler
-
- Float32_To_Int16_DitherClip_loop:
-
- mov edx, dword ptr [esi] // load floating point value into integer register
-
- and edx, 0x7FFFFFFF // mask off sign
- cmp edx, 0x3F800000 // greater than 1.0 or less than -1.0
-
- jg Float32_To_Int16_DitherClip_clamp
-
- // load unscaled value into st(0)
- fld dword ptr [esi] // stack: value, int scaler
- add esi, eax // increment source ptr
- //lea esi, [esi+eax]
- fmul st(0), st(1) // st(0) *= st(1), stack: value*(int scaler), int scaler
-
- /*
- // call PaUtil_GenerateFloatTriangularDither with C calling convention
- mov sourceByteStride, eax // save eax
- mov sourceEnd, ecx // save ecx
- push ditherGenerator // pass ditherGenerator parameter on stack
- call PaUtil_GenerateFloatTriangularDither // stack: dither, value*(int scaler), int scaler
- pop edx // clear parameter off stack
- mov ecx, sourceEnd // restore ecx
- mov eax, sourceByteStride // restore eax
- */
-
- // generate dither
- mov sourceByteStride, eax // save eax
- mov edx, 196314165
- mov eax, ditherRandSeed1
- mul edx // eax:edx = eax * 196314165
- //add eax, 907633515
- lea eax, [eax+907633515]
- mov ditherRandSeed1, eax
- mov edx, 196314165
- mov eax, ditherRandSeed2
- mul edx // eax:edx = eax * 196314165
- //add eax, 907633515
- lea eax, [eax+907633515]
- mov edx, ditherRandSeed1
- shr edx, PA_DITHER_SHIFT_
- mov ditherRandSeed2, eax
- shr eax, PA_DITHER_SHIFT_
- //add eax, edx // eax -> current
- lea eax, [eax+edx] // current = randSeed1>>x + randSeed2>>x
- mov edx, ditherPrevious
- neg edx
- lea edx, [eax+edx] // highpass = current - previous
- mov highpassedDither, edx
- mov ditherPrevious, eax // previous = current
- mov eax, sourceByteStride // restore eax
- fild highpassedDither
- fmul const_float_dither_scale_
- // end generate dither, dither signal in st(0)
-
- faddp st(1), st(0) // stack: dither * value*(int scaler), int scaler
- fistp word ptr [edi] // store scaled int into dest, stack: int scaler
- jmp Float32_To_Int16_DitherClip_stored
-
- Float32_To_Int16_DitherClip_clamp:
- mov edx, dword ptr [esi] // load floating point value into integer register
- shr edx, 31 // move sign bit into bit 0
- add esi, eax // increment source ptr
- //lea esi, [esi+eax]
- add dx, 0x7FFF // convert to maximum range integers
- mov word ptr [edi], dx // store clamped into into dest
-
- Float32_To_Int16_DitherClip_stored:
-
- add edi, ebx // increment destination ptr
- //lea edi, [edi+ebx]
-
- cmp esi, ecx // has src ptr reached end?
- jne Float32_To_Int16_DitherClip_loop
-
- ffree st(0)
- fincstp
-
- fwait
- fnclex
- fldcw savedFpuControlWord
- }
-
- ditherGenerator->previous = ditherPrevious;
- ditherGenerator->randSeed1 = ditherRandSeed1;
- ditherGenerator->randSeed2 = ditherRandSeed2;
-}
-
-/* -------------------------------------------------------------------------- */
-
-void PaUtil_InitializeX86PlainConverters( void )
-{
- paConverters.Float32_To_Int32 = Float32_To_Int32;
- paConverters.Float32_To_Int32_Clip = Float32_To_Int32_Clip;
- paConverters.Float32_To_Int32_DitherClip = Float32_To_Int32_DitherClip;
-
- paConverters.Float32_To_Int24 = Float32_To_Int24;
- paConverters.Float32_To_Int24_Clip = Float32_To_Int24_Clip;
- paConverters.Float32_To_Int24_DitherClip = Float32_To_Int24_DitherClip;
-
- paConverters.Float32_To_Int16 = Float32_To_Int16;
- paConverters.Float32_To_Int16_Clip = Float32_To_Int16_Clip;
- paConverters.Float32_To_Int16_DitherClip = Float32_To_Int16_DitherClip;
-}
-
-/* -------------------------------------------------------------------------- */
+/*
+ * PJMEDIA - Multimedia over IP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include "pa_x86_plain_converters.h"
+
+#include "pa_converters.h"
+#include "pa_dither.h"
+
+/*
+ plain intel assemby versions of standard pa converter functions.
+
+ the main reason these versions are faster than the equivalent C versions
+ is that float -> int casting is expensive in C on x86 because the rounding
+ mode needs to be changed for every cast. these versions only set
+ the rounding mode once outside the loop.
+
+ small additional speed gains are made by the way that clamping is
+ implemented.
+
+TODO:
+ o- inline dither code
+ o- implement Dither only (no-clip) versions
+ o- implement int8 and uint8 versions
+ o- test thouroughly
+
+ o- the packed 24 bit functions could benefit from unrolling and avoiding
+ byte and word sized register access.
+*/
+
+/* -------------------------------------------------------------------------- */
+
+/*
+#define PA_CLIP_( val, min, max )\
+ { val = ((val) < (min)) ? (min) : (((val) > (max)) ? (max) : (val)); }
+*/
+
+/*
+ the following notes were used to determine whether a floating point
+ value should be saturated (ie >1 or <-1) by loading it into an integer
+ register. these should be rewritten so that they make sense.
+
+ an ieee floating point value
+
+ 1.xxxxxxxxxxxxxxxxxxxx?
+
+
+ is less than or equal to 1 and greater than or equal to -1 either:
+
+ if the mantissa is 0 and the unbiased exponent is 0
+
+ OR
+
+ if the unbiased exponent < 0
+
+ this translates to:
+
+ if the mantissa is 0 and the biased exponent is 7F
+
+ or
+
+ if the biased exponent is less than 7F
+
+
+ therefore the value is greater than 1 or less than -1 if
+
+ the mantissa is not 0 and the biased exponent is 7F
+
+ or
+
+ if the biased exponent is greater than 7F
+
+
+ in other words, if we mask out the sign bit, the value is
+ greater than 1 or less than -1 if its integer representation is greater than:
+
+ 0 01111111 0000 0000 0000 0000 0000 000
+
+ 0011 1111 1000 0000 0000 0000 0000 0000 => 0x3F800000
+*/
+
+/* -------------------------------------------------------------------------- */
+
+static const short fpuControlWord_ = 0x033F; /*round to nearest, 64 bit precision, all exceptions masked*/
+static const double int32Scaler_ = 0x7FFFFFFF;
+static const double ditheredInt32Scaler_ = 0x7FFFFFFE;
+static const double int24Scaler_ = 0x7FFFFF;
+static const double ditheredInt24Scaler_ = 0x7FFFFE;
+static const double int16Scaler_ = 0x7FFF;
+static const double ditheredInt16Scaler_ = 0x7FFE;
+
+#define PA_DITHER_BITS_ (15)
+/* Multiply by PA_FLOAT_DITHER_SCALE_ to get a float between -2.0 and +1.99999 */
+#define PA_FLOAT_DITHER_SCALE_ (1.0 / ((1<<PA_DITHER_BITS_)-1))
+static const float const_float_dither_scale_ = (float) PA_FLOAT_DITHER_SCALE_;
+#define PA_DITHER_SHIFT_ ((32 - PA_DITHER_BITS_) + 1)
+
+/* -------------------------------------------------------------------------- */
+
+static void Float32_To_Int32(
+ void *destinationBuffer, signed int destinationStride,
+ void *sourceBuffer, signed int sourceStride,
+ unsigned int count, PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+/*
+ float *src = (float*)sourceBuffer;
+ signed long *dest = (signed long*)destinationBuffer;
+ (void)ditherGenerator; // unused parameter
+
+ while( count-- )
+ {
+ // REVIEW
+ double scaled = *src * 0x7FFFFFFF;
+ *dest = (signed long) scaled;
+
+ src += sourceStride;
+ dest += destinationStride;
+ }
+*/
+
+ short savedFpuControlWord;
+
+ (void) ditherGenerator; /* unused parameter */
+
+
+ __asm{
+ // esi -> source ptr
+ // eax -> source byte stride
+ // edi -> destination ptr
+ // ebx -> destination byte stride
+ // ecx -> source end ptr
+ // edx -> temp
+
+ mov esi, sourceBuffer
+
+ mov edx, 4 // sizeof float32 and int32
+ mov eax, sourceStride
+ imul eax, edx
+
+ mov ecx, count
+ imul ecx, eax
+ add ecx, esi
+
+ mov edi, destinationBuffer
+
+ mov ebx, destinationStride
+ imul ebx, edx
+
+ fwait
+ fstcw savedFpuControlWord
+ fldcw fpuControlWord_
+
+ fld int32Scaler_ // stack: (int)0x7FFFFFFF
+
+ Float32_To_Int32_loop:
+
+ // load unscaled value into st(0)
+ fld dword ptr [esi] // stack: value, (int)0x7FFFFFFF
+ add esi, eax // increment source ptr
+ //lea esi, [esi+eax]
+ fmul st(0), st(1) // st(0) *= st(1), stack: value*0x7FFFFFFF, (int)0x7FFFFFFF
+ /*
+ note: we could store to a temporary qword here which would cause
+ wraparound distortion instead of int indefinite 0x10. that would
+ be more work, and given that not enabling clipping is only advisable
+ when you know that your signal isn't going to clip it isn't worth it.
+ */
+ fistp dword ptr [edi] // pop st(0) into dest, stack: (int)0x7FFFFFFF
+
+ add edi, ebx // increment destination ptr
+ //lea edi, [edi+ebx]
+
+ cmp esi, ecx // has src ptr reached end?
+ jne Float32_To_Int32_loop
+
+ ffree st(0)
+ fincstp
+
+ fwait
+ fnclex
+ fldcw savedFpuControlWord
+ }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Float32_To_Int32_Clip(
+ void *destinationBuffer, signed int destinationStride,
+ void *sourceBuffer, signed int sourceStride,
+ unsigned int count, PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+/*
+ float *src = (float*)sourceBuffer;
+ signed long *dest = (signed long*)destinationBuffer;
+ (void) ditherGenerator; // unused parameter
+
+ while( count-- )
+ {
+ // REVIEW
+ double scaled = *src * 0x7FFFFFFF;
+ PA_CLIP_( scaled, -2147483648., 2147483647. );
+ *dest = (signed long) scaled;
+
+ src += sourceStride;
+ dest += destinationStride;
+ }
+*/
+
+ short savedFpuControlWord;
+
+ (void) ditherGenerator; /* unused parameter */
+
+ __asm{
+ // esi -> source ptr
+ // eax -> source byte stride
+ // edi -> destination ptr
+ // ebx -> destination byte stride
+ // ecx -> source end ptr
+ // edx -> temp
+
+ mov esi, sourceBuffer
+
+ mov edx, 4 // sizeof float32 and int32
+ mov eax, sourceStride
+ imul eax, edx
+
+ mov ecx, count
+ imul ecx, eax
+ add ecx, esi
+
+ mov edi, destinationBuffer
+
+ mov ebx, destinationStride
+ imul ebx, edx
+
+ fwait
+ fstcw savedFpuControlWord
+ fldcw fpuControlWord_
+
+ fld int32Scaler_ // stack: (int)0x7FFFFFFF
+
+ Float32_To_Int32_Clip_loop:
+
+ mov edx, dword ptr [esi] // load floating point value into integer register
+
+ and edx, 0x7FFFFFFF // mask off sign
+ cmp edx, 0x3F800000 // greater than 1.0 or less than -1.0
+
+ jg Float32_To_Int32_Clip_clamp
+
+ // load unscaled value into st(0)
+ fld dword ptr [esi] // stack: value, (int)0x7FFFFFFF
+ add esi, eax // increment source ptr
+ //lea esi, [esi+eax]
+ fmul st(0), st(1) // st(0) *= st(1), stack: value*0x7FFFFFFF, (int)0x7FFFFFFF
+ fistp dword ptr [edi] // pop st(0) into dest, stack: (int)0x7FFFFFFF
+ jmp Float32_To_Int32_Clip_stored
+
+ Float32_To_Int32_Clip_clamp:
+ mov edx, dword ptr [esi] // load floating point value into integer register
+ shr edx, 31 // move sign bit into bit 0
+ add esi, eax // increment source ptr
+ //lea esi, [esi+eax]
+ add edx, 0x7FFFFFFF // convert to maximum range integers
+ mov dword ptr [edi], edx
+
+ Float32_To_Int32_Clip_stored:
+
+ //add edi, ebx // increment destination ptr
+ lea edi, [edi+ebx]
+
+ cmp esi, ecx // has src ptr reached end?
+ jne Float32_To_Int32_Clip_loop
+
+ ffree st(0)
+ fincstp
+
+ fwait
+ fnclex
+ fldcw savedFpuControlWord
+ }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Float32_To_Int32_DitherClip(
+ void *destinationBuffer, signed int destinationStride,
+ void *sourceBuffer, signed int sourceStride,
+ unsigned int count, PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+ /*
+ float *src = (float*)sourceBuffer;
+ signed long *dest = (signed long*)destinationBuffer;
+
+ while( count-- )
+ {
+ // REVIEW
+ double dither = PaUtil_GenerateFloatTriangularDither( ditherGenerator );
+ // use smaller scaler to prevent overflow when we add the dither
+ double dithered = ((double)*src * (2147483646.0)) + dither;
+ PA_CLIP_( dithered, -2147483648., 2147483647. );
+ *dest = (signed long) dithered;
+
+
+ src += sourceStride;
+ dest += destinationStride;
+ }
+ */
+
+ short savedFpuControlWord;
+
+ // spill storage:
+ signed long sourceByteStride;
+ signed long highpassedDither;
+
+ // dither state:
+ unsigned long ditherPrevious = ditherGenerator->previous;
+ unsigned long ditherRandSeed1 = ditherGenerator->randSeed1;
+ unsigned long ditherRandSeed2 = ditherGenerator->randSeed2;
+
+ __asm{
+ // esi -> source ptr
+ // eax -> source byte stride
+ // edi -> destination ptr
+ // ebx -> destination byte stride
+ // ecx -> source end ptr
+ // edx -> temp
+
+ mov esi, sourceBuffer
+
+ mov edx, 4 // sizeof float32 and int32
+ mov eax, sourceStride
+ imul eax, edx
+
+ mov ecx, count
+ imul ecx, eax
+ add ecx, esi
+
+ mov edi, destinationBuffer
+
+ mov ebx, destinationStride
+ imul ebx, edx
+
+ fwait
+ fstcw savedFpuControlWord
+ fldcw fpuControlWord_
+
+ fld ditheredInt32Scaler_ // stack: int scaler
+
+ Float32_To_Int32_DitherClip_loop:
+
+ mov edx, dword ptr [esi] // load floating point value into integer register
+
+ and edx, 0x7FFFFFFF // mask off sign
+ cmp edx, 0x3F800000 // greater than 1.0 or less than -1.0
+
+ jg Float32_To_Int32_DitherClip_clamp
+
+ // load unscaled value into st(0)
+ fld dword ptr [esi] // stack: value, int scaler
+ add esi, eax // increment source ptr
+ //lea esi, [esi+eax]
+ fmul st(0), st(1) // st(0) *= st(1), stack: value*(int scaler), int scaler
+
+ /*
+ // call PaUtil_GenerateFloatTriangularDither with C calling convention
+ mov sourceByteStride, eax // save eax
+ mov sourceEnd, ecx // save ecx
+ push ditherGenerator // pass ditherGenerator parameter on stack
+ call PaUtil_GenerateFloatTriangularDither // stack: dither, value*(int scaler), int scaler
+ pop edx // clear parameter off stack
+ mov ecx, sourceEnd // restore ecx
+ mov eax, sourceByteStride // restore eax
+ */
+
+ // generate dither
+ mov sourceByteStride, eax // save eax
+ mov edx, 196314165
+ mov eax, ditherRandSeed1
+ mul edx // eax:edx = eax * 196314165
+ //add eax, 907633515
+ lea eax, [eax+907633515]
+ mov ditherRandSeed1, eax
+ mov edx, 196314165
+ mov eax, ditherRandSeed2
+ mul edx // eax:edx = eax * 196314165
+ //add eax, 907633515
+ lea eax, [eax+907633515]
+ mov edx, ditherRandSeed1
+ shr edx, PA_DITHER_SHIFT_
+ mov ditherRandSeed2, eax
+ shr eax, PA_DITHER_SHIFT_
+ //add eax, edx // eax -> current
+ lea eax, [eax+edx]
+ mov edx, ditherPrevious
+ neg edx
+ lea edx, [eax+edx] // highpass = current - previous
+ mov highpassedDither, edx
+ mov ditherPrevious, eax // previous = current
+ mov eax, sourceByteStride // restore eax
+ fild highpassedDither
+ fmul const_float_dither_scale_
+ // end generate dither, dither signal in st(0)
+
+ faddp st(1), st(0) // stack: dither + value*(int scaler), int scaler
+ fistp dword ptr [edi] // pop st(0) into dest, stack: int scaler
+ jmp Float32_To_Int32_DitherClip_stored
+
+ Float32_To_Int32_DitherClip_clamp:
+ mov edx, dword ptr [esi] // load floating point value into integer register
+ shr edx, 31 // move sign bit into bit 0
+ add esi, eax // increment source ptr
+ //lea esi, [esi+eax]
+ add edx, 0x7FFFFFFF // convert to maximum range integers
+ mov dword ptr [edi], edx
+
+ Float32_To_Int32_DitherClip_stored:
+
+ //add edi, ebx // increment destination ptr
+ lea edi, [edi+ebx]
+
+ cmp esi, ecx // has src ptr reached end?
+ jne Float32_To_Int32_DitherClip_loop
+
+ ffree st(0)
+ fincstp
+
+ fwait
+ fnclex
+ fldcw savedFpuControlWord
+ }
+
+ ditherGenerator->previous = ditherPrevious;
+ ditherGenerator->randSeed1 = ditherRandSeed1;
+ ditherGenerator->randSeed2 = ditherRandSeed2;
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Float32_To_Int24(
+ void *destinationBuffer, signed int destinationStride,
+ void *sourceBuffer, signed int sourceStride,
+ unsigned int count, PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+/*
+ float *src = (float*)sourceBuffer;
+ unsigned char *dest = (unsigned char*)destinationBuffer;
+ signed long temp;
+
+ (void) ditherGenerator; // unused parameter
+
+ while( count-- )
+ {
+ // convert to 32 bit and drop the low 8 bits
+ double scaled = *src * 0x7FFFFFFF;
+ temp = (signed long) scaled;
+
+ dest[0] = (unsigned char)(temp >> 8);
+ dest[1] = (unsigned char)(temp >> 16);
+ dest[2] = (unsigned char)(temp >> 24);
+
+ src += sourceStride;
+ dest += destinationStride * 3;
+ }
+*/
+
+ short savedFpuControlWord;
+
+ signed long tempInt32;
+
+ (void) ditherGenerator; /* unused parameter */
+
+ __asm{
+ // esi -> source ptr
+ // eax -> source byte stride
+ // edi -> destination ptr
+ // ebx -> destination byte stride
+ // ecx -> source end ptr
+ // edx -> temp
+
+ mov esi, sourceBuffer
+
+ mov edx, 4 // sizeof float32
+ mov eax, sourceStride
+ imul eax, edx
+
+ mov ecx, count
+ imul ecx, eax
+ add ecx, esi
+
+ mov edi, destinationBuffer
+
+ mov edx, 3 // sizeof int24
+ mov ebx, destinationStride
+ imul ebx, edx
+
+ fwait
+ fstcw savedFpuControlWord
+ fldcw fpuControlWord_
+
+ fld int24Scaler_ // stack: (int)0x7FFFFF
+
+ Float32_To_Int24_loop:
+
+ // load unscaled value into st(0)
+ fld dword ptr [esi] // stack: value, (int)0x7FFFFF
+ add esi, eax // increment source ptr
+ //lea esi, [esi+eax]
+ fmul st(0), st(1) // st(0) *= st(1), stack: value*0x7FFFFF, (int)0x7FFFFF
+ fistp tempInt32 // pop st(0) into tempInt32, stack: (int)0x7FFFFF
+ mov edx, tempInt32
+
+ mov byte ptr [edi], DL
+ shr edx, 8
+ //mov byte ptr [edi+1], DL
+ //mov byte ptr [edi+2], DH
+ mov word ptr [edi+1], DX
+
+ //add edi, ebx // increment destination ptr
+ lea edi, [edi+ebx]
+
+ cmp esi, ecx // has src ptr reached end?
+ jne Float32_To_Int24_loop
+
+ ffree st(0)
+ fincstp
+
+ fwait
+ fnclex
+ fldcw savedFpuControlWord
+ }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Float32_To_Int24_Clip(
+ void *destinationBuffer, signed int destinationStride,
+ void *sourceBuffer, signed int sourceStride,
+ unsigned int count, PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+/*
+ float *src = (float*)sourceBuffer;
+ unsigned char *dest = (unsigned char*)destinationBuffer;
+ signed long temp;
+
+ (void) ditherGenerator; // unused parameter
+
+ while( count-- )
+ {
+ // convert to 32 bit and drop the low 8 bits
+ double scaled = *src * 0x7FFFFFFF;
+ PA_CLIP_( scaled, -2147483648., 2147483647. );
+ temp = (signed long) scaled;
+
+ dest[0] = (unsigned char)(temp >> 8);
+ dest[1] = (unsigned char)(temp >> 16);
+ dest[2] = (unsigned char)(temp >> 24);
+
+ src += sourceStride;
+ dest += destinationStride * 3;
+ }
+*/
+
+ short savedFpuControlWord;
+
+ signed long tempInt32;
+
+ (void) ditherGenerator; /* unused parameter */
+
+ __asm{
+ // esi -> source ptr
+ // eax -> source byte stride
+ // edi -> destination ptr
+ // ebx -> destination byte stride
+ // ecx -> source end ptr
+ // edx -> temp
+
+ mov esi, sourceBuffer
+
+ mov edx, 4 // sizeof float32
+ mov eax, sourceStride
+ imul eax, edx
+
+ mov ecx, count
+ imul ecx, eax
+ add ecx, esi
+
+ mov edi, destinationBuffer
+
+ mov edx, 3 // sizeof int24
+ mov ebx, destinationStride
+ imul ebx, edx
+
+ fwait
+ fstcw savedFpuControlWord
+ fldcw fpuControlWord_
+
+ fld int24Scaler_ // stack: (int)0x7FFFFF
+
+ Float32_To_Int24_Clip_loop:
+
+ mov edx, dword ptr [esi] // load floating point value into integer register
+
+ and edx, 0x7FFFFFFF // mask off sign
+ cmp edx, 0x3F800000 // greater than 1.0 or less than -1.0
+
+ jg Float32_To_Int24_Clip_clamp
+
+ // load unscaled value into st(0)
+ fld dword ptr [esi] // stack: value, (int)0x7FFFFF
+ add esi, eax // increment source ptr
+ //lea esi, [esi+eax]
+ fmul st(0), st(1) // st(0) *= st(1), stack: value*0x7FFFFF, (int)0x7FFFFF
+ fistp tempInt32 // pop st(0) into tempInt32, stack: (int)0x7FFFFF
+ mov edx, tempInt32
+ jmp Float32_To_Int24_Clip_store
+
+ Float32_To_Int24_Clip_clamp:
+ mov edx, dword ptr [esi] // load floating point value into integer register
+ shr edx, 31 // move sign bit into bit 0
+ add esi, eax // increment source ptr
+ //lea esi, [esi+eax]
+ add edx, 0x7FFFFF // convert to maximum range integers
+
+ Float32_To_Int24_Clip_store:
+
+ mov byte ptr [edi], DL
+ shr edx, 8
+ //mov byte ptr [edi+1], DL
+ //mov byte ptr [edi+2], DH
+ mov word ptr [edi+1], DX
+
+ //add edi, ebx // increment destination ptr
+ lea edi, [edi+ebx]
+
+ cmp esi, ecx // has src ptr reached end?
+ jne Float32_To_Int24_Clip_loop
+
+ ffree st(0)
+ fincstp
+
+ fwait
+ fnclex
+ fldcw savedFpuControlWord
+ }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Float32_To_Int24_DitherClip(
+ void *destinationBuffer, signed int destinationStride,
+ void *sourceBuffer, signed int sourceStride,
+ unsigned int count, PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+/*
+ float *src = (float*)sourceBuffer;
+ unsigned char *dest = (unsigned char*)destinationBuffer;
+ signed long temp;
+
+ while( count-- )
+ {
+ // convert to 32 bit and drop the low 8 bits
+
+ // FIXME: the dither amplitude here appears to be too small by 8 bits
+ double dither = PaUtil_GenerateFloatTriangularDither( ditherGenerator );
+ // use smaller scaler to prevent overflow when we add the dither
+ double dithered = ((double)*src * (2147483646.0)) + dither;
+ PA_CLIP_( dithered, -2147483648., 2147483647. );
+
+ temp = (signed long) dithered;
+
+ dest[0] = (unsigned char)(temp >> 8);
+ dest[1] = (unsigned char)(temp >> 16);
+ dest[2] = (unsigned char)(temp >> 24);
+
+ src += sourceStride;
+ dest += destinationStride * 3;
+ }
+*/
+
+ short savedFpuControlWord;
+
+ // spill storage:
+ signed long sourceByteStride;
+ signed long highpassedDither;
+
+ // dither state:
+ unsigned long ditherPrevious = ditherGenerator->previous;
+ unsigned long ditherRandSeed1 = ditherGenerator->randSeed1;
+ unsigned long ditherRandSeed2 = ditherGenerator->randSeed2;
+
+ signed long tempInt32;
+
+ __asm{
+ // esi -> source ptr
+ // eax -> source byte stride
+ // edi -> destination ptr
+ // ebx -> destination byte stride
+ // ecx -> source end ptr
+ // edx -> temp
+
+ mov esi, sourceBuffer
+
+ mov edx, 4 // sizeof float32
+ mov eax, sourceStride
+ imul eax, edx
+
+ mov ecx, count
+ imul ecx, eax
+ add ecx, esi
+
+ mov edi, destinationBuffer
+
+ mov edx, 3 // sizeof int24
+ mov ebx, destinationStride
+ imul ebx, edx
+
+ fwait
+ fstcw savedFpuControlWord
+ fldcw fpuControlWord_
+
+ fld ditheredInt24Scaler_ // stack: int scaler
+
+ Float32_To_Int24_DitherClip_loop:
+
+ mov edx, dword ptr [esi] // load floating point value into integer register
+
+ and edx, 0x7FFFFFFF // mask off sign
+ cmp edx, 0x3F800000 // greater than 1.0 or less than -1.0
+
+ jg Float32_To_Int24_DitherClip_clamp
+
+ // load unscaled value into st(0)
+ fld dword ptr [esi] // stack: value, int scaler
+ add esi, eax // increment source ptr
+ //lea esi, [esi+eax]
+ fmul st(0), st(1) // st(0) *= st(1), stack: value*(int scaler), int scaler
+
+ /*
+ // call PaUtil_GenerateFloatTriangularDither with C calling convention
+ mov sourceByteStride, eax // save eax
+ mov sourceEnd, ecx // save ecx
+ push ditherGenerator // pass ditherGenerator parameter on stack
+ call PaUtil_GenerateFloatTriangularDither // stack: dither, value*(int scaler), int scaler
+ pop edx // clear parameter off stack
+ mov ecx, sourceEnd // restore ecx
+ mov eax, sourceByteStride // restore eax
+ */
+
+ // generate dither
+ mov sourceByteStride, eax // save eax
+ mov edx, 196314165
+ mov eax, ditherRandSeed1
+ mul edx // eax:edx = eax * 196314165
+ //add eax, 907633515
+ lea eax, [eax+907633515]
+ mov ditherRandSeed1, eax
+ mov edx, 196314165
+ mov eax, ditherRandSeed2
+ mul edx // eax:edx = eax * 196314165
+ //add eax, 907633515
+ lea eax, [eax+907633515]
+ mov edx, ditherRandSeed1
+ shr edx, PA_DITHER_SHIFT_
+ mov ditherRandSeed2, eax
+ shr eax, PA_DITHER_SHIFT_
+ //add eax, edx // eax -> current
+ lea eax, [eax+edx]
+ mov edx, ditherPrevious
+ neg edx
+ lea edx, [eax+edx] // highpass = current - previous
+ mov highpassedDither, edx
+ mov ditherPrevious, eax // previous = current
+ mov eax, sourceByteStride // restore eax
+ fild highpassedDither
+ fmul const_float_dither_scale_
+ // end generate dither, dither signal in st(0)
+
+ faddp st(1), st(0) // stack: dither * value*(int scaler), int scaler
+ fistp tempInt32 // pop st(0) into tempInt32, stack: int scaler
+ mov edx, tempInt32
+ jmp Float32_To_Int24_DitherClip_store
+
+ Float32_To_Int24_DitherClip_clamp:
+ mov edx, dword ptr [esi] // load floating point value into integer register
+ shr edx, 31 // move sign bit into bit 0
+ add esi, eax // increment source ptr
+ //lea esi, [esi+eax]
+ add edx, 0x7FFFFF // convert to maximum range integers
+
+ Float32_To_Int24_DitherClip_store:
+
+ mov byte ptr [edi], DL
+ shr edx, 8
+ //mov byte ptr [edi+1], DL
+ //mov byte ptr [edi+2], DH
+ mov word ptr [edi+1], DX
+
+ //add edi, ebx // increment destination ptr
+ lea edi, [edi+ebx]
+
+ cmp esi, ecx // has src ptr reached end?
+ jne Float32_To_Int24_DitherClip_loop
+
+ ffree st(0)
+ fincstp
+
+ fwait
+ fnclex
+ fldcw savedFpuControlWord
+ }
+
+ ditherGenerator->previous = ditherPrevious;
+ ditherGenerator->randSeed1 = ditherRandSeed1;
+ ditherGenerator->randSeed2 = ditherRandSeed2;
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Float32_To_Int16(
+ void *destinationBuffer, signed int destinationStride,
+ void *sourceBuffer, signed int sourceStride,
+ unsigned int count, PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+/*
+ float *src = (float*)sourceBuffer;
+ signed short *dest = (signed short*)destinationBuffer;
+ (void)ditherGenerator; // unused parameter
+
+ while( count-- )
+ {
+
+ short samp = (short) (*src * (32767.0f));
+ *dest = samp;
+
+ src += sourceStride;
+ dest += destinationStride;
+ }
+*/
+
+ short savedFpuControlWord;
+
+ (void) ditherGenerator; /* unused parameter */
+
+ __asm{
+ // esi -> source ptr
+ // eax -> source byte stride
+ // edi -> destination ptr
+ // ebx -> destination byte stride
+ // ecx -> source end ptr
+ // edx -> temp
+
+ mov esi, sourceBuffer
+
+ mov edx, 4 // sizeof float32
+ mov eax, sourceStride
+ imul eax, edx // source byte stride
+
+ mov ecx, count
+ imul ecx, eax
+ add ecx, esi // source end ptr = count * source byte stride + source ptr
+
+ mov edi, destinationBuffer
+
+ mov edx, 2 // sizeof int16
+ mov ebx, destinationStride
+ imul ebx, edx // destination byte stride
+
+ fwait
+ fstcw savedFpuControlWord
+ fldcw fpuControlWord_
+
+ fld int16Scaler_ // stack: (int)0x7FFF
+
+ Float32_To_Int16_loop:
+
+ // load unscaled value into st(0)
+ fld dword ptr [esi] // stack: value, (int)0x7FFF
+ add esi, eax // increment source ptr
+ //lea esi, [esi+eax]
+ fmul st(0), st(1) // st(0) *= st(1), stack: value*0x7FFF, (int)0x7FFF
+ fistp word ptr [edi] // store scaled int into dest, stack: (int)0x7FFF
+
+ add edi, ebx // increment destination ptr
+ //lea edi, [edi+ebx]
+
+ cmp esi, ecx // has src ptr reached end?
+ jne Float32_To_Int16_loop
+
+ ffree st(0)
+ fincstp
+
+ fwait
+ fnclex
+ fldcw savedFpuControlWord
+ }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Float32_To_Int16_Clip(
+ void *destinationBuffer, signed int destinationStride,
+ void *sourceBuffer, signed int sourceStride,
+ unsigned int count, PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+/*
+ float *src = (float*)sourceBuffer;
+ signed short *dest = (signed short*)destinationBuffer;
+ (void)ditherGenerator; // unused parameter
+
+ while( count-- )
+ {
+ long samp = (signed long) (*src * (32767.0f));
+ PA_CLIP_( samp, -0x8000, 0x7FFF );
+ *dest = (signed short) samp;
+
+ src += sourceStride;
+ dest += destinationStride;
+ }
+*/
+
+ short savedFpuControlWord;
+
+ (void) ditherGenerator; /* unused parameter */
+
+ __asm{
+ // esi -> source ptr
+ // eax -> source byte stride
+ // edi -> destination ptr
+ // ebx -> destination byte stride
+ // ecx -> source end ptr
+ // edx -> temp
+
+ mov esi, sourceBuffer
+
+ mov edx, 4 // sizeof float32
+ mov eax, sourceStride
+ imul eax, edx // source byte stride
+
+ mov ecx, count
+ imul ecx, eax
+ add ecx, esi // source end ptr = count * source byte stride + source ptr
+
+ mov edi, destinationBuffer
+
+ mov edx, 2 // sizeof int16
+ mov ebx, destinationStride
+ imul ebx, edx // destination byte stride
+
+ fwait
+ fstcw savedFpuControlWord
+ fldcw fpuControlWord_
+
+ fld int16Scaler_ // stack: (int)0x7FFF
+
+ Float32_To_Int16_Clip_loop:
+
+ mov edx, dword ptr [esi] // load floating point value into integer register
+
+ and edx, 0x7FFFFFFF // mask off sign
+ cmp edx, 0x3F800000 // greater than 1.0 or less than -1.0
+
+ jg Float32_To_Int16_Clip_clamp
+
+ // load unscaled value into st(0)
+ fld dword ptr [esi] // stack: value, (int)0x7FFF
+ add esi, eax // increment source ptr
+ //lea esi, [esi+eax]
+ fmul st(0), st(1) // st(0) *= st(1), stack: value*0x7FFF, (int)0x7FFF
+ fistp word ptr [edi] // store scaled int into dest, stack: (int)0x7FFF
+ jmp Float32_To_Int16_Clip_stored
+
+ Float32_To_Int16_Clip_clamp:
+ mov edx, dword ptr [esi] // load floating point value into integer register
+ shr edx, 31 // move sign bit into bit 0
+ add esi, eax // increment source ptr
+ //lea esi, [esi+eax]
+ add dx, 0x7FFF // convert to maximum range integers
+ mov word ptr [edi], dx // store clamped into into dest
+
+ Float32_To_Int16_Clip_stored:
+
+ add edi, ebx // increment destination ptr
+ //lea edi, [edi+ebx]
+
+ cmp esi, ecx // has src ptr reached end?
+ jne Float32_To_Int16_Clip_loop
+
+ ffree st(0)
+ fincstp
+
+ fwait
+ fnclex
+ fldcw savedFpuControlWord
+ }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Float32_To_Int16_DitherClip(
+ void *destinationBuffer, signed int destinationStride,
+ void *sourceBuffer, signed int sourceStride,
+ unsigned int count, PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+/*
+ float *src = (float*)sourceBuffer;
+ signed short *dest = (signed short*)destinationBuffer;
+ (void)ditherGenerator; // unused parameter
+
+ while( count-- )
+ {
+
+ float dither = PaUtil_GenerateFloatTriangularDither( ditherGenerator );
+ // use smaller scaler to prevent overflow when we add the dither
+ float dithered = (*src * (32766.0f)) + dither;
+ signed long samp = (signed long) dithered;
+ PA_CLIP_( samp, -0x8000, 0x7FFF );
+ *dest = (signed short) samp;
+
+ src += sourceStride;
+ dest += destinationStride;
+ }
+*/
+
+ short savedFpuControlWord;
+
+ // spill storage:
+ signed long sourceByteStride;
+ signed long highpassedDither;
+
+ // dither state:
+ unsigned long ditherPrevious = ditherGenerator->previous;
+ unsigned long ditherRandSeed1 = ditherGenerator->randSeed1;
+ unsigned long ditherRandSeed2 = ditherGenerator->randSeed2;
+
+ __asm{
+ // esi -> source ptr
+ // eax -> source byte stride
+ // edi -> destination ptr
+ // ebx -> destination byte stride
+ // ecx -> source end ptr
+ // edx -> temp
+
+ mov esi, sourceBuffer
+
+ mov edx, 4 // sizeof float32
+ mov eax, sourceStride
+ imul eax, edx // source byte stride
+
+ mov ecx, count
+ imul ecx, eax
+ add ecx, esi // source end ptr = count * source byte stride + source ptr
+
+ mov edi, destinationBuffer
+
+ mov edx, 2 // sizeof int16
+ mov ebx, destinationStride
+ imul ebx, edx // destination byte stride
+
+ fwait
+ fstcw savedFpuControlWord
+ fldcw fpuControlWord_
+
+ fld ditheredInt16Scaler_ // stack: int scaler
+
+ Float32_To_Int16_DitherClip_loop:
+
+ mov edx, dword ptr [esi] // load floating point value into integer register
+
+ and edx, 0x7FFFFFFF // mask off sign
+ cmp edx, 0x3F800000 // greater than 1.0 or less than -1.0
+
+ jg Float32_To_Int16_DitherClip_clamp
+
+ // load unscaled value into st(0)
+ fld dword ptr [esi] // stack: value, int scaler
+ add esi, eax // increment source ptr
+ //lea esi, [esi+eax]
+ fmul st(0), st(1) // st(0) *= st(1), stack: value*(int scaler), int scaler
+
+ /*
+ // call PaUtil_GenerateFloatTriangularDither with C calling convention
+ mov sourceByteStride, eax // save eax
+ mov sourceEnd, ecx // save ecx
+ push ditherGenerator // pass ditherGenerator parameter on stack
+ call PaUtil_GenerateFloatTriangularDither // stack: dither, value*(int scaler), int scaler
+ pop edx // clear parameter off stack
+ mov ecx, sourceEnd // restore ecx
+ mov eax, sourceByteStride // restore eax
+ */
+
+ // generate dither
+ mov sourceByteStride, eax // save eax
+ mov edx, 196314165
+ mov eax, ditherRandSeed1
+ mul edx // eax:edx = eax * 196314165
+ //add eax, 907633515
+ lea eax, [eax+907633515]
+ mov ditherRandSeed1, eax
+ mov edx, 196314165
+ mov eax, ditherRandSeed2
+ mul edx // eax:edx = eax * 196314165
+ //add eax, 907633515
+ lea eax, [eax+907633515]
+ mov edx, ditherRandSeed1
+ shr edx, PA_DITHER_SHIFT_
+ mov ditherRandSeed2, eax
+ shr eax, PA_DITHER_SHIFT_
+ //add eax, edx // eax -> current
+ lea eax, [eax+edx] // current = randSeed1>>x + randSeed2>>x
+ mov edx, ditherPrevious
+ neg edx
+ lea edx, [eax+edx] // highpass = current - previous
+ mov highpassedDither, edx
+ mov ditherPrevious, eax // previous = current
+ mov eax, sourceByteStride // restore eax
+ fild highpassedDither
+ fmul const_float_dither_scale_
+ // end generate dither, dither signal in st(0)
+
+ faddp st(1), st(0) // stack: dither * value*(int scaler), int scaler
+ fistp word ptr [edi] // store scaled int into dest, stack: int scaler
+ jmp Float32_To_Int16_DitherClip_stored
+
+ Float32_To_Int16_DitherClip_clamp:
+ mov edx, dword ptr [esi] // load floating point value into integer register
+ shr edx, 31 // move sign bit into bit 0
+ add esi, eax // increment source ptr
+ //lea esi, [esi+eax]
+ add dx, 0x7FFF // convert to maximum range integers
+ mov word ptr [edi], dx // store clamped into into dest
+
+ Float32_To_Int16_DitherClip_stored:
+
+ add edi, ebx // increment destination ptr
+ //lea edi, [edi+ebx]
+
+ cmp esi, ecx // has src ptr reached end?
+ jne Float32_To_Int16_DitherClip_loop
+
+ ffree st(0)
+ fincstp
+
+ fwait
+ fnclex
+ fldcw savedFpuControlWord
+ }
+
+ ditherGenerator->previous = ditherPrevious;
+ ditherGenerator->randSeed1 = ditherRandSeed1;
+ ditherGenerator->randSeed2 = ditherRandSeed2;
+}
+
+/* -------------------------------------------------------------------------- */
+
+void PaUtil_InitializeX86PlainConverters( void )
+{
+ paConverters.Float32_To_Int32 = Float32_To_Int32;
+ paConverters.Float32_To_Int32_Clip = Float32_To_Int32_Clip;
+ paConverters.Float32_To_Int32_DitherClip = Float32_To_Int32_DitherClip;
+
+ paConverters.Float32_To_Int24 = Float32_To_Int24;
+ paConverters.Float32_To_Int24_Clip = Float32_To_Int24_Clip;
+ paConverters.Float32_To_Int24_DitherClip = Float32_To_Int24_DitherClip;
+
+ paConverters.Float32_To_Int16 = Float32_To_Int16;
+ paConverters.Float32_To_Int16_Clip = Float32_To_Int16_Clip;
+ paConverters.Float32_To_Int16_DitherClip = Float32_To_Int16_DitherClip;
+}
+
+/* -------------------------------------------------------------------------- */
diff --git a/pjmedia/src/pjmedia/portaudio/pa_x86_plain_converters.h b/pjmedia/src/pjmedia/portaudio/pa_x86_plain_converters.h
index f56c710f..5aedc512 100644
--- a/pjmedia/src/pjmedia/portaudio/pa_x86_plain_converters.h
+++ b/pjmedia/src/pjmedia/portaudio/pa_x86_plain_converters.h
@@ -1,19 +1,40 @@
-#ifndef PA_X86_PLAIN_CONVERTERS_H
-#define PA_X86_PLAIN_CONVERTERS_H
-
-#ifdef __cplusplus
-extern "C"
-{
-#endif /* __cplusplus */
-
-
-/**
- @brief Install optimised converter functions suitable for all IA32 processors
-*/
-void PaUtil_InitializeX86PlainConverters( void );
-
-
-#ifdef __cplusplus
-}
-#endif /* __cplusplus */
-#endif /* PA_X86_PLAIN_CONVERTERS_H */
+/*
+ * PJMEDIA - Multimedia over IP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef PA_X86_PLAIN_CONVERTERS_H
+#define PA_X86_PLAIN_CONVERTERS_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif /* __cplusplus */
+
+
+/**
+ @brief Install optimised converter functions suitable for all IA32 processors
+*/
+void PaUtil_InitializeX86PlainConverters( void );
+
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+#endif /* PA_X86_PLAIN_CONVERTERS_H */
diff --git a/pjmedia/src/pjmedia/portaudio/portaudio.h b/pjmedia/src/pjmedia/portaudio/portaudio.h
index 341d92c9..aaf5c80e 100644
--- a/pjmedia/src/pjmedia/portaudio/portaudio.h
+++ b/pjmedia/src/pjmedia/portaudio/portaudio.h
@@ -1,1123 +1,1144 @@
-
-#ifndef PORTAUDIO_H
-#define PORTAUDIO_H
-/*
- * $Id: portaudio.h,v 1.5.2.50 2004/12/13 11:50:40 rossbencina Exp $
- * PortAudio Portable Real-Time Audio Library
- * PortAudio API Header File
- * Latest version available at: http://www.portaudio.com/
- *
- * Copyright (c) 1999-2002 Ross Bencina and Phil Burk
- *
- * Permission is hereby granted, free of charge, to any person obtaining
- * a copy of this software and associated documentation files
- * (the "Software"), to deal in the Software without restriction,
- * including without limitation the rights to use, copy, modify, merge,
- * publish, distribute, sublicense, and/or sell copies of the Software,
- * and to permit persons to whom the Software is furnished to do so,
- * subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be
- * included in all copies or substantial portions of the Software.
- *
- * Any person wishing to distribute modifications to the Software is
- * requested to send the modifications to the original developer so that
- * they can be incorporated into the canonical version.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
- * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
- * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
- * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
- * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
- */
-
-/** @file
- @brief The PortAudio API.
-*/
-
-
-#ifdef __cplusplus
-extern "C"
-{
-#endif /* __cplusplus */
-
-
-/** Retrieve the release number of the currently running PortAudio build,
- eg 1900.
-*/
-int Pa_GetVersion( void );
-
-
-/** Retrieve a textual description of the current PortAudio build,
- eg "PortAudio V19-devel 13 October 2002".
-*/
-const char* Pa_GetVersionText( void );
-
-
-/** Error codes returned by PortAudio functions.
- Note that with the exception of paNoError, all PaErrorCodes are negative.
-*/
-
-typedef int PaError;
-typedef enum PaErrorCode
-{
- paNoError = 0,
-
- paNotInitialized = -10000,
- paUnanticipatedHostError,
- paInvalidChannelCount,
- paInvalidSampleRate,
- paInvalidDevice,
- paInvalidFlag,
- paSampleFormatNotSupported,
- paBadIODeviceCombination,
- paInsufficientMemory,
- paBufferTooBig,
- paBufferTooSmall,
- paNullCallback,
- paBadStreamPtr,
- paTimedOut,
- paInternalError,
- paDeviceUnavailable,
- paIncompatibleHostApiSpecificStreamInfo,
- paStreamIsStopped,
- paStreamIsNotStopped,
- paInputOverflowed,
- paOutputUnderflowed,
- paHostApiNotFound,
- paInvalidHostApi,
- paCanNotReadFromACallbackStream, /**< @todo review error code name */
- paCanNotWriteToACallbackStream, /**< @todo review error code name */
- paCanNotReadFromAnOutputOnlyStream, /**< @todo review error code name */
- paCanNotWriteToAnInputOnlyStream, /**< @todo review error code name */
- paIncompatibleStreamHostApi
-} PaErrorCode;
-
-
-/** Translate the supplied PortAudio error code into a human readable
- message.
-*/
-const char *Pa_GetErrorText( PaError errorCode );
-
-
-/** Library initialization function - call this before using PortAudio.
- This function initialises internal data structures and prepares underlying
- host APIs for use. This function MUST be called before using any other
- PortAudio API functions.
-
- If Pa_Initialize() is called multiple times, each successful
- call must be matched with a corresponding call to Pa_Terminate().
- Pairs of calls to Pa_Initialize()/Pa_Terminate() may overlap, and are not
- required to be fully nested.
-
- Note that if Pa_Initialize() returns an error code, Pa_Terminate() should
- NOT be called.
-
- @return paNoError if successful, otherwise an error code indicating the cause
- of failure.
-
- @see Pa_Terminate
-*/
-PaError Pa_Initialize( void );
-
-
-/** Library termination function - call this when finished using PortAudio.
- This function deallocates all resources allocated by PortAudio since it was
- initializied by a call to Pa_Initialize(). In cases where Pa_Initialise() has
- been called multiple times, each call must be matched with a corresponding call
- to Pa_Terminate(). The final matching call to Pa_Terminate() will automatically
- close any PortAudio streams that are still open.
-
- Pa_Terminate() MUST be called before exiting a program which uses PortAudio.
- Failure to do so may result in serious resource leaks, such as audio devices
- not being available until the next reboot.
-
- @return paNoError if successful, otherwise an error code indicating the cause
- of failure.
-
- @see Pa_Initialize
-*/
-PaError Pa_Terminate( void );
-
-
-
-/** The type used to refer to audio devices. Values of this type usually
- range from 0 to (Pa_DeviceCount-1), and may also take on the PaNoDevice
- and paUseHostApiSpecificDeviceSpecification values.
-
- @see Pa_DeviceCount, paNoDevice, paUseHostApiSpecificDeviceSpecification
-*/
-typedef int PaDeviceIndex;
-
-
-/** A special PaDeviceIndex value indicating that no device is available,
- or should be used.
-
- @see PaDeviceIndex
-*/
-#define paNoDevice ((PaDeviceIndex)-1)
-
-
-/** A special PaDeviceIndex value indicating that the device(s) to be used
- are specified in the host api specific stream info structure.
-
- @see PaDeviceIndex
-*/
-#define paUseHostApiSpecificDeviceSpecification ((PaDeviceIndex)-2)
-
-
-/* Host API enumeration mechanism */
-
-/** The type used to enumerate to host APIs at runtime. Values of this type
- range from 0 to (Pa_GetHostApiCount()-1).
-
- @see Pa_GetHostApiCount
-*/
-typedef int PaHostApiIndex;
-
-
-/** Retrieve the number of available host APIs. Even if a host API is
- available it may have no devices available.
-
- @return A non-negative value indicating the number of available host APIs
- or, a PaErrorCode (which are always negative) if PortAudio is not initialized
- or an error is encountered.
-
- @see PaHostApiIndex
-*/
-PaHostApiIndex Pa_GetHostApiCount( void );
-
-
-/** Retrieve the index of the default host API. The default host API will be
- the lowest common denominator host API on the current platform and is
- unlikely to provide the best performance.
-
- @return A non-negative value ranging from 0 to (Pa_GetHostApiCount()-1)
- indicating the default host API index or, a PaErrorCode (which are always
- negative) if PortAudio is not initialized or an error is encountered.
-*/
-PaHostApiIndex Pa_GetDefaultHostApi( void );
-
-
-/** Unchanging unique identifiers for each supported host API. This type
- is used in the PaHostApiInfo structure. The values are guaranteed to be
- unique and to never change, thus allowing code to be written that
- conditionally uses host API specific extensions.
-
- New type ids will be allocated when support for a host API reaches
- "public alpha" status, prior to that developers should use the
- paInDevelopment type id.
-
- @see PaHostApiInfo
-*/
-typedef enum PaHostApiTypeId
-{
- paInDevelopment=0, /* use while developing support for a new host API */
- paDirectSound=1,
- paMME=2,
- paASIO=3,
- paSoundManager=4,
- paCoreAudio=5,
- paOSS=7,
- paALSA=8,
- paAL=9,
- paBeOS=10,
- paWDMKS=11,
- paJACK=12
-} PaHostApiTypeId;
-
-
-/** A structure containing information about a particular host API. */
-
-typedef struct PaHostApiInfo
-{
- /** this is struct version 1 */
- int structVersion;
- /** The well known unique identifier of this host API @see PaHostApiTypeId */
- PaHostApiTypeId type;
- /** A textual description of the host API for display on user interfaces. */
- const char *name;
-
- /** The number of devices belonging to this host API. This field may be
- used in conjunction with Pa_HostApiDeviceIndexToDeviceIndex() to enumerate
- all devices for this host API.
- @see Pa_HostApiDeviceIndexToDeviceIndex
- */
- int deviceCount;
-
- /** The the default input device for this host API. The value will be a
- device index ranging from 0 to (Pa_GetDeviceCount()-1), or paNoDevice
- if no default input device is available.
- */
- PaDeviceIndex defaultInputDevice;
-
- /** The the default output device for this host API. The value will be a
- device index ranging from 0 to (Pa_GetDeviceCount()-1), or paNoDevice
- if no default output device is available.
- */
- PaDeviceIndex defaultOutputDevice;
-
-} PaHostApiInfo;
-
-
-/** Retrieve a pointer to a structure containing information about a specific
- host Api.
-
- @param hostApi A valid host API index ranging from 0 to (Pa_GetHostApiCount()-1)
-
- @return A pointer to an immutable PaHostApiInfo structure describing
- a specific host API. If the hostApi parameter is out of range or an error
- is encountered, the function returns NULL.
-
- The returned structure is owned by the PortAudio implementation and must not
- be manipulated or freed. The pointer is only guaranteed to be valid between
- calls to Pa_Initialize() and Pa_Terminate().
-*/
-const PaHostApiInfo * Pa_GetHostApiInfo( PaHostApiIndex hostApi );
-
-
-/** Convert a static host API unique identifier, into a runtime
- host API index.
-
- @param type A unique host API identifier belonging to the PaHostApiTypeId
- enumeration.
-
- @return A valid PaHostApiIndex ranging from 0 to (Pa_GetHostApiCount()-1) or,
- a PaErrorCode (which are always negative) if PortAudio is not initialized
- or an error is encountered.
-
- The paHostApiNotFound error code indicates that the host API specified by the
- type parameter is not available.
-
- @see PaHostApiTypeId
-*/
-PaHostApiIndex Pa_HostApiTypeIdToHostApiIndex( PaHostApiTypeId type );
-
-
-/** Convert a host-API-specific device index to standard PortAudio device index.
- This function may be used in conjunction with the deviceCount field of
- PaHostApiInfo to enumerate all devices for the specified host API.
-
- @param hostApi A valid host API index ranging from 0 to (Pa_GetHostApiCount()-1)
-
- @param hostApiDeviceIndex A valid per-host device index in the range
- 0 to (Pa_GetHostApiInfo(hostApi)->deviceCount-1)
-
- @return A non-negative PaDeviceIndex ranging from 0 to (Pa_GetDeviceCount()-1)
- or, a PaErrorCode (which are always negative) if PortAudio is not initialized
- or an error is encountered.
-
- A paInvalidHostApi error code indicates that the host API index specified by
- the hostApi parameter is out of range.
-
- A paInvalidDevice error code indicates that the hostApiDeviceIndex parameter
- is out of range.
-
- @see PaHostApiInfo
-*/
-PaDeviceIndex Pa_HostApiDeviceIndexToDeviceIndex( PaHostApiIndex hostApi,
- int hostApiDeviceIndex );
-
-
-
-/** Structure used to return information about a host error condition.
-*/
-typedef struct PaHostErrorInfo{
- PaHostApiTypeId hostApiType; /**< the host API which returned the error code */
- long errorCode; /**< the error code returned */
- const char *errorText; /**< a textual description of the error if available, otherwise a zero-length string */
-}PaHostErrorInfo;
-
-
-/** Return information about the last host error encountered. The error
- information returned by Pa_GetLastHostErrorInfo() will never be modified
- asyncronously by errors occurring in other PortAudio owned threads
- (such as the thread that manages the stream callback.)
-
- This function is provided as a last resort, primarily to enhance debugging
- by providing clients with access to all available error information.
-
- @return A pointer to an immutable structure constaining information about
- the host error. The values in this structure will only be valid if a
- PortAudio function has previously returned the paUnanticipatedHostError
- error code.
-*/
-const PaHostErrorInfo* Pa_GetLastHostErrorInfo( void );
-
-
-
-/* Device enumeration and capabilities */
-
-/** Retrieve the number of available devices. The number of available devices
- may be zero.
-
- @return A non-negative value indicating the number of available devices or,
- a PaErrorCode (which are always negative) if PortAudio is not initialized
- or an error is encountered.
-*/
-PaDeviceIndex Pa_GetDeviceCount( void );
-
-
-/** Retrieve the index of the default input device. The result can be
- used in the inputDevice parameter to Pa_OpenStream().
-
- @return The default input device index for the default host API, or paNoDevice
- if no default input device is available or an error was encountered.
-*/
-PaDeviceIndex Pa_GetDefaultInputDevice( void );
-
-
-/** Retrieve the index of the default output device. The result can be
- used in the outputDevice parameter to Pa_OpenStream().
-
- @return The default output device index for the defualt host API, or paNoDevice
- if no default output device is available or an error was encountered.
-
- @note
- On the PC, the user can specify a default device by
- setting an environment variable. For example, to use device #1.
-<pre>
- set PA_RECOMMENDED_OUTPUT_DEVICE=1
-</pre>
- The user should first determine the available device ids by using
- the supplied application "pa_devs".
-*/
-PaDeviceIndex Pa_GetDefaultOutputDevice( void );
-
-
-/** The type used to represent monotonic time in seconds that can be used
- for syncronisation. The type is used for the outTime argument to the
- PaStreamCallback and as the result of Pa_GetStreamTime().
-
- @see PaStreamCallback, Pa_GetStreamTime
-*/
-typedef double PaTime;
-
-
-/** A type used to specify one or more sample formats. Each value indicates
- a possible format for sound data passed to and from the stream callback,
- Pa_ReadStream and Pa_WriteStream.
-
- The standard formats paFloat32, paInt16, paInt32, paInt24, paInt8
- and aUInt8 are usually implemented by all implementations.
-
- The floating point representation (paFloat32) uses +1.0 and -1.0 as the
- maximum and minimum respectively.
-
- paUInt8 is an unsigned 8 bit format where 128 is considered "ground"
-
- The paNonInterleaved flag indicates that a multichannel buffer is passed
- as a set of non-interleaved pointers.
-
- @see Pa_OpenStream, Pa_OpenDefaultStream, PaDeviceInfo
- @see paFloat32, paInt16, paInt32, paInt24, paInt8
- @see paUInt8, paCustomFormat, paNonInterleaved
-*/
-typedef unsigned long PaSampleFormat;
-
-
-#define paFloat32 ((PaSampleFormat) 0x00000001) /**< @see PaSampleFormat */
-#define paInt32 ((PaSampleFormat) 0x00000002) /**< @see PaSampleFormat */
-#define paInt24 ((PaSampleFormat) 0x00000004) /**< Packed 24 bit format. @see PaSampleFormat */
-#define paInt16 ((PaSampleFormat) 0x00000008) /**< @see PaSampleFormat */
-#define paInt8 ((PaSampleFormat) 0x00000010) /**< @see PaSampleFormat */
-#define paUInt8 ((PaSampleFormat) 0x00000020) /**< @see PaSampleFormat */
-#define paCustomFormat ((PaSampleFormat) 0x00010000)/**< @see PaSampleFormat */
-
-#define paNonInterleaved ((PaSampleFormat) 0x80000000)
-
-/** A structure providing information and capabilities of PortAudio devices.
- Devices may support input, output or both input and output.
-*/
-typedef struct PaDeviceInfo
-{
- int structVersion; /* this is struct version 2 */
- const char *name;
- PaHostApiIndex hostApi; /* note this is a host API index, not a type id*/
-
- int maxInputChannels;
- int maxOutputChannels;
-
- /* Default latency values for interactive performance. */
- PaTime defaultLowInputLatency;
- PaTime defaultLowOutputLatency;
- /* Default latency values for robust non-interactive applications (eg. playing sound files). */
- PaTime defaultHighInputLatency;
- PaTime defaultHighOutputLatency;
-
- double defaultSampleRate;
-} PaDeviceInfo;
-
-
-/** Retrieve a pointer to a PaDeviceInfo structure containing information
- about the specified device.
- @return A pointer to an immutable PaDeviceInfo structure. If the device
- parameter is out of range the function returns NULL.
-
- @param device A valid device index in the range 0 to (Pa_GetDeviceCount()-1)
-
- @note PortAudio manages the memory referenced by the returned pointer,
- the client must not manipulate or free the memory. The pointer is only
- guaranteed to be valid between calls to Pa_Initialize() and Pa_Terminate().
-
- @see PaDeviceInfo, PaDeviceIndex
-*/
-const PaDeviceInfo* Pa_GetDeviceInfo( PaDeviceIndex device );
-
-
-/** Parameters for one direction (input or output) of a stream.
-*/
-typedef struct PaStreamParameters
-{
- /** A valid device index in the range 0 to (Pa_GetDeviceCount()-1)
- specifying the device to be used or the special constant
- paUseHostApiSpecificDeviceSpecification which indicates that the actual
- device(s) to use are specified in hostApiSpecificStreamInfo.
- This field must not be set to paNoDevice.
- */
- PaDeviceIndex device;
-
- /** The number of channels of sound to be delivered to the
- stream callback or accessed by Pa_ReadStream() or Pa_WriteStream().
- It can range from 1 to the value of maxInputChannels in the
- PaDeviceInfo record for the device specified by the device parameter.
- */
- int channelCount;
-
- /** The sample format of the buffer provided to the stream callback,
- a_ReadStream() or Pa_WriteStream(). It may be any of the formats described
- by the PaSampleFormat enumeration.
- */
- PaSampleFormat sampleFormat;
-
- /** The desired latency in seconds. Where practical, implementations should
- configure their latency based on these parameters, otherwise they may
- choose the closest viable latency instead. Unless the suggested latency
- is greater than the absolute upper limit for the device implementations
- shouldround the suggestedLatency up to the next practial value - ie to
- provide an equal or higher latency than suggestedLatency whereever possibe.
- Actual latency values for an open stream may be retrieved using the
- inputLatency and outputLatency fields of the PaStreamInfo structure
- returned by Pa_GetStreamInfo().
- @see default*Latency in PaDeviceInfo, *Latency in PaStreamInfo
- */
- PaTime suggestedLatency;
-
- /** An optional pointer to a host api specific data structure
- containing additional information for device setup and/or stream processing.
- hostApiSpecificStreamInfo is never required for correct operation,
- if not used it should be set to NULL.
- */
- void *hostApiSpecificStreamInfo;
-
-} PaStreamParameters;
-
-
-/** Return code for Pa_IsFormatSupported indicating success. */
-#define paFormatIsSupported (0)
-
-/** Determine whether it would be possible to open a stream with the specified
- parameters.
-
- @param inputParameters A structure that describes the input parameters used to
- open a stream. The suggestedLatency field is ignored. See PaStreamParameters
- for a description of these parameters. inputParameters must be NULL for
- output-only streams.
-
- @param outputParameters A structure that describes the output parameters used
- to open a stream. The suggestedLatency field is ignored. See PaStreamParameters
- for a description of these parameters. outputParameters must be NULL for
- input-only streams.
-
- @param sampleRate The required sampleRate. For full-duplex streams it is the
- sample rate for both input and output
-
- @return Returns 0 if the format is supported, and an error code indicating why
- the format is not supported otherwise. The constant paFormatIsSupported is
- provided to compare with the return value for success.
-
- @see paFormatIsSupported, PaStreamParameters
-*/
-PaError Pa_IsFormatSupported( const PaStreamParameters *inputParameters,
- const PaStreamParameters *outputParameters,
- double sampleRate );
-
-
-
-/* Streaming types and functions */
-
-
-/**
- A single PaStream can provide multiple channels of real-time
- streaming audio input and output to a client application. A stream
- provides access to audio hardware represented by one or more
- PaDevices. Depending on the underlying Host API, it may be possible
- to open multiple streams using the same device, however this behavior
- is implementation defined. Portable applications should assume that
- a PaDevice may be simultaneously used by at most one PaStream.
-
- Pointers to PaStream objects are passed between PortAudio functions that
- operate on streams.
-
- @see Pa_OpenStream, Pa_OpenDefaultStream, Pa_OpenDefaultStream, Pa_CloseStream,
- Pa_StartStream, Pa_StopStream, Pa_AbortStream, Pa_IsStreamActive,
- Pa_GetStreamTime, Pa_GetStreamCpuLoad
-
-*/
-typedef void PaStream;
-
-
-/** Can be passed as the framesPerBuffer parameter to Pa_OpenStream()
- or Pa_OpenDefaultStream() to indicate that the stream callback will
- accept buffers of any size.
-*/
-#define paFramesPerBufferUnspecified (0)
-
-
-/** Flags used to control the behavior of a stream. They are passed as
- parameters to Pa_OpenStream or Pa_OpenDefaultStream. Multiple flags may be
- ORed together.
-
- @see Pa_OpenStream, Pa_OpenDefaultStream
- @see paNoFlag, paClipOff, paDitherOff, paNeverDropInput,
- paPrimeOutputBuffersUsingStreamCallback, paPlatformSpecificFlags
-*/
-typedef unsigned long PaStreamFlags;
-
-/** @see PaStreamFlags */
-#define paNoFlag ((PaStreamFlags) 0)
-
-/** Disable default clipping of out of range samples.
- @see PaStreamFlags
-*/
-#define paClipOff ((PaStreamFlags) 0x00000001)
-
-/** Disable default dithering.
- @see PaStreamFlags
-*/
-#define paDitherOff ((PaStreamFlags) 0x00000002)
-
-/** Flag requests that where possible a full duplex stream will not discard
- overflowed input samples without calling the stream callback. This flag is
- only valid for full duplex callback streams and only when used in combination
- with the paFramesPerBufferUnspecified (0) framesPerBuffer parameter. Using
- this flag incorrectly results in a paInvalidFlag error being returned from
- Pa_OpenStream and Pa_OpenDefaultStream.
-
- @see PaStreamFlags, paFramesPerBufferUnspecified
-*/
-#define paNeverDropInput ((PaStreamFlags) 0x00000004)
-
-/** Call the stream callback to fill initial output buffers, rather than the
- default behavior of priming the buffers with zeros (silence). This flag has
- no effect for input-only and blocking read/write streams.
-
- @see PaStreamFlags
-*/
-#define paPrimeOutputBuffersUsingStreamCallback ((PaStreamFlags) 0x00000008)
-
-/** A mask specifying the platform specific bits.
- @see PaStreamFlags
-*/
-#define paPlatformSpecificFlags ((PaStreamFlags)0xFFFF0000)
-
-/**
- Timing information for the buffers passed to the stream callback.
-*/
-typedef struct PaStreamCallbackTimeInfo{
- PaTime inputBufferAdcTime;
- PaTime currentTime;
- PaTime outputBufferDacTime;
-} PaStreamCallbackTimeInfo;
-
-
-/**
- Flag bit constants for the statusFlags to PaStreamCallback.
-
- @see paInputUnderflow, paInputOverflow, paOutputUnderflow, paOutputOverflow,
- paPrimingOutput
-*/
-typedef unsigned long PaStreamCallbackFlags;
-
-/** In a stream opened with paFramesPerBufferUnspecified, indicates that
- input data is all silence (zeros) because no real data is available. In a
- stream opened without paFramesPerBufferUnspecified, it indicates that one or
- more zero samples have been inserted into the input buffer to compensate
- for an input underflow.
- @see PaStreamCallbackFlags
-*/
-#define paInputUnderflow ((PaStreamCallbackFlags) 0x00000001)
-
-/** In a stream opened with paFramesPerBufferUnspecified, indicates that data
- prior to the first sample of the input buffer was discarded due to an
- overflow, possibly because the stream callback is using too much CPU time.
- Otherwise indicates that data prior to one or more samples in the
- input buffer was discarded.
- @see PaStreamCallbackFlags
-*/
-#define paInputOverflow ((PaStreamCallbackFlags) 0x00000002)
-
-/** Indicates that output data (or a gap) was inserted, possibly because the
- stream callback is using too much CPU time.
- @see PaStreamCallbackFlags
-*/
-#define paOutputUnderflow ((PaStreamCallbackFlags) 0x00000004)
-
-/** Indicates that output data will be discarded because no room is available.
- @see PaStreamCallbackFlags
-*/
-#define paOutputOverflow ((PaStreamCallbackFlags) 0x00000008)
-
-/** Some of all of the output data will be used to prime the stream, input
- data may be zero.
- @see PaStreamCallbackFlags
-*/
-#define paPrimingOutput ((PaStreamCallbackFlags) 0x00000010)
-
-/**
- Allowable return values for the PaStreamCallback.
- @see PaStreamCallback
-*/
-typedef enum PaStreamCallbackResult
-{
- paContinue=0,
- paComplete=1,
- paAbort=2
-} PaStreamCallbackResult;
-
-
-/**
- Functions of type PaStreamCallback are implemented by PortAudio clients.
- They consume, process or generate audio in response to requests from an
- active PortAudio stream.
-
- @param input and @param output are arrays of interleaved samples,
- the format, packing and number of channels used by the buffers are
- determined by parameters to Pa_OpenStream().
-
- @param frameCount The number of sample frames to be processed by
- the stream callback.
-
- @param timeInfo The time in seconds when the first sample of the input
- buffer was received at the audio input, the time in seconds when the first
- sample of the output buffer will begin being played at the audio output, and
- the time in seconds when the stream callback was called.
- See also Pa_GetStreamTime()
-
- @param statusFlags Flags indicating whether input and/or output buffers
- have been inserted or will be dropped to overcome underflow or overflow
- conditions.
-
- @param userData The value of a user supplied pointer passed to
- Pa_OpenStream() intended for storing synthesis data etc.
-
- @return
- The stream callback should return one of the values in the
- PaStreamCallbackResult enumeration. To ensure that the callback continues
- to be called, it should return paContinue (0). Either paComplete or paAbort
- can be returned to finish stream processing, after either of these values is
- returned the callback will not be called again. If paAbort is returned the
- stream will finish as soon as possible. If paComplete is returned, the stream
- will continue until all buffers generated by the callback have been played.
- This may be useful in applications such as soundfile players where a specific
- duration of output is required. However, it is not necessary to utilise this
- mechanism as Pa_StopStream(), Pa_AbortStream() or Pa_CloseStream() can also
- be used to stop the stream. The callback must always fill the entire output
- buffer irrespective of its return value.
-
- @see Pa_OpenStream, Pa_OpenDefaultStream
-
- @note With the exception of Pa_GetStreamCpuLoad() it is not permissable to call
- PortAudio API functions from within the stream callback.
-*/
-typedef int PaStreamCallback(
- const void *input, void *output,
- unsigned long frameCount,
- const PaStreamCallbackTimeInfo* timeInfo,
- PaStreamCallbackFlags statusFlags,
- void *userData );
-
-
-/** Opens a stream for either input, output or both.
-
- @param stream The address of a PaStream pointer which will receive
- a pointer to the newly opened stream.
-
- @param inputParameters A structure that describes the input parameters used by
- the opened stream. See PaStreamParameters for a description of these parameters.
- inputParameters must be NULL for output-only streams.
-
- @param outputParameters A structure that describes the output parameters used by
- the opened stream. See PaStreamParameters for a description of these parameters.
- outputParameters must be NULL for input-only streams.
-
- @param sampleRate The desired sampleRate. For full-duplex streams it is the
- sample rate for both input and output
-
- @param framesPerBuffer The number of frames passed to the stream callback
- function, or the preferred block granularity for a blocking read/write stream.
- The special value paFramesPerBufferUnspecified (0) may be used to request that
- the stream callback will recieve an optimal (and possibly varying) number of
- frames based on host requirements and the requested latency settings.
- Note: With some host APIs, the use of non-zero framesPerBuffer for a callback
- stream may introduce an additional layer of buffering which could introduce
- additional latency. PortAudio guarantees that the additional latency
- will be kept to the theoretical minimum however, it is strongly recommended
- that a non-zero framesPerBuffer value only be used when your algorithm
- requires a fixed number of frames per stream callback.
-
- @param streamFlags Flags which modify the behaviour of the streaming process.
- This parameter may contain a combination of flags ORed together. Some flags may
- only be relevant to certain buffer formats.
-
- @param streamCallback A pointer to a client supplied function that is responsible
- for processing and filling input and output buffers. If this parameter is NULL
- the stream will be opened in 'blocking read/write' mode. In blocking mode,
- the client can receive sample data using Pa_ReadStream and write sample data
- using Pa_WriteStream, the number of samples that may be read or written
- without blocking is returned by Pa_GetStreamReadAvailable and
- Pa_GetStreamWriteAvailable respectively.
-
- @param userData A client supplied pointer which is passed to the stream callback
- function. It could for example, contain a pointer to instance data necessary
- for processing the audio buffers. This parameter is ignored if streamCallback
- is NULL.
-
- @return
- Upon success Pa_OpenStream() returns paNoError and places a pointer to a
- valid PaStream in the stream argument. The stream is inactive (stopped).
- If a call to Pa_OpenStream() fails, a non-zero error code is returned (see
- PaError for possible error codes) and the value of stream is invalid.
-
- @see PaStreamParameters, PaStreamCallback, Pa_ReadStream, Pa_WriteStream,
- Pa_GetStreamReadAvailable, Pa_GetStreamWriteAvailable
-*/
-PaError Pa_OpenStream( PaStream** stream,
- const PaStreamParameters *inputParameters,
- const PaStreamParameters *outputParameters,
- double sampleRate,
- unsigned long framesPerBuffer,
- PaStreamFlags streamFlags,
- PaStreamCallback *streamCallback,
- void *userData );
-
-
-/** A simplified version of Pa_OpenStream() that opens the default input
- and/or output devices.
-
- @param stream The address of a PaStream pointer which will receive
- a pointer to the newly opened stream.
-
- @param numInputChannels The number of channels of sound that will be supplied
- to the stream callback or returned by Pa_ReadStream. It can range from 1 to
- the value of maxInputChannels in the PaDeviceInfo record for the default input
- device. If 0 the stream is opened as an output-only stream.
-
- @param numOutputChannels The number of channels of sound to be delivered to the
- stream callback or passed to Pa_WriteStream. It can range from 1 to the value
- of maxOutputChannels in the PaDeviceInfo record for the default output dvice.
- If 0 the stream is opened as an output-only stream.
-
- @param sampleFormat The sample format of both the input and output buffers
- provided to the callback or passed to and from Pa_ReadStream and Pa_WriteStream.
- sampleFormat may be any of the formats described by the PaSampleFormat
- enumeration.
-
- @param sampleRate Same as Pa_OpenStream parameter of the same name.
- @param framesPerBuffer Same as Pa_OpenStream parameter of the same name.
- @param streamCallback Same as Pa_OpenStream parameter of the same name.
- @param userData Same as Pa_OpenStream parameter of the same name.
-
- @return As for Pa_OpenStream
-
- @see Pa_OpenStream, PaStreamCallback
-*/
-PaError Pa_OpenDefaultStream( PaStream** stream,
- int numInputChannels,
- int numOutputChannels,
- PaSampleFormat sampleFormat,
- double sampleRate,
- unsigned long framesPerBuffer,
- PaStreamCallback *streamCallback,
- void *userData );
-
-
-/** Closes an audio stream. If the audio stream is active it
- discards any pending buffers as if Pa_AbortStream() had been called.
-*/
-PaError Pa_CloseStream( PaStream *stream );
-
-
-/** Functions of type PaStreamFinishedCallback are implemented by PortAudio
- clients. They can be registered with a stream using the Pa_SetStreamFinishedCallback
- function. Once registered they are called when the stream becomes inactive
- (ie once a call to Pa_StopStream() will not block).
- A stream will become inactive after the stream callback returns non-zero,
- or when Pa_StopStream or Pa_AbortStream is called. For a stream providing audio
- output, if the stream callback returns paComplete, or Pa_StopStream is called,
- the stream finished callback will not be called until all generated sample data
- has been played.
-
- @param userData The userData parameter supplied to Pa_OpenStream()
-
- @see Pa_SetStreamFinishedCallback
-*/
-typedef void PaStreamFinishedCallback( void *userData );
-
-
-/** Register a stream finished callback function which will be called when the
- stream becomes inactive. See the description of PaStreamFinishedCallback for
- further details about when the callback will be called.
-
- @param stream a pointer to a PaStream that is in the stopped state - if the
- stream is not stopped, the stream's finished callback will remain unchanged
- and an error code will be returned.
-
- @param streamFinishedCallback a pointer to a function with the same signature
- as PaStreamFinishedCallback, that will be called when the stream becomes
- inactive. Passing NULL for this parameter will un-register a previously
- registered stream finished callback function.
-
- @return on success returns paNoError, otherwise an error code indicating the cause
- of the error.
-
- @see PaStreamFinishedCallback
-*/
-PaError Pa_SetStreamFinishedCallback( PaStream *stream, PaStreamFinishedCallback* streamFinishedCallback );
-
-
-/** Commences audio processing.
-*/
-PaError Pa_StartStream( PaStream *stream );
-
-
-/** Terminates audio processing. It waits until all pending
- audio buffers have been played before it returns.
-*/
-PaError Pa_StopStream( PaStream *stream );
-
-
-/** Terminates audio processing immediately without waiting for pending
- buffers to complete.
-*/
-PaError Pa_AbortStream( PaStream *stream );
-
-
-/** Determine whether the stream is stopped.
- A stream is considered to be stopped prior to a successful call to
- Pa_StartStream and after a successful call to Pa_StopStream or Pa_AbortStream.
- If a stream callback returns a value other than paContinue the stream is NOT
- considered to be stopped.
-
- @return Returns one (1) when the stream is stopped, zero (0) when
- the stream is running or, a PaErrorCode (which are always negative) if
- PortAudio is not initialized or an error is encountered.
-
- @see Pa_StopStream, Pa_AbortStream, Pa_IsStreamActive
-*/
-PaError Pa_IsStreamStopped( PaStream *stream );
-
-
-/** Determine whether the stream is active.
- A stream is active after a successful call to Pa_StartStream(), until it
- becomes inactive either as a result of a call to Pa_StopStream() or
- Pa_AbortStream(), or as a result of a return value other than paContinue from
- the stream callback. In the latter case, the stream is considered inactive
- after the last buffer has finished playing.
-
- @return Returns one (1) when the stream is active (ie playing or recording
- audio), zero (0) when not playing or, a PaErrorCode (which are always negative)
- if PortAudio is not initialized or an error is encountered.
-
- @see Pa_StopStream, Pa_AbortStream, Pa_IsStreamStopped
-*/
-PaError Pa_IsStreamActive( PaStream *stream );
-
-
-
-/** A structure containing unchanging information about an open stream.
- @see Pa_GetStreamInfo
-*/
-
-typedef struct PaStreamInfo
-{
- /** this is struct version 1 */
- int structVersion;
-
- /** The input latency of the stream in seconds. This value provides the most
- accurate estimate of input latency available to the implementation. It may
- differ significantly from the suggestedLatency value passed to Pa_OpenStream().
- The value of this field will be zero (0.) for output-only streams.
- @see PaTime
- */
- PaTime inputLatency;
-
- /** The output latency of the stream in seconds. This value provides the most
- accurate estimate of output latency available to the implementation. It may
- differ significantly from the suggestedLatency value passed to Pa_OpenStream().
- The value of this field will be zero (0.) for input-only streams.
- @see PaTime
- */
- PaTime outputLatency;
-
- /** The sample rate of the stream in Hertz (samples per second). In cases
- where the hardware sample rate is inaccurate and PortAudio is aware of it,
- the value of this field may be different from the sampleRate parameter
- passed to Pa_OpenStream(). If information about the actual hardware sample
- rate is not available, this field will have the same value as the sampleRate
- parameter passed to Pa_OpenStream().
- */
- double sampleRate;
-
-} PaStreamInfo;
-
-
-/** Retrieve a pointer to a PaStreamInfo structure containing information
- about the specified stream.
- @return A pointer to an immutable PaStreamInfo structure. If the stream
- parameter invalid, or an error is encountered, the function returns NULL.
-
- @param stream A pointer to an open stream previously created with Pa_OpenStream.
-
- @note PortAudio manages the memory referenced by the returned pointer,
- the client must not manipulate or free the memory. The pointer is only
- guaranteed to be valid until the specified stream is closed.
-
- @see PaStreamInfo
-*/
-const PaStreamInfo* Pa_GetStreamInfo( PaStream *stream );
-
-
-/** Determine the current time for the stream according to the same clock used
- to generate buffer timestamps. This time may be used for syncronising other
- events to the audio stream, for example synchronizing audio to MIDI.
-
- @return The stream's current time in seconds, or 0 if an error occurred.
-
- @see PaTime, PaStreamCallback
-*/
-PaTime Pa_GetStreamTime( PaStream *stream );
-
-
-/** Retrieve CPU usage information for the specified stream.
- The "CPU Load" is a fraction of total CPU time consumed by a callback stream's
- audio processing routines including, but not limited to the client supplied
- stream callback. This function does not work with blocking read/write streams.
-
- This function may be called from the stream callback function or the
- application.
-
- @return
- A floating point value, typically between 0.0 and 1.0, where 1.0 indicates
- that the stream callback is consuming the maximum number of CPU cycles possible
- to maintain real-time operation. A value of 0.5 would imply that PortAudio and
- the stream callback was consuming roughly 50% of the available CPU time. The
- return value may exceed 1.0. A value of 0.0 will always be returned for a
- blocking read/write stream, or if an error occurrs.
-*/
-double Pa_GetStreamCpuLoad( PaStream* stream );
-
-
-/** Read samples from an input stream. The function doesn't return until
- the entire buffer has been filled - this may involve waiting for the operating
- system to supply the data.
-
- @param stream A pointer to an open stream previously created with Pa_OpenStream.
-
- @param buffer A pointer to a buffer of sample frames. The buffer contains
- samples in the format specified by the inputParameters->sampleFormat field
- used to open the stream, and the number of channels specified by
- inputParameters->numChannels. If non-interleaved samples were requested,
- buffer is a pointer to the first element of an array of non-interleaved
- buffer pointers, one for each channel.
-
- @param frames The number of frames to be read into buffer. This parameter
- is not constrained to a specific range, however high performance applications
- will want to match this parameter to the framesPerBuffer parameter used
- when opening the stream.
-
- @return On success PaNoError will be returned, or PaInputOverflowed if input
- data was discarded by PortAudio after the previous call and before this call.
-*/
-PaError Pa_ReadStream( PaStream* stream,
- void *buffer,
- unsigned long frames );
-
-
-/** Write samples to an output stream. This function doesn't return until the
- entire buffer has been consumed - this may involve waiting for the operating
- system to consume the data.
-
- @param stream A pointer to an open stream previously created with Pa_OpenStream.
-
- @param buffer A pointer to a buffer of sample frames. The buffer contains
- samples in the format specified by the outputParameters->sampleFormat field
- used to open the stream, and the number of channels specified by
- outputParameters->numChannels. If non-interleaved samples were requested,
- buffer is a pointer to the first element of an array of non-interleaved
- buffer pointers, one for each channel.
-
- @param frames The number of frames to be written from buffer. This parameter
- is not constrained to a specific range, however high performance applications
- will want to match this parameter to the framesPerBuffer parameter used
- when opening the stream.
-
- @return On success PaNoError will be returned, or paOutputUnderflowed if
- additional output data was inserted after the previous call and before this
- call.
-*/
-PaError Pa_WriteStream( PaStream* stream,
- const void *buffer,
- unsigned long frames );
-
-
-/** Retrieve the number of frames that can be read from the stream without
- waiting.
-
- @return Returns a non-negative value representing the maximum number of frames
- that can be read from the stream without blocking or busy waiting or, a
- PaErrorCode (which are always negative) if PortAudio is not initialized or an
- error is encountered.
-*/
-signed long Pa_GetStreamReadAvailable( PaStream* stream );
-
-
-/** Retrieve the number of frames that can be written to the stream without
- waiting.
-
- @return Returns a non-negative value representing the maximum number of frames
- that can be written to the stream without blocking or busy waiting or, a
- PaErrorCode (which are always negative) if PortAudio is not initialized or an
- error is encountered.
-*/
-signed long Pa_GetStreamWriteAvailable( PaStream* stream );
-
-
-/* Miscellaneous utilities */
-
-
-/** Retrieve the size of a given sample format in bytes.
-
- @return The size in bytes of a single sample in the specified format,
- or paSampleFormatNotSupported if the format is not supported.
-*/
-PaError Pa_GetSampleSize( PaSampleFormat format );
-
-
-/** Put the caller to sleep for at least 'msec' milliseconds. This function is
- provided only as a convenience for authors of portable code (such as the tests
- and examples in the PortAudio distribution.)
-
- The function may sleep longer than requested so don't rely on this for accurate
- musical timing.
-*/
-void Pa_Sleep( long msec );
-
-
-
-#ifdef __cplusplus
-}
-#endif /* __cplusplus */
-#endif /* PORTAUDIO_H */
+/*
+ * PJMEDIA - Multimedia over IP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef PORTAUDIO_H
+#define PORTAUDIO_H
+/*
+ * $Id: portaudio.h,v 1.5.2.50 2004/12/13 11:50:40 rossbencina Exp $
+ * PortAudio Portable Real-Time Audio Library
+ * PortAudio API Header File
+ * Latest version available at: http://www.portaudio.com/
+ *
+ * Copyright (c) 1999-2002 Ross Bencina and Phil Burk
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * Any person wishing to distribute modifications to the Software is
+ * requested to send the modifications to the original developer so that
+ * they can be incorporated into the canonical version.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+ * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+/** @file
+ @brief The PortAudio API.
+*/
+
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif /* __cplusplus */
+
+
+/** Retrieve the release number of the currently running PortAudio build,
+ eg 1900.
+*/
+int Pa_GetVersion( void );
+
+
+/** Retrieve a textual description of the current PortAudio build,
+ eg "PortAudio V19-devel 13 October 2002".
+*/
+const char* Pa_GetVersionText( void );
+
+
+/** Error codes returned by PortAudio functions.
+ Note that with the exception of paNoError, all PaErrorCodes are negative.
+*/
+
+typedef int PaError;
+typedef enum PaErrorCode
+{
+ paNoError = 0,
+
+ paNotInitialized = -10000,
+ paUnanticipatedHostError,
+ paInvalidChannelCount,
+ paInvalidSampleRate,
+ paInvalidDevice,
+ paInvalidFlag,
+ paSampleFormatNotSupported,
+ paBadIODeviceCombination,
+ paInsufficientMemory,
+ paBufferTooBig,
+ paBufferTooSmall,
+ paNullCallback,
+ paBadStreamPtr,
+ paTimedOut,
+ paInternalError,
+ paDeviceUnavailable,
+ paIncompatibleHostApiSpecificStreamInfo,
+ paStreamIsStopped,
+ paStreamIsNotStopped,
+ paInputOverflowed,
+ paOutputUnderflowed,
+ paHostApiNotFound,
+ paInvalidHostApi,
+ paCanNotReadFromACallbackStream, /**< @todo review error code name */
+ paCanNotWriteToACallbackStream, /**< @todo review error code name */
+ paCanNotReadFromAnOutputOnlyStream, /**< @todo review error code name */
+ paCanNotWriteToAnInputOnlyStream, /**< @todo review error code name */
+ paIncompatibleStreamHostApi
+} PaErrorCode;
+
+
+/** Translate the supplied PortAudio error code into a human readable
+ message.
+*/
+const char *Pa_GetErrorText( PaError errorCode );
+
+
+/** Library initialization function - call this before using PortAudio.
+ This function initialises internal data structures and prepares underlying
+ host APIs for use. This function MUST be called before using any other
+ PortAudio API functions.
+
+ If Pa_Initialize() is called multiple times, each successful
+ call must be matched with a corresponding call to Pa_Terminate().
+ Pairs of calls to Pa_Initialize()/Pa_Terminate() may overlap, and are not
+ required to be fully nested.
+
+ Note that if Pa_Initialize() returns an error code, Pa_Terminate() should
+ NOT be called.
+
+ @return paNoError if successful, otherwise an error code indicating the cause
+ of failure.
+
+ @see Pa_Terminate
+*/
+PaError Pa_Initialize( void );
+
+
+/** Library termination function - call this when finished using PortAudio.
+ This function deallocates all resources allocated by PortAudio since it was
+ initializied by a call to Pa_Initialize(). In cases where Pa_Initialise() has
+ been called multiple times, each call must be matched with a corresponding call
+ to Pa_Terminate(). The final matching call to Pa_Terminate() will automatically
+ close any PortAudio streams that are still open.
+
+ Pa_Terminate() MUST be called before exiting a program which uses PortAudio.
+ Failure to do so may result in serious resource leaks, such as audio devices
+ not being available until the next reboot.
+
+ @return paNoError if successful, otherwise an error code indicating the cause
+ of failure.
+
+ @see Pa_Initialize
+*/
+PaError Pa_Terminate( void );
+
+
+
+/** The type used to refer to audio devices. Values of this type usually
+ range from 0 to (Pa_DeviceCount-1), and may also take on the PaNoDevice
+ and paUseHostApiSpecificDeviceSpecification values.
+
+ @see Pa_DeviceCount, paNoDevice, paUseHostApiSpecificDeviceSpecification
+*/
+typedef int PaDeviceIndex;
+
+
+/** A special PaDeviceIndex value indicating that no device is available,
+ or should be used.
+
+ @see PaDeviceIndex
+*/
+#define paNoDevice ((PaDeviceIndex)-1)
+
+
+/** A special PaDeviceIndex value indicating that the device(s) to be used
+ are specified in the host api specific stream info structure.
+
+ @see PaDeviceIndex
+*/
+#define paUseHostApiSpecificDeviceSpecification ((PaDeviceIndex)-2)
+
+
+/* Host API enumeration mechanism */
+
+/** The type used to enumerate to host APIs at runtime. Values of this type
+ range from 0 to (Pa_GetHostApiCount()-1).
+
+ @see Pa_GetHostApiCount
+*/
+typedef int PaHostApiIndex;
+
+
+/** Retrieve the number of available host APIs. Even if a host API is
+ available it may have no devices available.
+
+ @return A non-negative value indicating the number of available host APIs
+ or, a PaErrorCode (which are always negative) if PortAudio is not initialized
+ or an error is encountered.
+
+ @see PaHostApiIndex
+*/
+PaHostApiIndex Pa_GetHostApiCount( void );
+
+
+/** Retrieve the index of the default host API. The default host API will be
+ the lowest common denominator host API on the current platform and is
+ unlikely to provide the best performance.
+
+ @return A non-negative value ranging from 0 to (Pa_GetHostApiCount()-1)
+ indicating the default host API index or, a PaErrorCode (which are always
+ negative) if PortAudio is not initialized or an error is encountered.
+*/
+PaHostApiIndex Pa_GetDefaultHostApi( void );
+
+
+/** Unchanging unique identifiers for each supported host API. This type
+ is used in the PaHostApiInfo structure. The values are guaranteed to be
+ unique and to never change, thus allowing code to be written that
+ conditionally uses host API specific extensions.
+
+ New type ids will be allocated when support for a host API reaches
+ "public alpha" status, prior to that developers should use the
+ paInDevelopment type id.
+
+ @see PaHostApiInfo
+*/
+typedef enum PaHostApiTypeId
+{
+ paInDevelopment=0, /* use while developing support for a new host API */
+ paDirectSound=1,
+ paMME=2,
+ paASIO=3,
+ paSoundManager=4,
+ paCoreAudio=5,
+ paOSS=7,
+ paALSA=8,
+ paAL=9,
+ paBeOS=10,
+ paWDMKS=11,
+ paJACK=12
+} PaHostApiTypeId;
+
+
+/** A structure containing information about a particular host API. */
+
+typedef struct PaHostApiInfo
+{
+ /** this is struct version 1 */
+ int structVersion;
+ /** The well known unique identifier of this host API @see PaHostApiTypeId */
+ PaHostApiTypeId type;
+ /** A textual description of the host API for display on user interfaces. */
+ const char *name;
+
+ /** The number of devices belonging to this host API. This field may be
+ used in conjunction with Pa_HostApiDeviceIndexToDeviceIndex() to enumerate
+ all devices for this host API.
+ @see Pa_HostApiDeviceIndexToDeviceIndex
+ */
+ int deviceCount;
+
+ /** The the default input device for this host API. The value will be a
+ device index ranging from 0 to (Pa_GetDeviceCount()-1), or paNoDevice
+ if no default input device is available.
+ */
+ PaDeviceIndex defaultInputDevice;
+
+ /** The the default output device for this host API. The value will be a
+ device index ranging from 0 to (Pa_GetDeviceCount()-1), or paNoDevice
+ if no default output device is available.
+ */
+ PaDeviceIndex defaultOutputDevice;
+
+} PaHostApiInfo;
+
+
+/** Retrieve a pointer to a structure containing information about a specific
+ host Api.
+
+ @param hostApi A valid host API index ranging from 0 to (Pa_GetHostApiCount()-1)
+
+ @return A pointer to an immutable PaHostApiInfo structure describing
+ a specific host API. If the hostApi parameter is out of range or an error
+ is encountered, the function returns NULL.
+
+ The returned structure is owned by the PortAudio implementation and must not
+ be manipulated or freed. The pointer is only guaranteed to be valid between
+ calls to Pa_Initialize() and Pa_Terminate().
+*/
+const PaHostApiInfo * Pa_GetHostApiInfo( PaHostApiIndex hostApi );
+
+
+/** Convert a static host API unique identifier, into a runtime
+ host API index.
+
+ @param type A unique host API identifier belonging to the PaHostApiTypeId
+ enumeration.
+
+ @return A valid PaHostApiIndex ranging from 0 to (Pa_GetHostApiCount()-1) or,
+ a PaErrorCode (which are always negative) if PortAudio is not initialized
+ or an error is encountered.
+
+ The paHostApiNotFound error code indicates that the host API specified by the
+ type parameter is not available.
+
+ @see PaHostApiTypeId
+*/
+PaHostApiIndex Pa_HostApiTypeIdToHostApiIndex( PaHostApiTypeId type );
+
+
+/** Convert a host-API-specific device index to standard PortAudio device index.
+ This function may be used in conjunction with the deviceCount field of
+ PaHostApiInfo to enumerate all devices for the specified host API.
+
+ @param hostApi A valid host API index ranging from 0 to (Pa_GetHostApiCount()-1)
+
+ @param hostApiDeviceIndex A valid per-host device index in the range
+ 0 to (Pa_GetHostApiInfo(hostApi)->deviceCount-1)
+
+ @return A non-negative PaDeviceIndex ranging from 0 to (Pa_GetDeviceCount()-1)
+ or, a PaErrorCode (which are always negative) if PortAudio is not initialized
+ or an error is encountered.
+
+ A paInvalidHostApi error code indicates that the host API index specified by
+ the hostApi parameter is out of range.
+
+ A paInvalidDevice error code indicates that the hostApiDeviceIndex parameter
+ is out of range.
+
+ @see PaHostApiInfo
+*/
+PaDeviceIndex Pa_HostApiDeviceIndexToDeviceIndex( PaHostApiIndex hostApi,
+ int hostApiDeviceIndex );
+
+
+
+/** Structure used to return information about a host error condition.
+*/
+typedef struct PaHostErrorInfo{
+ PaHostApiTypeId hostApiType; /**< the host API which returned the error code */
+ long errorCode; /**< the error code returned */
+ const char *errorText; /**< a textual description of the error if available, otherwise a zero-length string */
+}PaHostErrorInfo;
+
+
+/** Return information about the last host error encountered. The error
+ information returned by Pa_GetLastHostErrorInfo() will never be modified
+ asyncronously by errors occurring in other PortAudio owned threads
+ (such as the thread that manages the stream callback.)
+
+ This function is provided as a last resort, primarily to enhance debugging
+ by providing clients with access to all available error information.
+
+ @return A pointer to an immutable structure constaining information about
+ the host error. The values in this structure will only be valid if a
+ PortAudio function has previously returned the paUnanticipatedHostError
+ error code.
+*/
+const PaHostErrorInfo* Pa_GetLastHostErrorInfo( void );
+
+
+
+/* Device enumeration and capabilities */
+
+/** Retrieve the number of available devices. The number of available devices
+ may be zero.
+
+ @return A non-negative value indicating the number of available devices or,
+ a PaErrorCode (which are always negative) if PortAudio is not initialized
+ or an error is encountered.
+*/
+PaDeviceIndex Pa_GetDeviceCount( void );
+
+
+/** Retrieve the index of the default input device. The result can be
+ used in the inputDevice parameter to Pa_OpenStream().
+
+ @return The default input device index for the default host API, or paNoDevice
+ if no default input device is available or an error was encountered.
+*/
+PaDeviceIndex Pa_GetDefaultInputDevice( void );
+
+
+/** Retrieve the index of the default output device. The result can be
+ used in the outputDevice parameter to Pa_OpenStream().
+
+ @return The default output device index for the defualt host API, or paNoDevice
+ if no default output device is available or an error was encountered.
+
+ @note
+ On the PC, the user can specify a default device by
+ setting an environment variable. For example, to use device #1.
+<pre>
+ set PA_RECOMMENDED_OUTPUT_DEVICE=1
+</pre>
+ The user should first determine the available device ids by using
+ the supplied application "pa_devs".
+*/
+PaDeviceIndex Pa_GetDefaultOutputDevice( void );
+
+
+/** The type used to represent monotonic time in seconds that can be used
+ for syncronisation. The type is used for the outTime argument to the
+ PaStreamCallback and as the result of Pa_GetStreamTime().
+
+ @see PaStreamCallback, Pa_GetStreamTime
+*/
+typedef double PaTime;
+
+
+/** A type used to specify one or more sample formats. Each value indicates
+ a possible format for sound data passed to and from the stream callback,
+ Pa_ReadStream and Pa_WriteStream.
+
+ The standard formats paFloat32, paInt16, paInt32, paInt24, paInt8
+ and aUInt8 are usually implemented by all implementations.
+
+ The floating point representation (paFloat32) uses +1.0 and -1.0 as the
+ maximum and minimum respectively.
+
+ paUInt8 is an unsigned 8 bit format where 128 is considered "ground"
+
+ The paNonInterleaved flag indicates that a multichannel buffer is passed
+ as a set of non-interleaved pointers.
+
+ @see Pa_OpenStream, Pa_OpenDefaultStream, PaDeviceInfo
+ @see paFloat32, paInt16, paInt32, paInt24, paInt8
+ @see paUInt8, paCustomFormat, paNonInterleaved
+*/
+typedef unsigned long PaSampleFormat;
+
+
+#define paFloat32 ((PaSampleFormat) 0x00000001) /**< @see PaSampleFormat */
+#define paInt32 ((PaSampleFormat) 0x00000002) /**< @see PaSampleFormat */
+#define paInt24 ((PaSampleFormat) 0x00000004) /**< Packed 24 bit format. @see PaSampleFormat */
+#define paInt16 ((PaSampleFormat) 0x00000008) /**< @see PaSampleFormat */
+#define paInt8 ((PaSampleFormat) 0x00000010) /**< @see PaSampleFormat */
+#define paUInt8 ((PaSampleFormat) 0x00000020) /**< @see PaSampleFormat */
+#define paCustomFormat ((PaSampleFormat) 0x00010000)/**< @see PaSampleFormat */
+
+#define paNonInterleaved ((PaSampleFormat) 0x80000000)
+
+/** A structure providing information and capabilities of PortAudio devices.
+ Devices may support input, output or both input and output.
+*/
+typedef struct PaDeviceInfo
+{
+ int structVersion; /* this is struct version 2 */
+ const char *name;
+ PaHostApiIndex hostApi; /* note this is a host API index, not a type id*/
+
+ int maxInputChannels;
+ int maxOutputChannels;
+
+ /* Default latency values for interactive performance. */
+ PaTime defaultLowInputLatency;
+ PaTime defaultLowOutputLatency;
+ /* Default latency values for robust non-interactive applications (eg. playing sound files). */
+ PaTime defaultHighInputLatency;
+ PaTime defaultHighOutputLatency;
+
+ double defaultSampleRate;
+} PaDeviceInfo;
+
+
+/** Retrieve a pointer to a PaDeviceInfo structure containing information
+ about the specified device.
+ @return A pointer to an immutable PaDeviceInfo structure. If the device
+ parameter is out of range the function returns NULL.
+
+ @param device A valid device index in the range 0 to (Pa_GetDeviceCount()-1)
+
+ @note PortAudio manages the memory referenced by the returned pointer,
+ the client must not manipulate or free the memory. The pointer is only
+ guaranteed to be valid between calls to Pa_Initialize() and Pa_Terminate().
+
+ @see PaDeviceInfo, PaDeviceIndex
+*/
+const PaDeviceInfo* Pa_GetDeviceInfo( PaDeviceIndex device );
+
+
+/** Parameters for one direction (input or output) of a stream.
+*/
+typedef struct PaStreamParameters
+{
+ /** A valid device index in the range 0 to (Pa_GetDeviceCount()-1)
+ specifying the device to be used or the special constant
+ paUseHostApiSpecificDeviceSpecification which indicates that the actual
+ device(s) to use are specified in hostApiSpecificStreamInfo.
+ This field must not be set to paNoDevice.
+ */
+ PaDeviceIndex device;
+
+ /** The number of channels of sound to be delivered to the
+ stream callback or accessed by Pa_ReadStream() or Pa_WriteStream().
+ It can range from 1 to the value of maxInputChannels in the
+ PaDeviceInfo record for the device specified by the device parameter.
+ */
+ int channelCount;
+
+ /** The sample format of the buffer provided to the stream callback,
+ a_ReadStream() or Pa_WriteStream(). It may be any of the formats described
+ by the PaSampleFormat enumeration.
+ */
+ PaSampleFormat sampleFormat;
+
+ /** The desired latency in seconds. Where practical, implementations should
+ configure their latency based on these parameters, otherwise they may
+ choose the closest viable latency instead. Unless the suggested latency
+ is greater than the absolute upper limit for the device implementations
+ shouldround the suggestedLatency up to the next practial value - ie to
+ provide an equal or higher latency than suggestedLatency whereever possibe.
+ Actual latency values for an open stream may be retrieved using the
+ inputLatency and outputLatency fields of the PaStreamInfo structure
+ returned by Pa_GetStreamInfo().
+ @see default*Latency in PaDeviceInfo, *Latency in PaStreamInfo
+ */
+ PaTime suggestedLatency;
+
+ /** An optional pointer to a host api specific data structure
+ containing additional information for device setup and/or stream processing.
+ hostApiSpecificStreamInfo is never required for correct operation,
+ if not used it should be set to NULL.
+ */
+ void *hostApiSpecificStreamInfo;
+
+} PaStreamParameters;
+
+
+/** Return code for Pa_IsFormatSupported indicating success. */
+#define paFormatIsSupported (0)
+
+/** Determine whether it would be possible to open a stream with the specified
+ parameters.
+
+ @param inputParameters A structure that describes the input parameters used to
+ open a stream. The suggestedLatency field is ignored. See PaStreamParameters
+ for a description of these parameters. inputParameters must be NULL for
+ output-only streams.
+
+ @param outputParameters A structure that describes the output parameters used
+ to open a stream. The suggestedLatency field is ignored. See PaStreamParameters
+ for a description of these parameters. outputParameters must be NULL for
+ input-only streams.
+
+ @param sampleRate The required sampleRate. For full-duplex streams it is the
+ sample rate for both input and output
+
+ @return Returns 0 if the format is supported, and an error code indicating why
+ the format is not supported otherwise. The constant paFormatIsSupported is
+ provided to compare with the return value for success.
+
+ @see paFormatIsSupported, PaStreamParameters
+*/
+PaError Pa_IsFormatSupported( const PaStreamParameters *inputParameters,
+ const PaStreamParameters *outputParameters,
+ double sampleRate );
+
+
+
+/* Streaming types and functions */
+
+
+/**
+ A single PaStream can provide multiple channels of real-time
+ streaming audio input and output to a client application. A stream
+ provides access to audio hardware represented by one or more
+ PaDevices. Depending on the underlying Host API, it may be possible
+ to open multiple streams using the same device, however this behavior
+ is implementation defined. Portable applications should assume that
+ a PaDevice may be simultaneously used by at most one PaStream.
+
+ Pointers to PaStream objects are passed between PortAudio functions that
+ operate on streams.
+
+ @see Pa_OpenStream, Pa_OpenDefaultStream, Pa_OpenDefaultStream, Pa_CloseStream,
+ Pa_StartStream, Pa_StopStream, Pa_AbortStream, Pa_IsStreamActive,
+ Pa_GetStreamTime, Pa_GetStreamCpuLoad
+
+*/
+typedef void PaStream;
+
+
+/** Can be passed as the framesPerBuffer parameter to Pa_OpenStream()
+ or Pa_OpenDefaultStream() to indicate that the stream callback will
+ accept buffers of any size.
+*/
+#define paFramesPerBufferUnspecified (0)
+
+
+/** Flags used to control the behavior of a stream. They are passed as
+ parameters to Pa_OpenStream or Pa_OpenDefaultStream. Multiple flags may be
+ ORed together.
+
+ @see Pa_OpenStream, Pa_OpenDefaultStream
+ @see paNoFlag, paClipOff, paDitherOff, paNeverDropInput,
+ paPrimeOutputBuffersUsingStreamCallback, paPlatformSpecificFlags
+*/
+typedef unsigned long PaStreamFlags;
+
+/** @see PaStreamFlags */
+#define paNoFlag ((PaStreamFlags) 0)
+
+/** Disable default clipping of out of range samples.
+ @see PaStreamFlags
+*/
+#define paClipOff ((PaStreamFlags) 0x00000001)
+
+/** Disable default dithering.
+ @see PaStreamFlags
+*/
+#define paDitherOff ((PaStreamFlags) 0x00000002)
+
+/** Flag requests that where possible a full duplex stream will not discard
+ overflowed input samples without calling the stream callback. This flag is
+ only valid for full duplex callback streams and only when used in combination
+ with the paFramesPerBufferUnspecified (0) framesPerBuffer parameter. Using
+ this flag incorrectly results in a paInvalidFlag error being returned from
+ Pa_OpenStream and Pa_OpenDefaultStream.
+
+ @see PaStreamFlags, paFramesPerBufferUnspecified
+*/
+#define paNeverDropInput ((PaStreamFlags) 0x00000004)
+
+/** Call the stream callback to fill initial output buffers, rather than the
+ default behavior of priming the buffers with zeros (silence). This flag has
+ no effect for input-only and blocking read/write streams.
+
+ @see PaStreamFlags
+*/
+#define paPrimeOutputBuffersUsingStreamCallback ((PaStreamFlags) 0x00000008)
+
+/** A mask specifying the platform specific bits.
+ @see PaStreamFlags
+*/
+#define paPlatformSpecificFlags ((PaStreamFlags)0xFFFF0000)
+
+/**
+ Timing information for the buffers passed to the stream callback.
+*/
+typedef struct PaStreamCallbackTimeInfo{
+ PaTime inputBufferAdcTime;
+ PaTime currentTime;
+ PaTime outputBufferDacTime;
+} PaStreamCallbackTimeInfo;
+
+
+/**
+ Flag bit constants for the statusFlags to PaStreamCallback.
+
+ @see paInputUnderflow, paInputOverflow, paOutputUnderflow, paOutputOverflow,
+ paPrimingOutput
+*/
+typedef unsigned long PaStreamCallbackFlags;
+
+/** In a stream opened with paFramesPerBufferUnspecified, indicates that
+ input data is all silence (zeros) because no real data is available. In a
+ stream opened without paFramesPerBufferUnspecified, it indicates that one or
+ more zero samples have been inserted into the input buffer to compensate
+ for an input underflow.
+ @see PaStreamCallbackFlags
+*/
+#define paInputUnderflow ((PaStreamCallbackFlags) 0x00000001)
+
+/** In a stream opened with paFramesPerBufferUnspecified, indicates that data
+ prior to the first sample of the input buffer was discarded due to an
+ overflow, possibly because the stream callback is using too much CPU time.
+ Otherwise indicates that data prior to one or more samples in the
+ input buffer was discarded.
+ @see PaStreamCallbackFlags
+*/
+#define paInputOverflow ((PaStreamCallbackFlags) 0x00000002)
+
+/** Indicates that output data (or a gap) was inserted, possibly because the
+ stream callback is using too much CPU time.
+ @see PaStreamCallbackFlags
+*/
+#define paOutputUnderflow ((PaStreamCallbackFlags) 0x00000004)
+
+/** Indicates that output data will be discarded because no room is available.
+ @see PaStreamCallbackFlags
+*/
+#define paOutputOverflow ((PaStreamCallbackFlags) 0x00000008)
+
+/** Some of all of the output data will be used to prime the stream, input
+ data may be zero.
+ @see PaStreamCallbackFlags
+*/
+#define paPrimingOutput ((PaStreamCallbackFlags) 0x00000010)
+
+/**
+ Allowable return values for the PaStreamCallback.
+ @see PaStreamCallback
+*/
+typedef enum PaStreamCallbackResult
+{
+ paContinue=0,
+ paComplete=1,
+ paAbort=2
+} PaStreamCallbackResult;
+
+
+/**
+ Functions of type PaStreamCallback are implemented by PortAudio clients.
+ They consume, process or generate audio in response to requests from an
+ active PortAudio stream.
+
+ @param input and @param output are arrays of interleaved samples,
+ the format, packing and number of channels used by the buffers are
+ determined by parameters to Pa_OpenStream().
+
+ @param frameCount The number of sample frames to be processed by
+ the stream callback.
+
+ @param timeInfo The time in seconds when the first sample of the input
+ buffer was received at the audio input, the time in seconds when the first
+ sample of the output buffer will begin being played at the audio output, and
+ the time in seconds when the stream callback was called.
+ See also Pa_GetStreamTime()
+
+ @param statusFlags Flags indicating whether input and/or output buffers
+ have been inserted or will be dropped to overcome underflow or overflow
+ conditions.
+
+ @param userData The value of a user supplied pointer passed to
+ Pa_OpenStream() intended for storing synthesis data etc.
+
+ @return
+ The stream callback should return one of the values in the
+ PaStreamCallbackResult enumeration. To ensure that the callback continues
+ to be called, it should return paContinue (0). Either paComplete or paAbort
+ can be returned to finish stream processing, after either of these values is
+ returned the callback will not be called again. If paAbort is returned the
+ stream will finish as soon as possible. If paComplete is returned, the stream
+ will continue until all buffers generated by the callback have been played.
+ This may be useful in applications such as soundfile players where a specific
+ duration of output is required. However, it is not necessary to utilise this
+ mechanism as Pa_StopStream(), Pa_AbortStream() or Pa_CloseStream() can also
+ be used to stop the stream. The callback must always fill the entire output
+ buffer irrespective of its return value.
+
+ @see Pa_OpenStream, Pa_OpenDefaultStream
+
+ @note With the exception of Pa_GetStreamCpuLoad() it is not permissable to call
+ PortAudio API functions from within the stream callback.
+*/
+typedef int PaStreamCallback(
+ const void *input, void *output,
+ unsigned long frameCount,
+ const PaStreamCallbackTimeInfo* timeInfo,
+ PaStreamCallbackFlags statusFlags,
+ void *userData );
+
+
+/** Opens a stream for either input, output or both.
+
+ @param stream The address of a PaStream pointer which will receive
+ a pointer to the newly opened stream.
+
+ @param inputParameters A structure that describes the input parameters used by
+ the opened stream. See PaStreamParameters for a description of these parameters.
+ inputParameters must be NULL for output-only streams.
+
+ @param outputParameters A structure that describes the output parameters used by
+ the opened stream. See PaStreamParameters for a description of these parameters.
+ outputParameters must be NULL for input-only streams.
+
+ @param sampleRate The desired sampleRate. For full-duplex streams it is the
+ sample rate for both input and output
+
+ @param framesPerBuffer The number of frames passed to the stream callback
+ function, or the preferred block granularity for a blocking read/write stream.
+ The special value paFramesPerBufferUnspecified (0) may be used to request that
+ the stream callback will recieve an optimal (and possibly varying) number of
+ frames based on host requirements and the requested latency settings.
+ Note: With some host APIs, the use of non-zero framesPerBuffer for a callback
+ stream may introduce an additional layer of buffering which could introduce
+ additional latency. PortAudio guarantees that the additional latency
+ will be kept to the theoretical minimum however, it is strongly recommended
+ that a non-zero framesPerBuffer value only be used when your algorithm
+ requires a fixed number of frames per stream callback.
+
+ @param streamFlags Flags which modify the behaviour of the streaming process.
+ This parameter may contain a combination of flags ORed together. Some flags may
+ only be relevant to certain buffer formats.
+
+ @param streamCallback A pointer to a client supplied function that is responsible
+ for processing and filling input and output buffers. If this parameter is NULL
+ the stream will be opened in 'blocking read/write' mode. In blocking mode,
+ the client can receive sample data using Pa_ReadStream and write sample data
+ using Pa_WriteStream, the number of samples that may be read or written
+ without blocking is returned by Pa_GetStreamReadAvailable and
+ Pa_GetStreamWriteAvailable respectively.
+
+ @param userData A client supplied pointer which is passed to the stream callback
+ function. It could for example, contain a pointer to instance data necessary
+ for processing the audio buffers. This parameter is ignored if streamCallback
+ is NULL.
+
+ @return
+ Upon success Pa_OpenStream() returns paNoError and places a pointer to a
+ valid PaStream in the stream argument. The stream is inactive (stopped).
+ If a call to Pa_OpenStream() fails, a non-zero error code is returned (see
+ PaError for possible error codes) and the value of stream is invalid.
+
+ @see PaStreamParameters, PaStreamCallback, Pa_ReadStream, Pa_WriteStream,
+ Pa_GetStreamReadAvailable, Pa_GetStreamWriteAvailable
+*/
+PaError Pa_OpenStream( PaStream** stream,
+ const PaStreamParameters *inputParameters,
+ const PaStreamParameters *outputParameters,
+ double sampleRate,
+ unsigned long framesPerBuffer,
+ PaStreamFlags streamFlags,
+ PaStreamCallback *streamCallback,
+ void *userData );
+
+
+/** A simplified version of Pa_OpenStream() that opens the default input
+ and/or output devices.
+
+ @param stream The address of a PaStream pointer which will receive
+ a pointer to the newly opened stream.
+
+ @param numInputChannels The number of channels of sound that will be supplied
+ to the stream callback or returned by Pa_ReadStream. It can range from 1 to
+ the value of maxInputChannels in the PaDeviceInfo record for the default input
+ device. If 0 the stream is opened as an output-only stream.
+
+ @param numOutputChannels The number of channels of sound to be delivered to the
+ stream callback or passed to Pa_WriteStream. It can range from 1 to the value
+ of maxOutputChannels in the PaDeviceInfo record for the default output dvice.
+ If 0 the stream is opened as an output-only stream.
+
+ @param sampleFormat The sample format of both the input and output buffers
+ provided to the callback or passed to and from Pa_ReadStream and Pa_WriteStream.
+ sampleFormat may be any of the formats described by the PaSampleFormat
+ enumeration.
+
+ @param sampleRate Same as Pa_OpenStream parameter of the same name.
+ @param framesPerBuffer Same as Pa_OpenStream parameter of the same name.
+ @param streamCallback Same as Pa_OpenStream parameter of the same name.
+ @param userData Same as Pa_OpenStream parameter of the same name.
+
+ @return As for Pa_OpenStream
+
+ @see Pa_OpenStream, PaStreamCallback
+*/
+PaError Pa_OpenDefaultStream( PaStream** stream,
+ int numInputChannels,
+ int numOutputChannels,
+ PaSampleFormat sampleFormat,
+ double sampleRate,
+ unsigned long framesPerBuffer,
+ PaStreamCallback *streamCallback,
+ void *userData );
+
+
+/** Closes an audio stream. If the audio stream is active it
+ discards any pending buffers as if Pa_AbortStream() had been called.
+*/
+PaError Pa_CloseStream( PaStream *stream );
+
+
+/** Functions of type PaStreamFinishedCallback are implemented by PortAudio
+ clients. They can be registered with a stream using the Pa_SetStreamFinishedCallback
+ function. Once registered they are called when the stream becomes inactive
+ (ie once a call to Pa_StopStream() will not block).
+ A stream will become inactive after the stream callback returns non-zero,
+ or when Pa_StopStream or Pa_AbortStream is called. For a stream providing audio
+ output, if the stream callback returns paComplete, or Pa_StopStream is called,
+ the stream finished callback will not be called until all generated sample data
+ has been played.
+
+ @param userData The userData parameter supplied to Pa_OpenStream()
+
+ @see Pa_SetStreamFinishedCallback
+*/
+typedef void PaStreamFinishedCallback( void *userData );
+
+
+/** Register a stream finished callback function which will be called when the
+ stream becomes inactive. See the description of PaStreamFinishedCallback for
+ further details about when the callback will be called.
+
+ @param stream a pointer to a PaStream that is in the stopped state - if the
+ stream is not stopped, the stream's finished callback will remain unchanged
+ and an error code will be returned.
+
+ @param streamFinishedCallback a pointer to a function with the same signature
+ as PaStreamFinishedCallback, that will be called when the stream becomes
+ inactive. Passing NULL for this parameter will un-register a previously
+ registered stream finished callback function.
+
+ @return on success returns paNoError, otherwise an error code indicating the cause
+ of the error.
+
+ @see PaStreamFinishedCallback
+*/
+PaError Pa_SetStreamFinishedCallback( PaStream *stream, PaStreamFinishedCallback* streamFinishedCallback );
+
+
+/** Commences audio processing.
+*/
+PaError Pa_StartStream( PaStream *stream );
+
+
+/** Terminates audio processing. It waits until all pending
+ audio buffers have been played before it returns.
+*/
+PaError Pa_StopStream( PaStream *stream );
+
+
+/** Terminates audio processing immediately without waiting for pending
+ buffers to complete.
+*/
+PaError Pa_AbortStream( PaStream *stream );
+
+
+/** Determine whether the stream is stopped.
+ A stream is considered to be stopped prior to a successful call to
+ Pa_StartStream and after a successful call to Pa_StopStream or Pa_AbortStream.
+ If a stream callback returns a value other than paContinue the stream is NOT
+ considered to be stopped.
+
+ @return Returns one (1) when the stream is stopped, zero (0) when
+ the stream is running or, a PaErrorCode (which are always negative) if
+ PortAudio is not initialized or an error is encountered.
+
+ @see Pa_StopStream, Pa_AbortStream, Pa_IsStreamActive
+*/
+PaError Pa_IsStreamStopped( PaStream *stream );
+
+
+/** Determine whether the stream is active.
+ A stream is active after a successful call to Pa_StartStream(), until it
+ becomes inactive either as a result of a call to Pa_StopStream() or
+ Pa_AbortStream(), or as a result of a return value other than paContinue from
+ the stream callback. In the latter case, the stream is considered inactive
+ after the last buffer has finished playing.
+
+ @return Returns one (1) when the stream is active (ie playing or recording
+ audio), zero (0) when not playing or, a PaErrorCode (which are always negative)
+ if PortAudio is not initialized or an error is encountered.
+
+ @see Pa_StopStream, Pa_AbortStream, Pa_IsStreamStopped
+*/
+PaError Pa_IsStreamActive( PaStream *stream );
+
+
+
+/** A structure containing unchanging information about an open stream.
+ @see Pa_GetStreamInfo
+*/
+
+typedef struct PaStreamInfo
+{
+ /** this is struct version 1 */
+ int structVersion;
+
+ /** The input latency of the stream in seconds. This value provides the most
+ accurate estimate of input latency available to the implementation. It may
+ differ significantly from the suggestedLatency value passed to Pa_OpenStream().
+ The value of this field will be zero (0.) for output-only streams.
+ @see PaTime
+ */
+ PaTime inputLatency;
+
+ /** The output latency of the stream in seconds. This value provides the most
+ accurate estimate of output latency available to the implementation. It may
+ differ significantly from the suggestedLatency value passed to Pa_OpenStream().
+ The value of this field will be zero (0.) for input-only streams.
+ @see PaTime
+ */
+ PaTime outputLatency;
+
+ /** The sample rate of the stream in Hertz (samples per second). In cases
+ where the hardware sample rate is inaccurate and PortAudio is aware of it,
+ the value of this field may be different from the sampleRate parameter
+ passed to Pa_OpenStream(). If information about the actual hardware sample
+ rate is not available, this field will have the same value as the sampleRate
+ parameter passed to Pa_OpenStream().
+ */
+ double sampleRate;
+
+} PaStreamInfo;
+
+
+/** Retrieve a pointer to a PaStreamInfo structure containing information
+ about the specified stream.
+ @return A pointer to an immutable PaStreamInfo structure. If the stream
+ parameter invalid, or an error is encountered, the function returns NULL.
+
+ @param stream A pointer to an open stream previously created with Pa_OpenStream.
+
+ @note PortAudio manages the memory referenced by the returned pointer,
+ the client must not manipulate or free the memory. The pointer is only
+ guaranteed to be valid until the specified stream is closed.
+
+ @see PaStreamInfo
+*/
+const PaStreamInfo* Pa_GetStreamInfo( PaStream *stream );
+
+
+/** Determine the current time for the stream according to the same clock used
+ to generate buffer timestamps. This time may be used for syncronising other
+ events to the audio stream, for example synchronizing audio to MIDI.
+
+ @return The stream's current time in seconds, or 0 if an error occurred.
+
+ @see PaTime, PaStreamCallback
+*/
+PaTime Pa_GetStreamTime( PaStream *stream );
+
+
+/** Retrieve CPU usage information for the specified stream.
+ The "CPU Load" is a fraction of total CPU time consumed by a callback stream's
+ audio processing routines including, but not limited to the client supplied
+ stream callback. This function does not work with blocking read/write streams.
+
+ This function may be called from the stream callback function or the
+ application.
+
+ @return
+ A floating point value, typically between 0.0 and 1.0, where 1.0 indicates
+ that the stream callback is consuming the maximum number of CPU cycles possible
+ to maintain real-time operation. A value of 0.5 would imply that PortAudio and
+ the stream callback was consuming roughly 50% of the available CPU time. The
+ return value may exceed 1.0. A value of 0.0 will always be returned for a
+ blocking read/write stream, or if an error occurrs.
+*/
+double Pa_GetStreamCpuLoad( PaStream* stream );
+
+
+/** Read samples from an input stream. The function doesn't return until
+ the entire buffer has been filled - this may involve waiting for the operating
+ system to supply the data.
+
+ @param stream A pointer to an open stream previously created with Pa_OpenStream.
+
+ @param buffer A pointer to a buffer of sample frames. The buffer contains
+ samples in the format specified by the inputParameters->sampleFormat field
+ used to open the stream, and the number of channels specified by
+ inputParameters->numChannels. If non-interleaved samples were requested,
+ buffer is a pointer to the first element of an array of non-interleaved
+ buffer pointers, one for each channel.
+
+ @param frames The number of frames to be read into buffer. This parameter
+ is not constrained to a specific range, however high performance applications
+ will want to match this parameter to the framesPerBuffer parameter used
+ when opening the stream.
+
+ @return On success PaNoError will be returned, or PaInputOverflowed if input
+ data was discarded by PortAudio after the previous call and before this call.
+*/
+PaError Pa_ReadStream( PaStream* stream,
+ void *buffer,
+ unsigned long frames );
+
+
+/** Write samples to an output stream. This function doesn't return until the
+ entire buffer has been consumed - this may involve waiting for the operating
+ system to consume the data.
+
+ @param stream A pointer to an open stream previously created with Pa_OpenStream.
+
+ @param buffer A pointer to a buffer of sample frames. The buffer contains
+ samples in the format specified by the outputParameters->sampleFormat field
+ used to open the stream, and the number of channels specified by
+ outputParameters->numChannels. If non-interleaved samples were requested,
+ buffer is a pointer to the first element of an array of non-interleaved
+ buffer pointers, one for each channel.
+
+ @param frames The number of frames to be written from buffer. This parameter
+ is not constrained to a specific range, however high performance applications
+ will want to match this parameter to the framesPerBuffer parameter used
+ when opening the stream.
+
+ @return On success PaNoError will be returned, or paOutputUnderflowed if
+ additional output data was inserted after the previous call and before this
+ call.
+*/
+PaError Pa_WriteStream( PaStream* stream,
+ const void *buffer,
+ unsigned long frames );
+
+
+/** Retrieve the number of frames that can be read from the stream without
+ waiting.
+
+ @return Returns a non-negative value representing the maximum number of frames
+ that can be read from the stream without blocking or busy waiting or, a
+ PaErrorCode (which are always negative) if PortAudio is not initialized or an
+ error is encountered.
+*/
+signed long Pa_GetStreamReadAvailable( PaStream* stream );
+
+
+/** Retrieve the number of frames that can be written to the stream without
+ waiting.
+
+ @return Returns a non-negative value representing the maximum number of frames
+ that can be written to the stream without blocking or busy waiting or, a
+ PaErrorCode (which are always negative) if PortAudio is not initialized or an
+ error is encountered.
+*/
+signed long Pa_GetStreamWriteAvailable( PaStream* stream );
+
+
+/* Miscellaneous utilities */
+
+
+/** Retrieve the size of a given sample format in bytes.
+
+ @return The size in bytes of a single sample in the specified format,
+ or paSampleFormatNotSupported if the format is not supported.
+*/
+PaError Pa_GetSampleSize( PaSampleFormat format );
+
+
+/** Put the caller to sleep for at least 'msec' milliseconds. This function is
+ provided only as a convenience for authors of portable code (such as the tests
+ and examples in the PortAudio distribution.)
+
+ The function may sleep longer than requested so don't rely on this for accurate
+ musical timing.
+*/
+void Pa_Sleep( long msec );
+
+
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+#endif /* PORTAUDIO_H */
diff --git a/pjmedia/src/pjmedia/rtcp.c b/pjmedia/src/pjmedia/rtcp.c
index 04a4de52..baa05be9 100644
--- a/pjmedia/src/pjmedia/rtcp.c
+++ b/pjmedia/src/pjmedia/rtcp.c
@@ -1,202 +1,223 @@
-/* $Id$
- *
- */
-
-#include <pjmedia/rtcp.h>
-#include <pj/os.h> /* pj_gettimeofday */
-#include <pj/sock.h> /* pj_htonx, pj_ntohx */
-#include <string.h> /* memset */
-
-#define RTCP_SR 200
-#define RTCP_RR 201
-
-
-
-/*
- * Get NTP time.
- */
-static void rtcp_get_ntp_time(struct pj_rtcp_ntp_rec *ntp)
-{
- pj_time_val tv;
-
- pj_gettimeofday(&tv);
-
- ntp->hi = tv.sec;
- tv.msec = tv.msec % 1000;
- ntp->lo = tv.msec * 0xFFFF / 1000;
- ntp->lo <<= 16;
-}
-
-
-PJ_DEF(void) pj_rtcp_init(pj_rtcp_session *s, pj_uint32_t ssrc)
-{
- pj_rtcp_pkt *rtcp_pkt = &s->rtcp_pkt;
-
- memset(rtcp_pkt, 0, sizeof(pj_rtcp_pkt));
-
- /* Init time */
- s->rtcp_lsr.hi = s->rtcp_lsr.lo = 0;
- s->rtcp_lsr_time = 0;
-
- /* Init common RTCP header */
- rtcp_pkt->common.version = 2;
- rtcp_pkt->common.count = 1;
- rtcp_pkt->common.pt = RTCP_SR;
- rtcp_pkt->common.length = pj_htons(12);
-
- /* Init SR */
- rtcp_pkt->sr.ssrc = pj_htonl(ssrc);
-
- /* RR will be initialized on receipt of the first RTP packet. */
-}
-
-PJ_DEF(void) pj_rtcp_fini(pj_rtcp_session *session)
-{
- /* Nothing to do. */
- PJ_UNUSED_ARG(session)
-}
-
-static void rtcp_init_seq(pj_rtcp_session *s, pj_uint16_t seq)
-{
- s->received = 0;
- s->expected_prior = 0;
- s->received_prior = 0;
- s->transit = 0;
- s->jitter = 0;
-
- pj_rtp_seq_restart(&s->seq_ctrl, seq);
-}
-
-PJ_DEF(void) pj_rtcp_rx_rtp(pj_rtcp_session *s, pj_uint16_t seq, pj_uint32_t rtp_ts)
-{
- pj_uint32_t arrival;
- pj_int32_t transit;
- unsigned long timer_tick;
- pj_time_val tv;
- int status;
-
- /* Update sequence numbers (received, lost, etc). */
- status = pj_rtp_seq_update(&s->seq_ctrl, seq);
- if (status == PJ_RTP_ERR_SESSION_RESTARTED) {
- rtcp_init_seq(s, seq);
- status = 0;
- }
-
- if (status != 0)
- return;
-
- ++s->received;
-
- pj_gettimeofday(&tv);
- timer_tick = tv.sec * 1000 + tv.msec;
-
- /*
- * Calculate jitter (s->jitter is in timer tick unit)
- */
- PJ_TODO(SUPPORT_JITTER_CALCULATION_FOR_NON_8KHZ_SAMPLE_RATE)
-
- arrival = timer_tick << 3; // 8 samples per ms.
- transit = arrival - rtp_ts;
-
- if (s->transit == 0) {
- s->transit = transit;
- } else {
- pj_int32_t d, jitter = s->jitter;
-
- d = transit - s->transit;
- s->transit = transit;
- if (d < 0)
- d = -d;
-
- jitter += d - ((jitter + 8) >> 4);
- s->jitter = jitter;
- }
-}
-
-PJ_DEF(void) pj_rtcp_tx_rtp(pj_rtcp_session *s, pj_uint16_t bytes_payload_size)
-{
- pj_rtcp_pkt *rtcp_pkt = &s->rtcp_pkt;
- rtcp_pkt->sr.sender_pcount = pj_htonl( pj_ntohl(rtcp_pkt->sr.sender_pcount) + 1);
- rtcp_pkt->sr.sender_bcount = pj_htonl( pj_ntohl(rtcp_pkt->sr.sender_bcount) + bytes_payload_size );
-}
-
-static void rtcp_build_rtcp(pj_rtcp_session *s, pj_uint32_t receiver_ssrc)
-{
- pj_uint32_t expected;
- pj_uint32_t u32;
- pj_uint32_t expected_interval, received_interval, lost_interval;
- pj_rtcp_pkt *rtcp_pkt = &s->rtcp_pkt;
-
- /* SSRC and last_seq */
- rtcp_pkt->rr.ssrc = pj_htonl(receiver_ssrc);
- rtcp_pkt->rr.last_seq = (s->seq_ctrl.cycles & 0xFFFF0000L);
- rtcp_pkt->rr.last_seq += s->seq_ctrl.max_seq;
- rtcp_pkt->rr.last_seq = pj_htonl(rtcp_pkt->rr.last_seq);
-
- /* Jitter */
- rtcp_pkt->rr.jitter = pj_htonl(s->jitter >> 4);
-
- /* Total lost. */
- expected = pj_ntohl(rtcp_pkt->rr.last_seq) - s->seq_ctrl.base_seq + 1;
- u32 = expected - s->received;
- rtcp_pkt->rr.total_lost_2 = (u32 >> 16) & 0x00FF;
- rtcp_pkt->rr.total_lost_1 = (u32 >> 8) & 0x00FF;
- rtcp_pkt->rr.total_lost_0 = u32 & 0x00FF;
-
- /* Fraction lost calculation */
- expected_interval = expected - s->expected_prior;
- s->expected_prior = expected;
-
- received_interval = s->received - s->received_prior;
- s->received_prior = s->received;
-
- lost_interval = expected_interval - received_interval;
-
- if (expected_interval==0 || lost_interval == 0) {
- rtcp_pkt->rr.fract_lost = 0;
- } else {
- rtcp_pkt->rr.fract_lost = (lost_interval << 8) / expected_interval;
- }
-}
-
-PJ_DEF(void) pj_rtcp_build_rtcp(pj_rtcp_session *session, pj_rtcp_pkt **ret_p_pkt, int *len)
-{
- pj_rtcp_pkt *rtcp_pkt = &session->rtcp_pkt;
- pj_rtcp_ntp_rec ntp;
- pj_time_val now;
-
- rtcp_build_rtcp(session, session->peer_ssrc);
-
- /* Get current NTP time. */
- rtcp_get_ntp_time(&ntp);
-
- /* Fill in NTP timestamp in SR. */
- rtcp_pkt->sr.ntp_sec = pj_htonl(ntp.hi);
- rtcp_pkt->sr.ntp_frac = pj_htonl(ntp.lo);
-
- if (session->rtcp_lsr_time == 0 || session->rtcp_lsr.lo == 0) {
- rtcp_pkt->rr.lsr = 0;
- rtcp_pkt->rr.dlsr = 0;
- } else {
- unsigned msec_elapsed;
-
- /* Fill in LSR.
- LSR is the middle 32bit of the last SR NTP time received.
- */
- rtcp_pkt->rr.lsr = ((session->rtcp_lsr.hi & 0x0000FFFF) << 16) |
- ((session->rtcp_lsr.lo >> 16) & 0xFFFF);
- rtcp_pkt->rr.lsr = pj_htonl(rtcp_pkt->rr.lsr);
-
- /* Fill in DLSR.
- DLSR is Delay since Last SR, in 1/65536 seconds.
- */
- pj_gettimeofday(&now);
- msec_elapsed = (now.msec - session->rtcp_lsr_time);
- rtcp_pkt->rr.dlsr = pj_htonl((msec_elapsed * 65536) / 1000);
- }
-
- /* Return pointer. */
- *ret_p_pkt = rtcp_pkt;
- *len = sizeof(pj_rtcp_pkt);
-}
-
+/* $Id$
+ *
+ */
+/*
+ * PJMEDIA - Multimedia over IP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <pjmedia/rtcp.h>
+#include <pj/os.h> /* pj_gettimeofday */
+#include <pj/sock.h> /* pj_htonx, pj_ntohx */
+#include <string.h> /* memset */
+
+#define RTCP_SR 200
+#define RTCP_RR 201
+
+
+
+/*
+ * Get NTP time.
+ */
+static void rtcp_get_ntp_time(struct pj_rtcp_ntp_rec *ntp)
+{
+ pj_time_val tv;
+
+ pj_gettimeofday(&tv);
+
+ ntp->hi = tv.sec;
+ tv.msec = tv.msec % 1000;
+ ntp->lo = tv.msec * 0xFFFF / 1000;
+ ntp->lo <<= 16;
+}
+
+
+PJ_DEF(void) pj_rtcp_init(pj_rtcp_session *s, pj_uint32_t ssrc)
+{
+ pj_rtcp_pkt *rtcp_pkt = &s->rtcp_pkt;
+
+ memset(rtcp_pkt, 0, sizeof(pj_rtcp_pkt));
+
+ /* Init time */
+ s->rtcp_lsr.hi = s->rtcp_lsr.lo = 0;
+ s->rtcp_lsr_time = 0;
+
+ /* Init common RTCP header */
+ rtcp_pkt->common.version = 2;
+ rtcp_pkt->common.count = 1;
+ rtcp_pkt->common.pt = RTCP_SR;
+ rtcp_pkt->common.length = pj_htons(12);
+
+ /* Init SR */
+ rtcp_pkt->sr.ssrc = pj_htonl(ssrc);
+
+ /* RR will be initialized on receipt of the first RTP packet. */
+}
+
+PJ_DEF(void) pj_rtcp_fini(pj_rtcp_session *session)
+{
+ /* Nothing to do. */
+ PJ_UNUSED_ARG(session)
+}
+
+static void rtcp_init_seq(pj_rtcp_session *s, pj_uint16_t seq)
+{
+ s->received = 0;
+ s->expected_prior = 0;
+ s->received_prior = 0;
+ s->transit = 0;
+ s->jitter = 0;
+
+ pj_rtp_seq_restart(&s->seq_ctrl, seq);
+}
+
+PJ_DEF(void) pj_rtcp_rx_rtp(pj_rtcp_session *s, pj_uint16_t seq, pj_uint32_t rtp_ts)
+{
+ pj_uint32_t arrival;
+ pj_int32_t transit;
+ unsigned long timer_tick;
+ pj_time_val tv;
+ int status;
+
+ /* Update sequence numbers (received, lost, etc). */
+ status = pj_rtp_seq_update(&s->seq_ctrl, seq);
+ if (status == PJ_RTP_ERR_SESSION_RESTARTED) {
+ rtcp_init_seq(s, seq);
+ status = 0;
+ }
+
+ if (status != 0)
+ return;
+
+ ++s->received;
+
+ pj_gettimeofday(&tv);
+ timer_tick = tv.sec * 1000 + tv.msec;
+
+ /*
+ * Calculate jitter (s->jitter is in timer tick unit)
+ */
+ PJ_TODO(SUPPORT_JITTER_CALCULATION_FOR_NON_8KHZ_SAMPLE_RATE)
+
+ arrival = timer_tick << 3; // 8 samples per ms.
+ transit = arrival - rtp_ts;
+
+ if (s->transit == 0) {
+ s->transit = transit;
+ } else {
+ pj_int32_t d, jitter = s->jitter;
+
+ d = transit - s->transit;
+ s->transit = transit;
+ if (d < 0)
+ d = -d;
+
+ jitter += d - ((jitter + 8) >> 4);
+ s->jitter = jitter;
+ }
+}
+
+PJ_DEF(void) pj_rtcp_tx_rtp(pj_rtcp_session *s, pj_uint16_t bytes_payload_size)
+{
+ pj_rtcp_pkt *rtcp_pkt = &s->rtcp_pkt;
+ rtcp_pkt->sr.sender_pcount = pj_htonl( pj_ntohl(rtcp_pkt->sr.sender_pcount) + 1);
+ rtcp_pkt->sr.sender_bcount = pj_htonl( pj_ntohl(rtcp_pkt->sr.sender_bcount) + bytes_payload_size );
+}
+
+static void rtcp_build_rtcp(pj_rtcp_session *s, pj_uint32_t receiver_ssrc)
+{
+ pj_uint32_t expected;
+ pj_uint32_t u32;
+ pj_uint32_t expected_interval, received_interval, lost_interval;
+ pj_rtcp_pkt *rtcp_pkt = &s->rtcp_pkt;
+
+ /* SSRC and last_seq */
+ rtcp_pkt->rr.ssrc = pj_htonl(receiver_ssrc);
+ rtcp_pkt->rr.last_seq = (s->seq_ctrl.cycles & 0xFFFF0000L);
+ rtcp_pkt->rr.last_seq += s->seq_ctrl.max_seq;
+ rtcp_pkt->rr.last_seq = pj_htonl(rtcp_pkt->rr.last_seq);
+
+ /* Jitter */
+ rtcp_pkt->rr.jitter = pj_htonl(s->jitter >> 4);
+
+ /* Total lost. */
+ expected = pj_ntohl(rtcp_pkt->rr.last_seq) - s->seq_ctrl.base_seq + 1;
+ u32 = expected - s->received;
+ rtcp_pkt->rr.total_lost_2 = (u32 >> 16) & 0x00FF;
+ rtcp_pkt->rr.total_lost_1 = (u32 >> 8) & 0x00FF;
+ rtcp_pkt->rr.total_lost_0 = u32 & 0x00FF;
+
+ /* Fraction lost calculation */
+ expected_interval = expected - s->expected_prior;
+ s->expected_prior = expected;
+
+ received_interval = s->received - s->received_prior;
+ s->received_prior = s->received;
+
+ lost_interval = expected_interval - received_interval;
+
+ if (expected_interval==0 || lost_interval == 0) {
+ rtcp_pkt->rr.fract_lost = 0;
+ } else {
+ rtcp_pkt->rr.fract_lost = (lost_interval << 8) / expected_interval;
+ }
+}
+
+PJ_DEF(void) pj_rtcp_build_rtcp(pj_rtcp_session *session, pj_rtcp_pkt **ret_p_pkt, int *len)
+{
+ pj_rtcp_pkt *rtcp_pkt = &session->rtcp_pkt;
+ pj_rtcp_ntp_rec ntp;
+ pj_time_val now;
+
+ rtcp_build_rtcp(session, session->peer_ssrc);
+
+ /* Get current NTP time. */
+ rtcp_get_ntp_time(&ntp);
+
+ /* Fill in NTP timestamp in SR. */
+ rtcp_pkt->sr.ntp_sec = pj_htonl(ntp.hi);
+ rtcp_pkt->sr.ntp_frac = pj_htonl(ntp.lo);
+
+ if (session->rtcp_lsr_time == 0 || session->rtcp_lsr.lo == 0) {
+ rtcp_pkt->rr.lsr = 0;
+ rtcp_pkt->rr.dlsr = 0;
+ } else {
+ unsigned msec_elapsed;
+
+ /* Fill in LSR.
+ LSR is the middle 32bit of the last SR NTP time received.
+ */
+ rtcp_pkt->rr.lsr = ((session->rtcp_lsr.hi & 0x0000FFFF) << 16) |
+ ((session->rtcp_lsr.lo >> 16) & 0xFFFF);
+ rtcp_pkt->rr.lsr = pj_htonl(rtcp_pkt->rr.lsr);
+
+ /* Fill in DLSR.
+ DLSR is Delay since Last SR, in 1/65536 seconds.
+ */
+ pj_gettimeofday(&now);
+ msec_elapsed = (now.msec - session->rtcp_lsr_time);
+ rtcp_pkt->rr.dlsr = pj_htonl((msec_elapsed * 65536) / 1000);
+ }
+
+ /* Return pointer. */
+ *ret_p_pkt = rtcp_pkt;
+ *len = sizeof(pj_rtcp_pkt);
+}
+
diff --git a/pjmedia/src/pjmedia/rtcp.h b/pjmedia/src/pjmedia/rtcp.h
index d3d2baa3..fbe42d95 100644
--- a/pjmedia/src/pjmedia/rtcp.h
+++ b/pjmedia/src/pjmedia/rtcp.h
@@ -1,178 +1,199 @@
-/* $Id$
- *
- */
-
-#ifndef __PJMEDIA_RTCP_H__
-#define __PJMEDIA_RTCP_H__
-
-/**
- * @file rtcp.h
- * @brief RTCP implementation.
- */
-
-#include <pj/types.h>
-#include <pjmedia/rtp.h>
-
-PJ_BEGIN_DECL
-
-
-/**
- * @defgroup PJMED_RTCP RTCP
- * @ingroup PJMEDIA
- * @{
- */
-
-/**
- * RTCP sender report.
- */
-struct pj_rtcp_sr
-{
- pj_uint32_t ssrc;
- pj_uint32_t ntp_sec;
- pj_uint32_t ntp_frac;
- pj_uint32_t rtp_ts;
- pj_uint32_t sender_pcount;
- pj_uint32_t sender_bcount;
-};
-
-typedef struct pj_rtcp_sr pj_rtcp_sr;
-
-/**
- * RTCP receiver report.
- */
-struct pj_rtcp_rr
-{
- pj_uint32_t ssrc;
-#if defined(PJ_IS_BIG_ENDIAN) && PJ_IS_BIG_ENDIAN!=0
- pj_uint32_t fract_lost:8;
- pj_uint32_t total_lost_2:8;
- pj_uint32_t total_lost_1:8;
- pj_uint32_t total_lost_0:8;
-#else
- pj_uint32_t fract_lost:8;
- pj_uint32_t total_lost_0:8;
- pj_uint32_t total_lost_1:8;
- pj_uint32_t total_lost_2:8;
-#endif
- pj_uint32_t last_seq;
- pj_uint32_t jitter;
- pj_uint32_t lsr;
- pj_uint32_t dlsr;
-};
-
-typedef struct pj_rtcp_rr pj_rtcp_rr;
-
-/**
- * RTCP common header.
- */
-struct pj_rtcp_common
-{
-#if defined(PJ_IS_BIG_ENDIAN) && PJ_IS_BIG_ENDIAN!=0
- unsigned version:2; /* packet type */
- unsigned p:1; /* padding flag */
- unsigned count:5; /* varies by payload type */
- unsigned pt:8; /* payload type */
-#else
- unsigned count:5; /* varies by payload type */
- unsigned p:1; /* padding flag */
- unsigned version:2; /* packet type */
- unsigned pt:8; /* payload type */
-#endif
- pj_uint16_t length; /* packet length */
-};
-
-typedef struct pj_rtcp_common pj_rtcp_common;
-
-/**
- * RTCP packet.
- */
-struct pj_rtcp_pkt
-{
- pj_rtcp_common common;
- pj_rtcp_sr sr;
- pj_rtcp_rr rr; /* variable-length list */
-};
-
-typedef struct pj_rtcp_pkt pj_rtcp_pkt;
-
-/**
- * NTP time representation.
- */
-struct pj_rtcp_ntp_rec
-{
- pj_uint32_t hi;
- pj_uint32_t lo;
-};
-
-typedef struct pj_rtcp_ntp_rec pj_rtcp_ntp_rec;
-
-/**
- * RTCP session.
- */
-struct pj_rtcp_session
-{
- pj_rtcp_pkt rtcp_pkt;
-
- pj_rtp_seq_session seq_ctrl;
-
- pj_uint32_t received; /* packets received */
- pj_uint32_t expected_prior; /* packet expected at last interval */
- pj_uint32_t received_prior; /* packet received at last interval */
- pj_int32_t transit; /* relative trans time for prev pkt */
- pj_uint32_t jitter; /* estimated jitter */
-
- pj_rtcp_ntp_rec rtcp_lsr; /* NTP timestamp in last sender report received */
- unsigned rtcp_lsr_time; /* Time when last RTCP SR is received. */
- unsigned peer_ssrc; /* Peer SSRC */
-
-};
-
-typedef struct pj_rtcp_session pj_rtcp_session;
-
-/**
- * Init RTCP session.
- * @param session The session
- * @param ssrc The SSRC used in to identify the session.
- */
-PJ_DECL(void) pj_rtcp_init( pj_rtcp_session *session, pj_uint32_t ssrc );
-
-/**
- * Deinit RTCP session.
- * @param session The session.
- */
-PJ_DECL(void) pj_rtcp_fini( pj_rtcp_session *session);
-
-/**
- * Call this function everytime an RTP packet is received to let the RTCP
- * session do its internal calculations.
- * @param session The session.
- * @param seq The RTP packet sequence number, in host byte order.
- * @param ts The RTP packet timestamp, in host byte order.
- */
-PJ_DECL(void) pj_rtcp_rx_rtp( pj_rtcp_session *session, pj_uint16_t seq, pj_uint32_t ts );
-
-/**
- * Call this function everytime an RTP packet is sent to let the RTCP session
- * do its internal calculations.
- * @param session The session.
- * @param bytes_payload_size The payload size of the RTP packet (ie packet minus
- * RTP header).
- */
-PJ_DECL(void) pj_rtcp_tx_rtp( pj_rtcp_session *session, pj_uint16_t bytes_payload_size );
-
-/**
- * Build a RTCP SR/RR packet to be transmitted to remote RTP peer.
- * @param session The session.
- * @param rtcp_pkt [output] Upon return, it will contain pointer to the RTCP packet.
- * @param len [output] Upon return, it will indicate the size of the RTCP packet.
- */
-PJ_DECL(void) pj_rtcp_build_rtcp( pj_rtcp_session *session, pj_rtcp_pkt **rtcp_pkt, int *len );
-
-/**
- * @}
- */
-
-PJ_END_DECL
-
-
-#endif /* __PJMEDIA_RTCP_H__ */
+/* $Id$
+ *
+ */
+/*
+ * PJMEDIA - Multimedia over IP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef __PJMEDIA_RTCP_H__
+#define __PJMEDIA_RTCP_H__
+
+/**
+ * @file rtcp.h
+ * @brief RTCP implementation.
+ */
+
+#include <pj/types.h>
+#include <pjmedia/rtp.h>
+
+PJ_BEGIN_DECL
+
+
+/**
+ * @defgroup PJMED_RTCP RTCP
+ * @ingroup PJMEDIA
+ * @{
+ */
+
+/**
+ * RTCP sender report.
+ */
+struct pj_rtcp_sr
+{
+ pj_uint32_t ssrc;
+ pj_uint32_t ntp_sec;
+ pj_uint32_t ntp_frac;
+ pj_uint32_t rtp_ts;
+ pj_uint32_t sender_pcount;
+ pj_uint32_t sender_bcount;
+};
+
+typedef struct pj_rtcp_sr pj_rtcp_sr;
+
+/**
+ * RTCP receiver report.
+ */
+struct pj_rtcp_rr
+{
+ pj_uint32_t ssrc;
+#if defined(PJ_IS_BIG_ENDIAN) && PJ_IS_BIG_ENDIAN!=0
+ pj_uint32_t fract_lost:8;
+ pj_uint32_t total_lost_2:8;
+ pj_uint32_t total_lost_1:8;
+ pj_uint32_t total_lost_0:8;
+#else
+ pj_uint32_t fract_lost:8;
+ pj_uint32_t total_lost_0:8;
+ pj_uint32_t total_lost_1:8;
+ pj_uint32_t total_lost_2:8;
+#endif
+ pj_uint32_t last_seq;
+ pj_uint32_t jitter;
+ pj_uint32_t lsr;
+ pj_uint32_t dlsr;
+};
+
+typedef struct pj_rtcp_rr pj_rtcp_rr;
+
+/**
+ * RTCP common header.
+ */
+struct pj_rtcp_common
+{
+#if defined(PJ_IS_BIG_ENDIAN) && PJ_IS_BIG_ENDIAN!=0
+ unsigned version:2; /* packet type */
+ unsigned p:1; /* padding flag */
+ unsigned count:5; /* varies by payload type */
+ unsigned pt:8; /* payload type */
+#else
+ unsigned count:5; /* varies by payload type */
+ unsigned p:1; /* padding flag */
+ unsigned version:2; /* packet type */
+ unsigned pt:8; /* payload type */
+#endif
+ pj_uint16_t length; /* packet length */
+};
+
+typedef struct pj_rtcp_common pj_rtcp_common;
+
+/**
+ * RTCP packet.
+ */
+struct pj_rtcp_pkt
+{
+ pj_rtcp_common common;
+ pj_rtcp_sr sr;
+ pj_rtcp_rr rr; /* variable-length list */
+};
+
+typedef struct pj_rtcp_pkt pj_rtcp_pkt;
+
+/**
+ * NTP time representation.
+ */
+struct pj_rtcp_ntp_rec
+{
+ pj_uint32_t hi;
+ pj_uint32_t lo;
+};
+
+typedef struct pj_rtcp_ntp_rec pj_rtcp_ntp_rec;
+
+/**
+ * RTCP session.
+ */
+struct pj_rtcp_session
+{
+ pj_rtcp_pkt rtcp_pkt;
+
+ pj_rtp_seq_session seq_ctrl;
+
+ pj_uint32_t received; /* packets received */
+ pj_uint32_t expected_prior; /* packet expected at last interval */
+ pj_uint32_t received_prior; /* packet received at last interval */
+ pj_int32_t transit; /* relative trans time for prev pkt */
+ pj_uint32_t jitter; /* estimated jitter */
+
+ pj_rtcp_ntp_rec rtcp_lsr; /* NTP timestamp in last sender report received */
+ unsigned rtcp_lsr_time; /* Time when last RTCP SR is received. */
+ unsigned peer_ssrc; /* Peer SSRC */
+
+};
+
+typedef struct pj_rtcp_session pj_rtcp_session;
+
+/**
+ * Init RTCP session.
+ * @param session The session
+ * @param ssrc The SSRC used in to identify the session.
+ */
+PJ_DECL(void) pj_rtcp_init( pj_rtcp_session *session, pj_uint32_t ssrc );
+
+/**
+ * Deinit RTCP session.
+ * @param session The session.
+ */
+PJ_DECL(void) pj_rtcp_fini( pj_rtcp_session *session);
+
+/**
+ * Call this function everytime an RTP packet is received to let the RTCP
+ * session do its internal calculations.
+ * @param session The session.
+ * @param seq The RTP packet sequence number, in host byte order.
+ * @param ts The RTP packet timestamp, in host byte order.
+ */
+PJ_DECL(void) pj_rtcp_rx_rtp( pj_rtcp_session *session, pj_uint16_t seq, pj_uint32_t ts );
+
+/**
+ * Call this function everytime an RTP packet is sent to let the RTCP session
+ * do its internal calculations.
+ * @param session The session.
+ * @param bytes_payload_size The payload size of the RTP packet (ie packet minus
+ * RTP header).
+ */
+PJ_DECL(void) pj_rtcp_tx_rtp( pj_rtcp_session *session, pj_uint16_t bytes_payload_size );
+
+/**
+ * Build a RTCP SR/RR packet to be transmitted to remote RTP peer.
+ * @param session The session.
+ * @param rtcp_pkt [output] Upon return, it will contain pointer to the RTCP packet.
+ * @param len [output] Upon return, it will indicate the size of the RTCP packet.
+ */
+PJ_DECL(void) pj_rtcp_build_rtcp( pj_rtcp_session *session, pj_rtcp_pkt **rtcp_pkt, int *len );
+
+/**
+ * @}
+ */
+
+PJ_END_DECL
+
+
+#endif /* __PJMEDIA_RTCP_H__ */
diff --git a/pjmedia/src/pjmedia/rtp.c b/pjmedia/src/pjmedia/rtp.c
index bbb867b2..c1b92862 100644
--- a/pjmedia/src/pjmedia/rtp.c
+++ b/pjmedia/src/pjmedia/rtp.c
@@ -1,247 +1,268 @@
-/* $Id$
- *
- */
-
-#include <pjmedia/rtp.h>
-#include <pj/log.h>
-#include <pj/os.h> /* pj_gettimeofday() */
-#include <pj/sock.h> /* pj_htonx, pj_htonx */
-#include <string.h> /* memset() */
-
-#define THIS_FILE "rtp.c"
-
-#define RTP_VERSION 2
-
-#define RTP_SEQ_MOD (1 << 16)
-#define MAX_DROPOUT ((pj_int16_t)3000)
-#define MAX_MISORDER ((pj_int16_t)100)
-#define MIN_SEQUENTIAL ((pj_int16_t)2)
-
-
-PJ_DEF(pj_status_t) pj_rtp_session_init( pj_rtp_session *ses,
- int default_pt, pj_uint32_t sender_ssrc )
-{
- PJ_LOG(4, (THIS_FILE, "pj_rtp_session_init: ses=%p, default_pt=%d, ssrc=0x%x",
- ses, default_pt, sender_ssrc));
-
- /* Check RTP header packing. */
- if (sizeof(struct pj_rtp_hdr) != 12) {
- pj_assert(!"Wrong RTP header packing!");
- return PJ_RTP_ERR_RTP_PACKING;
- }
-
- /* If sender_ssrc is not specified, create from time value. */
- if (sender_ssrc == 0 || sender_ssrc == (pj_uint32_t)-1) {
- pj_time_val tv;
-
- pj_gettimeofday(&tv);
- sender_ssrc = (pj_uint32_t) pj_htonl(tv.sec);
- } else {
- sender_ssrc = pj_htonl(sender_ssrc);
- }
-
- /* Initialize session. */
- ses->out_extseq = 0;
- ses->peer_ssrc = 0;
-
- /* Sequence number will be initialized when the first RTP packet is receieved. */
-
- /* Build default header for outgoing RTP packet. */
- memset(ses, 0, sizeof(*ses));
- ses->out_hdr.v = RTP_VERSION;
- ses->out_hdr.p = 0;
- ses->out_hdr.x = 0;
- ses->out_hdr.cc = 0;
- ses->out_hdr.m = 0;
- ses->out_hdr.pt = (pj_uint8_t) default_pt;
- ses->out_hdr.seq = (pj_uint16_t) pj_htons( (pj_uint16_t)ses->out_extseq );
- ses->out_hdr.ts = 0;
- ses->out_hdr.ssrc = sender_ssrc;
-
- /* Keep some arguments as session defaults. */
- ses->out_pt = (pj_uint16_t) default_pt;
-
- return PJ_SUCCESS;
-}
-
-
-PJ_DEF(pj_status_t) pj_rtp_encode_rtp( pj_rtp_session *ses, int pt, int m,
- int payload_len, int ts_len,
- const void **rtphdr, int *hdrlen )
-{
- PJ_UNUSED_ARG(payload_len)
-
- PJ_LOG(6, (THIS_FILE,
- "pj_rtp_encode_rtp: ses=%p, pt=%d, m=%d, pt_len=%d, ts_len=%d",
- ses, pt, m, payload_len, ts_len));
-
- /* Update session. */
- ses->out_extseq++;
- ses->out_hdr.ts = pj_htonl(pj_ntohl(ses->out_hdr.ts)+ts_len);
-
- /* Create outgoing header. */
- ses->out_hdr.pt = (pj_uint8_t) ((pt == -1) ? ses->out_pt : pt);
- ses->out_hdr.m = (pj_uint16_t) m;
- ses->out_hdr.seq = pj_htons( (pj_uint16_t) ses->out_extseq);
-
- /* Return values */
- *rtphdr = &ses->out_hdr;
- *hdrlen = sizeof(pj_rtp_hdr);
-
- return PJ_SUCCESS;
-}
-
-
-PJ_DEF(pj_status_t) pj_rtp_decode_rtp( pj_rtp_session *ses,
- const void *pkt, int pkt_len,
- const pj_rtp_hdr **hdr,
- const void **payload,
- unsigned *payloadlen)
-{
- int offset;
-
- PJ_UNUSED_ARG(ses)
-
- PJ_LOG(6, (THIS_FILE,
- "pj_rtp_decode_rtp: ses=%p, pkt=%p, pkt_len=%d",
- ses, pkt, pkt_len));
-
- /* Assume RTP header at the start of packet. We'll verify this later. */
- *hdr = (pj_rtp_hdr*)pkt;
-
- /* Check RTP header sanity. */
- if ((*hdr)->v != RTP_VERSION) {
- PJ_LOG(4, (THIS_FILE, " invalid RTP version!"));
- return PJ_RTP_ERR_INVALID_VERSION;
- }
-
- /* Payload is located right after header plus CSRC */
- offset = sizeof(pj_rtp_hdr) + ((*hdr)->cc * sizeof(pj_uint32_t));
-
- /* Adjust offset if RTP extension is used. */
- if ((*hdr)->x) {
- pj_rtp_ext_hdr *ext = (pj_rtp_ext_hdr*) (((pj_uint8_t*)pkt) + offset);
- offset += (pj_ntohs(ext->length) * sizeof(pj_uint32_t));
- }
-
- /* Check that offset is less than packet size */
- if (offset >= pkt_len)
- return PJ_RTP_ERR_INVALID_PACKET;
-
- /* Find and set payload. */
- *payload = ((pj_uint8_t*)pkt) + offset;
- *payloadlen = pkt_len - offset;
-
- return PJ_SUCCESS;
-}
-
-
-PJ_DEF(pj_status_t) pj_rtp_session_update( pj_rtp_session *ses, const pj_rtp_hdr *hdr)
-{
- int status;
-
- /* Check SSRC. */
- if (ses->peer_ssrc == 0) ses->peer_ssrc = pj_ntohl(hdr->ssrc);
- /*
- if (pj_ntohl(ses->peer_ssrc) != hdr->ssrc) {
- PJ_LOG(4, (THIS_FILE, "pj_rtp_session_update: ses=%p, invalid ssrc 0x%p (!=0x%p)",
- ses, pj_ntohl(hdr->ssrc), ses->peer_ssrc));
- return PJ_RTP_ERR_INVALID_SSRC;
- }
- */
-
- /* Check payload type. */
- if (hdr->pt != ses->out_pt) {
- PJ_LOG(4, (THIS_FILE, "pj_rtp_session_update: ses=%p, invalid payload type %d (!=%d)",
- ses, hdr->pt, ses->out_pt));
- return PJ_RTP_ERR_INVALID_PT;
- }
-
- /* Initialize sequence number on first packet received. */
- if (ses->received == 0)
- pj_rtp_seq_init( &ses->seq_ctrl, pj_ntohs(hdr->seq) );
-
- /* Check sequence number to see if remote session has been restarted. */
- status = pj_rtp_seq_update( &ses->seq_ctrl, pj_ntohs(hdr->seq));
- if (status == PJ_RTP_ERR_SESSION_RESTARTED) {
- pj_rtp_seq_restart( &ses->seq_ctrl, pj_ntohs(hdr->seq));
- ++ses->received;
- } else if (status == 0 || status == PJ_RTP_ERR_SESSION_PROBATION) {
- ++ses->received;
- }
-
-
- return status;
-}
-
-
-void pj_rtp_seq_restart(pj_rtp_seq_session *sctrl, pj_uint16_t seq)
-{
- sctrl->base_seq = seq;
- sctrl->max_seq = seq;
- sctrl->bad_seq = RTP_SEQ_MOD + 1;
- sctrl->cycles = 0;
-}
-
-
-void pj_rtp_seq_init(pj_rtp_seq_session *sctrl, pj_uint16_t seq)
-{
- pj_rtp_seq_restart(sctrl, seq);
-
- sctrl->max_seq = (pj_uint16_t) (seq - 1);
- sctrl->probation = MIN_SEQUENTIAL;
-}
-
-
-int pj_rtp_seq_update(pj_rtp_seq_session *sctrl, pj_uint16_t seq)
-{
- pj_uint16_t udelta = (pj_uint16_t) (seq - sctrl->max_seq);
-
- /*
- * Source is not valid until MIN_SEQUENTIAL packets with
- * sequential sequence numbers have been received.
- */
- if (sctrl->probation) {
- /* packet is in sequence */
- if (seq == sctrl->max_seq+ 1) {
- sctrl->probation--;
- sctrl->max_seq = seq;
- if (sctrl->probation == 0) {
- return PJ_RTP_ERR_SESSION_RESTARTED;
- }
- } else {
- sctrl->probation = MIN_SEQUENTIAL - 1;
- sctrl->max_seq = seq;
- }
- return PJ_RTP_ERR_SESSION_PROBATION;
-
- } else if (udelta < MAX_DROPOUT) {
- /* in order, with permissible gap */
- if (seq < sctrl->max_seq) {
- /* Sequence number wrapped - count another 64K cycle. */
- sctrl->cycles += RTP_SEQ_MOD;
- }
- sctrl->max_seq = seq;
-
- } else if (udelta <= (RTP_SEQ_MOD - MAX_MISORDER)) {
- /* the sequence number made a very large jump */
- if (seq == sctrl->bad_seq) {
- /*
- * Two sequential packets -- assume that the other side
- * restarted without telling us so just re-sync
- * (i.e., pretend this was the first packet).
- */
- return PJ_RTP_ERR_SESSION_RESTARTED;
- }
- else {
- sctrl->bad_seq = (seq + 1) & (RTP_SEQ_MOD-1);
- return PJ_RTP_ERR_BAD_SEQUENCE;
- }
- } else {
- /* duplicate or reordered packet */
- }
-
- return 0;
-}
-
-
+/* $Id$
+ *
+ */
+/*
+ * PJMEDIA - Multimedia over IP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <pjmedia/rtp.h>
+#include <pj/log.h>
+#include <pj/os.h> /* pj_gettimeofday() */
+#include <pj/sock.h> /* pj_htonx, pj_htonx */
+#include <string.h> /* memset() */
+
+#define THIS_FILE "rtp.c"
+
+#define RTP_VERSION 2
+
+#define RTP_SEQ_MOD (1 << 16)
+#define MAX_DROPOUT ((pj_int16_t)3000)
+#define MAX_MISORDER ((pj_int16_t)100)
+#define MIN_SEQUENTIAL ((pj_int16_t)2)
+
+
+PJ_DEF(pj_status_t) pj_rtp_session_init( pj_rtp_session *ses,
+ int default_pt, pj_uint32_t sender_ssrc )
+{
+ PJ_LOG(4, (THIS_FILE, "pj_rtp_session_init: ses=%p, default_pt=%d, ssrc=0x%x",
+ ses, default_pt, sender_ssrc));
+
+ /* Check RTP header packing. */
+ if (sizeof(struct pj_rtp_hdr) != 12) {
+ pj_assert(!"Wrong RTP header packing!");
+ return PJ_RTP_ERR_RTP_PACKING;
+ }
+
+ /* If sender_ssrc is not specified, create from time value. */
+ if (sender_ssrc == 0 || sender_ssrc == (pj_uint32_t)-1) {
+ pj_time_val tv;
+
+ pj_gettimeofday(&tv);
+ sender_ssrc = (pj_uint32_t) pj_htonl(tv.sec);
+ } else {
+ sender_ssrc = pj_htonl(sender_ssrc);
+ }
+
+ /* Initialize session. */
+ ses->out_extseq = 0;
+ ses->peer_ssrc = 0;
+
+ /* Sequence number will be initialized when the first RTP packet is receieved. */
+
+ /* Build default header for outgoing RTP packet. */
+ memset(ses, 0, sizeof(*ses));
+ ses->out_hdr.v = RTP_VERSION;
+ ses->out_hdr.p = 0;
+ ses->out_hdr.x = 0;
+ ses->out_hdr.cc = 0;
+ ses->out_hdr.m = 0;
+ ses->out_hdr.pt = (pj_uint8_t) default_pt;
+ ses->out_hdr.seq = (pj_uint16_t) pj_htons( (pj_uint16_t)ses->out_extseq );
+ ses->out_hdr.ts = 0;
+ ses->out_hdr.ssrc = sender_ssrc;
+
+ /* Keep some arguments as session defaults. */
+ ses->out_pt = (pj_uint16_t) default_pt;
+
+ return PJ_SUCCESS;
+}
+
+
+PJ_DEF(pj_status_t) pj_rtp_encode_rtp( pj_rtp_session *ses, int pt, int m,
+ int payload_len, int ts_len,
+ const void **rtphdr, int *hdrlen )
+{
+ PJ_UNUSED_ARG(payload_len)
+
+ PJ_LOG(6, (THIS_FILE,
+ "pj_rtp_encode_rtp: ses=%p, pt=%d, m=%d, pt_len=%d, ts_len=%d",
+ ses, pt, m, payload_len, ts_len));
+
+ /* Update session. */
+ ses->out_extseq++;
+ ses->out_hdr.ts = pj_htonl(pj_ntohl(ses->out_hdr.ts)+ts_len);
+
+ /* Create outgoing header. */
+ ses->out_hdr.pt = (pj_uint8_t) ((pt == -1) ? ses->out_pt : pt);
+ ses->out_hdr.m = (pj_uint16_t) m;
+ ses->out_hdr.seq = pj_htons( (pj_uint16_t) ses->out_extseq);
+
+ /* Return values */
+ *rtphdr = &ses->out_hdr;
+ *hdrlen = sizeof(pj_rtp_hdr);
+
+ return PJ_SUCCESS;
+}
+
+
+PJ_DEF(pj_status_t) pj_rtp_decode_rtp( pj_rtp_session *ses,
+ const void *pkt, int pkt_len,
+ const pj_rtp_hdr **hdr,
+ const void **payload,
+ unsigned *payloadlen)
+{
+ int offset;
+
+ PJ_UNUSED_ARG(ses)
+
+ PJ_LOG(6, (THIS_FILE,
+ "pj_rtp_decode_rtp: ses=%p, pkt=%p, pkt_len=%d",
+ ses, pkt, pkt_len));
+
+ /* Assume RTP header at the start of packet. We'll verify this later. */
+ *hdr = (pj_rtp_hdr*)pkt;
+
+ /* Check RTP header sanity. */
+ if ((*hdr)->v != RTP_VERSION) {
+ PJ_LOG(4, (THIS_FILE, " invalid RTP version!"));
+ return PJ_RTP_ERR_INVALID_VERSION;
+ }
+
+ /* Payload is located right after header plus CSRC */
+ offset = sizeof(pj_rtp_hdr) + ((*hdr)->cc * sizeof(pj_uint32_t));
+
+ /* Adjust offset if RTP extension is used. */
+ if ((*hdr)->x) {
+ pj_rtp_ext_hdr *ext = (pj_rtp_ext_hdr*) (((pj_uint8_t*)pkt) + offset);
+ offset += (pj_ntohs(ext->length) * sizeof(pj_uint32_t));
+ }
+
+ /* Check that offset is less than packet size */
+ if (offset >= pkt_len)
+ return PJ_RTP_ERR_INVALID_PACKET;
+
+ /* Find and set payload. */
+ *payload = ((pj_uint8_t*)pkt) + offset;
+ *payloadlen = pkt_len - offset;
+
+ return PJ_SUCCESS;
+}
+
+
+PJ_DEF(pj_status_t) pj_rtp_session_update( pj_rtp_session *ses, const pj_rtp_hdr *hdr)
+{
+ int status;
+
+ /* Check SSRC. */
+ if (ses->peer_ssrc == 0) ses->peer_ssrc = pj_ntohl(hdr->ssrc);
+ /*
+ if (pj_ntohl(ses->peer_ssrc) != hdr->ssrc) {
+ PJ_LOG(4, (THIS_FILE, "pj_rtp_session_update: ses=%p, invalid ssrc 0x%p (!=0x%p)",
+ ses, pj_ntohl(hdr->ssrc), ses->peer_ssrc));
+ return PJ_RTP_ERR_INVALID_SSRC;
+ }
+ */
+
+ /* Check payload type. */
+ if (hdr->pt != ses->out_pt) {
+ PJ_LOG(4, (THIS_FILE, "pj_rtp_session_update: ses=%p, invalid payload type %d (!=%d)",
+ ses, hdr->pt, ses->out_pt));
+ return PJ_RTP_ERR_INVALID_PT;
+ }
+
+ /* Initialize sequence number on first packet received. */
+ if (ses->received == 0)
+ pj_rtp_seq_init( &ses->seq_ctrl, pj_ntohs(hdr->seq) );
+
+ /* Check sequence number to see if remote session has been restarted. */
+ status = pj_rtp_seq_update( &ses->seq_ctrl, pj_ntohs(hdr->seq));
+ if (status == PJ_RTP_ERR_SESSION_RESTARTED) {
+ pj_rtp_seq_restart( &ses->seq_ctrl, pj_ntohs(hdr->seq));
+ ++ses->received;
+ } else if (status == 0 || status == PJ_RTP_ERR_SESSION_PROBATION) {
+ ++ses->received;
+ }
+
+
+ return status;
+}
+
+
+void pj_rtp_seq_restart(pj_rtp_seq_session *sctrl, pj_uint16_t seq)
+{
+ sctrl->base_seq = seq;
+ sctrl->max_seq = seq;
+ sctrl->bad_seq = RTP_SEQ_MOD + 1;
+ sctrl->cycles = 0;
+}
+
+
+void pj_rtp_seq_init(pj_rtp_seq_session *sctrl, pj_uint16_t seq)
+{
+ pj_rtp_seq_restart(sctrl, seq);
+
+ sctrl->max_seq = (pj_uint16_t) (seq - 1);
+ sctrl->probation = MIN_SEQUENTIAL;
+}
+
+
+int pj_rtp_seq_update(pj_rtp_seq_session *sctrl, pj_uint16_t seq)
+{
+ pj_uint16_t udelta = (pj_uint16_t) (seq - sctrl->max_seq);
+
+ /*
+ * Source is not valid until MIN_SEQUENTIAL packets with
+ * sequential sequence numbers have been received.
+ */
+ if (sctrl->probation) {
+ /* packet is in sequence */
+ if (seq == sctrl->max_seq+ 1) {
+ sctrl->probation--;
+ sctrl->max_seq = seq;
+ if (sctrl->probation == 0) {
+ return PJ_RTP_ERR_SESSION_RESTARTED;
+ }
+ } else {
+ sctrl->probation = MIN_SEQUENTIAL - 1;
+ sctrl->max_seq = seq;
+ }
+ return PJ_RTP_ERR_SESSION_PROBATION;
+
+ } else if (udelta < MAX_DROPOUT) {
+ /* in order, with permissible gap */
+ if (seq < sctrl->max_seq) {
+ /* Sequence number wrapped - count another 64K cycle. */
+ sctrl->cycles += RTP_SEQ_MOD;
+ }
+ sctrl->max_seq = seq;
+
+ } else if (udelta <= (RTP_SEQ_MOD - MAX_MISORDER)) {
+ /* the sequence number made a very large jump */
+ if (seq == sctrl->bad_seq) {
+ /*
+ * Two sequential packets -- assume that the other side
+ * restarted without telling us so just re-sync
+ * (i.e., pretend this was the first packet).
+ */
+ return PJ_RTP_ERR_SESSION_RESTARTED;
+ }
+ else {
+ sctrl->bad_seq = (seq + 1) & (RTP_SEQ_MOD-1);
+ return PJ_RTP_ERR_BAD_SEQUENCE;
+ }
+ } else {
+ /* duplicate or reordered packet */
+ }
+
+ return 0;
+}
+
+
diff --git a/pjmedia/src/pjmedia/rtp.h b/pjmedia/src/pjmedia/rtp.h
index e705aa41..1615b2d4 100644
--- a/pjmedia/src/pjmedia/rtp.h
+++ b/pjmedia/src/pjmedia/rtp.h
@@ -1,242 +1,263 @@
-/* $Id$
- *
- */
-
-#ifndef __PJMEDIA_RTP_H__
-#define __PJMEDIA_RTP_H__
-
-#include <pj/types.h>
-
-/**
- * @file rtp.h
- * @brief RTP implementation.
- */
-
-PJ_BEGIN_DECL
-
-
-/**
- * @defgroup PJMED_RTP RTP
- * @ingroup PJMEDIA
- * @{
- *
- * The RTP module is designed to be dependent only to PJLIB, it does not depend
- * on any other parts of PJMEDIA library. The RTP module does not even depend
- * on any transports (sockets), to promote even more use.
- *
- * An RTCP implementation is also separated from this module.
- *
- * The functions that are provided by this module:
- * - creating RTP header for each outgoing packet.
- * - decoding RTP packet into RTP header and payload.
- * - provide simple RTP session management (sequence number, etc.)
- *
- * The RTP module does not use any dynamic memory at all.
- *
- * \section P1 How to Use the RTP Module
- *
- * First application must call #pj_rtp_session_init to initialize the RTP
- * session.
- *
- * When application wants to send RTP packet, it needs to call
- * #pj_rtp_encode_rtp to build the RTP header. Note that this WILL NOT build
- * the complete RTP packet, but instead only the header. Application can
- * then either concatenate the header with the payload, or send the two
- * fragments (the header and the payload) using scatter-gather transport API
- * (e.g. \a sendv()).
- *
- * When application receives an RTP packet, first it should call
- * #pj_rtp_decode_rtp to decode RTP header and payload, then it should call
- * #pj_rtp_session_update to check whether we can process the RTP payload,
- * and to let the RTP session updates its internal status. The decode function
- * is guaranteed to point the payload to the correct position regardless of
- * any options present in the RTP packet.
- *
- */
-
-
-#ifdef _MSC_VER
-# pragma warning ( disable : 4214 )
-#endif
-
-
-/**
- * Error codes.
- */
-enum pj_rtp_error_t
-{
- PJ_RTP_ERR_RTP_PACKING, /**< Invalid RTP packet. */
- PJ_RTP_ERR_INVALID_VERSION, /**< Invalid RTP version. */
- PJ_RTP_ERR_INVALID_SSRC, /**< Invalid SSRC. */
- PJ_RTP_ERR_INVALID_PT, /**< Invalid payload type. */
- PJ_RTP_ERR_INVALID_PACKET, /**< Invalid packet. */
- PJ_RTP_ERR_SESSION_RESTARTED, /**< Session has just been restarted. */
- PJ_RTP_ERR_SESSION_PROBATION, /**< Session in probation. */
- PJ_RTP_ERR_BAD_SEQUENCE, /**< Bad RTP sequence number. */
-};
-
-#pragma pack(1)
-/**
- * RTP packet header.
- */
-struct pj_rtp_hdr
-{
-#if defined(PJ_IS_BIG_ENDIAN) && (PJ_IS_BIG_ENDIAN!=0)
- pj_uint16_t v:2; /**< packet type/version */
- pj_uint16_t p:1; /**< padding flag */
- pj_uint16_t x:1; /**< extension flag */
- pj_uint16_t cc:4; /**< CSRC count */
- pj_uint16_t m:1; /**< marker bit */
- pj_uint16_t pt:7; /**< payload type */
-#else
- pj_uint16_t cc:4; /**< CSRC count */
- pj_uint16_t x:1; /**< header extension flag */
- pj_uint16_t p:1; /**< padding flag */
- pj_uint16_t v:2; /**< packet type/version */
- pj_uint16_t pt:7; /**< payload type */
- pj_uint16_t m:1; /**< marker bit */
-#endif
- pj_uint16_t seq; /**< sequence number */
- pj_uint32_t ts; /**< timestamp */
- pj_uint32_t ssrc; /**< synchronization source */
-};
-#pragma pack()
-
-typedef struct pj_rtp_hdr pj_rtp_hdr;
-
-/**
- * RTP extendsion header.
- */
-struct pj_rtp_ext_hdr
-{
- pj_uint16_t profile_data;
- pj_uint16_t length;
-};
-
-typedef struct pj_rtp_ext_hdr pj_rtp_ext_hdr;
-
-/**
- * A generic sequence number management, used by both RTP and RTCP.
- */
-struct pj_rtp_seq_session
-{
- pj_uint16_t max_seq; /**< highest sequence number heard */
- pj_uint32_t cycles; /**< shifted count of seq. number cycles */
- pj_uint32_t base_seq; /**< base seq number */
- pj_uint32_t bad_seq; /**< last 'bad' seq number + 1 */
- pj_uint32_t probation; /**< sequ. packets till source is valid */
-};
-
-typedef struct pj_rtp_seq_session pj_rtp_seq_session;
-
-/**
- * RTP session descriptor.
- */
-struct pj_rtp_session
-{
- pj_rtp_hdr out_hdr; /**< Saved header for outgoing packets. */
- pj_rtp_seq_session seq_ctrl; /**< Sequence number management. */
- pj_uint16_t out_pt; /**< Default outgoing payload type. */
- pj_uint32_t out_extseq; /**< Outgoing extended sequence number. */
- pj_uint32_t peer_ssrc; /**< Peer SSRC. */
- pj_uint32_t received; /**< Number of received packets. */
-};
-
-typedef struct pj_rtp_session pj_rtp_session;
-
-/**
- * \brief Initialize RTP session.
- * This function will initialize the RTP session according to given parameters.
- *
- * @param ses The session.
- * @param default_pt Default payload type.
- * @param sender_ssrc SSRC used for outgoing packets.
- *
- * @return zero if successfull.
- */
-PJ_DECL(pj_status_t) pj_rtp_session_init( pj_rtp_session *ses,
- int default_pt, pj_uint32_t sender_ssrc );
-
-/**
- * \brief Encode outgoing RTP packet header.
- * Create the RTP header based on arguments and current state of the RTP
- * session.
- *
- * @param ses The session.
- * @param pt Payload type.
- * @param m Marker flag.
- * @param payload_len Payload length in bytes.
- * @param ts_len Timestamp length.
- * @param rtphdr Upon return will point to RTP packet header.
- * @param hdrlen Upon return will indicate the size of RTP packet header
- *
- * @return zero if successfull.
- */
-PJ_DECL(pj_status_t) pj_rtp_encode_rtp( pj_rtp_session *ses, int pt, int m,
- int payload_len, int ts_len,
- const void **rtphdr, int *hdrlen );
-
-/**
- * \brief Decode an incoming RTP packet.
- * This function will decode incoming packet into RTP header and payload.
- * The decode function is guaranteed to point the payload to the correct
- * position regardless of any options present in the RTP packet.
- *
- * @param ses The session.
- * @param pkt The received RTP packet.
- * @param pkt_len The length of the packet.
- * @param hdr Upon return will point to the location of the RTP header
- * inside the packet.
- * @param payload Upon return will point to the location of the
- * payload inside the packet.
- * @param payloadlen Upon return will indicate the size of the payload.
- *
- * @return zero if successfull.
- */
-PJ_DECL(pj_status_t) pj_rtp_decode_rtp( pj_rtp_session *ses,
- const void *pkt, int pkt_len,
- const pj_rtp_hdr **hdr,
- const void **payload,
- unsigned *payloadlen);
-
-/**
- * \brief Update RTP session with an incoming RTP packet.
- * Call this function everytime
- * an RTP packet is received to check whether the packet can be received and to
- * let the RTP session performs its internal calculations.
- *
- * @param ses The session.
- * @param hdr The RTP header of the incoming packet.
- *
- * @return zero if the packet is valid and can be processed, otherwise will
- * return one of the error in #pj_rtp_error_t.
- */
-PJ_DECL(pj_status_t) pj_rtp_session_update( pj_rtp_session *ses,
- const pj_rtp_hdr *hdr);
-
-/**
-* \brief Internal.
- * Internal function for sequence control, shared by RTCP implementation.
- */
-void pj_rtp_seq_init(pj_rtp_seq_session *seq_ctrl, pj_uint16_t seq);
-
-/**
-* \brief Internal.
- * Internal function for sequence control, shared by RTCP implementation.
- */
-void pj_rtp_seq_restart(pj_rtp_seq_session *seq_ctrl, pj_uint16_t seq);
-
-/**
-* \brief Internal.
- * Internal function for sequence control, shared by RTCP implementation.
- */
-int pj_rtp_seq_update(pj_rtp_seq_session *seq_ctrl, pj_uint16_t seq);
-
-/**
- * @}
- */
-
-PJ_END_DECL
-
-
-#endif /* __PJMEDIA_RTP_H__ */
+/* $Id$
+ *
+ */
+/*
+ * PJMEDIA - Multimedia over IP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef __PJMEDIA_RTP_H__
+#define __PJMEDIA_RTP_H__
+
+#include <pj/types.h>
+
+/**
+ * @file rtp.h
+ * @brief RTP implementation.
+ */
+
+PJ_BEGIN_DECL
+
+
+/**
+ * @defgroup PJMED_RTP RTP
+ * @ingroup PJMEDIA
+ * @{
+ *
+ * The RTP module is designed to be dependent only to PJLIB, it does not depend
+ * on any other parts of PJMEDIA library. The RTP module does not even depend
+ * on any transports (sockets), to promote even more use.
+ *
+ * An RTCP implementation is also separated from this module.
+ *
+ * The functions that are provided by this module:
+ * - creating RTP header for each outgoing packet.
+ * - decoding RTP packet into RTP header and payload.
+ * - provide simple RTP session management (sequence number, etc.)
+ *
+ * The RTP module does not use any dynamic memory at all.
+ *
+ * \section P1 How to Use the RTP Module
+ *
+ * First application must call #pj_rtp_session_init to initialize the RTP
+ * session.
+ *
+ * When application wants to send RTP packet, it needs to call
+ * #pj_rtp_encode_rtp to build the RTP header. Note that this WILL NOT build
+ * the complete RTP packet, but instead only the header. Application can
+ * then either concatenate the header with the payload, or send the two
+ * fragments (the header and the payload) using scatter-gather transport API
+ * (e.g. \a sendv()).
+ *
+ * When application receives an RTP packet, first it should call
+ * #pj_rtp_decode_rtp to decode RTP header and payload, then it should call
+ * #pj_rtp_session_update to check whether we can process the RTP payload,
+ * and to let the RTP session updates its internal status. The decode function
+ * is guaranteed to point the payload to the correct position regardless of
+ * any options present in the RTP packet.
+ *
+ */
+
+
+#ifdef _MSC_VER
+# pragma warning ( disable : 4214 )
+#endif
+
+
+/**
+ * Error codes.
+ */
+enum pj_rtp_error_t
+{
+ PJ_RTP_ERR_RTP_PACKING, /**< Invalid RTP packet. */
+ PJ_RTP_ERR_INVALID_VERSION, /**< Invalid RTP version. */
+ PJ_RTP_ERR_INVALID_SSRC, /**< Invalid SSRC. */
+ PJ_RTP_ERR_INVALID_PT, /**< Invalid payload type. */
+ PJ_RTP_ERR_INVALID_PACKET, /**< Invalid packet. */
+ PJ_RTP_ERR_SESSION_RESTARTED, /**< Session has just been restarted. */
+ PJ_RTP_ERR_SESSION_PROBATION, /**< Session in probation. */
+ PJ_RTP_ERR_BAD_SEQUENCE, /**< Bad RTP sequence number. */
+};
+
+#pragma pack(1)
+/**
+ * RTP packet header.
+ */
+struct pj_rtp_hdr
+{
+#if defined(PJ_IS_BIG_ENDIAN) && (PJ_IS_BIG_ENDIAN!=0)
+ pj_uint16_t v:2; /**< packet type/version */
+ pj_uint16_t p:1; /**< padding flag */
+ pj_uint16_t x:1; /**< extension flag */
+ pj_uint16_t cc:4; /**< CSRC count */
+ pj_uint16_t m:1; /**< marker bit */
+ pj_uint16_t pt:7; /**< payload type */
+#else
+ pj_uint16_t cc:4; /**< CSRC count */
+ pj_uint16_t x:1; /**< header extension flag */
+ pj_uint16_t p:1; /**< padding flag */
+ pj_uint16_t v:2; /**< packet type/version */
+ pj_uint16_t pt:7; /**< payload type */
+ pj_uint16_t m:1; /**< marker bit */
+#endif
+ pj_uint16_t seq; /**< sequence number */
+ pj_uint32_t ts; /**< timestamp */
+ pj_uint32_t ssrc; /**< synchronization source */
+};
+#pragma pack()
+
+typedef struct pj_rtp_hdr pj_rtp_hdr;
+
+/**
+ * RTP extendsion header.
+ */
+struct pj_rtp_ext_hdr
+{
+ pj_uint16_t profile_data;
+ pj_uint16_t length;
+};
+
+typedef struct pj_rtp_ext_hdr pj_rtp_ext_hdr;
+
+/**
+ * A generic sequence number management, used by both RTP and RTCP.
+ */
+struct pj_rtp_seq_session
+{
+ pj_uint16_t max_seq; /**< highest sequence number heard */
+ pj_uint32_t cycles; /**< shifted count of seq. number cycles */
+ pj_uint32_t base_seq; /**< base seq number */
+ pj_uint32_t bad_seq; /**< last 'bad' seq number + 1 */
+ pj_uint32_t probation; /**< sequ. packets till source is valid */
+};
+
+typedef struct pj_rtp_seq_session pj_rtp_seq_session;
+
+/**
+ * RTP session descriptor.
+ */
+struct pj_rtp_session
+{
+ pj_rtp_hdr out_hdr; /**< Saved header for outgoing packets. */
+ pj_rtp_seq_session seq_ctrl; /**< Sequence number management. */
+ pj_uint16_t out_pt; /**< Default outgoing payload type. */
+ pj_uint32_t out_extseq; /**< Outgoing extended sequence number. */
+ pj_uint32_t peer_ssrc; /**< Peer SSRC. */
+ pj_uint32_t received; /**< Number of received packets. */
+};
+
+typedef struct pj_rtp_session pj_rtp_session;
+
+/**
+ * \brief Initialize RTP session.
+ * This function will initialize the RTP session according to given parameters.
+ *
+ * @param ses The session.
+ * @param default_pt Default payload type.
+ * @param sender_ssrc SSRC used for outgoing packets.
+ *
+ * @return zero if successfull.
+ */
+PJ_DECL(pj_status_t) pj_rtp_session_init( pj_rtp_session *ses,
+ int default_pt, pj_uint32_t sender_ssrc );
+
+/**
+ * \brief Encode outgoing RTP packet header.
+ * Create the RTP header based on arguments and current state of the RTP
+ * session.
+ *
+ * @param ses The session.
+ * @param pt Payload type.
+ * @param m Marker flag.
+ * @param payload_len Payload length in bytes.
+ * @param ts_len Timestamp length.
+ * @param rtphdr Upon return will point to RTP packet header.
+ * @param hdrlen Upon return will indicate the size of RTP packet header
+ *
+ * @return zero if successfull.
+ */
+PJ_DECL(pj_status_t) pj_rtp_encode_rtp( pj_rtp_session *ses, int pt, int m,
+ int payload_len, int ts_len,
+ const void **rtphdr, int *hdrlen );
+
+/**
+ * \brief Decode an incoming RTP packet.
+ * This function will decode incoming packet into RTP header and payload.
+ * The decode function is guaranteed to point the payload to the correct
+ * position regardless of any options present in the RTP packet.
+ *
+ * @param ses The session.
+ * @param pkt The received RTP packet.
+ * @param pkt_len The length of the packet.
+ * @param hdr Upon return will point to the location of the RTP header
+ * inside the packet.
+ * @param payload Upon return will point to the location of the
+ * payload inside the packet.
+ * @param payloadlen Upon return will indicate the size of the payload.
+ *
+ * @return zero if successfull.
+ */
+PJ_DECL(pj_status_t) pj_rtp_decode_rtp( pj_rtp_session *ses,
+ const void *pkt, int pkt_len,
+ const pj_rtp_hdr **hdr,
+ const void **payload,
+ unsigned *payloadlen);
+
+/**
+ * \brief Update RTP session with an incoming RTP packet.
+ * Call this function everytime
+ * an RTP packet is received to check whether the packet can be received and to
+ * let the RTP session performs its internal calculations.
+ *
+ * @param ses The session.
+ * @param hdr The RTP header of the incoming packet.
+ *
+ * @return zero if the packet is valid and can be processed, otherwise will
+ * return one of the error in #pj_rtp_error_t.
+ */
+PJ_DECL(pj_status_t) pj_rtp_session_update( pj_rtp_session *ses,
+ const pj_rtp_hdr *hdr);
+
+/**
+* \brief Internal.
+ * Internal function for sequence control, shared by RTCP implementation.
+ */
+void pj_rtp_seq_init(pj_rtp_seq_session *seq_ctrl, pj_uint16_t seq);
+
+/**
+* \brief Internal.
+ * Internal function for sequence control, shared by RTCP implementation.
+ */
+void pj_rtp_seq_restart(pj_rtp_seq_session *seq_ctrl, pj_uint16_t seq);
+
+/**
+* \brief Internal.
+ * Internal function for sequence control, shared by RTCP implementation.
+ */
+int pj_rtp_seq_update(pj_rtp_seq_session *seq_ctrl, pj_uint16_t seq);
+
+/**
+ * @}
+ */
+
+PJ_END_DECL
+
+
+#endif /* __PJMEDIA_RTP_H__ */
diff --git a/pjmedia/src/pjmedia/sdp.c b/pjmedia/src/pjmedia/sdp.c
index aea3a43d..c3b20a97 100644
--- a/pjmedia/src/pjmedia/sdp.c
+++ b/pjmedia/src/pjmedia/sdp.c
@@ -1,936 +1,957 @@
-/* $Id$
- *
- */
-
-
-#include <pjmedia/sdp.h>
-#include <pj/scanner.h>
-#include <pj/except.h>
-#include <pj/log.h>
-#include <pj/os.h>
-#include <pj/string.h>
-#include <pj/pool.h>
-
-enum {
- SKIP_WS = 0,
- SYNTAX_ERROR = 1,
-};
-#define TOKEN "-.!%*_=`'~"
-#define NTP_OFFSET ((pj_uint32_t)2208988800)
-#define LOG_THIS "sdp"
-
-/*
- * Prototypes for line parser.
- */
-static void parse_version(pj_scanner *scanner);
-static void parse_origin(pj_scanner *scanner, pjsdp_session_desc *ses);
-static void parse_time(pj_scanner *scanner, pjsdp_session_desc *ses);
-static void parse_generic_line(pj_scanner *scanner, pj_str_t *str);
-static void parse_connection_info(pj_scanner *scanner, pjsdp_conn_info *conn);
-static pjsdp_attr *parse_attr(pj_pool_t *pool, pj_scanner *scanner);
-static void parse_media(pj_scanner *scanner, pjsdp_media_desc *med);
-
-/*
- * Prototypes for attribute parsers.
- */
-static pjsdp_rtpmap_attr * parse_rtpmap_attr( pj_pool_t *pool, pj_scanner *scanner );
-static pjsdp_attr_string * parse_generic_string_attr( pj_pool_t *pool, pj_scanner *scanner );
-static pjsdp_attr_num * parse_generic_num_attr( pj_pool_t *pool, pj_scanner *scanner );
-static pjsdp_attr * parse_name_only_attr( pj_pool_t *pool, pj_scanner *scanner );
-static pjsdp_fmtp_attr * parse_fmtp_attr( pj_pool_t *pool, pj_scanner *scanner );
-
-
-/*
- * Prototypes for functions to print attribute.
- * All of them returns integer for the length printed, or -1 on error.
- */
-static int print_rtpmap_attr(const pjsdp_rtpmap_attr *attr,
- char *buf, int length);
-static int print_generic_string_attr(const pjsdp_attr_string *attr,
- char *buf, int length);
-static int print_generic_num_attr(const pjsdp_attr_num *attr,
- char *buf, int length);
-static int print_name_only_attr(const pjsdp_attr *attr,
- char *buf, int length);
-static int print_fmtp_attr(const pjsdp_fmtp_attr *attr,
- char *buf, int length);
-
-/*
- * Prototypes for cloning attributes.
- */
-static pjsdp_attr* clone_rtpmap_attr (pj_pool_t *pool, const pjsdp_attr *rhs);
-static pjsdp_attr* clone_generic_string_attr (pj_pool_t *pool, const pjsdp_attr *rhs);
-static pjsdp_attr* clone_generic_num_attr (pj_pool_t *pool, const pjsdp_attr *rhs);
-static pjsdp_attr* clone_name_only_attr (pj_pool_t *pool, const pjsdp_attr *rhs);
-static pjsdp_attr* clone_fmtp_attr (pj_pool_t *pool, const pjsdp_attr *rhs);
-
-
-/*
- * Prototypes
- */
-static void init_sdp_parser(void);
-
-
-typedef void * (*FPARSE)(pj_pool_t *pool, pj_scanner *scanner);
-typedef int (*FPRINT)(const void *attr, char *buf, int length);
-typedef pjsdp_attr* (*FCLONE)(pj_pool_t *pool, const pjsdp_attr *rhs);
-
-/*
- * Array of functions to print attribute.
- */
-static struct attr_map_rec
-{
- pj_str_t name;
- FPARSE parse_attr;
- FPRINT print_attr;
- FCLONE clone;
-} attr_map[] =
-{
- {{"rtpmap", 6}, (FPARSE)&parse_rtpmap_attr, (FPRINT)&print_rtpmap_attr, (FCLONE)&clone_rtpmap_attr},
- {{"cat", 3}, (FPARSE)&parse_generic_string_attr, (FPRINT)&print_generic_string_attr, (FCLONE)&clone_generic_string_attr},
- {{"keywds", 6}, (FPARSE)&parse_generic_string_attr, (FPRINT)&print_generic_string_attr, (FCLONE)&clone_generic_string_attr},
- {{"tool", 4}, (FPARSE)&parse_generic_string_attr, (FPRINT)&print_generic_string_attr, (FCLONE)&clone_generic_string_attr},
- {{"ptime", 5}, (FPARSE)&parse_generic_num_attr, (FPRINT)&print_generic_num_attr, (FCLONE)&clone_generic_num_attr},
- {{"recvonly", 8}, (FPARSE)&parse_name_only_attr, (FPRINT)&print_name_only_attr, (FCLONE)&clone_name_only_attr},
- {{"sendonly", 8}, (FPARSE)&parse_name_only_attr, (FPRINT)&print_name_only_attr, (FCLONE)&clone_name_only_attr},
- {{"sendrecv", 8}, (FPARSE)&parse_name_only_attr, (FPRINT)&print_name_only_attr, (FCLONE)&clone_name_only_attr},
- {{"orient", 6}, (FPARSE)&parse_generic_string_attr, (FPRINT)&print_generic_string_attr, (FCLONE)&clone_generic_string_attr},
- {{"type", 4}, (FPARSE)&parse_generic_string_attr, (FPRINT)&print_generic_string_attr, (FCLONE)&clone_generic_string_attr},
- {{"charset", 7}, (FPARSE)&parse_generic_string_attr, (FPRINT)&print_generic_string_attr, (FCLONE)&clone_generic_string_attr},
- {{"sdplang", 7}, (FPARSE)&parse_generic_string_attr, (FPRINT)&print_generic_string_attr, (FCLONE)&clone_generic_string_attr},
- {{"lang", 4}, (FPARSE)&parse_generic_string_attr, (FPRINT)&print_generic_string_attr, (FCLONE)&clone_generic_string_attr},
- {{"framerate", 9}, (FPARSE)&parse_generic_string_attr, (FPRINT)&print_generic_string_attr, (FCLONE)&clone_generic_string_attr},
- {{"quality", 7}, (FPARSE)&parse_generic_num_attr, (FPRINT)&print_generic_num_attr, (FCLONE)&clone_generic_num_attr},
- {{"fmtp", 4}, (FPARSE)&parse_fmtp_attr, (FPRINT)&print_fmtp_attr, (FCLONE)&clone_fmtp_attr},
- {{"inactive", 8}, (FPARSE)&parse_name_only_attr, (FPRINT)&print_name_only_attr, (FCLONE)&clone_name_only_attr},
- {{"", 0}, NULL, (FPRINT)&print_generic_string_attr, (FCLONE)&clone_generic_string_attr}
-};
-
-/*
- * Scanner character specification.
- */
-static int is_initialized;
-static pj_char_spec cs_token;
-
-static void init_sdp_parser(void)
-{
- if (is_initialized == 0) {
- is_initialized = 1;
- if (is_initialized != 1) {
- return;
- }
- }
- pj_cs_add_alpha(cs_token);
- pj_cs_add_num(cs_token);
- pj_cs_add_str( cs_token, TOKEN);
-}
-
-static int print_rtpmap_attr(const pjsdp_rtpmap_attr *rtpmap,
- char *buf, int len)
-{
- char *p = buf;
-
- if (len < 16+rtpmap->encoding_name.slen+rtpmap->parameter.slen) {
- return -1;
- }
-
- /* colon and payload type. */
- *p++ = ':';
- len = pj_utoa(rtpmap->payload_type, p);
- p += len;
-
- /* space, encoding name */
- *p++ = ' ';
- pj_memcpy(p, rtpmap->encoding_name.ptr, rtpmap->encoding_name.slen);
- p += rtpmap->encoding_name.slen;
-
- /* slash, clock-rate. */
- *p++ = '/';
- len = pj_utoa(rtpmap->clock_rate, p);
- p += len;
-
- /* optionally add encoding parameter. */
- if (rtpmap->parameter.slen) {
- *p++ = '/';
- pj_memcpy(p, rtpmap->parameter.ptr, rtpmap->parameter.slen);
- p += rtpmap->parameter.slen;
- }
-
- return p-buf;
-}
-
-static int print_generic_string_attr(const pjsdp_attr_string *attr,
- char *buf, int len)
-{
- char *p = buf;
-
- if (len < attr->value.slen + 4) {
- return -1;
- }
-
- /* colon and attribute value. */
- *p++ = ':';
- pj_memcpy(p, attr->value.ptr, attr->value.slen);
- p += attr->value.slen;
-
- return p-buf;
-}
-
-static int print_generic_num_attr(const pjsdp_attr_num *attr, char *buf, int len)
-{
- char *p = buf;
-
- if (len < 10) {
- return -1;
- }
- *p++ = ':';
- return pj_utoa(attr->value, p);
-}
-
-static int print_name_only_attr(const pjsdp_attr *attr, char *buf, int len)
-{
- PJ_UNUSED_ARG(attr)
- PJ_UNUSED_ARG(buf)
- PJ_UNUSED_ARG(len)
- return 0;
-}
-
-static int print_fmtp_attr(const pjsdp_fmtp_attr *fmtp, char *buf, int len)
-{
- char *p = buf;
-
- if (len < 4+fmtp->format.slen+fmtp->param.slen) {
- return -1;
- }
-
- /* colon and format. */
- *p++ = ':';
- pj_memcpy(p, fmtp->format.ptr, fmtp->format.slen);
- p += fmtp->format.slen;
-
- /* space and parameter. */
- *p++ = ' ';
- pj_memcpy(p, fmtp->param.ptr, fmtp->param.slen);
- p += fmtp->param.slen;
-
- return p-buf;
-}
-
-
-static int print_attr(const pjsdp_attr *attr, char *buf, int len)
-{
- char *p = buf;
- struct attr_map_rec *desc = &attr_map[attr->type];
-
- if (len < 16) {
- return -1;
- }
-
- *p++ = 'a';
- *p++ = '=';
- pj_memcpy(p, desc->name.ptr, desc->name.slen);
- p += desc->name.slen;
-
- len = (*desc->print_attr)(attr, p, (buf+len)-p);
- if (len < 0) {
- return -1;
- }
- p += len;
- *p++ = '\r';
- *p++ = '\n';
- return p-buf;
-}
-
-static pjsdp_attr* clone_rtpmap_attr (pj_pool_t *pool, const pjsdp_attr *p)
-{
- const pjsdp_rtpmap_attr *rhs = (const pjsdp_rtpmap_attr*)p;
- pjsdp_rtpmap_attr *attr = pj_pool_alloc (pool, sizeof(pjsdp_rtpmap_attr));
- if (!attr)
- return NULL;
-
- attr->type = rhs->type;
- attr->payload_type = rhs->payload_type;
- if (!pj_strdup (pool, &attr->encoding_name, &rhs->encoding_name)) return NULL;
- attr->clock_rate = rhs->clock_rate;
- if (!pj_strdup (pool, &attr->parameter, &rhs->parameter)) return NULL;
-
- return (pjsdp_attr*)attr;
-}
-
-static pjsdp_attr* clone_generic_string_attr (pj_pool_t *pool, const pjsdp_attr *p)
-{
- const pjsdp_attr_string* rhs = (const pjsdp_attr_string*) p;
- pjsdp_attr_string *attr = pj_pool_alloc (pool, sizeof(pjsdp_attr_string));
- if (!attr)
- return NULL;
-
- attr->type = rhs->type;
- if (!pj_strdup (pool, &attr->value, &rhs->value)) return NULL;
-
- return (pjsdp_attr*)attr;
-}
-
-static pjsdp_attr* clone_generic_num_attr (pj_pool_t *pool, const pjsdp_attr *p)
-{
- const pjsdp_attr_num* rhs = (const pjsdp_attr_num*) p;
- pjsdp_attr_num *attr = pj_pool_alloc (pool, sizeof(pjsdp_attr_num));
- if (!attr)
- return NULL;
-
- attr->type = rhs->type;
- attr->value = rhs->value;
-
- return (pjsdp_attr*)attr;
-}
-
-static pjsdp_attr* clone_name_only_attr (pj_pool_t *pool, const pjsdp_attr *rhs)
-{
- pjsdp_attr *attr = pj_pool_alloc (pool, sizeof(pjsdp_attr));
- if (!attr)
- return NULL;
-
- attr->type = rhs->type;
- return attr;
-}
-
-static pjsdp_attr* clone_fmtp_attr (pj_pool_t *pool, const pjsdp_attr *p)
-{
- const pjsdp_fmtp_attr* rhs = (const pjsdp_fmtp_attr*) p;
- pjsdp_fmtp_attr *attr = pj_pool_alloc (pool, sizeof(pjsdp_fmtp_attr));
- if (!attr)
- return NULL;
-
- attr->type = rhs->type;
- if (!pj_strdup (pool, &attr->format, &rhs->format)) return NULL;
- if (!pj_strdup (pool, &attr->param, &rhs->param)) return NULL;
-
- return (pjsdp_attr*)attr;
-}
-
-PJ_DEF(pjsdp_attr*) pjsdp_attr_clone (pj_pool_t *pool, const pjsdp_attr *rhs)
-{
- struct attr_map_rec *desc;
-
- if (rhs->type >= PJSDP_END_OF_ATTR) {
- pj_assert(0);
- return NULL;
- }
-
- desc = &attr_map[rhs->type];
- return (*desc->clone) (pool, rhs);
-}
-
-PJ_DEF(const pjsdp_attr*) pjsdp_attr_find (int count, const pjsdp_attr *attr_array[], int type)
-{
- int i;
-
- for (i=0; i<count; ++i) {
- if (attr_array[i]->type == type)
- return attr_array[i];
- }
- return NULL;
-}
-
-static int print_connection_info( pjsdp_conn_info *c, char *buf, int len)
-{
- char *p = buf;
-
- if (len < 8+c->net_type.slen+c->addr_type.slen+c->addr.slen) {
- return -1;
- }
- *p++ = 'c';
- *p++ = '=';
- pj_memcpy(p, c->net_type.ptr, c->net_type.slen);
- p += c->net_type.slen;
- *p++ = ' ';
- pj_memcpy(p, c->addr_type.ptr, c->addr_type.slen);
- p += c->addr_type.slen;
- *p++ = ' ';
- pj_memcpy(p, c->addr.ptr, c->addr.slen);
- p += c->addr.slen;
- *p++ = '\r';
- *p++ = '\n';
-
- return p-buf;
-}
-
-PJ_DEF(pjsdp_conn_info*) pjsdp_conn_info_clone (pj_pool_t *pool, const pjsdp_conn_info *rhs)
-{
- pjsdp_conn_info *c = pj_pool_alloc (pool, sizeof(pjsdp_conn_info));
- if (!c) return NULL;
-
- if (!pj_strdup (pool, &c->net_type, &rhs->net_type)) return NULL;
- if (!pj_strdup (pool, &c->addr_type, &rhs->addr_type)) return NULL;
- if (!pj_strdup (pool, &c->addr, &rhs->addr)) return NULL;
-
- return c;
-}
-
-static int print_media_desc( pjsdp_media_desc *m, char *buf, int len)
-{
- char *p = buf;
- char *end = buf+len;
- unsigned i;
- int printed;
-
- /* check length for the "m=" line. */
- if (len < m->desc.media.slen+m->desc.transport.slen+12+24) {
- return -1;
- }
- *p++ = 'm'; /* m= */
- *p++ = '=';
- pj_memcpy(p, m->desc.media.ptr, m->desc.media.slen);
- p += m->desc.media.slen;
- *p++ = ' ';
- printed = pj_utoa(m->desc.port, p);
- p += printed;
- if (m->desc.port_count > 1) {
- *p++ = '/';
- printed = pj_utoa(m->desc.port_count, p);
- p += printed;
- }
- *p++ = ' ';
- pj_memcpy(p, m->desc.transport.ptr, m->desc.transport.slen);
- p += m->desc.transport.slen;
- for (i=0; i<m->desc.fmt_count; ++i) {
- *p++ = ' ';
- pj_memcpy(p, m->desc.fmt[i].ptr, m->desc.fmt[i].slen);
- p += m->desc.fmt[i].slen;
- }
- *p++ = '\r';
- *p++ = '\n';
-
- /* print connection info, if present. */
- if (m->conn) {
- printed = print_connection_info(m->conn, p, end-p);
- if (printed < 0) {
- return -1;
- }
- p += printed;
- }
-
- /* print attributes. */
- for (i=0; i<m->attr_count; ++i) {
- printed = print_attr(m->attr[i], p, end-p);
- if (printed < 0) {
- return -1;
- }
- p += printed;
- }
-
- return p-buf;
-}
-
-PJ_DEF(pjsdp_media_desc*) pjsdp_media_desc_clone (pj_pool_t *pool,
- const pjsdp_media_desc *rhs)
-{
- unsigned int i;
- pjsdp_media_desc *m = pj_pool_alloc (pool, sizeof(pjsdp_media_desc));
- if (!m)
- return NULL;
-
- pj_strdup (pool, &m->desc.media, &rhs->desc.media);
- m->desc.port = rhs->desc.port;
- m->desc.port_count = rhs->desc.port_count;
- pj_strdup (pool, &m->desc.transport, &rhs->desc.transport);
- m->desc.fmt_count = rhs->desc.fmt_count;
- for (i=0; i<rhs->desc.fmt_count; ++i)
- m->desc.fmt[i] = rhs->desc.fmt[i];
-
- if (rhs->conn) {
- m->conn = pjsdp_conn_info_clone (pool, rhs->conn);
- if (!m->conn)
- return NULL;
- } else {
- m->conn = NULL;
- }
-
- m->attr_count = rhs->attr_count;
- for (i=0; i < rhs->attr_count; ++i) {
- m->attr[i] = pjsdp_attr_clone (pool, rhs->attr[i]);
- if (!m->attr[i])
- return NULL;
- }
-
- return m;
-}
-
-/** Check if the media description has the specified attribute. */
-PJ_DEF(pj_bool_t) pjsdp_media_desc_has_attr (const pjsdp_media_desc *m,
- pjsdp_attr_type_e attr_type)
-{
- unsigned i;
- for (i=0; i<m->attr_count; ++i) {
- pjsdp_attr *attr = m->attr[i];
- if (attr->type == attr_type)
- return 1;
- }
- return 0;
-}
-
-/** Find rtpmap attribute for the specified payload type. */
-PJ_DEF(const pjsdp_rtpmap_attr*)
-pjsdp_media_desc_find_rtpmap (const pjsdp_media_desc *m, unsigned pt)
-{
- unsigned i;
- for (i=0; i<m->attr_count; ++i) {
- pjsdp_attr *attr = m->attr[i];
- if (attr->type == PJSDP_ATTR_RTPMAP) {
- const pjsdp_rtpmap_attr* rtpmap = (const pjsdp_rtpmap_attr*)attr;
- if (rtpmap->payload_type == pt)
- return rtpmap;
- }
- }
- return NULL;
-}
-
-
-static int print_session(const pjsdp_session_desc *ses, char *buf, pj_ssize_t len)
-{
- char *p = buf;
- char *end = buf+len;
- unsigned i;
- int printed;
-
- /* Check length for v= and o= lines. */
- if (len < 5+
- 2+ses->origin.user.slen+18+
- ses->origin.net_type.slen+ses->origin.addr.slen + 2)
- {
- return -1;
- }
-
- /* SDP version (v= line) */
- pj_memcpy(p, "v=0\r\n", 5);
- p += 5;
-
- /* Owner (o=) line. */
- *p++ = 'o';
- *p++ = '=';
- pj_memcpy(p, ses->origin.user.ptr, ses->origin.user.slen);
- p += ses->origin.user.slen;
- *p++ = ' ';
- printed = pj_utoa(ses->origin.id, p);
- p += printed;
- *p++ = ' ';
- printed = pj_utoa(ses->origin.version, p);
- p += printed;
- *p++ = ' ';
- pj_memcpy(p, ses->origin.net_type.ptr, ses->origin.net_type.slen);
- p += ses->origin.net_type.slen;
- *p++ = ' ';
- pj_memcpy(p, ses->origin.addr_type.ptr, ses->origin.addr_type.slen);
- p += ses->origin.addr_type.slen;
- *p++ = ' ';
- pj_memcpy(p, ses->origin.addr.ptr, ses->origin.addr.slen);
- p += ses->origin.addr.slen;
- *p++ = '\r';
- *p++ = '\n';
-
- /* Session name (s=) line. */
- if ((end-p) < 8+ses->name.slen) {
- return -1;
- }
- *p++ = 's';
- *p++ = '=';
- pj_memcpy(p, ses->name.ptr, ses->name.slen);
- p += ses->name.slen;
- *p++ = '\r';
- *p++ = '\n';
-
- /* Time */
- if ((end-p) < 24) {
- return -1;
- }
- *p++ = 't';
- *p++ = '=';
- printed = pj_utoa(ses->time.start, p);
- p += printed;
- *p++ = ' ';
- printed = pj_utoa(ses->time.stop, p);
- p += printed;
- *p++ = '\r';
- *p++ = '\n';
-
- /* Connection line (c=) if exist. */
- if (ses->conn) {
- printed = print_connection_info(ses->conn, p, end-p);
- if (printed < 1) {
- return -1;
- }
- p += printed;
- }
-
- /* Print all attribute (a=) lines. */
- for (i=0; i<ses->attr_count; ++i) {
- printed = print_attr(ses->attr[i], p, end-p);
- if (printed < 0) {
- return -1;
- }
- p += printed;
- }
-
- /* Print media (m=) lines. */
- for (i=0; i<ses->media_count; ++i) {
- printed = print_media_desc(ses->media[i], p, end-p);
- if (printed < 0) {
- return -1;
- }
- p += printed;
- }
-
- return p-buf;
-}
-
-/******************************************************************************
- * PARSERS
- */
-
-static void parse_version(pj_scanner *scanner)
-{
- pj_scan_advance_n(scanner, 3, SKIP_WS);
- pj_scan_get_newline(scanner);
-}
-
-static void parse_origin(pj_scanner *scanner, pjsdp_session_desc *ses)
-{
- pj_str_t str;
-
- /* o= */
- pj_scan_advance_n(scanner, 2, SKIP_WS);
-
- /* username. */
- pj_scan_get_until_ch(scanner, ' ', &ses->origin.user);
- pj_scan_get_char(scanner);
-
- /* id */
- pj_scan_get_until_ch(scanner, ' ', &str);
- ses->origin.id = pj_strtoul(&str);
- pj_scan_get_char(scanner);
-
- /* version */
- pj_scan_get_until_ch(scanner, ' ', &str);
- ses->origin.version = pj_strtoul(&str);
- pj_scan_get_char(scanner);
-
- /* network-type */
- pj_scan_get_until_ch(scanner, ' ', &ses->origin.net_type);
- pj_scan_get_char(scanner);
-
- /* addr-type */
- pj_scan_get_until_ch(scanner, ' ', &ses->origin.addr_type);
- pj_scan_get_char(scanner);
-
- /* address */
- pj_scan_get_until_ch(scanner, '\r', &ses->origin.addr);
-
- /* newline */
- pj_scan_get_newline(scanner);
-}
-
-static void parse_time(pj_scanner *scanner, pjsdp_session_desc *ses)
-{
- pj_str_t str;
-
- /* t= */
- pj_scan_advance_n(scanner, 2, SKIP_WS);
-
- /* start time */
- pj_scan_get_until_ch(scanner, ' ', &str);
- ses->time.start = pj_strtoul(&str);
-
- pj_scan_get_char(scanner);
-
- /* stop time */
- pj_scan_get_until_ch(scanner, '\r', &str);
- ses->time.stop = pj_strtoul(&str);
-
- /* newline */
- pj_scan_get_newline(scanner);
-}
-
-static void parse_generic_line(pj_scanner *scanner, pj_str_t *str)
-{
- /* x= */
- pj_scan_advance_n(scanner, 2, SKIP_WS);
-
- /* get anything until newline. */
- pj_scan_get_until_ch(scanner, '\r', str);
-
- /* newline. */
- pj_scan_get_newline(scanner);
-}
-
-static void parse_connection_info(pj_scanner *scanner, pjsdp_conn_info *conn)
-{
- /* c= */
- pj_scan_advance_n(scanner, 2, SKIP_WS);
-
- /* network-type */
- pj_scan_get_until_ch(scanner, ' ', &conn->net_type);
- pj_scan_get_char(scanner);
-
- /* addr-type */
- pj_scan_get_until_ch(scanner, ' ', &conn->addr_type);
- pj_scan_get_char(scanner);
-
- /* address. */
- pj_scan_get_until_ch(scanner, '\r', &conn->addr);
-
- /* newline */
- pj_scan_get_newline(scanner);
-}
-
-static void parse_media(pj_scanner *scanner, pjsdp_media_desc *med)
-{
- pj_str_t str;
-
- /* m= */
- pj_scan_advance_n(scanner, 2, SKIP_WS);
-
- /* type */
- pj_scan_get_until_ch(scanner, ' ', &med->desc.media);
- pj_scan_get_char(scanner);
-
- /* port */
- pj_scan_get(scanner, cs_token, &str);
- med->desc.port = (unsigned short)pj_strtoul(&str);
- if (*scanner->current == '/') {
- /* port count */
- pj_scan_get_char(scanner);
- pj_scan_get(scanner, cs_token, &str);
- med->desc.port_count = pj_strtoul(&str);
-
- } else {
- med->desc.port_count = 0;
- }
-
- if (pj_scan_get_char(scanner) != ' ') {
- PJ_THROW(SYNTAX_ERROR);
- }
-
- /* transport */
- pj_scan_get_until_ch(scanner, ' ', &med->desc.transport);
-
- /* format list */
- med->desc.fmt_count = 0;
- while (*scanner->current == ' ') {
- pj_scan_get_char(scanner);
- pj_scan_get(scanner, cs_token, &med->desc.fmt[med->desc.fmt_count++]);
- }
-
- /* newline */
- pj_scan_get_newline(scanner);
-}
-
-static pjsdp_rtpmap_attr * parse_rtpmap_attr( pj_pool_t *pool, pj_scanner *scanner )
-{
- pjsdp_rtpmap_attr *rtpmap;
- pj_str_t str;
-
- rtpmap = pj_pool_calloc(pool, 1, sizeof(*rtpmap));
- if (pj_scan_get_char(scanner) != ':') {
- PJ_THROW(SYNTAX_ERROR);
- }
- pj_scan_get_until_ch(scanner, ' ', &str);
- rtpmap->payload_type = pj_strtoul(&str);
- pj_scan_get_char(scanner);
-
- pj_scan_get_until_ch(scanner, '/', &rtpmap->encoding_name);
- pj_scan_get_char(scanner);
- pj_scan_get(scanner, cs_token, &str);
- rtpmap->clock_rate = pj_strtoul(&str);
-
- if (*scanner->current == '/') {
- pj_scan_get_char(scanner);
- pj_scan_get_until_ch(scanner, '\r', &rtpmap->parameter);
- }
-
- return rtpmap;
-}
-
-static pjsdp_attr_string * parse_generic_string_attr( pj_pool_t *pool, pj_scanner *scanner )
-{
- pjsdp_attr_string *attr;
- attr = pj_pool_calloc(pool, 1, sizeof(*attr));
-
- if (pj_scan_get_char(scanner) != ':') {
- PJ_THROW(SYNTAX_ERROR);
- }
- pj_scan_get_until_ch(scanner, '\r', &attr->value);
- return attr;
-}
-
-static pjsdp_attr_num * parse_generic_num_attr( pj_pool_t *pool, pj_scanner *scanner )
-{
- pjsdp_attr_num *attr;
- pj_str_t str;
-
- attr = pj_pool_calloc(pool, 1, sizeof(*attr));
-
- if (pj_scan_get_char(scanner) != ':') {
- PJ_THROW(SYNTAX_ERROR);
- }
- pj_scan_get_until_ch(scanner, '\r', &str);
- attr->value = pj_strtoul(&str);
- return attr;
-}
-
-static pjsdp_attr * parse_name_only_attr( pj_pool_t *pool, pj_scanner *scanner )
-{
- pjsdp_attr *attr;
-
- PJ_UNUSED_ARG(scanner)
- attr = pj_pool_calloc(pool, 1, sizeof(*attr));
- return attr;
-}
-
-static pjsdp_fmtp_attr * parse_fmtp_attr( pj_pool_t *pool, pj_scanner *scanner )
-{
- pjsdp_fmtp_attr *fmtp;
-
- fmtp = pj_pool_calloc(pool, 1, sizeof(*fmtp));
-
- if (pj_scan_get_char(scanner) != ':') {
- PJ_THROW(SYNTAX_ERROR);
- }
- pj_scan_get_until_ch(scanner, ' ', &fmtp->format);
- pj_scan_get_char(scanner);
- pj_scan_get_until_ch(scanner, '\r', &fmtp->param);
- return fmtp;
-}
-
-static pjsdp_attr *parse_attr( pj_pool_t *pool, pj_scanner *scanner)
-{
- void * (*parse_func)(pj_pool_t *pool, pj_scanner *scanner) = NULL;
- pj_str_t attrname;
- unsigned i;
- pjsdp_attr *attr;
-
- /* skip a= */
- pj_scan_advance_n(scanner, 2, SKIP_WS);
-
- /* get attr name. */
- pj_scan_get(scanner, cs_token, &attrname);
-
- /* find entry to handle attrname */
- for (i=0; i<PJ_ARRAY_SIZE(attr_map); ++i) {
- struct attr_map_rec *p = &attr_map[i];
- if (pj_strcmp(&attrname, &p->name) == 0) {
- parse_func = p->parse_attr;
- break;
- }
- }
-
- /* fallback to generic string parser. */
- if (parse_func == NULL) {
- parse_func = &parse_generic_string_attr;
- }
-
- attr = (*parse_func)(pool, scanner);
- attr->type = i;
-
- /* newline */
- pj_scan_get_newline(scanner);
-
- return attr;
-}
-
-static void on_scanner_error(pj_scanner *scanner)
-{
- PJ_UNUSED_ARG(scanner)
-
- PJ_THROW(SYNTAX_ERROR);
-}
-
-/*
- * Parse SDP message.
- */
-PJ_DEF(pjsdp_session_desc*) pjsdp_parse( char *buf, pj_size_t len,
- pj_pool_t *pool)
-{
- pj_scanner scanner;
- pjsdp_session_desc *session;
- pjsdp_media_desc *media = NULL;
- void *attr;
- pjsdp_conn_info *conn;
- pj_str_t dummy;
- int cur_name = 254;
- PJ_USE_EXCEPTION;
-
- init_sdp_parser();
-
- pj_scan_init(&scanner, buf, len, 0, &on_scanner_error);
- session = pj_pool_calloc(pool, 1, sizeof(*session));
-
- PJ_TRY {
- while (!pj_scan_is_eof(&scanner)) {
- cur_name = *scanner.current;
- switch (cur_name) {
- case 'a':
- attr = parse_attr(pool, &scanner);
- if (attr) {
- if (media) {
- media->attr[media->attr_count++] = attr;
- } else {
- session->attr[session->attr_count++] = attr;
- }
- }
- break;
- case 'o':
- parse_origin(&scanner, session);
- break;
- case 's':
- parse_generic_line(&scanner, &session->name);
- break;
- case 'c':
- conn = pj_pool_calloc(pool, 1, sizeof(*conn));
- parse_connection_info(&scanner, conn);
- if (media) {
- media->conn = conn;
- } else {
- session->conn = conn;
- }
- break;
- case 't':
- parse_time(&scanner, session);
- break;
- case 'm':
- media = pj_pool_calloc(pool, 1, sizeof(*media));
- parse_media(&scanner, media);
- session->media[ session->media_count++ ] = media;
- break;
- case 'v':
- parse_version(&scanner);
- break;
- default:
- parse_generic_line(&scanner, &dummy);
- break;
- }
- }
- }
- PJ_CATCH(SYNTAX_ERROR) {
- PJ_LOG(2, (LOG_THIS, "Syntax error in SDP parser '%c' line %d col %d",
- cur_name, scanner.line, scanner.col));
- if (!pj_scan_is_eof(&scanner)) {
- if (*scanner.current != '\r') {
- pj_scan_get_until_ch(&scanner, '\r', &dummy);
- }
- pj_scan_get_newline(&scanner);
- }
- }
- PJ_END;
-
- pj_scan_fini(&scanner);
- return session;
-}
-
-/*
- * Print SDP description.
- */
-PJ_DEF(int) pjsdp_print( const pjsdp_session_desc *desc, char *buf, pj_size_t size)
-{
- return print_session(desc, buf, size);
-}
-
-
+/* $Id$
+ *
+ */
+/*
+ * PJMEDIA - Multimedia over IP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+
+#include <pjmedia/sdp.h>
+#include <pj/scanner.h>
+#include <pj/except.h>
+#include <pj/log.h>
+#include <pj/os.h>
+#include <pj/string.h>
+#include <pj/pool.h>
+
+enum {
+ SKIP_WS = 0,
+ SYNTAX_ERROR = 1,
+};
+#define TOKEN "-.!%*_=`'~"
+#define NTP_OFFSET ((pj_uint32_t)2208988800)
+#define LOG_THIS "sdp"
+
+/*
+ * Prototypes for line parser.
+ */
+static void parse_version(pj_scanner *scanner);
+static void parse_origin(pj_scanner *scanner, pjsdp_session_desc *ses);
+static void parse_time(pj_scanner *scanner, pjsdp_session_desc *ses);
+static void parse_generic_line(pj_scanner *scanner, pj_str_t *str);
+static void parse_connection_info(pj_scanner *scanner, pjsdp_conn_info *conn);
+static pjsdp_attr *parse_attr(pj_pool_t *pool, pj_scanner *scanner);
+static void parse_media(pj_scanner *scanner, pjsdp_media_desc *med);
+
+/*
+ * Prototypes for attribute parsers.
+ */
+static pjsdp_rtpmap_attr * parse_rtpmap_attr( pj_pool_t *pool, pj_scanner *scanner );
+static pjsdp_attr_string * parse_generic_string_attr( pj_pool_t *pool, pj_scanner *scanner );
+static pjsdp_attr_num * parse_generic_num_attr( pj_pool_t *pool, pj_scanner *scanner );
+static pjsdp_attr * parse_name_only_attr( pj_pool_t *pool, pj_scanner *scanner );
+static pjsdp_fmtp_attr * parse_fmtp_attr( pj_pool_t *pool, pj_scanner *scanner );
+
+
+/*
+ * Prototypes for functions to print attribute.
+ * All of them returns integer for the length printed, or -1 on error.
+ */
+static int print_rtpmap_attr(const pjsdp_rtpmap_attr *attr,
+ char *buf, int length);
+static int print_generic_string_attr(const pjsdp_attr_string *attr,
+ char *buf, int length);
+static int print_generic_num_attr(const pjsdp_attr_num *attr,
+ char *buf, int length);
+static int print_name_only_attr(const pjsdp_attr *attr,
+ char *buf, int length);
+static int print_fmtp_attr(const pjsdp_fmtp_attr *attr,
+ char *buf, int length);
+
+/*
+ * Prototypes for cloning attributes.
+ */
+static pjsdp_attr* clone_rtpmap_attr (pj_pool_t *pool, const pjsdp_attr *rhs);
+static pjsdp_attr* clone_generic_string_attr (pj_pool_t *pool, const pjsdp_attr *rhs);
+static pjsdp_attr* clone_generic_num_attr (pj_pool_t *pool, const pjsdp_attr *rhs);
+static pjsdp_attr* clone_name_only_attr (pj_pool_t *pool, const pjsdp_attr *rhs);
+static pjsdp_attr* clone_fmtp_attr (pj_pool_t *pool, const pjsdp_attr *rhs);
+
+
+/*
+ * Prototypes
+ */
+static void init_sdp_parser(void);
+
+
+typedef void * (*FPARSE)(pj_pool_t *pool, pj_scanner *scanner);
+typedef int (*FPRINT)(const void *attr, char *buf, int length);
+typedef pjsdp_attr* (*FCLONE)(pj_pool_t *pool, const pjsdp_attr *rhs);
+
+/*
+ * Array of functions to print attribute.
+ */
+static struct attr_map_rec
+{
+ pj_str_t name;
+ FPARSE parse_attr;
+ FPRINT print_attr;
+ FCLONE clone;
+} attr_map[] =
+{
+ {{"rtpmap", 6}, (FPARSE)&parse_rtpmap_attr, (FPRINT)&print_rtpmap_attr, (FCLONE)&clone_rtpmap_attr},
+ {{"cat", 3}, (FPARSE)&parse_generic_string_attr, (FPRINT)&print_generic_string_attr, (FCLONE)&clone_generic_string_attr},
+ {{"keywds", 6}, (FPARSE)&parse_generic_string_attr, (FPRINT)&print_generic_string_attr, (FCLONE)&clone_generic_string_attr},
+ {{"tool", 4}, (FPARSE)&parse_generic_string_attr, (FPRINT)&print_generic_string_attr, (FCLONE)&clone_generic_string_attr},
+ {{"ptime", 5}, (FPARSE)&parse_generic_num_attr, (FPRINT)&print_generic_num_attr, (FCLONE)&clone_generic_num_attr},
+ {{"recvonly", 8}, (FPARSE)&parse_name_only_attr, (FPRINT)&print_name_only_attr, (FCLONE)&clone_name_only_attr},
+ {{"sendonly", 8}, (FPARSE)&parse_name_only_attr, (FPRINT)&print_name_only_attr, (FCLONE)&clone_name_only_attr},
+ {{"sendrecv", 8}, (FPARSE)&parse_name_only_attr, (FPRINT)&print_name_only_attr, (FCLONE)&clone_name_only_attr},
+ {{"orient", 6}, (FPARSE)&parse_generic_string_attr, (FPRINT)&print_generic_string_attr, (FCLONE)&clone_generic_string_attr},
+ {{"type", 4}, (FPARSE)&parse_generic_string_attr, (FPRINT)&print_generic_string_attr, (FCLONE)&clone_generic_string_attr},
+ {{"charset", 7}, (FPARSE)&parse_generic_string_attr, (FPRINT)&print_generic_string_attr, (FCLONE)&clone_generic_string_attr},
+ {{"sdplang", 7}, (FPARSE)&parse_generic_string_attr, (FPRINT)&print_generic_string_attr, (FCLONE)&clone_generic_string_attr},
+ {{"lang", 4}, (FPARSE)&parse_generic_string_attr, (FPRINT)&print_generic_string_attr, (FCLONE)&clone_generic_string_attr},
+ {{"framerate", 9}, (FPARSE)&parse_generic_string_attr, (FPRINT)&print_generic_string_attr, (FCLONE)&clone_generic_string_attr},
+ {{"quality", 7}, (FPARSE)&parse_generic_num_attr, (FPRINT)&print_generic_num_attr, (FCLONE)&clone_generic_num_attr},
+ {{"fmtp", 4}, (FPARSE)&parse_fmtp_attr, (FPRINT)&print_fmtp_attr, (FCLONE)&clone_fmtp_attr},
+ {{"inactive", 8}, (FPARSE)&parse_name_only_attr, (FPRINT)&print_name_only_attr, (FCLONE)&clone_name_only_attr},
+ {{"", 0}, NULL, (FPRINT)&print_generic_string_attr, (FCLONE)&clone_generic_string_attr}
+};
+
+/*
+ * Scanner character specification.
+ */
+static int is_initialized;
+static pj_char_spec cs_token;
+
+static void init_sdp_parser(void)
+{
+ if (is_initialized == 0) {
+ is_initialized = 1;
+ if (is_initialized != 1) {
+ return;
+ }
+ }
+ pj_cs_add_alpha(cs_token);
+ pj_cs_add_num(cs_token);
+ pj_cs_add_str( cs_token, TOKEN);
+}
+
+static int print_rtpmap_attr(const pjsdp_rtpmap_attr *rtpmap,
+ char *buf, int len)
+{
+ char *p = buf;
+
+ if (len < 16+rtpmap->encoding_name.slen+rtpmap->parameter.slen) {
+ return -1;
+ }
+
+ /* colon and payload type. */
+ *p++ = ':';
+ len = pj_utoa(rtpmap->payload_type, p);
+ p += len;
+
+ /* space, encoding name */
+ *p++ = ' ';
+ pj_memcpy(p, rtpmap->encoding_name.ptr, rtpmap->encoding_name.slen);
+ p += rtpmap->encoding_name.slen;
+
+ /* slash, clock-rate. */
+ *p++ = '/';
+ len = pj_utoa(rtpmap->clock_rate, p);
+ p += len;
+
+ /* optionally add encoding parameter. */
+ if (rtpmap->parameter.slen) {
+ *p++ = '/';
+ pj_memcpy(p, rtpmap->parameter.ptr, rtpmap->parameter.slen);
+ p += rtpmap->parameter.slen;
+ }
+
+ return p-buf;
+}
+
+static int print_generic_string_attr(const pjsdp_attr_string *attr,
+ char *buf, int len)
+{
+ char *p = buf;
+
+ if (len < attr->value.slen + 4) {
+ return -1;
+ }
+
+ /* colon and attribute value. */
+ *p++ = ':';
+ pj_memcpy(p, attr->value.ptr, attr->value.slen);
+ p += attr->value.slen;
+
+ return p-buf;
+}
+
+static int print_generic_num_attr(const pjsdp_attr_num *attr, char *buf, int len)
+{
+ char *p = buf;
+
+ if (len < 10) {
+ return -1;
+ }
+ *p++ = ':';
+ return pj_utoa(attr->value, p);
+}
+
+static int print_name_only_attr(const pjsdp_attr *attr, char *buf, int len)
+{
+ PJ_UNUSED_ARG(attr)
+ PJ_UNUSED_ARG(buf)
+ PJ_UNUSED_ARG(len)
+ return 0;
+}
+
+static int print_fmtp_attr(const pjsdp_fmtp_attr *fmtp, char *buf, int len)
+{
+ char *p = buf;
+
+ if (len < 4+fmtp->format.slen+fmtp->param.slen) {
+ return -1;
+ }
+
+ /* colon and format. */
+ *p++ = ':';
+ pj_memcpy(p, fmtp->format.ptr, fmtp->format.slen);
+ p += fmtp->format.slen;
+
+ /* space and parameter. */
+ *p++ = ' ';
+ pj_memcpy(p, fmtp->param.ptr, fmtp->param.slen);
+ p += fmtp->param.slen;
+
+ return p-buf;
+}
+
+
+static int print_attr(const pjsdp_attr *attr, char *buf, int len)
+{
+ char *p = buf;
+ struct attr_map_rec *desc = &attr_map[attr->type];
+
+ if (len < 16) {
+ return -1;
+ }
+
+ *p++ = 'a';
+ *p++ = '=';
+ pj_memcpy(p, desc->name.ptr, desc->name.slen);
+ p += desc->name.slen;
+
+ len = (*desc->print_attr)(attr, p, (buf+len)-p);
+ if (len < 0) {
+ return -1;
+ }
+ p += len;
+ *p++ = '\r';
+ *p++ = '\n';
+ return p-buf;
+}
+
+static pjsdp_attr* clone_rtpmap_attr (pj_pool_t *pool, const pjsdp_attr *p)
+{
+ const pjsdp_rtpmap_attr *rhs = (const pjsdp_rtpmap_attr*)p;
+ pjsdp_rtpmap_attr *attr = pj_pool_alloc (pool, sizeof(pjsdp_rtpmap_attr));
+ if (!attr)
+ return NULL;
+
+ attr->type = rhs->type;
+ attr->payload_type = rhs->payload_type;
+ if (!pj_strdup (pool, &attr->encoding_name, &rhs->encoding_name)) return NULL;
+ attr->clock_rate = rhs->clock_rate;
+ if (!pj_strdup (pool, &attr->parameter, &rhs->parameter)) return NULL;
+
+ return (pjsdp_attr*)attr;
+}
+
+static pjsdp_attr* clone_generic_string_attr (pj_pool_t *pool, const pjsdp_attr *p)
+{
+ const pjsdp_attr_string* rhs = (const pjsdp_attr_string*) p;
+ pjsdp_attr_string *attr = pj_pool_alloc (pool, sizeof(pjsdp_attr_string));
+ if (!attr)
+ return NULL;
+
+ attr->type = rhs->type;
+ if (!pj_strdup (pool, &attr->value, &rhs->value)) return NULL;
+
+ return (pjsdp_attr*)attr;
+}
+
+static pjsdp_attr* clone_generic_num_attr (pj_pool_t *pool, const pjsdp_attr *p)
+{
+ const pjsdp_attr_num* rhs = (const pjsdp_attr_num*) p;
+ pjsdp_attr_num *attr = pj_pool_alloc (pool, sizeof(pjsdp_attr_num));
+ if (!attr)
+ return NULL;
+
+ attr->type = rhs->type;
+ attr->value = rhs->value;
+
+ return (pjsdp_attr*)attr;
+}
+
+static pjsdp_attr* clone_name_only_attr (pj_pool_t *pool, const pjsdp_attr *rhs)
+{
+ pjsdp_attr *attr = pj_pool_alloc (pool, sizeof(pjsdp_attr));
+ if (!attr)
+ return NULL;
+
+ attr->type = rhs->type;
+ return attr;
+}
+
+static pjsdp_attr* clone_fmtp_attr (pj_pool_t *pool, const pjsdp_attr *p)
+{
+ const pjsdp_fmtp_attr* rhs = (const pjsdp_fmtp_attr*) p;
+ pjsdp_fmtp_attr *attr = pj_pool_alloc (pool, sizeof(pjsdp_fmtp_attr));
+ if (!attr)
+ return NULL;
+
+ attr->type = rhs->type;
+ if (!pj_strdup (pool, &attr->format, &rhs->format)) return NULL;
+ if (!pj_strdup (pool, &attr->param, &rhs->param)) return NULL;
+
+ return (pjsdp_attr*)attr;
+}
+
+PJ_DEF(pjsdp_attr*) pjsdp_attr_clone (pj_pool_t *pool, const pjsdp_attr *rhs)
+{
+ struct attr_map_rec *desc;
+
+ if (rhs->type >= PJSDP_END_OF_ATTR) {
+ pj_assert(0);
+ return NULL;
+ }
+
+ desc = &attr_map[rhs->type];
+ return (*desc->clone) (pool, rhs);
+}
+
+PJ_DEF(const pjsdp_attr*) pjsdp_attr_find (int count, const pjsdp_attr *attr_array[], int type)
+{
+ int i;
+
+ for (i=0; i<count; ++i) {
+ if (attr_array[i]->type == type)
+ return attr_array[i];
+ }
+ return NULL;
+}
+
+static int print_connection_info( pjsdp_conn_info *c, char *buf, int len)
+{
+ char *p = buf;
+
+ if (len < 8+c->net_type.slen+c->addr_type.slen+c->addr.slen) {
+ return -1;
+ }
+ *p++ = 'c';
+ *p++ = '=';
+ pj_memcpy(p, c->net_type.ptr, c->net_type.slen);
+ p += c->net_type.slen;
+ *p++ = ' ';
+ pj_memcpy(p, c->addr_type.ptr, c->addr_type.slen);
+ p += c->addr_type.slen;
+ *p++ = ' ';
+ pj_memcpy(p, c->addr.ptr, c->addr.slen);
+ p += c->addr.slen;
+ *p++ = '\r';
+ *p++ = '\n';
+
+ return p-buf;
+}
+
+PJ_DEF(pjsdp_conn_info*) pjsdp_conn_info_clone (pj_pool_t *pool, const pjsdp_conn_info *rhs)
+{
+ pjsdp_conn_info *c = pj_pool_alloc (pool, sizeof(pjsdp_conn_info));
+ if (!c) return NULL;
+
+ if (!pj_strdup (pool, &c->net_type, &rhs->net_type)) return NULL;
+ if (!pj_strdup (pool, &c->addr_type, &rhs->addr_type)) return NULL;
+ if (!pj_strdup (pool, &c->addr, &rhs->addr)) return NULL;
+
+ return c;
+}
+
+static int print_media_desc( pjsdp_media_desc *m, char *buf, int len)
+{
+ char *p = buf;
+ char *end = buf+len;
+ unsigned i;
+ int printed;
+
+ /* check length for the "m=" line. */
+ if (len < m->desc.media.slen+m->desc.transport.slen+12+24) {
+ return -1;
+ }
+ *p++ = 'm'; /* m= */
+ *p++ = '=';
+ pj_memcpy(p, m->desc.media.ptr, m->desc.media.slen);
+ p += m->desc.media.slen;
+ *p++ = ' ';
+ printed = pj_utoa(m->desc.port, p);
+ p += printed;
+ if (m->desc.port_count > 1) {
+ *p++ = '/';
+ printed = pj_utoa(m->desc.port_count, p);
+ p += printed;
+ }
+ *p++ = ' ';
+ pj_memcpy(p, m->desc.transport.ptr, m->desc.transport.slen);
+ p += m->desc.transport.slen;
+ for (i=0; i<m->desc.fmt_count; ++i) {
+ *p++ = ' ';
+ pj_memcpy(p, m->desc.fmt[i].ptr, m->desc.fmt[i].slen);
+ p += m->desc.fmt[i].slen;
+ }
+ *p++ = '\r';
+ *p++ = '\n';
+
+ /* print connection info, if present. */
+ if (m->conn) {
+ printed = print_connection_info(m->conn, p, end-p);
+ if (printed < 0) {
+ return -1;
+ }
+ p += printed;
+ }
+
+ /* print attributes. */
+ for (i=0; i<m->attr_count; ++i) {
+ printed = print_attr(m->attr[i], p, end-p);
+ if (printed < 0) {
+ return -1;
+ }
+ p += printed;
+ }
+
+ return p-buf;
+}
+
+PJ_DEF(pjsdp_media_desc*) pjsdp_media_desc_clone (pj_pool_t *pool,
+ const pjsdp_media_desc *rhs)
+{
+ unsigned int i;
+ pjsdp_media_desc *m = pj_pool_alloc (pool, sizeof(pjsdp_media_desc));
+ if (!m)
+ return NULL;
+
+ pj_strdup (pool, &m->desc.media, &rhs->desc.media);
+ m->desc.port = rhs->desc.port;
+ m->desc.port_count = rhs->desc.port_count;
+ pj_strdup (pool, &m->desc.transport, &rhs->desc.transport);
+ m->desc.fmt_count = rhs->desc.fmt_count;
+ for (i=0; i<rhs->desc.fmt_count; ++i)
+ m->desc.fmt[i] = rhs->desc.fmt[i];
+
+ if (rhs->conn) {
+ m->conn = pjsdp_conn_info_clone (pool, rhs->conn);
+ if (!m->conn)
+ return NULL;
+ } else {
+ m->conn = NULL;
+ }
+
+ m->attr_count = rhs->attr_count;
+ for (i=0; i < rhs->attr_count; ++i) {
+ m->attr[i] = pjsdp_attr_clone (pool, rhs->attr[i]);
+ if (!m->attr[i])
+ return NULL;
+ }
+
+ return m;
+}
+
+/** Check if the media description has the specified attribute. */
+PJ_DEF(pj_bool_t) pjsdp_media_desc_has_attr (const pjsdp_media_desc *m,
+ pjsdp_attr_type_e attr_type)
+{
+ unsigned i;
+ for (i=0; i<m->attr_count; ++i) {
+ pjsdp_attr *attr = m->attr[i];
+ if (attr->type == attr_type)
+ return 1;
+ }
+ return 0;
+}
+
+/** Find rtpmap attribute for the specified payload type. */
+PJ_DEF(const pjsdp_rtpmap_attr*)
+pjsdp_media_desc_find_rtpmap (const pjsdp_media_desc *m, unsigned pt)
+{
+ unsigned i;
+ for (i=0; i<m->attr_count; ++i) {
+ pjsdp_attr *attr = m->attr[i];
+ if (attr->type == PJSDP_ATTR_RTPMAP) {
+ const pjsdp_rtpmap_attr* rtpmap = (const pjsdp_rtpmap_attr*)attr;
+ if (rtpmap->payload_type == pt)
+ return rtpmap;
+ }
+ }
+ return NULL;
+}
+
+
+static int print_session(const pjsdp_session_desc *ses, char *buf, pj_ssize_t len)
+{
+ char *p = buf;
+ char *end = buf+len;
+ unsigned i;
+ int printed;
+
+ /* Check length for v= and o= lines. */
+ if (len < 5+
+ 2+ses->origin.user.slen+18+
+ ses->origin.net_type.slen+ses->origin.addr.slen + 2)
+ {
+ return -1;
+ }
+
+ /* SDP version (v= line) */
+ pj_memcpy(p, "v=0\r\n", 5);
+ p += 5;
+
+ /* Owner (o=) line. */
+ *p++ = 'o';
+ *p++ = '=';
+ pj_memcpy(p, ses->origin.user.ptr, ses->origin.user.slen);
+ p += ses->origin.user.slen;
+ *p++ = ' ';
+ printed = pj_utoa(ses->origin.id, p);
+ p += printed;
+ *p++ = ' ';
+ printed = pj_utoa(ses->origin.version, p);
+ p += printed;
+ *p++ = ' ';
+ pj_memcpy(p, ses->origin.net_type.ptr, ses->origin.net_type.slen);
+ p += ses->origin.net_type.slen;
+ *p++ = ' ';
+ pj_memcpy(p, ses->origin.addr_type.ptr, ses->origin.addr_type.slen);
+ p += ses->origin.addr_type.slen;
+ *p++ = ' ';
+ pj_memcpy(p, ses->origin.addr.ptr, ses->origin.addr.slen);
+ p += ses->origin.addr.slen;
+ *p++ = '\r';
+ *p++ = '\n';
+
+ /* Session name (s=) line. */
+ if ((end-p) < 8+ses->name.slen) {
+ return -1;
+ }
+ *p++ = 's';
+ *p++ = '=';
+ pj_memcpy(p, ses->name.ptr, ses->name.slen);
+ p += ses->name.slen;
+ *p++ = '\r';
+ *p++ = '\n';
+
+ /* Time */
+ if ((end-p) < 24) {
+ return -1;
+ }
+ *p++ = 't';
+ *p++ = '=';
+ printed = pj_utoa(ses->time.start, p);
+ p += printed;
+ *p++ = ' ';
+ printed = pj_utoa(ses->time.stop, p);
+ p += printed;
+ *p++ = '\r';
+ *p++ = '\n';
+
+ /* Connection line (c=) if exist. */
+ if (ses->conn) {
+ printed = print_connection_info(ses->conn, p, end-p);
+ if (printed < 1) {
+ return -1;
+ }
+ p += printed;
+ }
+
+ /* Print all attribute (a=) lines. */
+ for (i=0; i<ses->attr_count; ++i) {
+ printed = print_attr(ses->attr[i], p, end-p);
+ if (printed < 0) {
+ return -1;
+ }
+ p += printed;
+ }
+
+ /* Print media (m=) lines. */
+ for (i=0; i<ses->media_count; ++i) {
+ printed = print_media_desc(ses->media[i], p, end-p);
+ if (printed < 0) {
+ return -1;
+ }
+ p += printed;
+ }
+
+ return p-buf;
+}
+
+/******************************************************************************
+ * PARSERS
+ */
+
+static void parse_version(pj_scanner *scanner)
+{
+ pj_scan_advance_n(scanner, 3, SKIP_WS);
+ pj_scan_get_newline(scanner);
+}
+
+static void parse_origin(pj_scanner *scanner, pjsdp_session_desc *ses)
+{
+ pj_str_t str;
+
+ /* o= */
+ pj_scan_advance_n(scanner, 2, SKIP_WS);
+
+ /* username. */
+ pj_scan_get_until_ch(scanner, ' ', &ses->origin.user);
+ pj_scan_get_char(scanner);
+
+ /* id */
+ pj_scan_get_until_ch(scanner, ' ', &str);
+ ses->origin.id = pj_strtoul(&str);
+ pj_scan_get_char(scanner);
+
+ /* version */
+ pj_scan_get_until_ch(scanner, ' ', &str);
+ ses->origin.version = pj_strtoul(&str);
+ pj_scan_get_char(scanner);
+
+ /* network-type */
+ pj_scan_get_until_ch(scanner, ' ', &ses->origin.net_type);
+ pj_scan_get_char(scanner);
+
+ /* addr-type */
+ pj_scan_get_until_ch(scanner, ' ', &ses->origin.addr_type);
+ pj_scan_get_char(scanner);
+
+ /* address */
+ pj_scan_get_until_ch(scanner, '\r', &ses->origin.addr);
+
+ /* newline */
+ pj_scan_get_newline(scanner);
+}
+
+static void parse_time(pj_scanner *scanner, pjsdp_session_desc *ses)
+{
+ pj_str_t str;
+
+ /* t= */
+ pj_scan_advance_n(scanner, 2, SKIP_WS);
+
+ /* start time */
+ pj_scan_get_until_ch(scanner, ' ', &str);
+ ses->time.start = pj_strtoul(&str);
+
+ pj_scan_get_char(scanner);
+
+ /* stop time */
+ pj_scan_get_until_ch(scanner, '\r', &str);
+ ses->time.stop = pj_strtoul(&str);
+
+ /* newline */
+ pj_scan_get_newline(scanner);
+}
+
+static void parse_generic_line(pj_scanner *scanner, pj_str_t *str)
+{
+ /* x= */
+ pj_scan_advance_n(scanner, 2, SKIP_WS);
+
+ /* get anything until newline. */
+ pj_scan_get_until_ch(scanner, '\r', str);
+
+ /* newline. */
+ pj_scan_get_newline(scanner);
+}
+
+static void parse_connection_info(pj_scanner *scanner, pjsdp_conn_info *conn)
+{
+ /* c= */
+ pj_scan_advance_n(scanner, 2, SKIP_WS);
+
+ /* network-type */
+ pj_scan_get_until_ch(scanner, ' ', &conn->net_type);
+ pj_scan_get_char(scanner);
+
+ /* addr-type */
+ pj_scan_get_until_ch(scanner, ' ', &conn->addr_type);
+ pj_scan_get_char(scanner);
+
+ /* address. */
+ pj_scan_get_until_ch(scanner, '\r', &conn->addr);
+
+ /* newline */
+ pj_scan_get_newline(scanner);
+}
+
+static void parse_media(pj_scanner *scanner, pjsdp_media_desc *med)
+{
+ pj_str_t str;
+
+ /* m= */
+ pj_scan_advance_n(scanner, 2, SKIP_WS);
+
+ /* type */
+ pj_scan_get_until_ch(scanner, ' ', &med->desc.media);
+ pj_scan_get_char(scanner);
+
+ /* port */
+ pj_scan_get(scanner, cs_token, &str);
+ med->desc.port = (unsigned short)pj_strtoul(&str);
+ if (*scanner->current == '/') {
+ /* port count */
+ pj_scan_get_char(scanner);
+ pj_scan_get(scanner, cs_token, &str);
+ med->desc.port_count = pj_strtoul(&str);
+
+ } else {
+ med->desc.port_count = 0;
+ }
+
+ if (pj_scan_get_char(scanner) != ' ') {
+ PJ_THROW(SYNTAX_ERROR);
+ }
+
+ /* transport */
+ pj_scan_get_until_ch(scanner, ' ', &med->desc.transport);
+
+ /* format list */
+ med->desc.fmt_count = 0;
+ while (*scanner->current == ' ') {
+ pj_scan_get_char(scanner);
+ pj_scan_get(scanner, cs_token, &med->desc.fmt[med->desc.fmt_count++]);
+ }
+
+ /* newline */
+ pj_scan_get_newline(scanner);
+}
+
+static pjsdp_rtpmap_attr * parse_rtpmap_attr( pj_pool_t *pool, pj_scanner *scanner )
+{
+ pjsdp_rtpmap_attr *rtpmap;
+ pj_str_t str;
+
+ rtpmap = pj_pool_calloc(pool, 1, sizeof(*rtpmap));
+ if (pj_scan_get_char(scanner) != ':') {
+ PJ_THROW(SYNTAX_ERROR);
+ }
+ pj_scan_get_until_ch(scanner, ' ', &str);
+ rtpmap->payload_type = pj_strtoul(&str);
+ pj_scan_get_char(scanner);
+
+ pj_scan_get_until_ch(scanner, '/', &rtpmap->encoding_name);
+ pj_scan_get_char(scanner);
+ pj_scan_get(scanner, cs_token, &str);
+ rtpmap->clock_rate = pj_strtoul(&str);
+
+ if (*scanner->current == '/') {
+ pj_scan_get_char(scanner);
+ pj_scan_get_until_ch(scanner, '\r', &rtpmap->parameter);
+ }
+
+ return rtpmap;
+}
+
+static pjsdp_attr_string * parse_generic_string_attr( pj_pool_t *pool, pj_scanner *scanner )
+{
+ pjsdp_attr_string *attr;
+ attr = pj_pool_calloc(pool, 1, sizeof(*attr));
+
+ if (pj_scan_get_char(scanner) != ':') {
+ PJ_THROW(SYNTAX_ERROR);
+ }
+ pj_scan_get_until_ch(scanner, '\r', &attr->value);
+ return attr;
+}
+
+static pjsdp_attr_num * parse_generic_num_attr( pj_pool_t *pool, pj_scanner *scanner )
+{
+ pjsdp_attr_num *attr;
+ pj_str_t str;
+
+ attr = pj_pool_calloc(pool, 1, sizeof(*attr));
+
+ if (pj_scan_get_char(scanner) != ':') {
+ PJ_THROW(SYNTAX_ERROR);
+ }
+ pj_scan_get_until_ch(scanner, '\r', &str);
+ attr->value = pj_strtoul(&str);
+ return attr;
+}
+
+static pjsdp_attr * parse_name_only_attr( pj_pool_t *pool, pj_scanner *scanner )
+{
+ pjsdp_attr *attr;
+
+ PJ_UNUSED_ARG(scanner)
+ attr = pj_pool_calloc(pool, 1, sizeof(*attr));
+ return attr;
+}
+
+static pjsdp_fmtp_attr * parse_fmtp_attr( pj_pool_t *pool, pj_scanner *scanner )
+{
+ pjsdp_fmtp_attr *fmtp;
+
+ fmtp = pj_pool_calloc(pool, 1, sizeof(*fmtp));
+
+ if (pj_scan_get_char(scanner) != ':') {
+ PJ_THROW(SYNTAX_ERROR);
+ }
+ pj_scan_get_until_ch(scanner, ' ', &fmtp->format);
+ pj_scan_get_char(scanner);
+ pj_scan_get_until_ch(scanner, '\r', &fmtp->param);
+ return fmtp;
+}
+
+static pjsdp_attr *parse_attr( pj_pool_t *pool, pj_scanner *scanner)
+{
+ void * (*parse_func)(pj_pool_t *pool, pj_scanner *scanner) = NULL;
+ pj_str_t attrname;
+ unsigned i;
+ pjsdp_attr *attr;
+
+ /* skip a= */
+ pj_scan_advance_n(scanner, 2, SKIP_WS);
+
+ /* get attr name. */
+ pj_scan_get(scanner, cs_token, &attrname);
+
+ /* find entry to handle attrname */
+ for (i=0; i<PJ_ARRAY_SIZE(attr_map); ++i) {
+ struct attr_map_rec *p = &attr_map[i];
+ if (pj_strcmp(&attrname, &p->name) == 0) {
+ parse_func = p->parse_attr;
+ break;
+ }
+ }
+
+ /* fallback to generic string parser. */
+ if (parse_func == NULL) {
+ parse_func = &parse_generic_string_attr;
+ }
+
+ attr = (*parse_func)(pool, scanner);
+ attr->type = i;
+
+ /* newline */
+ pj_scan_get_newline(scanner);
+
+ return attr;
+}
+
+static void on_scanner_error(pj_scanner *scanner)
+{
+ PJ_UNUSED_ARG(scanner)
+
+ PJ_THROW(SYNTAX_ERROR);
+}
+
+/*
+ * Parse SDP message.
+ */
+PJ_DEF(pjsdp_session_desc*) pjsdp_parse( char *buf, pj_size_t len,
+ pj_pool_t *pool)
+{
+ pj_scanner scanner;
+ pjsdp_session_desc *session;
+ pjsdp_media_desc *media = NULL;
+ void *attr;
+ pjsdp_conn_info *conn;
+ pj_str_t dummy;
+ int cur_name = 254;
+ PJ_USE_EXCEPTION;
+
+ init_sdp_parser();
+
+ pj_scan_init(&scanner, buf, len, 0, &on_scanner_error);
+ session = pj_pool_calloc(pool, 1, sizeof(*session));
+
+ PJ_TRY {
+ while (!pj_scan_is_eof(&scanner)) {
+ cur_name = *scanner.current;
+ switch (cur_name) {
+ case 'a':
+ attr = parse_attr(pool, &scanner);
+ if (attr) {
+ if (media) {
+ media->attr[media->attr_count++] = attr;
+ } else {
+ session->attr[session->attr_count++] = attr;
+ }
+ }
+ break;
+ case 'o':
+ parse_origin(&scanner, session);
+ break;
+ case 's':
+ parse_generic_line(&scanner, &session->name);
+ break;
+ case 'c':
+ conn = pj_pool_calloc(pool, 1, sizeof(*conn));
+ parse_connection_info(&scanner, conn);
+ if (media) {
+ media->conn = conn;
+ } else {
+ session->conn = conn;
+ }
+ break;
+ case 't':
+ parse_time(&scanner, session);
+ break;
+ case 'm':
+ media = pj_pool_calloc(pool, 1, sizeof(*media));
+ parse_media(&scanner, media);
+ session->media[ session->media_count++ ] = media;
+ break;
+ case 'v':
+ parse_version(&scanner);
+ break;
+ default:
+ parse_generic_line(&scanner, &dummy);
+ break;
+ }
+ }
+ }
+ PJ_CATCH(SYNTAX_ERROR) {
+ PJ_LOG(2, (LOG_THIS, "Syntax error in SDP parser '%c' line %d col %d",
+ cur_name, scanner.line, scanner.col));
+ if (!pj_scan_is_eof(&scanner)) {
+ if (*scanner.current != '\r') {
+ pj_scan_get_until_ch(&scanner, '\r', &dummy);
+ }
+ pj_scan_get_newline(&scanner);
+ }
+ }
+ PJ_END;
+
+ pj_scan_fini(&scanner);
+ return session;
+}
+
+/*
+ * Print SDP description.
+ */
+PJ_DEF(int) pjsdp_print( const pjsdp_session_desc *desc, char *buf, pj_size_t size)
+{
+ return print_session(desc, buf, size);
+}
+
+
diff --git a/pjmedia/src/pjmedia/sdp.h b/pjmedia/src/pjmedia/sdp.h
index 6fc4ec40..5d537c99 100644
--- a/pjmedia/src/pjmedia/sdp.h
+++ b/pjmedia/src/pjmedia/sdp.h
@@ -1,318 +1,339 @@
-/* $Id$
- *
- */
-
-#ifndef __PJSDP_SDP_H__
-#define __PJSDP_SDP_H__
-
-/**
- * @defgroup PJSDP SDP Library
- */
-/**
- * @file sdp.h
- * @brief SDP header file.
- */
-/**
- * @defgroup PJ_SDP_H SDP stack.
- * @ingroup PJSDP
- * @{
- *
- * This SDP module consists of SDP parser, SDP structure, and function to
- * print back the structure as SDP message.
- */
-
-#include <pj/types.h>
-
-PJ_BEGIN_DECL
-
-#define PJSDP_MAX_FMT 32
-#define PJSDP_MAX_ATTR 32
-#define PJSDP_MAX_MEDIA 16
-
-/**
- * This enumeration describes the attribute type.
- */
-typedef enum pjsdp_attr_type_e
-{
- PJSDP_ATTR_RTPMAP,
- PJSDP_ATTR_CAT,
- PJSDP_ATTR_KEYWORDS,
- PJSDP_ATTR_TOOL,
- PJSDP_ATTR_PTIME,
- PJSDP_ATTR_RECV_ONLY,
- PJSDP_ATTR_SEND_ONLY,
- PJSDP_ATTR_SEND_RECV,
- PJSDP_ATTR_ORIENT,
- PJSDP_ATTR_TYPE,
- PJSDP_ATTR_CHARSET,
- PJSDP_ATTR_SDP_LANG,
- PJSDP_ATTR_LANG,
- PJSDP_ATTR_FRAME_RATE,
- PJSDP_ATTR_QUALITY,
- PJSDP_ATTR_FMTP,
- PJSDP_ATTR_INACTIVE,
- PJSDP_ATTR_GENERIC,
- PJSDP_END_OF_ATTR,
-} pjsdp_attr_type_e;
-
-
-/**
- * This structure keeps the common attributes that all 'descendants'
- * will have.
- */
-typedef struct pjsdp_attr
-{
- pjsdp_attr_type_e type; /**< Attribute type. */
-} pjsdp_attr;
-
-
-/**
- * This is the structure to represent generic attribute which has a
- * string value.
- */
-typedef struct pjsdp_attr_string
-{
- pjsdp_attr_type_e type;
- pj_str_t value;
-} pjsdp_attr_string;
-
-
-/**
- * This is the structure to represent generic SDP attribute which has
- * a numeric value.
- */
-typedef struct pjsdp_attr_num
-{
- pjsdp_attr_type_e type;
- pj_uint32_t value;
-} pjsdp_attr_num;
-
-
-/**
- * SDP \a rtpmap attribute.
- */
-typedef struct pjsdp_rtpmap_attr
-{
- pjsdp_attr_type_e type;
- unsigned payload_type;
- pj_str_t encoding_name;
- unsigned clock_rate;
- pj_str_t parameter;
-} pjsdp_rtpmap_attr;
-
-
-/**
- * SDP \a fmtp attribute.
- */
-typedef struct pjsdp_fmtp_attr
-{
- pjsdp_attr_type_e type;
- pj_str_t format;
- pj_str_t param;
-} pjsdp_fmtp_attr;
-
-
-/**
- * SDP generic attribute.
- */
-typedef struct pjsdp_generic_attr
-{
- pjsdp_attr_type_e type;
- pj_str_t name;
- pj_str_t value;
-} pjsdp_generic_attr;
-
-
-/** SDP \a cat attribute. */
-typedef struct pjsdp_attr_string pjsdp_cat_attr;
-
-/** SDP \a keywds attribute. */
-typedef struct pjsdp_attr_string pjsdp_keywds_attr;
-
-/** SDP \a tool attribute. */
-typedef struct pjsdp_attr_string pjsdp_tool_attr;
-
-/** SDP \a ptime attribute. */
-typedef struct pjsdp_attr_num pjsdp_ptime_attr;
-
-/** SDP \a recvonly attribute. */
-typedef struct pjsdp_attr pjsdp_recv_only_attr;
-
-/** SDP \a sendonly attribute. */
-typedef struct pjsdp_attr pjsdp_send_only_attr;
-
-/** SDP \a sendrecv attribute. */
-typedef struct pjsdp_attr pjsdp_send_recv_attr;
-
-/** SDP \a orient attribute. */
-typedef struct pjsdp_attr_string pjsdp_orient_attr;
-
-/** SDP \a type attribute. */
-typedef struct pjsdp_attr_string pjsdp_type_attr;
-
-/** SDP \a charset attribute. */
-typedef struct pjsdp_attr_string pjsdp_charset_attr;
-
-/** SDP \a sdplang attribute. */
-typedef struct pjsdp_attr_string pjsdp_sdp_lang_attr;
-
-/** SDP \a lang attribute. */
-typedef struct pjsdp_attr_string pjsdp_lang_attr;
-
-/** SDP \a framerate attribute. */
-typedef struct pjsdp_attr_string pjsdp_frame_rate_attr;
-
-/** SDP \a quality attribute. */
-typedef struct pjsdp_attr_num pjsdp_quality_attr;
-
-/** SDP \a inactive attribute. */
-typedef struct pjsdp_attr pjsdp_inactive_attr;
-
-/** Clone attribute */
-PJ_DECL(pjsdp_attr*) pjsdp_attr_clone (pj_pool_t *pool, const pjsdp_attr *rhs);
-
-/** Find attribute */
-PJ_DECL(const pjsdp_attr*) pjsdp_attr_find (int count, const pjsdp_attr *attr_array[], int type);
-
-/**
- * SDP connection info.
- */
-typedef struct pjsdp_conn_info
-{
- pj_str_t net_type;
- pj_str_t addr_type;
- pj_str_t addr;
-} pjsdp_conn_info;
-
-/**
- *Clone connection info.
- *
- * @param pool Pool to allocate memory for the new connection info.
- * @param rhs The connection into to clone.
- *
- * @return the new connection info.
- */
-PJ_DECL(pjsdp_conn_info*) pjsdp_conn_info_clone (pj_pool_t *pool,
- const pjsdp_conn_info *rhs);
-
-/**
- * SDP media description.
- */
-typedef struct pjsdp_media_desc
-{
- struct
- {
- pj_str_t media;
- pj_uint16_t port;
- unsigned port_count;
- pj_str_t transport;
- unsigned fmt_count;
- pj_str_t fmt[PJSDP_MAX_FMT];
- } desc;
-
- pjsdp_conn_info *conn;
- unsigned attr_count;
- pjsdp_attr *attr[PJSDP_MAX_ATTR];
-
-} pjsdp_media_desc;
-
-/**
- * Clone SDP media description.
- *
- * @param pool Pool to allocate memory for the new media description.
- * @param rhs The media descriptin to clone.
- *
- * @return a new media description.
- */
-PJ_DECL(pjsdp_media_desc*) pjsdp_media_desc_clone (pj_pool_t *pool,
- const pjsdp_media_desc *rhs);
-
-/**
- * Check if the media description has the specified attribute.
- *
- * @param m The SDP media description.
- * @param attr_type The attribute type.
- *
- * @return nonzero if true.
- */
-PJ_DECL(pj_bool_t) pjsdp_media_desc_has_attr (const pjsdp_media_desc *m,
- pjsdp_attr_type_e attr_type);
-
-/**
- * Find rtpmap attribute for the specified payload type.
- *
- * @param m The SDP media description.
- * @param pt RTP payload type.
- *
- * @return the SDP rtpmap attribute for the payload type, or NULL if not found.
- */
-PJ_DECL(const pjsdp_rtpmap_attr*)
-pjsdp_media_desc_find_rtpmap (const pjsdp_media_desc *m, unsigned pt);
-
-
-/**
- * This structure describes SDP session description.
- */
-typedef struct pjsdp_session_desc
-{
- struct
- {
- pj_str_t user;
- pj_uint32_t id;
- pj_uint32_t version;
- pj_str_t net_type;
- pj_str_t addr_type;
- pj_str_t addr;
- } origin;
-
- pj_str_t name;
- pjsdp_conn_info *conn;
-
- struct
- {
- pj_uint32_t start;
- pj_uint32_t stop;
- } time;
-
- unsigned attr_count;
- pjsdp_attr *attr[PJSDP_MAX_ATTR];
-
- unsigned media_count;
- pjsdp_media_desc *media[PJSDP_MAX_MEDIA];
-
-} pjsdp_session_desc;
-
-
-/**
- * Parse SDP message.
- *
- * @param buf The message buffer.
- * @param len The length of the message.
- * @param pool The pool to allocate SDP session description.
- *
- * @return SDP session description.
- */
-PJ_DECL(pjsdp_session_desc*) pjsdp_parse( char *buf, pj_size_t len,
- pj_pool_t *pool);
-
-/**
- * Print SDP description to a buffer.
- *
- * @param buf The buffer.
- * @param size The buffer length.
- * @param desc The SDP session description.
- *
- * @return the length printed, or -1.
- */
-PJ_DECL(int) pjsdp_print( const pjsdp_session_desc *desc,
- char *buf, pj_size_t size);
-
-
-/**
- * @}
- */
-
-PJ_END_DECL
-
-#endif /* __PJSDP_SDP_H__ */
-
+/* $Id$
+ *
+ */
+/*
+ * PJMEDIA - Multimedia over IP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef __PJSDP_SDP_H__
+#define __PJSDP_SDP_H__
+
+/**
+ * @defgroup PJSDP SDP Library
+ */
+/**
+ * @file sdp.h
+ * @brief SDP header file.
+ */
+/**
+ * @defgroup PJ_SDP_H SDP stack.
+ * @ingroup PJSDP
+ * @{
+ *
+ * This SDP module consists of SDP parser, SDP structure, and function to
+ * print back the structure as SDP message.
+ */
+
+#include <pj/types.h>
+
+PJ_BEGIN_DECL
+
+#define PJSDP_MAX_FMT 32
+#define PJSDP_MAX_ATTR 32
+#define PJSDP_MAX_MEDIA 16
+
+/**
+ * This enumeration describes the attribute type.
+ */
+typedef enum pjsdp_attr_type_e
+{
+ PJSDP_ATTR_RTPMAP,
+ PJSDP_ATTR_CAT,
+ PJSDP_ATTR_KEYWORDS,
+ PJSDP_ATTR_TOOL,
+ PJSDP_ATTR_PTIME,
+ PJSDP_ATTR_RECV_ONLY,
+ PJSDP_ATTR_SEND_ONLY,
+ PJSDP_ATTR_SEND_RECV,
+ PJSDP_ATTR_ORIENT,
+ PJSDP_ATTR_TYPE,
+ PJSDP_ATTR_CHARSET,
+ PJSDP_ATTR_SDP_LANG,
+ PJSDP_ATTR_LANG,
+ PJSDP_ATTR_FRAME_RATE,
+ PJSDP_ATTR_QUALITY,
+ PJSDP_ATTR_FMTP,
+ PJSDP_ATTR_INACTIVE,
+ PJSDP_ATTR_GENERIC,
+ PJSDP_END_OF_ATTR,
+} pjsdp_attr_type_e;
+
+
+/**
+ * This structure keeps the common attributes that all 'descendants'
+ * will have.
+ */
+typedef struct pjsdp_attr
+{
+ pjsdp_attr_type_e type; /**< Attribute type. */
+} pjsdp_attr;
+
+
+/**
+ * This is the structure to represent generic attribute which has a
+ * string value.
+ */
+typedef struct pjsdp_attr_string
+{
+ pjsdp_attr_type_e type;
+ pj_str_t value;
+} pjsdp_attr_string;
+
+
+/**
+ * This is the structure to represent generic SDP attribute which has
+ * a numeric value.
+ */
+typedef struct pjsdp_attr_num
+{
+ pjsdp_attr_type_e type;
+ pj_uint32_t value;
+} pjsdp_attr_num;
+
+
+/**
+ * SDP \a rtpmap attribute.
+ */
+typedef struct pjsdp_rtpmap_attr
+{
+ pjsdp_attr_type_e type;
+ unsigned payload_type;
+ pj_str_t encoding_name;
+ unsigned clock_rate;
+ pj_str_t parameter;
+} pjsdp_rtpmap_attr;
+
+
+/**
+ * SDP \a fmtp attribute.
+ */
+typedef struct pjsdp_fmtp_attr
+{
+ pjsdp_attr_type_e type;
+ pj_str_t format;
+ pj_str_t param;
+} pjsdp_fmtp_attr;
+
+
+/**
+ * SDP generic attribute.
+ */
+typedef struct pjsdp_generic_attr
+{
+ pjsdp_attr_type_e type;
+ pj_str_t name;
+ pj_str_t value;
+} pjsdp_generic_attr;
+
+
+/** SDP \a cat attribute. */
+typedef struct pjsdp_attr_string pjsdp_cat_attr;
+
+/** SDP \a keywds attribute. */
+typedef struct pjsdp_attr_string pjsdp_keywds_attr;
+
+/** SDP \a tool attribute. */
+typedef struct pjsdp_attr_string pjsdp_tool_attr;
+
+/** SDP \a ptime attribute. */
+typedef struct pjsdp_attr_num pjsdp_ptime_attr;
+
+/** SDP \a recvonly attribute. */
+typedef struct pjsdp_attr pjsdp_recv_only_attr;
+
+/** SDP \a sendonly attribute. */
+typedef struct pjsdp_attr pjsdp_send_only_attr;
+
+/** SDP \a sendrecv attribute. */
+typedef struct pjsdp_attr pjsdp_send_recv_attr;
+
+/** SDP \a orient attribute. */
+typedef struct pjsdp_attr_string pjsdp_orient_attr;
+
+/** SDP \a type attribute. */
+typedef struct pjsdp_attr_string pjsdp_type_attr;
+
+/** SDP \a charset attribute. */
+typedef struct pjsdp_attr_string pjsdp_charset_attr;
+
+/** SDP \a sdplang attribute. */
+typedef struct pjsdp_attr_string pjsdp_sdp_lang_attr;
+
+/** SDP \a lang attribute. */
+typedef struct pjsdp_attr_string pjsdp_lang_attr;
+
+/** SDP \a framerate attribute. */
+typedef struct pjsdp_attr_string pjsdp_frame_rate_attr;
+
+/** SDP \a quality attribute. */
+typedef struct pjsdp_attr_num pjsdp_quality_attr;
+
+/** SDP \a inactive attribute. */
+typedef struct pjsdp_attr pjsdp_inactive_attr;
+
+/** Clone attribute */
+PJ_DECL(pjsdp_attr*) pjsdp_attr_clone (pj_pool_t *pool, const pjsdp_attr *rhs);
+
+/** Find attribute */
+PJ_DECL(const pjsdp_attr*) pjsdp_attr_find (int count, const pjsdp_attr *attr_array[], int type);
+
+/**
+ * SDP connection info.
+ */
+typedef struct pjsdp_conn_info
+{
+ pj_str_t net_type;
+ pj_str_t addr_type;
+ pj_str_t addr;
+} pjsdp_conn_info;
+
+/**
+ *Clone connection info.
+ *
+ * @param pool Pool to allocate memory for the new connection info.
+ * @param rhs The connection into to clone.
+ *
+ * @return the new connection info.
+ */
+PJ_DECL(pjsdp_conn_info*) pjsdp_conn_info_clone (pj_pool_t *pool,
+ const pjsdp_conn_info *rhs);
+
+/**
+ * SDP media description.
+ */
+typedef struct pjsdp_media_desc
+{
+ struct
+ {
+ pj_str_t media;
+ pj_uint16_t port;
+ unsigned port_count;
+ pj_str_t transport;
+ unsigned fmt_count;
+ pj_str_t fmt[PJSDP_MAX_FMT];
+ } desc;
+
+ pjsdp_conn_info *conn;
+ unsigned attr_count;
+ pjsdp_attr *attr[PJSDP_MAX_ATTR];
+
+} pjsdp_media_desc;
+
+/**
+ * Clone SDP media description.
+ *
+ * @param pool Pool to allocate memory for the new media description.
+ * @param rhs The media descriptin to clone.
+ *
+ * @return a new media description.
+ */
+PJ_DECL(pjsdp_media_desc*) pjsdp_media_desc_clone (pj_pool_t *pool,
+ const pjsdp_media_desc *rhs);
+
+/**
+ * Check if the media description has the specified attribute.
+ *
+ * @param m The SDP media description.
+ * @param attr_type The attribute type.
+ *
+ * @return nonzero if true.
+ */
+PJ_DECL(pj_bool_t) pjsdp_media_desc_has_attr (const pjsdp_media_desc *m,
+ pjsdp_attr_type_e attr_type);
+
+/**
+ * Find rtpmap attribute for the specified payload type.
+ *
+ * @param m The SDP media description.
+ * @param pt RTP payload type.
+ *
+ * @return the SDP rtpmap attribute for the payload type, or NULL if not found.
+ */
+PJ_DECL(const pjsdp_rtpmap_attr*)
+pjsdp_media_desc_find_rtpmap (const pjsdp_media_desc *m, unsigned pt);
+
+
+/**
+ * This structure describes SDP session description.
+ */
+typedef struct pjsdp_session_desc
+{
+ struct
+ {
+ pj_str_t user;
+ pj_uint32_t id;
+ pj_uint32_t version;
+ pj_str_t net_type;
+ pj_str_t addr_type;
+ pj_str_t addr;
+ } origin;
+
+ pj_str_t name;
+ pjsdp_conn_info *conn;
+
+ struct
+ {
+ pj_uint32_t start;
+ pj_uint32_t stop;
+ } time;
+
+ unsigned attr_count;
+ pjsdp_attr *attr[PJSDP_MAX_ATTR];
+
+ unsigned media_count;
+ pjsdp_media_desc *media[PJSDP_MAX_MEDIA];
+
+} pjsdp_session_desc;
+
+
+/**
+ * Parse SDP message.
+ *
+ * @param buf The message buffer.
+ * @param len The length of the message.
+ * @param pool The pool to allocate SDP session description.
+ *
+ * @return SDP session description.
+ */
+PJ_DECL(pjsdp_session_desc*) pjsdp_parse( char *buf, pj_size_t len,
+ pj_pool_t *pool);
+
+/**
+ * Print SDP description to a buffer.
+ *
+ * @param buf The buffer.
+ * @param size The buffer length.
+ * @param desc The SDP session description.
+ *
+ * @return the length printed, or -1.
+ */
+PJ_DECL(int) pjsdp_print( const pjsdp_session_desc *desc,
+ char *buf, pj_size_t size);
+
+
+/**
+ * @}
+ */
+
+PJ_END_DECL
+
+#endif /* __PJSDP_SDP_H__ */
+
diff --git a/pjmedia/src/pjmedia/session.c b/pjmedia/src/pjmedia/session.c
index d8e5b67c..56a5e9fe 100644
--- a/pjmedia/src/pjmedia/session.c
+++ b/pjmedia/src/pjmedia/session.c
@@ -1,818 +1,839 @@
-/* $Id$
- *
- */
-#include <pjmedia/session.h>
-#include <pj/log.h>
-#include <pj/os.h>
-#include <pj/pool.h>
-#include <pj/string.h>
-
-typedef struct pj_media_stream_desc
-{
- pj_media_stream_info info;
- pj_media_stream_t *enc_stream, *dec_stream;
-} pj_media_stream_desc;
-
-struct pj_media_session_t
-{
- pj_pool_t *pool;
- pj_med_mgr_t *mediamgr;
- unsigned stream_cnt;
- pj_media_stream_desc *stream_desc[PJSDP_MAX_MEDIA];
-};
-
-#define THIS_FILE "session.c"
-
-#define PJ_MEDIA_SESSION_SIZE (48*1024)
-#define PJ_MEDIA_SESSION_INC 1024
-
-static const pj_str_t ID_AUDIO = { "audio", 5};
-static const pj_str_t ID_VIDEO = { "video", 5};
-static const pj_str_t ID_IN = { "IN", 2 };
-static const pj_str_t ID_IP4 = { "IP4", 3};
-static const pj_str_t ID_RTP_AVP = { "RTP/AVP", 7 };
-static const pj_str_t ID_SDP_NAME = { "pjmedia", 7 };
-
-static void session_init (pj_media_session_t *ses)
-{
- pj_memset (ses, 0, sizeof(pj_media_session_t));
-}
-
-
-/**
- * Create new session offering.
- */
-PJ_DEF(pj_media_session_t*)
-pj_media_session_create (pj_med_mgr_t *mgr, const pj_media_sock_info *sock_info)
-{
- pj_pool_factory *pf;
- pj_pool_t *pool;
- pj_media_session_t *session;
- pj_media_stream_desc *sd;
- unsigned i, codec_cnt;
- pj_codec_mgr *cm;
- const pj_codec_id *codecs[PJSDP_MAX_FMT];
-
- pf = pj_med_mgr_get_pool_factory(mgr);
-
- pool = pj_pool_create( pf, "session", PJ_MEDIA_SESSION_SIZE, PJ_MEDIA_SESSION_INC, NULL);
- if (!pool)
- return NULL;
-
- session = pj_pool_alloc(pool, sizeof(pj_media_session_t));
- if (!session)
- return NULL;
-
- session_init (session);
-
- session->pool = pool;
- session->mediamgr = mgr;
-
- /* Create first stream */
- sd = pj_pool_calloc (pool, 1, sizeof(pj_media_stream_desc));
- if (!sd)
- return NULL;
-
- sd->info.type = ID_AUDIO;
- sd->info.dir = PJ_MEDIA_DIR_ENCODING_DECODING;
- sd->info.transport = ID_RTP_AVP;
- pj_memcpy(&sd->info.sock_info, sock_info, sizeof(*sock_info));
-
- /* Enum audio codecs. */
- sd->info.fmt_cnt = 0;
- cm = pj_med_mgr_get_codec_mgr (mgr);
- codec_cnt = pj_codec_mgr_enum_codecs(cm, PJSDP_MAX_FMT, codecs);
- if (codec_cnt > PJSDP_MAX_FMT) codec_cnt = PJSDP_MAX_FMT;
- for (i=0; i<codec_cnt; ++i) {
- if (codecs[i]->type != PJ_MEDIA_TYPE_AUDIO)
- continue;
-
- sd->info.fmt[sd->info.fmt_cnt].pt = codecs[i]->pt;
- sd->info.fmt[sd->info.fmt_cnt].sample_rate = codecs[i]->sample_rate;
- pj_strdup (pool, &sd->info.fmt[sd->info.fmt_cnt].encoding_name, &codecs[i]->encoding_name);
- ++sd->info.fmt_cnt;
- }
-
- session->stream_desc[session->stream_cnt++] = sd;
-
- return session;
-}
-
-static int sdp_check (const pjsdp_session_desc *sdp)
-{
- int has_conn = 0;
- unsigned i;
-
- if (sdp->conn)
- has_conn = 1;
-
- if (sdp->media_count == 0) {
- PJ_LOG(4,(THIS_FILE, "SDP check failed: no media stream definition"));
- return -1;
- }
-
- for (i=0; i<sdp->media_count; ++i) {
- pjsdp_media_desc *m = sdp->media[i];
-
- if (!m) {
- pj_assert(0);
- return -1;
- }
-
- if (m->desc.fmt_count == 0) {
- PJ_LOG(4,(THIS_FILE, "SDP check failed: no format listed in media stream"));
- return -1;
- }
-
- if (!has_conn && m->conn == NULL) {
- PJ_LOG(4,(THIS_FILE, "SDP check failed: no connection information for media"));
- return -1;
- }
- }
-
- return 0;
-}
-
-/*
- * Create local stream definition that matches SDP received from peer.
- */
-static pj_media_stream_desc*
-create_stream_from_sdp (pj_pool_t *pool, pj_med_mgr_t *mgr, const pjsdp_conn_info *conn,
- const pjsdp_media_desc *m, const pj_media_sock_info *sock_info)
-{
- pj_media_stream_desc *sd;
-
- sd = pj_pool_calloc (pool, 1, sizeof(pj_media_stream_desc));
- if (!sd) {
- PJ_LOG(2,(THIS_FILE, "No memory to allocate stream descriptor"));
- return NULL;
- }
-
- if (pj_stricmp(&conn->net_type, &ID_IN)==0 &&
- pj_stricmp(&conn->addr_type, &ID_IP4)==0 &&
- pj_stricmp(&m->desc.media, &ID_AUDIO)==0 &&
- pj_stricmp(&m->desc.transport, &ID_RTP_AVP) == 0)
- {
- /*
- * Got audio stream.
- */
- unsigned i, codec_cnt;
- pj_codec_mgr *cm;
- const pj_codec_id *codecs[PJSDP_MAX_FMT];
-
- sd->info.type = ID_AUDIO;
- sd->info.transport = ID_RTP_AVP;
- pj_memcpy(&sd->info.sock_info, sock_info, sizeof(*sock_info));
- sd->info.rem_port = m->desc.port;
- pj_strdup (pool, &sd->info.rem_addr, &conn->addr);
-
- /* Enum audio codecs. */
- sd->info.fmt_cnt = 0;
- cm = pj_med_mgr_get_codec_mgr (mgr);
- codec_cnt = pj_codec_mgr_enum_codecs (cm, PJSDP_MAX_FMT, codecs);
- if (codec_cnt > PJSDP_MAX_FMT) codec_cnt = PJSDP_MAX_FMT;
-
- /* Find just one codec which we can support. */
- for (i=0; i<m->desc.fmt_count && sd->info.fmt_cnt == 0; ++i) {
- unsigned j, fmt_i;
-
- /* For static payload, just match payload type. */
- /* Else match clock rate and encoding name. */
- fmt_i = pj_strtoul(&m->desc.fmt[i]);
- if (fmt_i < PJ_RTP_PT_DYNAMIC) {
- for (j=0; j<codec_cnt; ++j) {
- if (codecs[j]->pt == fmt_i) {
- sd->info.fmt_cnt = 1;
- sd->info.fmt[0].type = PJ_MEDIA_TYPE_AUDIO;
- sd->info.fmt[0].pt = codecs[j]->pt;
- sd->info.fmt[0].sample_rate = codecs[j]->sample_rate;
- pj_strdup (pool, &sd->info.fmt[0].encoding_name, &codecs[j]->encoding_name);
- break;
- }
- }
- } else {
-
- /* Find the rtpmap for the payload type. */
- const pjsdp_rtpmap_attr *rtpmap = pjsdp_media_desc_find_rtpmap (m, fmt_i);
-
- /* Don't accept the media if no rtpmap for dynamic PT. */
- if (rtpmap == NULL) {
- PJ_LOG(4,(THIS_FILE, "SDP: No rtpmap found for payload id %d", m->desc.fmt[i]));
- continue;
- }
-
- /* Check whether we can take this codec. */
- for (j=0; j<codec_cnt; ++j) {
- if (rtpmap->clock_rate == codecs[j]->sample_rate &&
- pj_stricmp(&rtpmap->encoding_name, &codecs[j]->encoding_name) == 0)
- {
- sd->info.fmt_cnt = 1;
- sd->info.fmt[0].type = PJ_MEDIA_TYPE_AUDIO;
- sd->info.fmt[0].pt = codecs[j]->pt;
- sd->info.fmt[0].sample_rate = codecs[j]->sample_rate;
- sd->info.fmt[0].encoding_name = codecs[j]->encoding_name;
- break;
- }
- }
- }
- }
-
- /* Match codec and direction. */
- if (sd->info.fmt_cnt == 0 || m->desc.port == 0 ||
- pjsdp_media_desc_has_attr(m, PJSDP_ATTR_INACTIVE))
- {
- sd->info.dir = PJ_MEDIA_DIR_NONE;
- }
- else if (pjsdp_media_desc_has_attr(m, PJSDP_ATTR_RECV_ONLY)) {
- sd->info.dir = PJ_MEDIA_DIR_ENCODING;
- }
- else if (pjsdp_media_desc_has_attr(m, PJSDP_ATTR_SEND_ONLY)) {
- sd->info.dir = PJ_MEDIA_DIR_DECODING;
- }
- else {
- sd->info.dir = PJ_MEDIA_DIR_ENCODING_DECODING;
- }
-
- } else {
- /* Unsupported media stream. */
- unsigned fmt_num;
- const pjsdp_rtpmap_attr *rtpmap = NULL;
-
- pj_strdup(pool, &sd->info.type, &m->desc.media);
- pj_strdup(pool, &sd->info.transport, &m->desc.transport);
- pj_memset(&sd->info.sock_info, 0, sizeof(*sock_info));
- pj_strdup (pool, &sd->info.rem_addr, &conn->addr);
- sd->info.rem_port = m->desc.port;
-
- /* Just put one format and rtpmap, so that we don't have to make
- * special exception when we convert this stream to SDP.
- */
-
- /* Find the rtpmap for the payload type. */
- fmt_num = pj_strtoul(&m->desc.fmt[0]);
- rtpmap = pjsdp_media_desc_find_rtpmap (m, fmt_num);
-
- sd->info.fmt_cnt = 1;
- if (pj_stricmp(&m->desc.media, &ID_VIDEO)==0) {
- sd->info.fmt[0].type = PJ_MEDIA_TYPE_VIDEO;
- sd->info.fmt[0].pt = fmt_num;
- if (rtpmap) {
- pj_strdup (pool, &sd->info.fmt[0].encoding_name,
- &rtpmap->encoding_name);
- sd->info.fmt[0].sample_rate = rtpmap->clock_rate;
- }
- } else {
- sd->info.fmt[0].type = PJ_MEDIA_TYPE_UNKNOWN;
- pj_strdup(pool, &sd->info.fmt[0].encoding_name, &m->desc.fmt[0]);
- }
-
- sd->info.dir = PJ_MEDIA_DIR_NONE;
- }
-
- return sd;
-}
-
-/**
- * Create new session based on peer's offering.
- */
-PJ_DEF(pj_media_session_t*)
-pj_media_session_create_from_sdp (pj_med_mgr_t *mgr, const pjsdp_session_desc *sdp,
- const pj_media_sock_info *sock_info)
-{
- pj_pool_factory *pf;
- pj_pool_t *pool;
- pj_media_session_t *session;
- unsigned i;
-
- if (sdp_check(sdp) != 0)
- return NULL;
-
- pf = pj_med_mgr_get_pool_factory(mgr);
- pool = pj_pool_create( pf, "session", PJ_MEDIA_SESSION_SIZE, PJ_MEDIA_SESSION_INC, NULL);
- if (!pool)
- return NULL;
-
- session = pj_pool_alloc(pool, sizeof(pj_media_session_t));
- if (!session) {
- PJ_LOG(3,(THIS_FILE, "No memory to create media session descriptor"));
- pj_pool_release (pool);
- return NULL;
- }
-
- session_init (session);
-
- session->pool = pool;
- session->mediamgr = mgr;
-
- /* Enumerate each media stream and create our peer. */
- for (i=0; i<sdp->media_count; ++i) {
- const pjsdp_conn_info *conn;
- const pjsdp_media_desc *m;
- pj_media_stream_desc *sd;
-
- m = sdp->media[i];
- conn = m->conn ? m->conn : sdp->conn;
-
- /*
- * Bug:
- * the sock_info below is used by more than one 'm' lines
- */
- PJ_TODO(SUPPORT_MORE_THAN_ONE_SDP_M_LINES)
-
- sd = create_stream_from_sdp (pool, mgr, conn, m, sock_info);
- pj_assert (sd);
-
- session->stream_desc[session->stream_cnt++] = sd;
- }
-
- return session;
-}
-
-/**
- * Duplicate session. The new session is inactive.
- */
-PJ_DEF(pj_media_session_t*)
-pj_media_session_clone (const pj_media_session_t *rhs)
-{
- pj_pool_factory *pf;
- pj_pool_t *pool;
- pj_media_session_t *session;
- unsigned i;
-
- pf = pj_med_mgr_get_pool_factory(rhs->mediamgr);
- pool = pj_pool_create( pf, "session", PJ_MEDIA_SESSION_SIZE, PJ_MEDIA_SESSION_INC, NULL);
- if (!pool) {
- return NULL;
- }
-
- session = pj_pool_alloc (pool, sizeof(*session));
- if (!session) {
- PJ_LOG(3,(THIS_FILE, "No memory to create media session descriptor"));
- pj_pool_release (pool);
- return NULL;
- }
-
- session->pool = pool;
- session->mediamgr = rhs->mediamgr;
- session->stream_cnt = rhs->stream_cnt;
-
- for (i=0; i<rhs->stream_cnt; ++i) {
- pj_media_stream_desc *sd1 = pj_pool_alloc (session->pool, sizeof(pj_media_stream_desc));
- const pj_media_stream_desc *sd2 = rhs->stream_desc[i];
-
- if (!sd1) {
- PJ_LOG(3,(THIS_FILE, "No memory to create media stream descriptor"));
- pj_pool_release (pool);
- return NULL;
- }
-
- session->stream_desc[i] = sd1;
- sd1->enc_stream = sd1->dec_stream = NULL;
- pj_strdup (pool, &sd1->info.type, &sd2->info.type);
- sd1->info.dir = sd2->info.dir;
- pj_strdup (pool, &sd1->info.transport, &sd2->info.transport);
- pj_memcpy(&sd1->info.sock_info, &sd2->info.sock_info, sizeof(pj_media_sock_info));
- pj_strdup (pool, &sd1->info.rem_addr, &sd2->info.rem_addr);
- sd1->info.rem_port = sd2->info.rem_port;
- sd1->info.fmt_cnt = sd2->info.fmt_cnt;
- pj_memcpy (sd1->info.fmt, sd2->info.fmt, sizeof(sd2->info.fmt));
- }
-
- return session;
-}
-
-/**
- * Create SDP description from the session.
- */
-PJ_DEF(pjsdp_session_desc*)
-pj_media_session_create_sdp (const pj_media_session_t *session, pj_pool_t *pool,
- pj_bool_t only_first_fmt)
-{
- pjsdp_session_desc *sdp;
- pj_time_val tv;
- unsigned i;
- pj_media_sock_info *c_addr = NULL;
-
- if (session->stream_cnt == 0) {
- pj_assert(0);
- return NULL;
- }
-
- sdp = pj_pool_calloc (pool, 1, sizeof(pjsdp_session_desc));
- if (!sdp) {
- PJ_LOG(3,(THIS_FILE, "No memory to allocate SDP session descriptor"));
- return NULL;
- }
-
- pj_gettimeofday(&tv);
-
- sdp->origin.user = pj_str("-");
- sdp->origin.version = sdp->origin.id = tv.sec + 2208988800UL;
- sdp->origin.net_type = ID_IN;
- sdp->origin.addr_type = ID_IP4;
- sdp->origin.addr = *pj_gethostname();
-
- sdp->name = ID_SDP_NAME;
-
- /* If all media addresses are the same, then put the connection
- * info in the session level, otherwise put it in media stream
- * level.
- */
- for (i=0; i<session->stream_cnt; ++i) {
- if (c_addr == NULL) {
- c_addr = &session->stream_desc[i]->info.sock_info;
- } else if (c_addr->rtp_addr_name.sin_addr.s_addr != session->stream_desc[i]->info.sock_info.rtp_addr_name.sin_addr.s_addr)
- {
- c_addr = NULL;
- break;
- }
- }
-
- if (c_addr) {
- /* All addresses are the same, put connection info in session level. */
- sdp->conn = pj_pool_alloc (pool, sizeof(pjsdp_conn_info));
- if (!sdp->conn) {
- PJ_LOG(2,(THIS_FILE, "No memory to allocate SDP connection info"));
- return NULL;
- }
-
- sdp->conn->net_type = ID_IN;
- sdp->conn->addr_type = ID_IP4;
- pj_strdup2 (pool, &sdp->conn->addr, pj_inet_ntoa(c_addr->rtp_addr_name.sin_addr));
- }
-
- sdp->time.start = sdp->time.stop = 0;
- sdp->attr_count = 0;
-
- /* Create each media. */
- sdp->media_count = 0;
- for (i=0; i<session->stream_cnt; ++i) {
- const pj_media_stream_desc *sd = session->stream_desc[i];
- pjsdp_media_desc *m;
- unsigned j;
- unsigned fmt_cnt;
- pjsdp_attr *attr;
-
- m = pj_pool_calloc (pool, 1, sizeof(pjsdp_media_desc));
- if (!m) {
- PJ_LOG(3,(THIS_FILE, "No memory to allocate SDP media stream descriptor"));
- return NULL;
- }
-
- sdp->media[sdp->media_count++] = m;
-
- pj_strdup (pool, &m->desc.media, &sd->info.type);
- m->desc.port = pj_ntohs(sd->info.sock_info.rtp_addr_name.sin_port);
- m->desc.port_count = 1;
- pj_strdup (pool, &m->desc.transport, &sd->info.transport);
-
- /* Add format and rtpmap for each codec. */
- m->desc.fmt_count = 0;
- m->attr_count = 0;
- fmt_cnt = sd->info.fmt_cnt;
- if (fmt_cnt > 0 && only_first_fmt)
- fmt_cnt = 1;
- for (j=0; j<fmt_cnt; ++j) {
- pjsdp_rtpmap_attr *rtpmap;
- pj_str_t *fmt = &m->desc.fmt[m->desc.fmt_count++];
-
- if (sd->info.fmt[j].type==PJ_MEDIA_TYPE_UNKNOWN) {
- pj_strdup(pool, fmt, &sd->info.fmt[j].encoding_name);
- } else {
- fmt->ptr = pj_pool_alloc(pool, 8);
- fmt->slen = pj_utoa(sd->info.fmt[j].pt, fmt->ptr);
-
- rtpmap = pj_pool_calloc(pool, 1, sizeof(pjsdp_rtpmap_attr));
- if (rtpmap) {
- m->attr[m->attr_count++] = (pjsdp_attr*)rtpmap;
- rtpmap->type = PJSDP_ATTR_RTPMAP;
- rtpmap->payload_type = sd->info.fmt[j].pt;
- rtpmap->clock_rate = sd->info.fmt[j].sample_rate;
- pj_strdup (pool, &rtpmap->encoding_name, &sd->info.fmt[j].encoding_name);
- } else {
- PJ_LOG(3,(THIS_FILE, "No memory to allocate SDP rtpmap descriptor"));
- }
- }
- }
-
- /* If we don't have connection info in session level, create one. */
- if (sdp->conn == NULL) {
- m->conn = pj_pool_alloc (pool, sizeof(pjsdp_conn_info));
- if (m->conn) {
- m->conn->net_type = ID_IN;
- m->conn->addr_type = ID_IP4;
- pj_strdup2 (pool, &m->conn->addr, pj_inet_ntoa(sd->info.sock_info.rtp_addr_name.sin_addr));
- } else {
- PJ_LOG(3,(THIS_FILE, "No memory to allocate SDP media connection info"));
- return NULL;
- }
- }
-
- /* Add additional attribute to the media stream. */
- attr = pj_pool_alloc(pool, sizeof(pjsdp_attr));
- if (!attr) {
- PJ_LOG(3,(THIS_FILE, "No memory to allocate SDP attribute"));
- return NULL;
- }
- m->attr[m->attr_count++] = attr;
-
- switch (sd->info.dir) {
- case PJ_MEDIA_DIR_NONE:
- attr->type = PJSDP_ATTR_INACTIVE;
- break;
- case PJ_MEDIA_DIR_ENCODING:
- attr->type = PJSDP_ATTR_SEND_ONLY;
- break;
- case PJ_MEDIA_DIR_DECODING:
- attr->type = PJSDP_ATTR_RECV_ONLY;
- break;
- case PJ_MEDIA_DIR_ENCODING_DECODING:
- attr->type = PJSDP_ATTR_SEND_RECV;
- break;
- }
- }
-
- return sdp;
-}
-
-/**
- * Update session with SDP answer from peer.
- */
-PJ_DEF(pj_status_t)
-pj_media_session_update (pj_media_session_t *session,
- const pjsdp_session_desc *sdp)
-{
- unsigned i;
- unsigned count;
-
- /* Check SDP */
- if (sdp_check (sdp) != 0) {
- return -1;
- }
-
- /* If the media stream count doesn't match, only update one. */
- if (session->stream_cnt != sdp->media_count) {
- PJ_LOG(3,(THIS_FILE, "pj_media_session_update : "
- "SDP media count mismatch! (rmt=%d, lcl=%d)",
- sdp->media_count, session->stream_cnt));
- count = (session->stream_cnt < sdp->media_count) ?
- session->stream_cnt : sdp->media_count;
- } else {
- count = session->stream_cnt;
- }
-
- for (i=0; i<count; ++i) {
- pj_media_stream_desc *sd = session->stream_desc[i];
- const pjsdp_media_desc *m = sdp->media[i];
- const pjsdp_conn_info *conn;
- unsigned j;
-
- /* Check that the session is not active. */
- pj_assert (sd->enc_stream == NULL && sd->dec_stream == NULL);
-
- conn = m->conn ? m->conn : sdp->conn;
- pj_assert(conn);
-
- /* Update remote address. */
- sd->info.rem_port = m->desc.port;
- pj_strdup (session->pool, &sd->info.rem_addr, &conn->addr);
-
- /* Select one active codec according to what peer wants. */
- for (j=0; j<sd->info.fmt_cnt; ++j) {
- unsigned fmt_0 = pj_strtoul(&m->desc.fmt[0]);
- if (sd->info.fmt[j].pt == fmt_0) {
- pj_codec_id temp;
-
- /* Put active format to the front. */
- if (j == 0)
- break;
-
- pj_memcpy(&temp, &sd->info.fmt[0], sizeof(temp));
- pj_memcpy(&sd->info.fmt[0], &sd->info.fmt[j], sizeof(temp));
- pj_memcpy(&sd->info.fmt[j], &temp, sizeof(temp));
- break;
- }
- }
-
- if (j == sd->info.fmt_cnt) {
- /* Peer has answered SDP with new codec, which doesn't exist
- * in the offer!
- * Mute this media.
- */
- PJ_LOG(3,(THIS_FILE, "Peer has answered SDP with new codec!"));
- sd->info.dir = PJ_MEDIA_DIR_NONE;
- continue;
- }
-
- /* Check direction. */
- if (m->desc.port == 0 || pjsdp_media_desc_has_attr(m, PJSDP_ATTR_INACTIVE)) {
- sd->info.dir = PJ_MEDIA_DIR_NONE;
- }
- else if (pjsdp_media_desc_has_attr(m, PJSDP_ATTR_RECV_ONLY)) {
- sd->info.dir = PJ_MEDIA_DIR_ENCODING;
- }
- else if (pjsdp_media_desc_has_attr(m, PJSDP_ATTR_SEND_ONLY)) {
- sd->info.dir = PJ_MEDIA_DIR_DECODING;
- }
- else {
- sd->info.dir = PJ_MEDIA_DIR_ENCODING_DECODING;
- }
- }
-
- return 0;
-}
-
-/**
- * Enumerate media streams in the session.
- */
-PJ_DEF(unsigned)
-pj_media_session_enum_streams (const pj_media_session_t *session,
- unsigned count, const pj_media_stream_info *info[])
-{
- unsigned i;
-
- if (count > session->stream_cnt)
- count = session->stream_cnt;
-
- for (i=0; i<count; ++i) {
- info[i] = &session->stream_desc[i]->info;
- }
-
- return session->stream_cnt;
-}
-
-/**
- * Get statistics
- */
-PJ_DEF(pj_status_t)
-pj_media_session_get_stat (const pj_media_session_t *session, unsigned index,
- pj_media_stream_stat *tx_stat,
- pj_media_stream_stat *rx_stat)
-{
- pj_media_stream_desc *sd;
- int stat_cnt = 0;
-
- if (index >= session->stream_cnt) {
- pj_assert(0);
- return -1;
- }
-
- sd = session->stream_desc[index];
-
- if (sd->enc_stream && tx_stat) {
- pj_media_stream_get_stat (sd->enc_stream, tx_stat);
- ++stat_cnt;
- } else if (tx_stat) {
- pj_memset (tx_stat, 0, sizeof(*tx_stat));
- }
-
- if (sd->dec_stream && rx_stat) {
- pj_media_stream_get_stat (sd->dec_stream, rx_stat);
- ++stat_cnt;
- } else if (rx_stat) {
- pj_memset (rx_stat, 0, sizeof(*rx_stat));
- }
-
- return stat_cnt ? 0 : -1;
-}
-
-/**
- * Modify stream, only when stream is inactive.
- */
-PJ_DEF(pj_status_t)
-pj_media_session_modify_stream (pj_media_session_t *session, unsigned index,
- unsigned modify_flag, const pj_media_stream_info *info)
-{
- pj_media_stream_desc *sd;
-
- if (index >= session->stream_cnt) {
- pj_assert(0);
- return -1;
- }
-
- sd = session->stream_desc[index];
-
- if (sd->enc_stream || sd->dec_stream) {
- pj_assert(0);
- return -1;
- }
-
- if (modify_flag & PJ_MEDIA_STREAM_MODIFY_DIR) {
- sd->info.dir = info->dir;
- }
-
- return 0;
-}
-
-/**
- * Activate media session.
- */
-PJ_DEF(pj_status_t)
-pj_media_session_activate (pj_media_session_t *session)
-{
- unsigned i;
- pj_status_t status = 0;
-
- for (i=0; i<session->stream_cnt; ++i) {
- pj_status_t rc;
- rc = pj_media_session_activate_stream (session, i);
- if (status == 0)
- status = rc;
- }
- return status;
-}
-
-/**
- * Activate individual stream in media session.
- */
-PJ_DEF(pj_status_t)
-pj_media_session_activate_stream (pj_media_session_t *session, unsigned index)
-{
- pj_media_stream_desc *sd;
- pj_media_stream_create_param scp;
- pj_status_t status;
- pj_time_val tv;
-
- if (index < 0 || index >= session->stream_cnt) {
- pj_assert(0);
- return -1;
- }
-
- sd = session->stream_desc[index];
-
- if (sd->enc_stream || sd->dec_stream) {
- /* Stream already active. */
- pj_assert(0);
- return 0;
- }
-
- pj_gettimeofday(&tv);
-
- /* Initialize parameter to create stream. */
- pj_memset (&scp, 0, sizeof(scp));
- scp.codec_id = &sd->info.fmt[0];
- scp.mediamgr = session->mediamgr;
- scp.dir = sd->info.dir;
- scp.rtp_sock = sd->info.sock_info.rtp_sock;
- scp.rtcp_sock = sd->info.sock_info.rtcp_sock;
- scp.remote_addr = pj_pool_calloc (session->pool, 1, sizeof(pj_sockaddr_in));
- pj_sockaddr_init (scp.remote_addr, &sd->info.rem_addr, sd->info.rem_port);
- scp.ssrc = tv.sec;
- scp.jb_min = 1;
- scp.jb_max = 15;
- scp.jb_maxcnt = 16;
-
- /* Build the stream! */
- status = pj_media_stream_create (session->pool, &sd->enc_stream, &sd->dec_stream, &scp);
-
- if (status==0 && sd->enc_stream) {
- status = pj_media_stream_start (sd->enc_stream);
- if (status != 0)
- goto on_error;
- }
- if (status==0 && sd->dec_stream) {
- status = pj_media_stream_start (sd->dec_stream);
- if (status != 0)
- goto on_error;
- }
- return status;
-
-on_error:
- if (sd->enc_stream) {
- pj_media_stream_destroy (sd->enc_stream);
- sd->enc_stream = NULL;
- }
- if (sd->dec_stream) {
- pj_media_stream_destroy (sd->dec_stream);
- sd->dec_stream = NULL;
- }
- return status;
-}
-
-/**
- * Destroy media session.
- */
-PJ_DEF(pj_status_t)
-pj_media_session_destroy (pj_media_session_t *session)
-{
- unsigned i;
-
- if (!session)
- return -1;
-
- for (i=0; i<session->stream_cnt; ++i) {
- pj_media_stream_desc *sd = session->stream_desc[i];
-
- if (sd->enc_stream) {
- pj_media_stream_destroy (sd->enc_stream);
- sd->enc_stream = NULL;
- }
- if (sd->dec_stream) {
- pj_media_stream_destroy (sd->dec_stream);
- sd->dec_stream = NULL;
- }
- }
- pj_pool_release (session->pool);
- return 0;
-}
-
+/* $Id$
+ *
+ */
+/*
+ * PJMEDIA - Multimedia over IP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include <pjmedia/session.h>
+#include <pj/log.h>
+#include <pj/os.h>
+#include <pj/pool.h>
+#include <pj/string.h>
+
+typedef struct pj_media_stream_desc
+{
+ pj_media_stream_info info;
+ pj_media_stream_t *enc_stream, *dec_stream;
+} pj_media_stream_desc;
+
+struct pj_media_session_t
+{
+ pj_pool_t *pool;
+ pj_med_mgr_t *mediamgr;
+ unsigned stream_cnt;
+ pj_media_stream_desc *stream_desc[PJSDP_MAX_MEDIA];
+};
+
+#define THIS_FILE "session.c"
+
+#define PJ_MEDIA_SESSION_SIZE (48*1024)
+#define PJ_MEDIA_SESSION_INC 1024
+
+static const pj_str_t ID_AUDIO = { "audio", 5};
+static const pj_str_t ID_VIDEO = { "video", 5};
+static const pj_str_t ID_IN = { "IN", 2 };
+static const pj_str_t ID_IP4 = { "IP4", 3};
+static const pj_str_t ID_RTP_AVP = { "RTP/AVP", 7 };
+static const pj_str_t ID_SDP_NAME = { "pjmedia", 7 };
+
+static void session_init (pj_media_session_t *ses)
+{
+ pj_memset (ses, 0, sizeof(pj_media_session_t));
+}
+
+
+/**
+ * Create new session offering.
+ */
+PJ_DEF(pj_media_session_t*)
+pj_media_session_create (pj_med_mgr_t *mgr, const pj_media_sock_info *sock_info)
+{
+ pj_pool_factory *pf;
+ pj_pool_t *pool;
+ pj_media_session_t *session;
+ pj_media_stream_desc *sd;
+ unsigned i, codec_cnt;
+ pj_codec_mgr *cm;
+ const pj_codec_id *codecs[PJSDP_MAX_FMT];
+
+ pf = pj_med_mgr_get_pool_factory(mgr);
+
+ pool = pj_pool_create( pf, "session", PJ_MEDIA_SESSION_SIZE, PJ_MEDIA_SESSION_INC, NULL);
+ if (!pool)
+ return NULL;
+
+ session = pj_pool_alloc(pool, sizeof(pj_media_session_t));
+ if (!session)
+ return NULL;
+
+ session_init (session);
+
+ session->pool = pool;
+ session->mediamgr = mgr;
+
+ /* Create first stream */
+ sd = pj_pool_calloc (pool, 1, sizeof(pj_media_stream_desc));
+ if (!sd)
+ return NULL;
+
+ sd->info.type = ID_AUDIO;
+ sd->info.dir = PJ_MEDIA_DIR_ENCODING_DECODING;
+ sd->info.transport = ID_RTP_AVP;
+ pj_memcpy(&sd->info.sock_info, sock_info, sizeof(*sock_info));
+
+ /* Enum audio codecs. */
+ sd->info.fmt_cnt = 0;
+ cm = pj_med_mgr_get_codec_mgr (mgr);
+ codec_cnt = pj_codec_mgr_enum_codecs(cm, PJSDP_MAX_FMT, codecs);
+ if (codec_cnt > PJSDP_MAX_FMT) codec_cnt = PJSDP_MAX_FMT;
+ for (i=0; i<codec_cnt; ++i) {
+ if (codecs[i]->type != PJ_MEDIA_TYPE_AUDIO)
+ continue;
+
+ sd->info.fmt[sd->info.fmt_cnt].pt = codecs[i]->pt;
+ sd->info.fmt[sd->info.fmt_cnt].sample_rate = codecs[i]->sample_rate;
+ pj_strdup (pool, &sd->info.fmt[sd->info.fmt_cnt].encoding_name, &codecs[i]->encoding_name);
+ ++sd->info.fmt_cnt;
+ }
+
+ session->stream_desc[session->stream_cnt++] = sd;
+
+ return session;
+}
+
+static int sdp_check (const pjsdp_session_desc *sdp)
+{
+ int has_conn = 0;
+ unsigned i;
+
+ if (sdp->conn)
+ has_conn = 1;
+
+ if (sdp->media_count == 0) {
+ PJ_LOG(4,(THIS_FILE, "SDP check failed: no media stream definition"));
+ return -1;
+ }
+
+ for (i=0; i<sdp->media_count; ++i) {
+ pjsdp_media_desc *m = sdp->media[i];
+
+ if (!m) {
+ pj_assert(0);
+ return -1;
+ }
+
+ if (m->desc.fmt_count == 0) {
+ PJ_LOG(4,(THIS_FILE, "SDP check failed: no format listed in media stream"));
+ return -1;
+ }
+
+ if (!has_conn && m->conn == NULL) {
+ PJ_LOG(4,(THIS_FILE, "SDP check failed: no connection information for media"));
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * Create local stream definition that matches SDP received from peer.
+ */
+static pj_media_stream_desc*
+create_stream_from_sdp (pj_pool_t *pool, pj_med_mgr_t *mgr, const pjsdp_conn_info *conn,
+ const pjsdp_media_desc *m, const pj_media_sock_info *sock_info)
+{
+ pj_media_stream_desc *sd;
+
+ sd = pj_pool_calloc (pool, 1, sizeof(pj_media_stream_desc));
+ if (!sd) {
+ PJ_LOG(2,(THIS_FILE, "No memory to allocate stream descriptor"));
+ return NULL;
+ }
+
+ if (pj_stricmp(&conn->net_type, &ID_IN)==0 &&
+ pj_stricmp(&conn->addr_type, &ID_IP4)==0 &&
+ pj_stricmp(&m->desc.media, &ID_AUDIO)==0 &&
+ pj_stricmp(&m->desc.transport, &ID_RTP_AVP) == 0)
+ {
+ /*
+ * Got audio stream.
+ */
+ unsigned i, codec_cnt;
+ pj_codec_mgr *cm;
+ const pj_codec_id *codecs[PJSDP_MAX_FMT];
+
+ sd->info.type = ID_AUDIO;
+ sd->info.transport = ID_RTP_AVP;
+ pj_memcpy(&sd->info.sock_info, sock_info, sizeof(*sock_info));
+ sd->info.rem_port = m->desc.port;
+ pj_strdup (pool, &sd->info.rem_addr, &conn->addr);
+
+ /* Enum audio codecs. */
+ sd->info.fmt_cnt = 0;
+ cm = pj_med_mgr_get_codec_mgr (mgr);
+ codec_cnt = pj_codec_mgr_enum_codecs (cm, PJSDP_MAX_FMT, codecs);
+ if (codec_cnt > PJSDP_MAX_FMT) codec_cnt = PJSDP_MAX_FMT;
+
+ /* Find just one codec which we can support. */
+ for (i=0; i<m->desc.fmt_count && sd->info.fmt_cnt == 0; ++i) {
+ unsigned j, fmt_i;
+
+ /* For static payload, just match payload type. */
+ /* Else match clock rate and encoding name. */
+ fmt_i = pj_strtoul(&m->desc.fmt[i]);
+ if (fmt_i < PJ_RTP_PT_DYNAMIC) {
+ for (j=0; j<codec_cnt; ++j) {
+ if (codecs[j]->pt == fmt_i) {
+ sd->info.fmt_cnt = 1;
+ sd->info.fmt[0].type = PJ_MEDIA_TYPE_AUDIO;
+ sd->info.fmt[0].pt = codecs[j]->pt;
+ sd->info.fmt[0].sample_rate = codecs[j]->sample_rate;
+ pj_strdup (pool, &sd->info.fmt[0].encoding_name, &codecs[j]->encoding_name);
+ break;
+ }
+ }
+ } else {
+
+ /* Find the rtpmap for the payload type. */
+ const pjsdp_rtpmap_attr *rtpmap = pjsdp_media_desc_find_rtpmap (m, fmt_i);
+
+ /* Don't accept the media if no rtpmap for dynamic PT. */
+ if (rtpmap == NULL) {
+ PJ_LOG(4,(THIS_FILE, "SDP: No rtpmap found for payload id %d", m->desc.fmt[i]));
+ continue;
+ }
+
+ /* Check whether we can take this codec. */
+ for (j=0; j<codec_cnt; ++j) {
+ if (rtpmap->clock_rate == codecs[j]->sample_rate &&
+ pj_stricmp(&rtpmap->encoding_name, &codecs[j]->encoding_name) == 0)
+ {
+ sd->info.fmt_cnt = 1;
+ sd->info.fmt[0].type = PJ_MEDIA_TYPE_AUDIO;
+ sd->info.fmt[0].pt = codecs[j]->pt;
+ sd->info.fmt[0].sample_rate = codecs[j]->sample_rate;
+ sd->info.fmt[0].encoding_name = codecs[j]->encoding_name;
+ break;
+ }
+ }
+ }
+ }
+
+ /* Match codec and direction. */
+ if (sd->info.fmt_cnt == 0 || m->desc.port == 0 ||
+ pjsdp_media_desc_has_attr(m, PJSDP_ATTR_INACTIVE))
+ {
+ sd->info.dir = PJ_MEDIA_DIR_NONE;
+ }
+ else if (pjsdp_media_desc_has_attr(m, PJSDP_ATTR_RECV_ONLY)) {
+ sd->info.dir = PJ_MEDIA_DIR_ENCODING;
+ }
+ else if (pjsdp_media_desc_has_attr(m, PJSDP_ATTR_SEND_ONLY)) {
+ sd->info.dir = PJ_MEDIA_DIR_DECODING;
+ }
+ else {
+ sd->info.dir = PJ_MEDIA_DIR_ENCODING_DECODING;
+ }
+
+ } else {
+ /* Unsupported media stream. */
+ unsigned fmt_num;
+ const pjsdp_rtpmap_attr *rtpmap = NULL;
+
+ pj_strdup(pool, &sd->info.type, &m->desc.media);
+ pj_strdup(pool, &sd->info.transport, &m->desc.transport);
+ pj_memset(&sd->info.sock_info, 0, sizeof(*sock_info));
+ pj_strdup (pool, &sd->info.rem_addr, &conn->addr);
+ sd->info.rem_port = m->desc.port;
+
+ /* Just put one format and rtpmap, so that we don't have to make
+ * special exception when we convert this stream to SDP.
+ */
+
+ /* Find the rtpmap for the payload type. */
+ fmt_num = pj_strtoul(&m->desc.fmt[0]);
+ rtpmap = pjsdp_media_desc_find_rtpmap (m, fmt_num);
+
+ sd->info.fmt_cnt = 1;
+ if (pj_stricmp(&m->desc.media, &ID_VIDEO)==0) {
+ sd->info.fmt[0].type = PJ_MEDIA_TYPE_VIDEO;
+ sd->info.fmt[0].pt = fmt_num;
+ if (rtpmap) {
+ pj_strdup (pool, &sd->info.fmt[0].encoding_name,
+ &rtpmap->encoding_name);
+ sd->info.fmt[0].sample_rate = rtpmap->clock_rate;
+ }
+ } else {
+ sd->info.fmt[0].type = PJ_MEDIA_TYPE_UNKNOWN;
+ pj_strdup(pool, &sd->info.fmt[0].encoding_name, &m->desc.fmt[0]);
+ }
+
+ sd->info.dir = PJ_MEDIA_DIR_NONE;
+ }
+
+ return sd;
+}
+
+/**
+ * Create new session based on peer's offering.
+ */
+PJ_DEF(pj_media_session_t*)
+pj_media_session_create_from_sdp (pj_med_mgr_t *mgr, const pjsdp_session_desc *sdp,
+ const pj_media_sock_info *sock_info)
+{
+ pj_pool_factory *pf;
+ pj_pool_t *pool;
+ pj_media_session_t *session;
+ unsigned i;
+
+ if (sdp_check(sdp) != 0)
+ return NULL;
+
+ pf = pj_med_mgr_get_pool_factory(mgr);
+ pool = pj_pool_create( pf, "session", PJ_MEDIA_SESSION_SIZE, PJ_MEDIA_SESSION_INC, NULL);
+ if (!pool)
+ return NULL;
+
+ session = pj_pool_alloc(pool, sizeof(pj_media_session_t));
+ if (!session) {
+ PJ_LOG(3,(THIS_FILE, "No memory to create media session descriptor"));
+ pj_pool_release (pool);
+ return NULL;
+ }
+
+ session_init (session);
+
+ session->pool = pool;
+ session->mediamgr = mgr;
+
+ /* Enumerate each media stream and create our peer. */
+ for (i=0; i<sdp->media_count; ++i) {
+ const pjsdp_conn_info *conn;
+ const pjsdp_media_desc *m;
+ pj_media_stream_desc *sd;
+
+ m = sdp->media[i];
+ conn = m->conn ? m->conn : sdp->conn;
+
+ /*
+ * Bug:
+ * the sock_info below is used by more than one 'm' lines
+ */
+ PJ_TODO(SUPPORT_MORE_THAN_ONE_SDP_M_LINES)
+
+ sd = create_stream_from_sdp (pool, mgr, conn, m, sock_info);
+ pj_assert (sd);
+
+ session->stream_desc[session->stream_cnt++] = sd;
+ }
+
+ return session;
+}
+
+/**
+ * Duplicate session. The new session is inactive.
+ */
+PJ_DEF(pj_media_session_t*)
+pj_media_session_clone (const pj_media_session_t *rhs)
+{
+ pj_pool_factory *pf;
+ pj_pool_t *pool;
+ pj_media_session_t *session;
+ unsigned i;
+
+ pf = pj_med_mgr_get_pool_factory(rhs->mediamgr);
+ pool = pj_pool_create( pf, "session", PJ_MEDIA_SESSION_SIZE, PJ_MEDIA_SESSION_INC, NULL);
+ if (!pool) {
+ return NULL;
+ }
+
+ session = pj_pool_alloc (pool, sizeof(*session));
+ if (!session) {
+ PJ_LOG(3,(THIS_FILE, "No memory to create media session descriptor"));
+ pj_pool_release (pool);
+ return NULL;
+ }
+
+ session->pool = pool;
+ session->mediamgr = rhs->mediamgr;
+ session->stream_cnt = rhs->stream_cnt;
+
+ for (i=0; i<rhs->stream_cnt; ++i) {
+ pj_media_stream_desc *sd1 = pj_pool_alloc (session->pool, sizeof(pj_media_stream_desc));
+ const pj_media_stream_desc *sd2 = rhs->stream_desc[i];
+
+ if (!sd1) {
+ PJ_LOG(3,(THIS_FILE, "No memory to create media stream descriptor"));
+ pj_pool_release (pool);
+ return NULL;
+ }
+
+ session->stream_desc[i] = sd1;
+ sd1->enc_stream = sd1->dec_stream = NULL;
+ pj_strdup (pool, &sd1->info.type, &sd2->info.type);
+ sd1->info.dir = sd2->info.dir;
+ pj_strdup (pool, &sd1->info.transport, &sd2->info.transport);
+ pj_memcpy(&sd1->info.sock_info, &sd2->info.sock_info, sizeof(pj_media_sock_info));
+ pj_strdup (pool, &sd1->info.rem_addr, &sd2->info.rem_addr);
+ sd1->info.rem_port = sd2->info.rem_port;
+ sd1->info.fmt_cnt = sd2->info.fmt_cnt;
+ pj_memcpy (sd1->info.fmt, sd2->info.fmt, sizeof(sd2->info.fmt));
+ }
+
+ return session;
+}
+
+/**
+ * Create SDP description from the session.
+ */
+PJ_DEF(pjsdp_session_desc*)
+pj_media_session_create_sdp (const pj_media_session_t *session, pj_pool_t *pool,
+ pj_bool_t only_first_fmt)
+{
+ pjsdp_session_desc *sdp;
+ pj_time_val tv;
+ unsigned i;
+ pj_media_sock_info *c_addr = NULL;
+
+ if (session->stream_cnt == 0) {
+ pj_assert(0);
+ return NULL;
+ }
+
+ sdp = pj_pool_calloc (pool, 1, sizeof(pjsdp_session_desc));
+ if (!sdp) {
+ PJ_LOG(3,(THIS_FILE, "No memory to allocate SDP session descriptor"));
+ return NULL;
+ }
+
+ pj_gettimeofday(&tv);
+
+ sdp->origin.user = pj_str("-");
+ sdp->origin.version = sdp->origin.id = tv.sec + 2208988800UL;
+ sdp->origin.net_type = ID_IN;
+ sdp->origin.addr_type = ID_IP4;
+ sdp->origin.addr = *pj_gethostname();
+
+ sdp->name = ID_SDP_NAME;
+
+ /* If all media addresses are the same, then put the connection
+ * info in the session level, otherwise put it in media stream
+ * level.
+ */
+ for (i=0; i<session->stream_cnt; ++i) {
+ if (c_addr == NULL) {
+ c_addr = &session->stream_desc[i]->info.sock_info;
+ } else if (c_addr->rtp_addr_name.sin_addr.s_addr != session->stream_desc[i]->info.sock_info.rtp_addr_name.sin_addr.s_addr)
+ {
+ c_addr = NULL;
+ break;
+ }
+ }
+
+ if (c_addr) {
+ /* All addresses are the same, put connection info in session level. */
+ sdp->conn = pj_pool_alloc (pool, sizeof(pjsdp_conn_info));
+ if (!sdp->conn) {
+ PJ_LOG(2,(THIS_FILE, "No memory to allocate SDP connection info"));
+ return NULL;
+ }
+
+ sdp->conn->net_type = ID_IN;
+ sdp->conn->addr_type = ID_IP4;
+ pj_strdup2 (pool, &sdp->conn->addr, pj_inet_ntoa(c_addr->rtp_addr_name.sin_addr));
+ }
+
+ sdp->time.start = sdp->time.stop = 0;
+ sdp->attr_count = 0;
+
+ /* Create each media. */
+ sdp->media_count = 0;
+ for (i=0; i<session->stream_cnt; ++i) {
+ const pj_media_stream_desc *sd = session->stream_desc[i];
+ pjsdp_media_desc *m;
+ unsigned j;
+ unsigned fmt_cnt;
+ pjsdp_attr *attr;
+
+ m = pj_pool_calloc (pool, 1, sizeof(pjsdp_media_desc));
+ if (!m) {
+ PJ_LOG(3,(THIS_FILE, "No memory to allocate SDP media stream descriptor"));
+ return NULL;
+ }
+
+ sdp->media[sdp->media_count++] = m;
+
+ pj_strdup (pool, &m->desc.media, &sd->info.type);
+ m->desc.port = pj_ntohs(sd->info.sock_info.rtp_addr_name.sin_port);
+ m->desc.port_count = 1;
+ pj_strdup (pool, &m->desc.transport, &sd->info.transport);
+
+ /* Add format and rtpmap for each codec. */
+ m->desc.fmt_count = 0;
+ m->attr_count = 0;
+ fmt_cnt = sd->info.fmt_cnt;
+ if (fmt_cnt > 0 && only_first_fmt)
+ fmt_cnt = 1;
+ for (j=0; j<fmt_cnt; ++j) {
+ pjsdp_rtpmap_attr *rtpmap;
+ pj_str_t *fmt = &m->desc.fmt[m->desc.fmt_count++];
+
+ if (sd->info.fmt[j].type==PJ_MEDIA_TYPE_UNKNOWN) {
+ pj_strdup(pool, fmt, &sd->info.fmt[j].encoding_name);
+ } else {
+ fmt->ptr = pj_pool_alloc(pool, 8);
+ fmt->slen = pj_utoa(sd->info.fmt[j].pt, fmt->ptr);
+
+ rtpmap = pj_pool_calloc(pool, 1, sizeof(pjsdp_rtpmap_attr));
+ if (rtpmap) {
+ m->attr[m->attr_count++] = (pjsdp_attr*)rtpmap;
+ rtpmap->type = PJSDP_ATTR_RTPMAP;
+ rtpmap->payload_type = sd->info.fmt[j].pt;
+ rtpmap->clock_rate = sd->info.fmt[j].sample_rate;
+ pj_strdup (pool, &rtpmap->encoding_name, &sd->info.fmt[j].encoding_name);
+ } else {
+ PJ_LOG(3,(THIS_FILE, "No memory to allocate SDP rtpmap descriptor"));
+ }
+ }
+ }
+
+ /* If we don't have connection info in session level, create one. */
+ if (sdp->conn == NULL) {
+ m->conn = pj_pool_alloc (pool, sizeof(pjsdp_conn_info));
+ if (m->conn) {
+ m->conn->net_type = ID_IN;
+ m->conn->addr_type = ID_IP4;
+ pj_strdup2 (pool, &m->conn->addr, pj_inet_ntoa(sd->info.sock_info.rtp_addr_name.sin_addr));
+ } else {
+ PJ_LOG(3,(THIS_FILE, "No memory to allocate SDP media connection info"));
+ return NULL;
+ }
+ }
+
+ /* Add additional attribute to the media stream. */
+ attr = pj_pool_alloc(pool, sizeof(pjsdp_attr));
+ if (!attr) {
+ PJ_LOG(3,(THIS_FILE, "No memory to allocate SDP attribute"));
+ return NULL;
+ }
+ m->attr[m->attr_count++] = attr;
+
+ switch (sd->info.dir) {
+ case PJ_MEDIA_DIR_NONE:
+ attr->type = PJSDP_ATTR_INACTIVE;
+ break;
+ case PJ_MEDIA_DIR_ENCODING:
+ attr->type = PJSDP_ATTR_SEND_ONLY;
+ break;
+ case PJ_MEDIA_DIR_DECODING:
+ attr->type = PJSDP_ATTR_RECV_ONLY;
+ break;
+ case PJ_MEDIA_DIR_ENCODING_DECODING:
+ attr->type = PJSDP_ATTR_SEND_RECV;
+ break;
+ }
+ }
+
+ return sdp;
+}
+
+/**
+ * Update session with SDP answer from peer.
+ */
+PJ_DEF(pj_status_t)
+pj_media_session_update (pj_media_session_t *session,
+ const pjsdp_session_desc *sdp)
+{
+ unsigned i;
+ unsigned count;
+
+ /* Check SDP */
+ if (sdp_check (sdp) != 0) {
+ return -1;
+ }
+
+ /* If the media stream count doesn't match, only update one. */
+ if (session->stream_cnt != sdp->media_count) {
+ PJ_LOG(3,(THIS_FILE, "pj_media_session_update : "
+ "SDP media count mismatch! (rmt=%d, lcl=%d)",
+ sdp->media_count, session->stream_cnt));
+ count = (session->stream_cnt < sdp->media_count) ?
+ session->stream_cnt : sdp->media_count;
+ } else {
+ count = session->stream_cnt;
+ }
+
+ for (i=0; i<count; ++i) {
+ pj_media_stream_desc *sd = session->stream_desc[i];
+ const pjsdp_media_desc *m = sdp->media[i];
+ const pjsdp_conn_info *conn;
+ unsigned j;
+
+ /* Check that the session is not active. */
+ pj_assert (sd->enc_stream == NULL && sd->dec_stream == NULL);
+
+ conn = m->conn ? m->conn : sdp->conn;
+ pj_assert(conn);
+
+ /* Update remote address. */
+ sd->info.rem_port = m->desc.port;
+ pj_strdup (session->pool, &sd->info.rem_addr, &conn->addr);
+
+ /* Select one active codec according to what peer wants. */
+ for (j=0; j<sd->info.fmt_cnt; ++j) {
+ unsigned fmt_0 = pj_strtoul(&m->desc.fmt[0]);
+ if (sd->info.fmt[j].pt == fmt_0) {
+ pj_codec_id temp;
+
+ /* Put active format to the front. */
+ if (j == 0)
+ break;
+
+ pj_memcpy(&temp, &sd->info.fmt[0], sizeof(temp));
+ pj_memcpy(&sd->info.fmt[0], &sd->info.fmt[j], sizeof(temp));
+ pj_memcpy(&sd->info.fmt[j], &temp, sizeof(temp));
+ break;
+ }
+ }
+
+ if (j == sd->info.fmt_cnt) {
+ /* Peer has answered SDP with new codec, which doesn't exist
+ * in the offer!
+ * Mute this media.
+ */
+ PJ_LOG(3,(THIS_FILE, "Peer has answered SDP with new codec!"));
+ sd->info.dir = PJ_MEDIA_DIR_NONE;
+ continue;
+ }
+
+ /* Check direction. */
+ if (m->desc.port == 0 || pjsdp_media_desc_has_attr(m, PJSDP_ATTR_INACTIVE)) {
+ sd->info.dir = PJ_MEDIA_DIR_NONE;
+ }
+ else if (pjsdp_media_desc_has_attr(m, PJSDP_ATTR_RECV_ONLY)) {
+ sd->info.dir = PJ_MEDIA_DIR_ENCODING;
+ }
+ else if (pjsdp_media_desc_has_attr(m, PJSDP_ATTR_SEND_ONLY)) {
+ sd->info.dir = PJ_MEDIA_DIR_DECODING;
+ }
+ else {
+ sd->info.dir = PJ_MEDIA_DIR_ENCODING_DECODING;
+ }
+ }
+
+ return 0;
+}
+
+/**
+ * Enumerate media streams in the session.
+ */
+PJ_DEF(unsigned)
+pj_media_session_enum_streams (const pj_media_session_t *session,
+ unsigned count, const pj_media_stream_info *info[])
+{
+ unsigned i;
+
+ if (count > session->stream_cnt)
+ count = session->stream_cnt;
+
+ for (i=0; i<count; ++i) {
+ info[i] = &session->stream_desc[i]->info;
+ }
+
+ return session->stream_cnt;
+}
+
+/**
+ * Get statistics
+ */
+PJ_DEF(pj_status_t)
+pj_media_session_get_stat (const pj_media_session_t *session, unsigned index,
+ pj_media_stream_stat *tx_stat,
+ pj_media_stream_stat *rx_stat)
+{
+ pj_media_stream_desc *sd;
+ int stat_cnt = 0;
+
+ if (index >= session->stream_cnt) {
+ pj_assert(0);
+ return -1;
+ }
+
+ sd = session->stream_desc[index];
+
+ if (sd->enc_stream && tx_stat) {
+ pj_media_stream_get_stat (sd->enc_stream, tx_stat);
+ ++stat_cnt;
+ } else if (tx_stat) {
+ pj_memset (tx_stat, 0, sizeof(*tx_stat));
+ }
+
+ if (sd->dec_stream && rx_stat) {
+ pj_media_stream_get_stat (sd->dec_stream, rx_stat);
+ ++stat_cnt;
+ } else if (rx_stat) {
+ pj_memset (rx_stat, 0, sizeof(*rx_stat));
+ }
+
+ return stat_cnt ? 0 : -1;
+}
+
+/**
+ * Modify stream, only when stream is inactive.
+ */
+PJ_DEF(pj_status_t)
+pj_media_session_modify_stream (pj_media_session_t *session, unsigned index,
+ unsigned modify_flag, const pj_media_stream_info *info)
+{
+ pj_media_stream_desc *sd;
+
+ if (index >= session->stream_cnt) {
+ pj_assert(0);
+ return -1;
+ }
+
+ sd = session->stream_desc[index];
+
+ if (sd->enc_stream || sd->dec_stream) {
+ pj_assert(0);
+ return -1;
+ }
+
+ if (modify_flag & PJ_MEDIA_STREAM_MODIFY_DIR) {
+ sd->info.dir = info->dir;
+ }
+
+ return 0;
+}
+
+/**
+ * Activate media session.
+ */
+PJ_DEF(pj_status_t)
+pj_media_session_activate (pj_media_session_t *session)
+{
+ unsigned i;
+ pj_status_t status = 0;
+
+ for (i=0; i<session->stream_cnt; ++i) {
+ pj_status_t rc;
+ rc = pj_media_session_activate_stream (session, i);
+ if (status == 0)
+ status = rc;
+ }
+ return status;
+}
+
+/**
+ * Activate individual stream in media session.
+ */
+PJ_DEF(pj_status_t)
+pj_media_session_activate_stream (pj_media_session_t *session, unsigned index)
+{
+ pj_media_stream_desc *sd;
+ pj_media_stream_create_param scp;
+ pj_status_t status;
+ pj_time_val tv;
+
+ if (index < 0 || index >= session->stream_cnt) {
+ pj_assert(0);
+ return -1;
+ }
+
+ sd = session->stream_desc[index];
+
+ if (sd->enc_stream || sd->dec_stream) {
+ /* Stream already active. */
+ pj_assert(0);
+ return 0;
+ }
+
+ pj_gettimeofday(&tv);
+
+ /* Initialize parameter to create stream. */
+ pj_memset (&scp, 0, sizeof(scp));
+ scp.codec_id = &sd->info.fmt[0];
+ scp.mediamgr = session->mediamgr;
+ scp.dir = sd->info.dir;
+ scp.rtp_sock = sd->info.sock_info.rtp_sock;
+ scp.rtcp_sock = sd->info.sock_info.rtcp_sock;
+ scp.remote_addr = pj_pool_calloc (session->pool, 1, sizeof(pj_sockaddr_in));
+ pj_sockaddr_init (scp.remote_addr, &sd->info.rem_addr, sd->info.rem_port);
+ scp.ssrc = tv.sec;
+ scp.jb_min = 1;
+ scp.jb_max = 15;
+ scp.jb_maxcnt = 16;
+
+ /* Build the stream! */
+ status = pj_media_stream_create (session->pool, &sd->enc_stream, &sd->dec_stream, &scp);
+
+ if (status==0 && sd->enc_stream) {
+ status = pj_media_stream_start (sd->enc_stream);
+ if (status != 0)
+ goto on_error;
+ }
+ if (status==0 && sd->dec_stream) {
+ status = pj_media_stream_start (sd->dec_stream);
+ if (status != 0)
+ goto on_error;
+ }
+ return status;
+
+on_error:
+ if (sd->enc_stream) {
+ pj_media_stream_destroy (sd->enc_stream);
+ sd->enc_stream = NULL;
+ }
+ if (sd->dec_stream) {
+ pj_media_stream_destroy (sd->dec_stream);
+ sd->dec_stream = NULL;
+ }
+ return status;
+}
+
+/**
+ * Destroy media session.
+ */
+PJ_DEF(pj_status_t)
+pj_media_session_destroy (pj_media_session_t *session)
+{
+ unsigned i;
+
+ if (!session)
+ return -1;
+
+ for (i=0; i<session->stream_cnt; ++i) {
+ pj_media_stream_desc *sd = session->stream_desc[i];
+
+ if (sd->enc_stream) {
+ pj_media_stream_destroy (sd->enc_stream);
+ sd->enc_stream = NULL;
+ }
+ if (sd->dec_stream) {
+ pj_media_stream_destroy (sd->dec_stream);
+ sd->dec_stream = NULL;
+ }
+ }
+ pj_pool_release (session->pool);
+ return 0;
+}
+
diff --git a/pjmedia/src/pjmedia/session.h b/pjmedia/src/pjmedia/session.h
index 3ae9d40b..411b7f87 100644
--- a/pjmedia/src/pjmedia/session.h
+++ b/pjmedia/src/pjmedia/session.h
@@ -1,138 +1,159 @@
-/* $Id$
- *
- */
-
-#ifndef __PJMEDIA_SESSION_H__
-#define __PJMEDIA_SESSION_H__
-
-
-/**
- * @file session.h
- * @brief Media Session.
- */
-
-#include <pj/types.h>
-#include <pjmedia/mediamgr.h>
-#include <pjmedia/stream.h>
-#include <pjmedia/sdp.h>
-
-PJ_BEGIN_DECL
-
-/**
- * @defgroup PJMED_SES Media session
- * @ingroup PJMEDIA
- * @{
- */
-
-/** Opaque declaration of media session. */
-typedef struct pj_media_session_t pj_media_session_t;
-
-/** Media socket info. */
-typedef struct pj_media_sock_info
-{
- pj_sock_t rtp_sock, rtcp_sock;
- pj_sockaddr_in rtp_addr_name;
-} pj_media_sock_info;
-
-/** Stream info. */
-typedef struct pj_media_stream_info
-{
- pj_str_t type;
- pj_media_dir_t dir;
- pj_str_t transport;
- pj_media_sock_info sock_info;
- pj_str_t rem_addr;
- unsigned short rem_port;
- unsigned fmt_cnt;
- pj_codec_id fmt[PJSDP_MAX_FMT];
-
-} pj_media_stream_info;
-
-/** Flag for modifying stream. */
-enum
-{
- PJ_MEDIA_STREAM_MODIFY_DIR = 1,
-};
-
-/**
- * Create new session offering.
- */
-PJ_DECL(pj_media_session_t*)
-pj_media_session_create ( pj_med_mgr_t *mgr, const pj_media_sock_info *skinfo );
-
-/**
- * Create new session based on peer's offering.
- */
-PJ_DECL(pj_media_session_t*)
-pj_media_session_create_from_sdp ( pj_med_mgr_t *mgr, const pjsdp_session_desc *sdp,
- const pj_media_sock_info *skinfo);
-
-/**
- * Duplicate session. The new session is inactive.
- */
-PJ_DECL(pj_media_session_t*)
-pj_media_session_clone (const pj_media_session_t *session);
-
-/**
- * Create SDP description from the session.
- */
-PJ_DECL(pjsdp_session_desc*)
-pj_media_session_create_sdp ( const pj_media_session_t *session, pj_pool_t *pool,
- pj_bool_t only_first_fmt);
-
-/**
- * Update session with SDP answer from peer. The session must NOT active.
- */
-PJ_DECL(pj_status_t)
-pj_media_session_update ( pj_media_session_t *session,
- const pjsdp_session_desc *sdp);
-
-/**
- * Enumerate media streams in the session.
- * @return the actual number of streams.
- */
-PJ_DECL(unsigned)
-pj_media_session_enum_streams (const pj_media_session_t *session,
- unsigned count, const pj_media_stream_info *info[]);
-
-/**
- * Get stream statistics.
- */
-PJ_DECL(pj_status_t)
-pj_media_session_get_stat (const pj_media_session_t *session, unsigned index,
- pj_media_stream_stat *tx_stat,
- pj_media_stream_stat *rx_stat);
-
-/**
- * Modify stream, only when stream is inactive.
- */
-PJ_DECL(pj_status_t)
-pj_media_session_modify_stream (pj_media_session_t *session, unsigned index,
- unsigned modify_flag, const pj_media_stream_info *info);
-
-/**
- * Activate all streams in media session.
- */
-PJ_DECL(pj_status_t)
-pj_media_session_activate (pj_media_session_t *session);
-
-/**
- * Activate individual stream in media session.
- */
-PJ_DECL(pj_status_t)
-pj_media_session_activate_stream (pj_media_session_t *session, unsigned index);
-
-/**
- * Destroy media session.
- */
-PJ_DECL(pj_status_t)
-pj_media_session_destroy (pj_media_session_t *session);
-
-
-/**
- * @}
- */
-
-PJ_END_DECL
-
-#endif /* __PJMEDIA_SESSION_H__ */
+/* $Id$
+ *
+ */
+/*
+ * PJMEDIA - Multimedia over IP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef __PJMEDIA_SESSION_H__
+#define __PJMEDIA_SESSION_H__
+
+
+/**
+ * @file session.h
+ * @brief Media Session.
+ */
+
+#include <pj/types.h>
+#include <pjmedia/mediamgr.h>
+#include <pjmedia/stream.h>
+#include <pjmedia/sdp.h>
+
+PJ_BEGIN_DECL
+
+/**
+ * @defgroup PJMED_SES Media session
+ * @ingroup PJMEDIA
+ * @{
+ */
+
+/** Opaque declaration of media session. */
+typedef struct pj_media_session_t pj_media_session_t;
+
+/** Media socket info. */
+typedef struct pj_media_sock_info
+{
+ pj_sock_t rtp_sock, rtcp_sock;
+ pj_sockaddr_in rtp_addr_name;
+} pj_media_sock_info;
+
+/** Stream info. */
+typedef struct pj_media_stream_info
+{
+ pj_str_t type;
+ pj_media_dir_t dir;
+ pj_str_t transport;
+ pj_media_sock_info sock_info;
+ pj_str_t rem_addr;
+ unsigned short rem_port;
+ unsigned fmt_cnt;
+ pj_codec_id fmt[PJSDP_MAX_FMT];
+
+} pj_media_stream_info;
+
+/** Flag for modifying stream. */
+enum
+{
+ PJ_MEDIA_STREAM_MODIFY_DIR = 1,
+};
+
+/**
+ * Create new session offering.
+ */
+PJ_DECL(pj_media_session_t*)
+pj_media_session_create ( pj_med_mgr_t *mgr, const pj_media_sock_info *skinfo );
+
+/**
+ * Create new session based on peer's offering.
+ */
+PJ_DECL(pj_media_session_t*)
+pj_media_session_create_from_sdp ( pj_med_mgr_t *mgr, const pjsdp_session_desc *sdp,
+ const pj_media_sock_info *skinfo);
+
+/**
+ * Duplicate session. The new session is inactive.
+ */
+PJ_DECL(pj_media_session_t*)
+pj_media_session_clone (const pj_media_session_t *session);
+
+/**
+ * Create SDP description from the session.
+ */
+PJ_DECL(pjsdp_session_desc*)
+pj_media_session_create_sdp ( const pj_media_session_t *session, pj_pool_t *pool,
+ pj_bool_t only_first_fmt);
+
+/**
+ * Update session with SDP answer from peer. The session must NOT active.
+ */
+PJ_DECL(pj_status_t)
+pj_media_session_update ( pj_media_session_t *session,
+ const pjsdp_session_desc *sdp);
+
+/**
+ * Enumerate media streams in the session.
+ * @return the actual number of streams.
+ */
+PJ_DECL(unsigned)
+pj_media_session_enum_streams (const pj_media_session_t *session,
+ unsigned count, const pj_media_stream_info *info[]);
+
+/**
+ * Get stream statistics.
+ */
+PJ_DECL(pj_status_t)
+pj_media_session_get_stat (const pj_media_session_t *session, unsigned index,
+ pj_media_stream_stat *tx_stat,
+ pj_media_stream_stat *rx_stat);
+
+/**
+ * Modify stream, only when stream is inactive.
+ */
+PJ_DECL(pj_status_t)
+pj_media_session_modify_stream (pj_media_session_t *session, unsigned index,
+ unsigned modify_flag, const pj_media_stream_info *info);
+
+/**
+ * Activate all streams in media session.
+ */
+PJ_DECL(pj_status_t)
+pj_media_session_activate (pj_media_session_t *session);
+
+/**
+ * Activate individual stream in media session.
+ */
+PJ_DECL(pj_status_t)
+pj_media_session_activate_stream (pj_media_session_t *session, unsigned index);
+
+/**
+ * Destroy media session.
+ */
+PJ_DECL(pj_status_t)
+pj_media_session_destroy (pj_media_session_t *session);
+
+
+/**
+ * @}
+ */
+
+PJ_END_DECL
+
+#endif /* __PJMEDIA_SESSION_H__ */
diff --git a/pjmedia/src/pjmedia/sound.h b/pjmedia/src/pjmedia/sound.h
index 997885a9..a3f51632 100644
--- a/pjmedia/src/pjmedia/sound.h
+++ b/pjmedia/src/pjmedia/sound.h
@@ -1,187 +1,208 @@
-/* $Id$
- *
- */
-
-#ifndef __PJMEDIA_SOUND_H__
-#define __PJMEDIA_SOUND_H__
-
-
-/**
- * @file sound.h
- * @brief Sound player and recorder device framework.
- */
-
-#include <pj/pool.h>
-
-PJ_BEGIN_DECL
-
-/**
- * @defgroup PJMED_SND Sound device abstraction.
- * @ingroup PJMEDIA
- * @{
- */
-
-/** Opaque data type for audio stream. */
-typedef struct pj_snd_stream pj_snd_stream;
-
-/**
- * Device information structure returned by #pj_snd_get_dev_info.
- */
-typedef struct pj_snd_dev_info
-{
- char name[64]; /**< Device name. */
- unsigned input_count; /**< Max number of input channels. */
- unsigned output_count; /**< Max number of output channels. */
- unsigned default_samples_per_sec;/**< Default sampling rate. */
-} pj_snd_dev_info;
-
-/**
- * Sound device parameter, to be specified when calling #pj_snd_open_recorder
- * or #pj_snd_open_player.
- */
-typedef struct pj_snd_stream_info
-{
- unsigned samples_per_sec; /* Sampling rate. */
- unsigned bits_per_sample; /* No of bits per sample. */
- unsigned samples_per_frame; /* No of samples per frame. */
- unsigned bytes_per_frame; /* No of bytes per frame. */
- unsigned frames_per_packet; /* No of frames per packet. */
-} pj_snd_stream_info;
-
-/**
- * This callback is called by player stream when it needs additional data
- * to be played by the device. Application must fill in the whole of output
- * buffer with sound samples.
- *
- * @param user_data User data associated with the stream.
- * @param timestamp Timestamp, in samples.
- * @param output Buffer to be filled out by application.
- * @param size The size requested in bytes, which will be equal to
- * the size of one whole packet.
- *
- * @return Non-zero to stop the stream.
- */
-typedef pj_status_t (*pj_snd_play_cb)(/* in */ void *user_data,
- /* in */ pj_uint32_t timestamp,
- /* out */ void *output,
- /* out */ unsigned size);
-
-/**
- * This callback is called by recorder stream when it has captured the whole
- * packet worth of audio samples.
- *
- * @param user_data User data associated with the stream.
- * @param timestamp Timestamp, in samples.
- * @param output Buffer containing the captured audio samples.
- * @param size The size of the data in the buffer, in bytes.
- *
- * @return Non-zero to stop the stream.
- */
-typedef pj_status_t (*pj_snd_rec_cb)(/* in */ void *user_data,
- /* in */ pj_uint32_t timestamp,
- /* in */ const void *input,
- /* in*/ unsigned size);
-
-/**
- * Init the sound library.
- *
- * @param factory The sound factory.
- *
- * @return Zero on success.
- */
-PJ_DECL(pj_status_t) pj_snd_init(pj_pool_factory *factory);
-
-
-/**
- * Get the number of devices detected by the library.
- *
- * @return Number of devices.
- */
-PJ_DECL(int) pj_snd_get_dev_count(void);
-
-
-/**
- * Get device info.
- *
- * @param index The index of the device, which should be in the range
- * from zero to #pj_snd_get_dev_count - 1.
- */
-PJ_DECL(const pj_snd_dev_info*) pj_snd_get_dev_info(unsigned index);
-
-
-/**
- * Create a new audio stream for audio capture purpose.
- *
- * @param index Device index, or -1 to let the library choose the first
- * available device, or -2 to use NULL device.
- * @param param Stream parameters.
- * @param rec_cb Callback to handle captured audio samples.
- * @param user_data User data to be associated with the stream.
- *
- * @return Audio stream, or NULL if failed.
- */
-PJ_DECL(pj_snd_stream*) pj_snd_open_recorder(int index,
- const pj_snd_stream_info *param,
- pj_snd_rec_cb rec_cb,
- void *user_data);
-
-/**
- * Create a new audio stream for playing audio samples.
- *
- * @param index Device index, or -1 to let the library choose the first
- * available device, or -2 to use NULL device.
- * @param param Stream parameters.
- * @param play_cb Callback to supply audio samples.
- * @param user_data User data to be associated with the stream.
- *
- * @return Audio stream, or NULL if failed.
- */
-PJ_DECL(pj_snd_stream*) pj_snd_open_player(int index,
- const pj_snd_stream_info *param,
- pj_snd_play_cb play_cb,
- void *user_data);
-
-/**
- * Start the stream.
- *
- * @param stream The recorder or player stream.
- *
- * @return Zero on success.
- */
-PJ_DECL(pj_status_t) pj_snd_stream_start(pj_snd_stream *stream);
-
-/**
- * Stop the stream.
- *
- * @param stream The recorder or player stream.
- *
- * @return Zero on success.
- */
-PJ_DECL(pj_status_t) pj_snd_stream_stop(pj_snd_stream *stream);
-
-/**
- * Destroy the stream.
- *
- * @param stream The recorder of player stream.
- *
- * @return Zero on success.
- */
-PJ_DECL(pj_status_t) pj_snd_stream_close(pj_snd_stream *stream);
-
-/**
- * Deinitialize sound library.
- *
- * @return Zero on success.
- */
-PJ_DECL(pj_status_t) pj_snd_deinit(void);
-
-
-
-/**
- * @}
- */
-
-PJ_END_DECL
-
-
-#endif /* __PJMEDIA_SOUND_H__ */
+/* $Id$
+ *
+ */
+/*
+ * PJMEDIA - Multimedia over IP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef __PJMEDIA_SOUND_H__
+#define __PJMEDIA_SOUND_H__
+
+
+/**
+ * @file sound.h
+ * @brief Sound player and recorder device framework.
+ */
+
+#include <pj/pool.h>
+
+PJ_BEGIN_DECL
+
+/**
+ * @defgroup PJMED_SND Sound device abstraction.
+ * @ingroup PJMEDIA
+ * @{
+ */
+
+/** Opaque data type for audio stream. */
+typedef struct pj_snd_stream pj_snd_stream;
+
+/**
+ * Device information structure returned by #pj_snd_get_dev_info.
+ */
+typedef struct pj_snd_dev_info
+{
+ char name[64]; /**< Device name. */
+ unsigned input_count; /**< Max number of input channels. */
+ unsigned output_count; /**< Max number of output channels. */
+ unsigned default_samples_per_sec;/**< Default sampling rate. */
+} pj_snd_dev_info;
+
+/**
+ * Sound device parameter, to be specified when calling #pj_snd_open_recorder
+ * or #pj_snd_open_player.
+ */
+typedef struct pj_snd_stream_info
+{
+ unsigned samples_per_sec; /* Sampling rate. */
+ unsigned bits_per_sample; /* No of bits per sample. */
+ unsigned samples_per_frame; /* No of samples per frame. */
+ unsigned bytes_per_frame; /* No of bytes per frame. */
+ unsigned frames_per_packet; /* No of frames per packet. */
+} pj_snd_stream_info;
+
+/**
+ * This callback is called by player stream when it needs additional data
+ * to be played by the device. Application must fill in the whole of output
+ * buffer with sound samples.
+ *
+ * @param user_data User data associated with the stream.
+ * @param timestamp Timestamp, in samples.
+ * @param output Buffer to be filled out by application.
+ * @param size The size requested in bytes, which will be equal to
+ * the size of one whole packet.
+ *
+ * @return Non-zero to stop the stream.
+ */
+typedef pj_status_t (*pj_snd_play_cb)(/* in */ void *user_data,
+ /* in */ pj_uint32_t timestamp,
+ /* out */ void *output,
+ /* out */ unsigned size);
+
+/**
+ * This callback is called by recorder stream when it has captured the whole
+ * packet worth of audio samples.
+ *
+ * @param user_data User data associated with the stream.
+ * @param timestamp Timestamp, in samples.
+ * @param output Buffer containing the captured audio samples.
+ * @param size The size of the data in the buffer, in bytes.
+ *
+ * @return Non-zero to stop the stream.
+ */
+typedef pj_status_t (*pj_snd_rec_cb)(/* in */ void *user_data,
+ /* in */ pj_uint32_t timestamp,
+ /* in */ const void *input,
+ /* in*/ unsigned size);
+
+/**
+ * Init the sound library.
+ *
+ * @param factory The sound factory.
+ *
+ * @return Zero on success.
+ */
+PJ_DECL(pj_status_t) pj_snd_init(pj_pool_factory *factory);
+
+
+/**
+ * Get the number of devices detected by the library.
+ *
+ * @return Number of devices.
+ */
+PJ_DECL(int) pj_snd_get_dev_count(void);
+
+
+/**
+ * Get device info.
+ *
+ * @param index The index of the device, which should be in the range
+ * from zero to #pj_snd_get_dev_count - 1.
+ */
+PJ_DECL(const pj_snd_dev_info*) pj_snd_get_dev_info(unsigned index);
+
+
+/**
+ * Create a new audio stream for audio capture purpose.
+ *
+ * @param index Device index, or -1 to let the library choose the first
+ * available device, or -2 to use NULL device.
+ * @param param Stream parameters.
+ * @param rec_cb Callback to handle captured audio samples.
+ * @param user_data User data to be associated with the stream.
+ *
+ * @return Audio stream, or NULL if failed.
+ */
+PJ_DECL(pj_snd_stream*) pj_snd_open_recorder(int index,
+ const pj_snd_stream_info *param,
+ pj_snd_rec_cb rec_cb,
+ void *user_data);
+
+/**
+ * Create a new audio stream for playing audio samples.
+ *
+ * @param index Device index, or -1 to let the library choose the first
+ * available device, or -2 to use NULL device.
+ * @param param Stream parameters.
+ * @param play_cb Callback to supply audio samples.
+ * @param user_data User data to be associated with the stream.
+ *
+ * @return Audio stream, or NULL if failed.
+ */
+PJ_DECL(pj_snd_stream*) pj_snd_open_player(int index,
+ const pj_snd_stream_info *param,
+ pj_snd_play_cb play_cb,
+ void *user_data);
+
+/**
+ * Start the stream.
+ *
+ * @param stream The recorder or player stream.
+ *
+ * @return Zero on success.
+ */
+PJ_DECL(pj_status_t) pj_snd_stream_start(pj_snd_stream *stream);
+
+/**
+ * Stop the stream.
+ *
+ * @param stream The recorder or player stream.
+ *
+ * @return Zero on success.
+ */
+PJ_DECL(pj_status_t) pj_snd_stream_stop(pj_snd_stream *stream);
+
+/**
+ * Destroy the stream.
+ *
+ * @param stream The recorder of player stream.
+ *
+ * @return Zero on success.
+ */
+PJ_DECL(pj_status_t) pj_snd_stream_close(pj_snd_stream *stream);
+
+/**
+ * Deinitialize sound library.
+ *
+ * @return Zero on success.
+ */
+PJ_DECL(pj_status_t) pj_snd_deinit(void);
+
+
+
+/**
+ * @}
+ */
+
+PJ_END_DECL
+
+
+#endif /* __PJMEDIA_SOUND_H__ */
diff --git a/pjmedia/src/pjmedia/stream.c b/pjmedia/src/pjmedia/stream.c
index 1420a678..7419984d 100644
--- a/pjmedia/src/pjmedia/stream.c
+++ b/pjmedia/src/pjmedia/stream.c
@@ -1,630 +1,651 @@
-/* $Id$
- *
- */
-
-#include <pjmedia/stream.h>
-#include <pjmedia/rtp.h>
-#include <pjmedia/rtcp.h>
-#include <pjmedia/jbuf.h>
-#include <pj/os.h>
-#include <pj/log.h>
-#include <pj/string.h> /* memcpy() */
-#include <pj/pool.h>
-#include <stdlib.h>
-
-#define THISFILE "stream.c"
-#define ERRLEVEL 1
-
-#define PJ_MAX_FRAME_DURATION_MS 200
-#define PJ_MAX_BUFFER_SIZE_MS 2000
-#define PJ_MAX_MTU 1500
-
-struct jb_frame
-{
- unsigned size;
- void *buf;
-};
-
-#define pj_fifobuf_alloc(fifo,size) malloc(size)
-#define pj_fifobuf_unalloc(fifo,buf) free(buf)
-#define pj_fifobuf_free(fifo, buf) free(buf)
-
-enum stream_state
-{
- STREAM_STOPPED,
- STREAM_STARTED,
-};
-
-struct pj_media_stream_t
-{
- pj_media_dir_t dir;
- int pt;
- int state;
- pj_media_stream_stat stat;
- pj_media_stream_t *peer;
- pj_snd_stream_info snd_info;
- pj_snd_stream *snd_stream;
- pj_mutex_t *mutex;
- unsigned in_pkt_size;
- void *in_pkt;
- unsigned out_pkt_size;
- void *out_pkt;
- unsigned pcm_buf_size;
- void *pcm_buf;
- //pj_fifobuf_t fifobuf;
- pj_codec_mgr *codec_mgr;
- pj_codec *codec;
- pj_rtp_session rtp;
- pj_rtcp_session *rtcp;
- pj_jitter_buffer *jb;
- pj_sock_t rtp_sock;
- pj_sock_t rtcp_sock;
- pj_sockaddr_in dst_addr;
- pj_thread_t *transport_thread;
- int thread_quit_flag;
-};
-
-
-static pj_status_t play_callback(/* in */ void *user_data,
- /* in */ pj_uint32_t timestamp,
- /* out */ void *frame,
- /*inout*/ unsigned size)
-{
- pj_media_stream_t *channel = user_data;
- struct jb_frame *jb_frame;
- void *p;
- pj_uint32_t extseq;
- pj_status_t status;
- struct pj_audio_frame frame_in, frame_out;
-
- PJ_UNUSED_ARG(timestamp)
-
- /* Lock mutex */
- pj_mutex_lock (channel->mutex);
-
- if (!channel->codec) {
- pj_mutex_unlock (channel->mutex);
- return -1;
- }
-
- /* Get frame from jitter buffer. */
- status = pj_jb_get (channel->jb, &extseq, &p);
- jb_frame = p;
- if (status != 0 || jb_frame == NULL) {
- pj_memset(frame, 0, size);
- pj_mutex_unlock(channel->mutex);
- return 0;
- }
-
- /* Decode */
- frame_in.buf = jb_frame->buf;
- frame_in.size = jb_frame->size;
- frame_in.type = PJ_AUDIO_FRAME_AUDIO; /* ignored */
- frame_out.buf = channel->pcm_buf;
- status = channel->codec->op->decode (channel->codec, &frame_in,
- channel->pcm_buf_size, &frame_out);
- if (status != 0) {
- PJ_LOG(3, (THISFILE, "decode() has return error status %d",
- status));
-
- pj_memset(frame, 0, size);
- pj_fifobuf_free (&channel->fifobuf, jb_frame);
- pj_mutex_unlock(channel->mutex);
- return 0;
- }
-
- /* Put in sound buffer. */
- if (frame_out.size > size) {
- PJ_LOG(3, (THISFILE, "Sound playout buffer truncated %d bytes",
- frame_out.size - size));
- frame_out.size = size;
- }
-
- pj_memcpy(frame, frame_out.buf, size);
-
- pj_fifobuf_free (&channel->fifobuf, jb_frame);
- pj_mutex_unlock(channel->mutex);
- return 0;
-}
-
-static pj_status_t rec_callback( /* in */ void *user_data,
- /* in */ pj_uint32_t timestamp,
- /* in */ const void *frame,
- /* in */ unsigned size)
-{
- pj_media_stream_t *channel = user_data;
- pj_status_t status = 0;
- struct pj_audio_frame frame_in, frame_out;
- int ts_len;
- void *rtphdr;
- int rtphdrlen;
- int sent;
-#if 0
- static FILE *fhnd = NULL;
-#endif
-
- PJ_UNUSED_ARG(timestamp)
-
- /* Start locking channel mutex */
- pj_mutex_lock (channel->mutex);
-
- if (!channel->codec) {
- status = -1;
- goto on_return;
- }
-
- /* Encode. */
- frame_in.type = PJ_MEDIA_TYPE_AUDIO;
- frame_in.buf = (void*)frame;
- frame_in.size = size;
- frame_out.buf = ((char*)channel->out_pkt) + sizeof(pj_rtp_hdr);
- status = channel->codec->op->encode (channel->codec, &frame_in,
- channel->out_pkt_size - sizeof(pj_rtp_hdr),
- &frame_out);
- if (status != 0) {
- PJ_LOG(3,(THISFILE, "Codec encode() has returned error status %d",
- status));
- goto on_return;
- }
-
- /* Encapsulate. */
- ts_len = size / (channel->snd_info.bits_per_sample / 8);
- status = pj_rtp_encode_rtp (&channel->rtp, channel->pt, 0,
- frame_out.size, ts_len,
- (const void**)&rtphdr, &rtphdrlen);
- if (status != 0) {
- PJ_LOG(3,(THISFILE, "RTP encode_rtp() has returned error status %d",
- status));
- goto on_return;
- }
-
- if (rtphdrlen != sizeof(pj_rtp_hdr)) {
- /* We don't support RTP with extended header yet. */
- PJ_TODO(SUPPORT_SENDING_RTP_WITH_EXTENDED_HEADER);
- PJ_LOG(3,(THISFILE, "Unsupported extended RTP header for transmission"));
- goto on_return;
- }
-
- pj_memcpy(channel->out_pkt, rtphdr, sizeof(pj_rtp_hdr));
-
- /* Send. */
- sent = pj_sock_sendto (channel->rtp_sock, channel->out_pkt, frame_out.size+sizeof(pj_rtp_hdr), 0,
- &channel->dst_addr, sizeof(channel->dst_addr));
- if (sent != (int)frame_out.size + (int)sizeof(pj_rtp_hdr)) {
- pj_perror(THISFILE, "Error sending RTP packet to %s:%d",
- pj_sockaddr_get_str_addr(&channel->dst_addr),
- pj_sockaddr_get_port(&channel->dst_addr));
- goto on_return;
- }
-
- /* Update stat */
- channel->stat.pkt_tx++;
- channel->stat.oct_tx += frame_out.size+sizeof(pj_rtp_hdr);
-
-#if 0
- if (fhnd == NULL) {
- fhnd = fopen("RTP.DAT", "wb");
- if (fhnd) {
- fwrite (channel->out_pkt, frame_out.size+sizeof(pj_rtp_hdr), 1, fhnd);
- fclose(fhnd);
- }
- }
-#endif
-
-on_return:
- pj_mutex_unlock (channel->mutex);
- return status;
-}
-
-
-static void* PJ_THREAD_FUNC stream_decoder_transport_thread (void*arg)
-{
- pj_media_stream_t *channel = arg;
-
- while (!channel->thread_quit_flag) {
- int len, size;
- const pj_rtp_hdr *hdr;
- const void *payload;
- unsigned payloadlen;
- int status;
- struct jb_frame *jb_frame;
-
- /* Wait for packet. */
- fd_set fds;
- pj_time_val timeout;
-
- PJ_FD_ZERO (&fds);
- PJ_FD_SET (channel->rtp_sock, &fds);
- timeout.sec = 0;
- timeout.msec = 100;
-
- /* Wait with timeout. */
- status = pj_sock_select(channel->rtp_sock, &fds, NULL, NULL, &timeout);
- if (status != 1)
- continue;
-
- /* Get packet from socket. */
- len = pj_sock_recv (channel->rtp_sock, channel->in_pkt, channel->in_pkt_size, 0);
- if (len < 1) {
- if (pj_getlasterror() == PJ_ECONNRESET) {
- /* On Win2K SP2 (or above) and WinXP, recv() will get WSAECONNRESET
- when the sending side receives ICMP port unreachable.
- */
- continue;
- }
- pj_perror(THISFILE, "Error receiving packet from socket (len=%d)", len);
- pj_thread_sleep(1);
- continue;
- }
-
- if (channel->state != STREAM_STARTED)
- continue;
-
- if (channel->thread_quit_flag)
- break;
-
- /* Start locking the channel. */
- pj_mutex_lock (channel->mutex);
-
- /* Update RTP and RTCP session. */
- status = pj_rtp_decode_rtp (&channel->rtp, channel->in_pkt, len, &hdr, &payload, &payloadlen);
- if (status != 0) {
- pj_mutex_unlock (channel->mutex);
- PJ_LOG(4,(THISFILE, "RTP decode_rtp() has returned error status %d", status));
- continue;
- }
- status = pj_rtp_session_update (&channel->rtp, hdr);
- if (status != 0 && status != PJ_RTP_ERR_SESSION_PROBATION && status != PJ_RTP_ERR_SESSION_RESTARTED) {
- pj_mutex_unlock (channel->mutex);
- PJ_LOG(4,(THISFILE, "RTP session_update() has returned error status %d", status));
- continue;
- }
- pj_rtcp_rx_rtp (channel->rtcp, pj_ntohs(hdr->seq), pj_ntohl(hdr->ts));
-
- /* Update stat */
- channel->stat.pkt_rx++;
- channel->stat.oct_rx += len;
-
- /* Copy to FIFO buffer. */
- size = payloadlen+sizeof(struct jb_frame);
- jb_frame = pj_fifobuf_alloc (&channel->fifobuf, size);
- if (jb_frame == NULL) {
- pj_mutex_unlock (channel->mutex);
- PJ_LOG(4,(THISFILE, "Unable to allocate %d bytes FIFO buffer", size));
- continue;
- }
-
- /* Copy the payload */
- jb_frame->size = payloadlen;
- jb_frame->buf = ((char*)jb_frame) + sizeof(struct jb_frame);
- pj_memcpy (jb_frame->buf, payload, payloadlen);
-
- /* Put to jitter buffer. */
- status = pj_jb_put (channel->jb, pj_ntohs(hdr->seq), jb_frame);
- if (status != 0) {
- pj_fifobuf_unalloc (&channel->fifobuf, jb_frame);
- pj_mutex_unlock (channel->mutex);
- PJ_LOG(4,(THISFILE, "Jitter buffer put() has returned error status %d", status));
- continue;
- }
-
- pj_mutex_unlock (channel->mutex);
- }
-
- return NULL;
-}
-
-static void init_snd_param_from_codec_attr (pj_snd_stream_info *param,
- const pj_codec_attr *attr)
-{
- param->bits_per_sample = attr->pcm_bits_per_sample;
- param->bytes_per_frame = 2;
- param->frames_per_packet = attr->sample_rate * attr->ptime / 1000;
- param->samples_per_frame = 1;
- param->samples_per_sec = attr->sample_rate;
-}
-
-static pj_media_stream_t *create_channel ( pj_pool_t *pool,
- pj_media_dir_t dir,
- pj_media_stream_t *peer,
- pj_codec_id *codec_id,
- pj_media_stream_create_param *param)
-{
- pj_media_stream_t *channel;
- pj_codec_attr codec_attr;
- void *ptr;
- unsigned size;
- int status;
-
- /* Allocate memory for channel descriptor */
- size = sizeof(pj_media_stream_t);
- channel = pj_pool_calloc(pool, 1, size);
- if (!channel) {
- PJ_LOG(1,(THISFILE, "Unable to allocate %u bytes channel descriptor",
- size));
- return NULL;
- }
-
- channel->dir = dir;
- channel->pt = codec_id->pt;
- channel->peer = peer;
- channel->codec_mgr = pj_med_mgr_get_codec_mgr (param->mediamgr);
- channel->rtp_sock = param->rtp_sock;
- channel->rtcp_sock = param->rtcp_sock;
- channel->dst_addr = *param->remote_addr;
- channel->state = STREAM_STOPPED;
-
- /* Create mutex for the channel. */
- channel->mutex = pj_mutex_create(pool, NULL, PJ_MUTEX_SIMPLE);
- if (channel->mutex == NULL)
- goto err_cleanup;
-
- /* Create and initialize codec, only if peer is not present.
- We only use one codec instance for both encoder and decoder.
- */
- if (peer && peer->codec) {
- channel->codec = peer->codec;
- status = channel->codec->factory->op->default_attr(channel->codec->factory, codec_id,
- &codec_attr);
- if (status != 0) {
- goto err_cleanup;
- }
-
- } else {
- channel->codec = pj_codec_mgr_alloc_codec(channel->codec_mgr, codec_id);
- if (channel->codec == NULL) {
- goto err_cleanup;
- }
-
- status = channel->codec->factory->op->default_attr(channel->codec->factory, codec_id,
- &codec_attr);
- if (status != 0) {
- goto err_cleanup;
- }
-
- codec_attr.pt = codec_id->pt;
- status = channel->codec->op->open(channel->codec, &codec_attr);
- if (status != 0) {
- goto err_cleanup;
- }
- }
-
- /* Allocate buffer for incoming packet. */
- channel->in_pkt_size = PJ_MAX_MTU;
- channel->in_pkt = pj_pool_alloc(pool, channel->in_pkt_size);
- if (!channel->in_pkt) {
- PJ_LOG(1, (THISFILE, "Unable to allocate %u bytes incoming packet buffer",
- channel->in_pkt_size));
- goto err_cleanup;
- }
-
- /* Allocate buffer for outgoing packet. */
- channel->out_pkt_size = sizeof(pj_rtp_hdr) +
- codec_attr.avg_bps / 8 * PJ_MAX_FRAME_DURATION_MS / 1000;
- if (channel->out_pkt_size > PJ_MAX_MTU)
- channel->out_pkt_size = PJ_MAX_MTU;
- channel->out_pkt = pj_pool_alloc(pool, channel->out_pkt_size);
- if (!channel->out_pkt) {
- PJ_LOG(1, (THISFILE, "Unable to allocate %u bytes encoding buffer",
- channel->out_pkt_size));
- goto err_cleanup;
- }
-
- /* Allocate buffer for decoding to PCM */
- channel->pcm_buf_size = codec_attr.sample_rate *
- codec_attr.pcm_bits_per_sample / 8 *
- PJ_MAX_FRAME_DURATION_MS / 1000;
- channel->pcm_buf = pj_pool_alloc (pool, channel->pcm_buf_size);
- if (!channel->pcm_buf) {
- PJ_LOG(1, (THISFILE, "Unable to allocate %u bytes PCM buffer",
- channel->pcm_buf_size));
- goto err_cleanup;
- }
-
- /* Allocate buffer for frames put in jitter buffer. */
- size = codec_attr.avg_bps / 8 * PJ_MAX_BUFFER_SIZE_MS / 1000;
- ptr = pj_pool_alloc(pool, size);
- if (!ptr) {
- PJ_LOG(1, (THISFILE, "Unable to allocate %u bytes jitter buffer",
- channel->pcm_buf_size));
- goto err_cleanup;
- }
- //pj_fifobuf_init (&channel->fifobuf, ptr, size);
-
- /* Create and initialize sound device */
- init_snd_param_from_codec_attr (&channel->snd_info, &codec_attr);
-
- if (dir == PJ_MEDIA_DIR_ENCODING)
- channel->snd_stream = pj_snd_open_recorder(-1, &channel->snd_info,
- &rec_callback, channel);
- else
- channel->snd_stream = pj_snd_open_player(-1, &channel->snd_info,
- &play_callback, channel);
-
- if (!channel->snd_stream)
- goto err_cleanup;
-
- /* Create RTP and RTCP sessions. */
- if (pj_rtp_session_init(&channel->rtp, codec_id->pt, param->ssrc) != 0) {
- PJ_LOG(1, (THISFILE, "RTP session initialization error"));
- goto err_cleanup;
- }
-
- /* For decoder, create RTCP session, jitter buffer, and transport thread. */
- if (dir == PJ_MEDIA_DIR_DECODING) {
- channel->rtcp = pj_pool_calloc(pool, 1, sizeof(pj_rtcp_session));
- if (!channel->rtcp) {
- PJ_LOG(1, (THISFILE, "Unable to allocate RTCP session"));
- goto err_cleanup;
- }
-
- pj_rtcp_init(channel->rtcp, param->ssrc);
-
- channel->jb = pj_pool_calloc(pool, 1, sizeof(pj_jitter_buffer));
- if (!channel->jb) {
- PJ_LOG(1, (THISFILE, "Unable to allocate jitter buffer descriptor"));
- goto err_cleanup;
- }
- if (pj_jb_init(channel->jb, pool, param->jb_min, param->jb_max, param->jb_maxcnt)) {
- PJ_LOG(1, (THISFILE, "Unable to allocate jitter buffer"));
- goto err_cleanup;
- }
-
- channel->transport_thread = pj_thread_create(pool, "decode",
- &stream_decoder_transport_thread, channel,
- 0, NULL, 0);
- if (!channel->transport_thread) {
- pj_perror(THISFILE, "Unable to create transport thread");
- goto err_cleanup;
- }
- }
-
- /* Done. */
- return channel;
-
-err_cleanup:
- pj_media_stream_destroy(channel);
- return NULL;
-}
-
-
-PJ_DEF(pj_status_t) pj_media_stream_create (pj_pool_t *pool,
- pj_media_stream_t **enc_stream,
- pj_media_stream_t **dec_stream,
- pj_media_stream_create_param *param)
-{
- *dec_stream = *enc_stream = NULL;
-
- if (param->dir & PJ_MEDIA_DIR_DECODING) {
- *dec_stream =
- create_channel(pool, PJ_MEDIA_DIR_DECODING, NULL, param->codec_id, param);
- if (!*dec_stream)
- return -1;
- }
-
- if (param->dir & PJ_MEDIA_DIR_ENCODING) {
- *enc_stream =
- create_channel(pool, PJ_MEDIA_DIR_ENCODING, *dec_stream, param->codec_id, param);
- if (!*enc_stream) {
- if (*dec_stream) {
- pj_media_stream_destroy(*dec_stream);
- *dec_stream = NULL;
- }
- return -1;
- }
-
- if (*dec_stream) {
- (*dec_stream)->peer = *enc_stream;
- }
- }
-
- return 0;
-}
-
-PJ_DEF(pj_status_t) pj_media_stream_start (pj_media_stream_t *channel)
-{
- pj_status_t status;
-
- status = pj_snd_stream_start(channel->snd_stream);
-
- if (status == 0)
- channel->state = STREAM_STARTED;
- return status;
-}
-
-PJ_DEF(pj_status_t) pj_media_stream_get_stat (const pj_media_stream_t *stream,
- pj_media_stream_stat *stat)
-{
- if (stream->dir == PJ_MEDIA_DIR_ENCODING) {
- pj_memcpy (stat, &stream->stat, sizeof(*stat));
- } else {
- pj_rtcp_pkt *rtcp_pkt;
- int len;
-
- pj_memset (stat, 0, sizeof(*stat));
- pj_assert (stream->rtcp != 0);
- pj_rtcp_build_rtcp (stream->rtcp, &rtcp_pkt, &len);
-
- stat->pkt_rx = stream->stat.pkt_rx;
- stat->oct_rx = stream->stat.oct_rx;
-
- PJ_TODO(SUPPORT_JITTER_CALCULATION_FOR_NON_8KHZ_SAMPLE_RATE)
- stat->jitter = pj_ntohl(rtcp_pkt->rr.jitter) / 8;
- stat->pkt_lost = (rtcp_pkt->rr.total_lost_2 << 16) +
- (rtcp_pkt->rr.total_lost_1 << 8) +
- rtcp_pkt->rr.total_lost_0;
- }
- return 0;
-}
-
-PJ_DEF(pj_status_t) pj_media_stream_pause (pj_media_stream_t *channel)
-{
- PJ_UNUSED_ARG(channel)
- return -1;
-}
-
-PJ_DEF(pj_status_t) pj_media_stream_resume (pj_media_stream_t *channel)
-{
- PJ_UNUSED_ARG(channel)
- return -1;
-}
-
-PJ_DEF(pj_status_t) pj_media_stream_destroy (pj_media_stream_t *channel)
-{
- channel->thread_quit_flag = 1;
-
- pj_mutex_lock (channel->mutex);
- if (channel->peer)
- pj_mutex_lock (channel->peer->mutex);
-
- if (channel->jb) {
- /* No need to deinitialize jitter buffer. */
- }
- if (channel->transport_thread) {
- pj_thread_join(channel->transport_thread);
- pj_thread_destroy(channel->transport_thread);
- channel->transport_thread = NULL;
- }
- if (channel->snd_stream != NULL) {
- pj_mutex_unlock (channel->mutex);
- pj_snd_stream_stop(channel->snd_stream);
- pj_mutex_lock (channel->mutex);
- pj_snd_stream_close(channel->snd_stream);
- channel->snd_stream = NULL;
- }
- if (channel->codec) {
- channel->codec->op->close(channel->codec);
- pj_codec_mgr_dealloc_codec(channel->codec_mgr, channel->codec);
- channel->codec = NULL;
- }
- if (channel->peer) {
- pj_media_stream_t *peer = channel->peer;
- peer->peer = NULL;
- peer->codec = NULL;
- peer->thread_quit_flag = 1;
- if (peer->transport_thread) {
- pj_mutex_unlock (peer->mutex);
- pj_thread_join(peer->transport_thread);
- pj_mutex_lock (peer->mutex);
- pj_thread_destroy(peer->transport_thread);
- peer->transport_thread = NULL;
- }
- if (peer->snd_stream) {
- pj_mutex_unlock (peer->mutex);
- pj_snd_stream_stop(peer->snd_stream);
- pj_mutex_lock (peer->mutex);
- pj_snd_stream_close(peer->snd_stream);
- peer->snd_stream = NULL;
- }
- }
-
- channel->state = STREAM_STOPPED;
-
- if (channel->peer)
- pj_mutex_unlock (channel->peer->mutex);
- pj_mutex_unlock(channel->mutex);
- pj_mutex_destroy(channel->mutex);
-
- return 0;
-}
-
+/* $Id$
+ *
+ */
+/*
+ * PJMEDIA - Multimedia over IP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <pjmedia/stream.h>
+#include <pjmedia/rtp.h>
+#include <pjmedia/rtcp.h>
+#include <pjmedia/jbuf.h>
+#include <pj/os.h>
+#include <pj/log.h>
+#include <pj/string.h> /* memcpy() */
+#include <pj/pool.h>
+#include <stdlib.h>
+
+#define THISFILE "stream.c"
+#define ERRLEVEL 1
+
+#define PJ_MAX_FRAME_DURATION_MS 200
+#define PJ_MAX_BUFFER_SIZE_MS 2000
+#define PJ_MAX_MTU 1500
+
+struct jb_frame
+{
+ unsigned size;
+ void *buf;
+};
+
+#define pj_fifobuf_alloc(fifo,size) malloc(size)
+#define pj_fifobuf_unalloc(fifo,buf) free(buf)
+#define pj_fifobuf_free(fifo, buf) free(buf)
+
+enum stream_state
+{
+ STREAM_STOPPED,
+ STREAM_STARTED,
+};
+
+struct pj_media_stream_t
+{
+ pj_media_dir_t dir;
+ int pt;
+ int state;
+ pj_media_stream_stat stat;
+ pj_media_stream_t *peer;
+ pj_snd_stream_info snd_info;
+ pj_snd_stream *snd_stream;
+ pj_mutex_t *mutex;
+ unsigned in_pkt_size;
+ void *in_pkt;
+ unsigned out_pkt_size;
+ void *out_pkt;
+ unsigned pcm_buf_size;
+ void *pcm_buf;
+ //pj_fifobuf_t fifobuf;
+ pj_codec_mgr *codec_mgr;
+ pj_codec *codec;
+ pj_rtp_session rtp;
+ pj_rtcp_session *rtcp;
+ pj_jitter_buffer *jb;
+ pj_sock_t rtp_sock;
+ pj_sock_t rtcp_sock;
+ pj_sockaddr_in dst_addr;
+ pj_thread_t *transport_thread;
+ int thread_quit_flag;
+};
+
+
+static pj_status_t play_callback(/* in */ void *user_data,
+ /* in */ pj_uint32_t timestamp,
+ /* out */ void *frame,
+ /*inout*/ unsigned size)
+{
+ pj_media_stream_t *channel = user_data;
+ struct jb_frame *jb_frame;
+ void *p;
+ pj_uint32_t extseq;
+ pj_status_t status;
+ struct pj_audio_frame frame_in, frame_out;
+
+ PJ_UNUSED_ARG(timestamp)
+
+ /* Lock mutex */
+ pj_mutex_lock (channel->mutex);
+
+ if (!channel->codec) {
+ pj_mutex_unlock (channel->mutex);
+ return -1;
+ }
+
+ /* Get frame from jitter buffer. */
+ status = pj_jb_get (channel->jb, &extseq, &p);
+ jb_frame = p;
+ if (status != 0 || jb_frame == NULL) {
+ pj_memset(frame, 0, size);
+ pj_mutex_unlock(channel->mutex);
+ return 0;
+ }
+
+ /* Decode */
+ frame_in.buf = jb_frame->buf;
+ frame_in.size = jb_frame->size;
+ frame_in.type = PJ_AUDIO_FRAME_AUDIO; /* ignored */
+ frame_out.buf = channel->pcm_buf;
+ status = channel->codec->op->decode (channel->codec, &frame_in,
+ channel->pcm_buf_size, &frame_out);
+ if (status != 0) {
+ PJ_LOG(3, (THISFILE, "decode() has return error status %d",
+ status));
+
+ pj_memset(frame, 0, size);
+ pj_fifobuf_free (&channel->fifobuf, jb_frame);
+ pj_mutex_unlock(channel->mutex);
+ return 0;
+ }
+
+ /* Put in sound buffer. */
+ if (frame_out.size > size) {
+ PJ_LOG(3, (THISFILE, "Sound playout buffer truncated %d bytes",
+ frame_out.size - size));
+ frame_out.size = size;
+ }
+
+ pj_memcpy(frame, frame_out.buf, size);
+
+ pj_fifobuf_free (&channel->fifobuf, jb_frame);
+ pj_mutex_unlock(channel->mutex);
+ return 0;
+}
+
+static pj_status_t rec_callback( /* in */ void *user_data,
+ /* in */ pj_uint32_t timestamp,
+ /* in */ const void *frame,
+ /* in */ unsigned size)
+{
+ pj_media_stream_t *channel = user_data;
+ pj_status_t status = 0;
+ struct pj_audio_frame frame_in, frame_out;
+ int ts_len;
+ void *rtphdr;
+ int rtphdrlen;
+ int sent;
+#if 0
+ static FILE *fhnd = NULL;
+#endif
+
+ PJ_UNUSED_ARG(timestamp)
+
+ /* Start locking channel mutex */
+ pj_mutex_lock (channel->mutex);
+
+ if (!channel->codec) {
+ status = -1;
+ goto on_return;
+ }
+
+ /* Encode. */
+ frame_in.type = PJ_MEDIA_TYPE_AUDIO;
+ frame_in.buf = (void*)frame;
+ frame_in.size = size;
+ frame_out.buf = ((char*)channel->out_pkt) + sizeof(pj_rtp_hdr);
+ status = channel->codec->op->encode (channel->codec, &frame_in,
+ channel->out_pkt_size - sizeof(pj_rtp_hdr),
+ &frame_out);
+ if (status != 0) {
+ PJ_LOG(3,(THISFILE, "Codec encode() has returned error status %d",
+ status));
+ goto on_return;
+ }
+
+ /* Encapsulate. */
+ ts_len = size / (channel->snd_info.bits_per_sample / 8);
+ status = pj_rtp_encode_rtp (&channel->rtp, channel->pt, 0,
+ frame_out.size, ts_len,
+ (const void**)&rtphdr, &rtphdrlen);
+ if (status != 0) {
+ PJ_LOG(3,(THISFILE, "RTP encode_rtp() has returned error status %d",
+ status));
+ goto on_return;
+ }
+
+ if (rtphdrlen != sizeof(pj_rtp_hdr)) {
+ /* We don't support RTP with extended header yet. */
+ PJ_TODO(SUPPORT_SENDING_RTP_WITH_EXTENDED_HEADER);
+ PJ_LOG(3,(THISFILE, "Unsupported extended RTP header for transmission"));
+ goto on_return;
+ }
+
+ pj_memcpy(channel->out_pkt, rtphdr, sizeof(pj_rtp_hdr));
+
+ /* Send. */
+ sent = pj_sock_sendto (channel->rtp_sock, channel->out_pkt, frame_out.size+sizeof(pj_rtp_hdr), 0,
+ &channel->dst_addr, sizeof(channel->dst_addr));
+ if (sent != (int)frame_out.size + (int)sizeof(pj_rtp_hdr)) {
+ pj_perror(THISFILE, "Error sending RTP packet to %s:%d",
+ pj_sockaddr_get_str_addr(&channel->dst_addr),
+ pj_sockaddr_get_port(&channel->dst_addr));
+ goto on_return;
+ }
+
+ /* Update stat */
+ channel->stat.pkt_tx++;
+ channel->stat.oct_tx += frame_out.size+sizeof(pj_rtp_hdr);
+
+#if 0
+ if (fhnd == NULL) {
+ fhnd = fopen("RTP.DAT", "wb");
+ if (fhnd) {
+ fwrite (channel->out_pkt, frame_out.size+sizeof(pj_rtp_hdr), 1, fhnd);
+ fclose(fhnd);
+ }
+ }
+#endif
+
+on_return:
+ pj_mutex_unlock (channel->mutex);
+ return status;
+}
+
+
+static void* PJ_THREAD_FUNC stream_decoder_transport_thread (void*arg)
+{
+ pj_media_stream_t *channel = arg;
+
+ while (!channel->thread_quit_flag) {
+ int len, size;
+ const pj_rtp_hdr *hdr;
+ const void *payload;
+ unsigned payloadlen;
+ int status;
+ struct jb_frame *jb_frame;
+
+ /* Wait for packet. */
+ fd_set fds;
+ pj_time_val timeout;
+
+ PJ_FD_ZERO (&fds);
+ PJ_FD_SET (channel->rtp_sock, &fds);
+ timeout.sec = 0;
+ timeout.msec = 100;
+
+ /* Wait with timeout. */
+ status = pj_sock_select(channel->rtp_sock, &fds, NULL, NULL, &timeout);
+ if (status != 1)
+ continue;
+
+ /* Get packet from socket. */
+ len = pj_sock_recv (channel->rtp_sock, channel->in_pkt, channel->in_pkt_size, 0);
+ if (len < 1) {
+ if (pj_getlasterror() == PJ_ECONNRESET) {
+ /* On Win2K SP2 (or above) and WinXP, recv() will get WSAECONNRESET
+ when the sending side receives ICMP port unreachable.
+ */
+ continue;
+ }
+ pj_perror(THISFILE, "Error receiving packet from socket (len=%d)", len);
+ pj_thread_sleep(1);
+ continue;
+ }
+
+ if (channel->state != STREAM_STARTED)
+ continue;
+
+ if (channel->thread_quit_flag)
+ break;
+
+ /* Start locking the channel. */
+ pj_mutex_lock (channel->mutex);
+
+ /* Update RTP and RTCP session. */
+ status = pj_rtp_decode_rtp (&channel->rtp, channel->in_pkt, len, &hdr, &payload, &payloadlen);
+ if (status != 0) {
+ pj_mutex_unlock (channel->mutex);
+ PJ_LOG(4,(THISFILE, "RTP decode_rtp() has returned error status %d", status));
+ continue;
+ }
+ status = pj_rtp_session_update (&channel->rtp, hdr);
+ if (status != 0 && status != PJ_RTP_ERR_SESSION_PROBATION && status != PJ_RTP_ERR_SESSION_RESTARTED) {
+ pj_mutex_unlock (channel->mutex);
+ PJ_LOG(4,(THISFILE, "RTP session_update() has returned error status %d", status));
+ continue;
+ }
+ pj_rtcp_rx_rtp (channel->rtcp, pj_ntohs(hdr->seq), pj_ntohl(hdr->ts));
+
+ /* Update stat */
+ channel->stat.pkt_rx++;
+ channel->stat.oct_rx += len;
+
+ /* Copy to FIFO buffer. */
+ size = payloadlen+sizeof(struct jb_frame);
+ jb_frame = pj_fifobuf_alloc (&channel->fifobuf, size);
+ if (jb_frame == NULL) {
+ pj_mutex_unlock (channel->mutex);
+ PJ_LOG(4,(THISFILE, "Unable to allocate %d bytes FIFO buffer", size));
+ continue;
+ }
+
+ /* Copy the payload */
+ jb_frame->size = payloadlen;
+ jb_frame->buf = ((char*)jb_frame) + sizeof(struct jb_frame);
+ pj_memcpy (jb_frame->buf, payload, payloadlen);
+
+ /* Put to jitter buffer. */
+ status = pj_jb_put (channel->jb, pj_ntohs(hdr->seq), jb_frame);
+ if (status != 0) {
+ pj_fifobuf_unalloc (&channel->fifobuf, jb_frame);
+ pj_mutex_unlock (channel->mutex);
+ PJ_LOG(4,(THISFILE, "Jitter buffer put() has returned error status %d", status));
+ continue;
+ }
+
+ pj_mutex_unlock (channel->mutex);
+ }
+
+ return NULL;
+}
+
+static void init_snd_param_from_codec_attr (pj_snd_stream_info *param,
+ const pj_codec_attr *attr)
+{
+ param->bits_per_sample = attr->pcm_bits_per_sample;
+ param->bytes_per_frame = 2;
+ param->frames_per_packet = attr->sample_rate * attr->ptime / 1000;
+ param->samples_per_frame = 1;
+ param->samples_per_sec = attr->sample_rate;
+}
+
+static pj_media_stream_t *create_channel ( pj_pool_t *pool,
+ pj_media_dir_t dir,
+ pj_media_stream_t *peer,
+ pj_codec_id *codec_id,
+ pj_media_stream_create_param *param)
+{
+ pj_media_stream_t *channel;
+ pj_codec_attr codec_attr;
+ void *ptr;
+ unsigned size;
+ int status;
+
+ /* Allocate memory for channel descriptor */
+ size = sizeof(pj_media_stream_t);
+ channel = pj_pool_calloc(pool, 1, size);
+ if (!channel) {
+ PJ_LOG(1,(THISFILE, "Unable to allocate %u bytes channel descriptor",
+ size));
+ return NULL;
+ }
+
+ channel->dir = dir;
+ channel->pt = codec_id->pt;
+ channel->peer = peer;
+ channel->codec_mgr = pj_med_mgr_get_codec_mgr (param->mediamgr);
+ channel->rtp_sock = param->rtp_sock;
+ channel->rtcp_sock = param->rtcp_sock;
+ channel->dst_addr = *param->remote_addr;
+ channel->state = STREAM_STOPPED;
+
+ /* Create mutex for the channel. */
+ channel->mutex = pj_mutex_create(pool, NULL, PJ_MUTEX_SIMPLE);
+ if (channel->mutex == NULL)
+ goto err_cleanup;
+
+ /* Create and initialize codec, only if peer is not present.
+ We only use one codec instance for both encoder and decoder.
+ */
+ if (peer && peer->codec) {
+ channel->codec = peer->codec;
+ status = channel->codec->factory->op->default_attr(channel->codec->factory, codec_id,
+ &codec_attr);
+ if (status != 0) {
+ goto err_cleanup;
+ }
+
+ } else {
+ channel->codec = pj_codec_mgr_alloc_codec(channel->codec_mgr, codec_id);
+ if (channel->codec == NULL) {
+ goto err_cleanup;
+ }
+
+ status = channel->codec->factory->op->default_attr(channel->codec->factory, codec_id,
+ &codec_attr);
+ if (status != 0) {
+ goto err_cleanup;
+ }
+
+ codec_attr.pt = codec_id->pt;
+ status = channel->codec->op->open(channel->codec, &codec_attr);
+ if (status != 0) {
+ goto err_cleanup;
+ }
+ }
+
+ /* Allocate buffer for incoming packet. */
+ channel->in_pkt_size = PJ_MAX_MTU;
+ channel->in_pkt = pj_pool_alloc(pool, channel->in_pkt_size);
+ if (!channel->in_pkt) {
+ PJ_LOG(1, (THISFILE, "Unable to allocate %u bytes incoming packet buffer",
+ channel->in_pkt_size));
+ goto err_cleanup;
+ }
+
+ /* Allocate buffer for outgoing packet. */
+ channel->out_pkt_size = sizeof(pj_rtp_hdr) +
+ codec_attr.avg_bps / 8 * PJ_MAX_FRAME_DURATION_MS / 1000;
+ if (channel->out_pkt_size > PJ_MAX_MTU)
+ channel->out_pkt_size = PJ_MAX_MTU;
+ channel->out_pkt = pj_pool_alloc(pool, channel->out_pkt_size);
+ if (!channel->out_pkt) {
+ PJ_LOG(1, (THISFILE, "Unable to allocate %u bytes encoding buffer",
+ channel->out_pkt_size));
+ goto err_cleanup;
+ }
+
+ /* Allocate buffer for decoding to PCM */
+ channel->pcm_buf_size = codec_attr.sample_rate *
+ codec_attr.pcm_bits_per_sample / 8 *
+ PJ_MAX_FRAME_DURATION_MS / 1000;
+ channel->pcm_buf = pj_pool_alloc (pool, channel->pcm_buf_size);
+ if (!channel->pcm_buf) {
+ PJ_LOG(1, (THISFILE, "Unable to allocate %u bytes PCM buffer",
+ channel->pcm_buf_size));
+ goto err_cleanup;
+ }
+
+ /* Allocate buffer for frames put in jitter buffer. */
+ size = codec_attr.avg_bps / 8 * PJ_MAX_BUFFER_SIZE_MS / 1000;
+ ptr = pj_pool_alloc(pool, size);
+ if (!ptr) {
+ PJ_LOG(1, (THISFILE, "Unable to allocate %u bytes jitter buffer",
+ channel->pcm_buf_size));
+ goto err_cleanup;
+ }
+ //pj_fifobuf_init (&channel->fifobuf, ptr, size);
+
+ /* Create and initialize sound device */
+ init_snd_param_from_codec_attr (&channel->snd_info, &codec_attr);
+
+ if (dir == PJ_MEDIA_DIR_ENCODING)
+ channel->snd_stream = pj_snd_open_recorder(-1, &channel->snd_info,
+ &rec_callback, channel);
+ else
+ channel->snd_stream = pj_snd_open_player(-1, &channel->snd_info,
+ &play_callback, channel);
+
+ if (!channel->snd_stream)
+ goto err_cleanup;
+
+ /* Create RTP and RTCP sessions. */
+ if (pj_rtp_session_init(&channel->rtp, codec_id->pt, param->ssrc) != 0) {
+ PJ_LOG(1, (THISFILE, "RTP session initialization error"));
+ goto err_cleanup;
+ }
+
+ /* For decoder, create RTCP session, jitter buffer, and transport thread. */
+ if (dir == PJ_MEDIA_DIR_DECODING) {
+ channel->rtcp = pj_pool_calloc(pool, 1, sizeof(pj_rtcp_session));
+ if (!channel->rtcp) {
+ PJ_LOG(1, (THISFILE, "Unable to allocate RTCP session"));
+ goto err_cleanup;
+ }
+
+ pj_rtcp_init(channel->rtcp, param->ssrc);
+
+ channel->jb = pj_pool_calloc(pool, 1, sizeof(pj_jitter_buffer));
+ if (!channel->jb) {
+ PJ_LOG(1, (THISFILE, "Unable to allocate jitter buffer descriptor"));
+ goto err_cleanup;
+ }
+ if (pj_jb_init(channel->jb, pool, param->jb_min, param->jb_max, param->jb_maxcnt)) {
+ PJ_LOG(1, (THISFILE, "Unable to allocate jitter buffer"));
+ goto err_cleanup;
+ }
+
+ channel->transport_thread = pj_thread_create(pool, "decode",
+ &stream_decoder_transport_thread, channel,
+ 0, NULL, 0);
+ if (!channel->transport_thread) {
+ pj_perror(THISFILE, "Unable to create transport thread");
+ goto err_cleanup;
+ }
+ }
+
+ /* Done. */
+ return channel;
+
+err_cleanup:
+ pj_media_stream_destroy(channel);
+ return NULL;
+}
+
+
+PJ_DEF(pj_status_t) pj_media_stream_create (pj_pool_t *pool,
+ pj_media_stream_t **enc_stream,
+ pj_media_stream_t **dec_stream,
+ pj_media_stream_create_param *param)
+{
+ *dec_stream = *enc_stream = NULL;
+
+ if (param->dir & PJ_MEDIA_DIR_DECODING) {
+ *dec_stream =
+ create_channel(pool, PJ_MEDIA_DIR_DECODING, NULL, param->codec_id, param);
+ if (!*dec_stream)
+ return -1;
+ }
+
+ if (param->dir & PJ_MEDIA_DIR_ENCODING) {
+ *enc_stream =
+ create_channel(pool, PJ_MEDIA_DIR_ENCODING, *dec_stream, param->codec_id, param);
+ if (!*enc_stream) {
+ if (*dec_stream) {
+ pj_media_stream_destroy(*dec_stream);
+ *dec_stream = NULL;
+ }
+ return -1;
+ }
+
+ if (*dec_stream) {
+ (*dec_stream)->peer = *enc_stream;
+ }
+ }
+
+ return 0;
+}
+
+PJ_DEF(pj_status_t) pj_media_stream_start (pj_media_stream_t *channel)
+{
+ pj_status_t status;
+
+ status = pj_snd_stream_start(channel->snd_stream);
+
+ if (status == 0)
+ channel->state = STREAM_STARTED;
+ return status;
+}
+
+PJ_DEF(pj_status_t) pj_media_stream_get_stat (const pj_media_stream_t *stream,
+ pj_media_stream_stat *stat)
+{
+ if (stream->dir == PJ_MEDIA_DIR_ENCODING) {
+ pj_memcpy (stat, &stream->stat, sizeof(*stat));
+ } else {
+ pj_rtcp_pkt *rtcp_pkt;
+ int len;
+
+ pj_memset (stat, 0, sizeof(*stat));
+ pj_assert (stream->rtcp != 0);
+ pj_rtcp_build_rtcp (stream->rtcp, &rtcp_pkt, &len);
+
+ stat->pkt_rx = stream->stat.pkt_rx;
+ stat->oct_rx = stream->stat.oct_rx;
+
+ PJ_TODO(SUPPORT_JITTER_CALCULATION_FOR_NON_8KHZ_SAMPLE_RATE)
+ stat->jitter = pj_ntohl(rtcp_pkt->rr.jitter) / 8;
+ stat->pkt_lost = (rtcp_pkt->rr.total_lost_2 << 16) +
+ (rtcp_pkt->rr.total_lost_1 << 8) +
+ rtcp_pkt->rr.total_lost_0;
+ }
+ return 0;
+}
+
+PJ_DEF(pj_status_t) pj_media_stream_pause (pj_media_stream_t *channel)
+{
+ PJ_UNUSED_ARG(channel)
+ return -1;
+}
+
+PJ_DEF(pj_status_t) pj_media_stream_resume (pj_media_stream_t *channel)
+{
+ PJ_UNUSED_ARG(channel)
+ return -1;
+}
+
+PJ_DEF(pj_status_t) pj_media_stream_destroy (pj_media_stream_t *channel)
+{
+ channel->thread_quit_flag = 1;
+
+ pj_mutex_lock (channel->mutex);
+ if (channel->peer)
+ pj_mutex_lock (channel->peer->mutex);
+
+ if (channel->jb) {
+ /* No need to deinitialize jitter buffer. */
+ }
+ if (channel->transport_thread) {
+ pj_thread_join(channel->transport_thread);
+ pj_thread_destroy(channel->transport_thread);
+ channel->transport_thread = NULL;
+ }
+ if (channel->snd_stream != NULL) {
+ pj_mutex_unlock (channel->mutex);
+ pj_snd_stream_stop(channel->snd_stream);
+ pj_mutex_lock (channel->mutex);
+ pj_snd_stream_close(channel->snd_stream);
+ channel->snd_stream = NULL;
+ }
+ if (channel->codec) {
+ channel->codec->op->close(channel->codec);
+ pj_codec_mgr_dealloc_codec(channel->codec_mgr, channel->codec);
+ channel->codec = NULL;
+ }
+ if (channel->peer) {
+ pj_media_stream_t *peer = channel->peer;
+ peer->peer = NULL;
+ peer->codec = NULL;
+ peer->thread_quit_flag = 1;
+ if (peer->transport_thread) {
+ pj_mutex_unlock (peer->mutex);
+ pj_thread_join(peer->transport_thread);
+ pj_mutex_lock (peer->mutex);
+ pj_thread_destroy(peer->transport_thread);
+ peer->transport_thread = NULL;
+ }
+ if (peer->snd_stream) {
+ pj_mutex_unlock (peer->mutex);
+ pj_snd_stream_stop(peer->snd_stream);
+ pj_mutex_lock (peer->mutex);
+ pj_snd_stream_close(peer->snd_stream);
+ peer->snd_stream = NULL;
+ }
+ }
+
+ channel->state = STREAM_STOPPED;
+
+ if (channel->peer)
+ pj_mutex_unlock (channel->peer->mutex);
+ pj_mutex_unlock(channel->mutex);
+ pj_mutex_destroy(channel->mutex);
+
+ return 0;
+}
+
diff --git a/pjmedia/src/pjmedia/stream.h b/pjmedia/src/pjmedia/stream.h
index d9f38a08..a9d43a2c 100644
--- a/pjmedia/src/pjmedia/stream.h
+++ b/pjmedia/src/pjmedia/stream.h
@@ -1,85 +1,106 @@
-/* $Id$
- *
- */
-
-#ifndef __PJMEDIA_STREAM_H__
-#define __PJMEDIA_STREAM_H__
-
-
-/**
- * @file stream.h
- * @brief Stream of media.
- */
-
-#include <pjmedia/sound.h>
-#include <pjmedia/codec.h>
-#include <pjmedia/mediamgr.h>
-#include <pj/sock.h>
-
-PJ_BEGIN_DECL
-
-
-/**
- * @defgroup PJMED_SES Media session
- * @ingroup PJMEDIA
- * @{
- */
-
-typedef struct pj_media_stream_t pj_media_stream_t;
-
-/** Parameter for creating channel. */
-typedef struct pj_media_stream_create_param
-{
- /** Codec ID, must NOT be NULL. */
- pj_codec_id *codec_id;
-
- /** Media manager, must NOT be NULL. */
- pj_med_mgr_t *mediamgr;
-
- /** Direction: IN_OUT, or IN only, or OUT only. */
- pj_media_dir_t dir;
-
- /** RTP socket. */
- pj_sock_t rtp_sock;
-
- /** RTCP socket. */
- pj_sock_t rtcp_sock;
-
- /** Address of remote */
- pj_sockaddr_in *remote_addr;
-
- /** RTP SSRC */
- pj_uint32_t ssrc;
-
- /** Jitter buffer parameters. */
- int jb_min, jb_max, jb_maxcnt;
-
-} pj_media_stream_create_param;
-
-typedef struct pj_media_stream_stat
-{
- pj_uint32_t pkt_tx, pkt_rx; /* packets transmitted/received */
- pj_uint32_t oct_tx, oct_rx; /* octets transmitted/received */
- pj_uint32_t jitter; /* receive jitter in ms */
- pj_uint32_t pkt_lost; /* total packet lost count */
-} pj_media_stream_stat;
-
-PJ_DECL(pj_status_t) pj_media_stream_create (pj_pool_t *pool,
- pj_media_stream_t **enc_stream,
- pj_media_stream_t **dec_stream,
- pj_media_stream_create_param *param);
-PJ_DECL(pj_status_t) pj_media_stream_start (pj_media_stream_t *stream);
-PJ_DECL(pj_status_t) pj_media_stream_get_stat (const pj_media_stream_t *stream,
- pj_media_stream_stat *stat);
-PJ_DECL(pj_status_t) pj_media_stream_pause (pj_media_stream_t *stream);
-PJ_DECL(pj_status_t) pj_media_stream_resume (pj_media_stream_t *stream);
-PJ_DECL(pj_status_t) pj_media_stream_destroy (pj_media_stream_t *stream);
-
-/**
- * @}
- */
-
-PJ_END_DECL
-
-
-#endif /* __PJMEDIA_STREAM_H__ */
+/* $Id$
+ *
+ */
+/*
+ * PJMEDIA - Multimedia over IP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef __PJMEDIA_STREAM_H__
+#define __PJMEDIA_STREAM_H__
+
+
+/**
+ * @file stream.h
+ * @brief Stream of media.
+ */
+
+#include <pjmedia/sound.h>
+#include <pjmedia/codec.h>
+#include <pjmedia/mediamgr.h>
+#include <pj/sock.h>
+
+PJ_BEGIN_DECL
+
+
+/**
+ * @defgroup PJMED_SES Media session
+ * @ingroup PJMEDIA
+ * @{
+ */
+
+typedef struct pj_media_stream_t pj_media_stream_t;
+
+/** Parameter for creating channel. */
+typedef struct pj_media_stream_create_param
+{
+ /** Codec ID, must NOT be NULL. */
+ pj_codec_id *codec_id;
+
+ /** Media manager, must NOT be NULL. */
+ pj_med_mgr_t *mediamgr;
+
+ /** Direction: IN_OUT, or IN only, or OUT only. */
+ pj_media_dir_t dir;
+
+ /** RTP socket. */
+ pj_sock_t rtp_sock;
+
+ /** RTCP socket. */
+ pj_sock_t rtcp_sock;
+
+ /** Address of remote */
+ pj_sockaddr_in *remote_addr;
+
+ /** RTP SSRC */
+ pj_uint32_t ssrc;
+
+ /** Jitter buffer parameters. */
+ int jb_min, jb_max, jb_maxcnt;
+
+} pj_media_stream_create_param;
+
+typedef struct pj_media_stream_stat
+{
+ pj_uint32_t pkt_tx, pkt_rx; /* packets transmitted/received */
+ pj_uint32_t oct_tx, oct_rx; /* octets transmitted/received */
+ pj_uint32_t jitter; /* receive jitter in ms */
+ pj_uint32_t pkt_lost; /* total packet lost count */
+} pj_media_stream_stat;
+
+PJ_DECL(pj_status_t) pj_media_stream_create (pj_pool_t *pool,
+ pj_media_stream_t **enc_stream,
+ pj_media_stream_t **dec_stream,
+ pj_media_stream_create_param *param);
+PJ_DECL(pj_status_t) pj_media_stream_start (pj_media_stream_t *stream);
+PJ_DECL(pj_status_t) pj_media_stream_get_stat (const pj_media_stream_t *stream,
+ pj_media_stream_stat *stat);
+PJ_DECL(pj_status_t) pj_media_stream_pause (pj_media_stream_t *stream);
+PJ_DECL(pj_status_t) pj_media_stream_resume (pj_media_stream_t *stream);
+PJ_DECL(pj_status_t) pj_media_stream_destroy (pj_media_stream_t *stream);
+
+/**
+ * @}
+ */
+
+PJ_END_DECL
+
+
+#endif /* __PJMEDIA_STREAM_H__ */
diff --git a/pjmedia/src/test/audio_tool.c b/pjmedia/src/test/audio_tool.c
index 8282258a..5e046b89 100644
--- a/pjmedia/src/test/audio_tool.c
+++ b/pjmedia/src/test/audio_tool.c
@@ -1,394 +1,415 @@
-/* $Id$
- *
- */
-#include <pjmedia.h>
-#include <pjlib.h>
-#include <stdio.h>
-
-#define THIS_FILE "audio_tool.c"
-
-static pj_caching_pool caching_pool;
-static pj_pool_factory *pf;
-static FILE *fhnd;
-static pj_med_mgr_t *mm;
-static pj_codec *codec;
-static pj_codec_attr cattr;
-
-
-#define WRITE_ORIGINAL_PCM 0
-#if WRITE_ORIGINAL_PCM
-static FILE *fhnd_pcm;
-#endif
-
-static char talker_sdp[] =
- "v=0\r\n"
- "o=- 0 0 IN IP4 127.0.0.1\r\n"
- "s=-\r\n"
- "c=IN IP4 127.0.0.1\r\n"
- "t=0 0\r\n"
- "m=audio 4002 RTP/AVP 0\r\n"
- "a=rtpmap:0 PCMU/8000\r\n"
- "a=sendonly\r\n";
-static char listener_sdp[] =
- "v=0\r\n"
- "o=- 0 0 IN IP4 127.0.0.1\r\n"
- "s=-\r\n"
- "c=IN IP4 127.0.0.1\r\n"
- "t=0 0\r\n"
- "m=audio 4000 RTP/AVP 0\r\n"
- "a=rtpmap:0 PCMU/8000\r\n"
- "a=recvonly\r\n";
-
-static pj_status_t play_callback(/* in */ void *user_data,
- /* in */ pj_uint32_t timestamp,
- /* out */ void *frame,
- /* out */ unsigned size)
-{
- char pkt[160];
- struct pj_audio_frame in, out;
- int frmsz = cattr.avg_bps / 8 * cattr.ptime / 1000;
-
- if (fread(pkt, frmsz, 1, fhnd) != 1) {
- puts("EOF");
- return -1;
- } else {
- in.type = PJ_AUDIO_FRAME_AUDIO;
- in.buf = pkt;
- in.size = frmsz;
- out.buf = frame;
- if (codec->op->decode (codec, &in, size, &out) != 0)
- return -1;
-
- size = out.size;
- return 0;
- }
-}
-
-static pj_status_t rec_callback( /* in */ void *user_data,
- /* in */ pj_uint32_t timestamp,
- /* in */ const void *frame,
- /* in*/ unsigned size)
-{
- char pkt[160];
- struct pj_audio_frame in, out;
- //int frmsz = cattr.avg_bps / 8 * cattr.ptime / 1000;
-
-#if WRITE_ORIGINAL_PCM
- fwrite(frame, size, 1, fhnd_pcm);
-#endif
-
- in.type = PJ_AUDIO_FRAME_AUDIO;
- in.buf = (void*)frame;
- in.size = size;
- out.buf = pkt;
-
- if (codec->op->encode(codec, &in, sizeof(pkt), &out) != 0)
- return -1;
-
- if (fwrite(pkt, out.size, 1, fhnd) != 1)
- return -1;
- return 0;
-}
-
-static pj_status_t init()
-{
- pj_codec_mgr *cm;
- pj_codec_id id;
- int i;
-
- pj_caching_pool_init(&caching_pool, &pj_pool_factory_default_policy, 0);
- pf = &caching_pool.factory;
-
- if (pj_snd_init(&caching_pool.factory))
- return -1;
-
- PJ_LOG(3,(THIS_FILE, "Dumping audio devices:"));
- for (i=0; i<pj_snd_get_dev_count(); ++i) {
- const pj_snd_dev_info *info;
- info = pj_snd_get_dev_info(i);
- PJ_LOG(3,(THIS_FILE, " %d: %s\t(%d in, %d out",
- i, info->name,
- info->input_count, info->output_count));
- }
-
- mm = pj_med_mgr_create (&caching_pool.factory);
- cm = pj_med_mgr_get_codec_mgr (mm);
-
- id.type = PJ_MEDIA_TYPE_AUDIO;
- id.pt = 0;
- id.encoding_name = pj_str("PCMU");
- id.sample_rate = 8000;
-
- codec = pj_codec_mgr_alloc_codec (cm, &id);
- codec->op->default_attr(codec, &cattr);
- codec->op->open(codec, &cattr);
- return 0;
-}
-
-static pj_status_t deinit()
-{
- pj_codec_mgr *cm;
- cm = pj_med_mgr_get_codec_mgr (mm);
- codec->op->close(codec);
- pj_codec_mgr_dealloc_codec (cm, codec);
- pj_med_mgr_destroy (mm);
- pj_caching_pool_destroy(&caching_pool);
- return 0;
-}
-
-static pj_status_t record_file (const char *filename)
-{
- pj_snd_stream *stream;
- pj_snd_stream_info info;
- int status;
- char s[10];
-
- printf("Recording to file %s...\n", filename);
-
- fhnd = fopen(filename, "wb");
- if (!fhnd)
- return -1;
-
-#if WRITE_ORIGINAL_PCM
- fhnd_pcm = fopen("ORIGINAL.PCM", "wb");
- if (!fhnd_pcm)
- return -1;
-#endif
-
- pj_memset(&info, 0, sizeof(info));
- info.bits_per_sample = 16;
- info.bytes_per_frame = 2;
- info.frames_per_packet = 160;
- info.samples_per_frame = 1;
- info.samples_per_sec = 8000;
-
- stream = pj_snd_open_recorder(-1, &info, &rec_callback, NULL);
- if (!stream)
- return -1;
-
- status = pj_snd_stream_start(stream);
- if (status != 0)
- goto on_error;
-
- puts("Press <ENTER> to exit recording");
- fgets(s, sizeof(s), stdin);
-
- pj_snd_stream_stop(stream);
- pj_snd_stream_close(stream);
-
-#if WRITE_ORIGINAL_PCM
- fclose(fhnd_pcm);
-#endif
- fclose(fhnd);
- return 0;
-
-on_error:
- pj_snd_stream_stop(stream);
- pj_snd_stream_close(stream);
- return -1;
-}
-
-
-static pj_status_t play_file (const char *filename)
-{
- pj_snd_stream *stream;
- pj_snd_stream_info info;
- int status;
- char s[10];
-
- printf("Playing file %s...\n", filename);
-
- fhnd = fopen(filename, "rb");
- if (!fhnd)
- return -1;
-
- pj_memset(&info, 0, sizeof(info));
- info.bits_per_sample = 16;
- info.bytes_per_frame = 2;
- info.frames_per_packet = 160;
- info.samples_per_frame = 1;
- info.samples_per_sec = 8000;
-
- stream = pj_snd_open_player(-1, &info, &play_callback, NULL);
- if (!stream)
- return -1;
-
- status = pj_snd_stream_start(stream);
- if (status != 0)
- goto on_error;
-
- puts("Press <ENTER> to exit playing");
- fgets(s, sizeof(s), stdin);
-
- pj_snd_stream_stop(stream);
- pj_snd_stream_close(stream);
-
- fclose(fhnd);
- return 0;
-
-on_error:
- pj_snd_stream_stop(stream);
- pj_snd_stream_close(stream);
- return -1;
-}
-
-static int create_ses_by_remote_sdp(int local_port, char *sdp)
-{
- pj_media_session_t *ses = NULL;
- pjsdp_session_desc *sdp_ses;
- pj_media_sock_info skinfo;
- pj_pool_t *pool;
- char s[4];
- const pj_media_stream_info *info[2];
- int i, count;
-
- pool = pj_pool_create(pf, "sdp", 1024, 0, NULL);
- if (!pool) {
- PJ_LOG(1,(THIS_FILE, "Unable to create pool"));
- return -1;
- }
-
- pj_memset(&skinfo, 0, sizeof(skinfo));
- skinfo.rtp_sock = skinfo.rtcp_sock = pj_sock_socket(PJ_AF_INET, PJ_SOCK_DGRAM, 0, 0);
- if (skinfo.rtp_sock == PJ_INVALID_SOCKET) {
- PJ_LOG(1,(THIS_FILE, "Unable to create socket"));
- goto on_error;
- }
-
- pj_sockaddr_init2(&skinfo.rtp_addr_name, "0.0.0.0", local_port);
- if (pj_sock_bind(skinfo.rtp_sock, (struct pj_sockaddr*)&skinfo.rtp_addr_name, sizeof(pj_sockaddr_in)) != 0) {
- PJ_LOG(1,(THIS_FILE, "Unable to bind socket"));
- goto on_error;
- }
-
- sdp_ses = pjsdp_parse(sdp, strlen(sdp), pool);
- if (!sdp_ses) {
- PJ_LOG(1,(THIS_FILE, "Error parsing SDP"));
- goto on_error;
- }
-
- ses = pj_media_session_create_from_sdp(mm, sdp_ses, &skinfo);
- if (!ses) {
- PJ_LOG(1,(THIS_FILE, "Unable to create session from SDP"));
- goto on_error;
- }
-
- if (pj_media_session_activate(ses) != 0) {
- PJ_LOG(1,(THIS_FILE, "Error activating session"));
- goto on_error;
- }
-
- count = pj_media_session_enum_streams(ses, 2, info);
- printf("\nDumping streams: \n");
- for (i=0; i<count; ++i) {
- const char *dir;
- char *local_ip;
-
- switch (info[i]->dir) {
- case PJ_MEDIA_DIR_NONE:
- dir = "- NONE -"; break;
- case PJ_MEDIA_DIR_ENCODING:
- dir = "SENDONLY"; break;
- case PJ_MEDIA_DIR_DECODING:
- dir = "RECVONLY"; break;
- case PJ_MEDIA_DIR_ENCODING_DECODING:
- dir = "SENDRECV"; break;
- default:
- dir = "?UNKNOWN"; break;
- }
-
- local_ip = pj_sockaddr_get_str_addr(&info[i]->sock_info.rtp_addr_name);
-
- printf(" Stream %d: %.*s %s local=%s:%d remote=%.*s:%d\n",
- i, info[i]->type.slen, info[i]->type.ptr,
- dir,
- local_ip, pj_sockaddr_get_port(&info[i]->sock_info.rtp_addr_name),
- info[i]->rem_addr.slen, info[i]->rem_addr.ptr, info[i]->rem_port);
- }
-
- puts("Press <ENTER> to quit");
- fgets(s, sizeof(s), stdin);
-
- pj_media_session_destroy(ses);
- pj_sock_close(skinfo.rtp_sock);
- pj_pool_release(pool);
-
- return 0;
-
-on_error:
- if (ses)
- pj_media_session_destroy(ses);
- if (skinfo.rtp_sock != PJ_INVALID_SOCKET)
- pj_sock_close(skinfo.rtp_sock);
- if (pool)
- pj_pool_release(pool);
- return -1;
-}
-
-#if WRITE_ORIGINAL_PCM
-static pj_status_t convert(const char *src, const char *dst)
-{
- char pcm[320];
- char frame[160];
- struct pj_audio_frame in, out;
-
- fhnd_pcm = fopen(src, "rb");
- if (!fhnd_pcm)
- return -1;
- fhnd = fopen(dst, "wb");
- if (!fhnd)
- return -1;
-
- while (fread(pcm, 320, 1, fhnd_pcm) == 1) {
-
- in.type = PJ_AUDIO_FRAME_AUDIO;
- in.buf = pcm;
- in.size = 320;
- out.buf = frame;
-
- if (codec->op->encode(codec, &in, 160, &out) != 0)
- break;
-
- if (fwrite(frame, out.size, 1, fhnd) != 1)
- break;
-
- }
-
- fclose(fhnd);
- fclose(fhnd_pcm);
- return 0;
-}
-#endif
-
-static void usage(const char *exe)
-{
- printf("Usage: %s <command> <file>\n", exe);
- puts("where:");
- puts(" <command> play|record|send|recv");
-}
-
-int main(int argc, char *argv[])
-{
- if (argc < 2) {
- usage(argv[0]);
- return 1;
- }
-
- pj_init();
-
- init();
-
- if (stricmp(argv[1], "record")==0) {
- record_file("FILE.PCM");
- } else if (stricmp(argv[1], "play")==0) {
- play_file("FILE.PCM");
- } else if (stricmp(argv[1], "send")==0) {
- create_ses_by_remote_sdp(4002, listener_sdp);
- } else if (stricmp(argv[1], "recv")==0) {
- create_ses_by_remote_sdp(4000, talker_sdp);
- } else {
- usage(argv[0]);
- }
- deinit();
- return 0;
-}
+/* $Id$
+ *
+ */
+/*
+ * PJMEDIA - Multimedia over IP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include <pjmedia.h>
+#include <pjlib.h>
+#include <stdio.h>
+
+#define THIS_FILE "audio_tool.c"
+
+static pj_caching_pool caching_pool;
+static pj_pool_factory *pf;
+static FILE *fhnd;
+static pj_med_mgr_t *mm;
+static pj_codec *codec;
+static pj_codec_attr cattr;
+
+
+#define WRITE_ORIGINAL_PCM 0
+#if WRITE_ORIGINAL_PCM
+static FILE *fhnd_pcm;
+#endif
+
+static char talker_sdp[] =
+ "v=0\r\n"
+ "o=- 0 0 IN IP4 127.0.0.1\r\n"
+ "s=-\r\n"
+ "c=IN IP4 127.0.0.1\r\n"
+ "t=0 0\r\n"
+ "m=audio 4002 RTP/AVP 0\r\n"
+ "a=rtpmap:0 PCMU/8000\r\n"
+ "a=sendonly\r\n";
+static char listener_sdp[] =
+ "v=0\r\n"
+ "o=- 0 0 IN IP4 127.0.0.1\r\n"
+ "s=-\r\n"
+ "c=IN IP4 127.0.0.1\r\n"
+ "t=0 0\r\n"
+ "m=audio 4000 RTP/AVP 0\r\n"
+ "a=rtpmap:0 PCMU/8000\r\n"
+ "a=recvonly\r\n";
+
+static pj_status_t play_callback(/* in */ void *user_data,
+ /* in */ pj_uint32_t timestamp,
+ /* out */ void *frame,
+ /* out */ unsigned size)
+{
+ char pkt[160];
+ struct pj_audio_frame in, out;
+ int frmsz = cattr.avg_bps / 8 * cattr.ptime / 1000;
+
+ if (fread(pkt, frmsz, 1, fhnd) != 1) {
+ puts("EOF");
+ return -1;
+ } else {
+ in.type = PJ_AUDIO_FRAME_AUDIO;
+ in.buf = pkt;
+ in.size = frmsz;
+ out.buf = frame;
+ if (codec->op->decode (codec, &in, size, &out) != 0)
+ return -1;
+
+ size = out.size;
+ return 0;
+ }
+}
+
+static pj_status_t rec_callback( /* in */ void *user_data,
+ /* in */ pj_uint32_t timestamp,
+ /* in */ const void *frame,
+ /* in*/ unsigned size)
+{
+ char pkt[160];
+ struct pj_audio_frame in, out;
+ //int frmsz = cattr.avg_bps / 8 * cattr.ptime / 1000;
+
+#if WRITE_ORIGINAL_PCM
+ fwrite(frame, size, 1, fhnd_pcm);
+#endif
+
+ in.type = PJ_AUDIO_FRAME_AUDIO;
+ in.buf = (void*)frame;
+ in.size = size;
+ out.buf = pkt;
+
+ if (codec->op->encode(codec, &in, sizeof(pkt), &out) != 0)
+ return -1;
+
+ if (fwrite(pkt, out.size, 1, fhnd) != 1)
+ return -1;
+ return 0;
+}
+
+static pj_status_t init()
+{
+ pj_codec_mgr *cm;
+ pj_codec_id id;
+ int i;
+
+ pj_caching_pool_init(&caching_pool, &pj_pool_factory_default_policy, 0);
+ pf = &caching_pool.factory;
+
+ if (pj_snd_init(&caching_pool.factory))
+ return -1;
+
+ PJ_LOG(3,(THIS_FILE, "Dumping audio devices:"));
+ for (i=0; i<pj_snd_get_dev_count(); ++i) {
+ const pj_snd_dev_info *info;
+ info = pj_snd_get_dev_info(i);
+ PJ_LOG(3,(THIS_FILE, " %d: %s\t(%d in, %d out",
+ i, info->name,
+ info->input_count, info->output_count));
+ }
+
+ mm = pj_med_mgr_create (&caching_pool.factory);
+ cm = pj_med_mgr_get_codec_mgr (mm);
+
+ id.type = PJ_MEDIA_TYPE_AUDIO;
+ id.pt = 0;
+ id.encoding_name = pj_str("PCMU");
+ id.sample_rate = 8000;
+
+ codec = pj_codec_mgr_alloc_codec (cm, &id);
+ codec->op->default_attr(codec, &cattr);
+ codec->op->open(codec, &cattr);
+ return 0;
+}
+
+static pj_status_t deinit()
+{
+ pj_codec_mgr *cm;
+ cm = pj_med_mgr_get_codec_mgr (mm);
+ codec->op->close(codec);
+ pj_codec_mgr_dealloc_codec (cm, codec);
+ pj_med_mgr_destroy (mm);
+ pj_caching_pool_destroy(&caching_pool);
+ return 0;
+}
+
+static pj_status_t record_file (const char *filename)
+{
+ pj_snd_stream *stream;
+ pj_snd_stream_info info;
+ int status;
+ char s[10];
+
+ printf("Recording to file %s...\n", filename);
+
+ fhnd = fopen(filename, "wb");
+ if (!fhnd)
+ return -1;
+
+#if WRITE_ORIGINAL_PCM
+ fhnd_pcm = fopen("ORIGINAL.PCM", "wb");
+ if (!fhnd_pcm)
+ return -1;
+#endif
+
+ pj_memset(&info, 0, sizeof(info));
+ info.bits_per_sample = 16;
+ info.bytes_per_frame = 2;
+ info.frames_per_packet = 160;
+ info.samples_per_frame = 1;
+ info.samples_per_sec = 8000;
+
+ stream = pj_snd_open_recorder(-1, &info, &rec_callback, NULL);
+ if (!stream)
+ return -1;
+
+ status = pj_snd_stream_start(stream);
+ if (status != 0)
+ goto on_error;
+
+ puts("Press <ENTER> to exit recording");
+ fgets(s, sizeof(s), stdin);
+
+ pj_snd_stream_stop(stream);
+ pj_snd_stream_close(stream);
+
+#if WRITE_ORIGINAL_PCM
+ fclose(fhnd_pcm);
+#endif
+ fclose(fhnd);
+ return 0;
+
+on_error:
+ pj_snd_stream_stop(stream);
+ pj_snd_stream_close(stream);
+ return -1;
+}
+
+
+static pj_status_t play_file (const char *filename)
+{
+ pj_snd_stream *stream;
+ pj_snd_stream_info info;
+ int status;
+ char s[10];
+
+ printf("Playing file %s...\n", filename);
+
+ fhnd = fopen(filename, "rb");
+ if (!fhnd)
+ return -1;
+
+ pj_memset(&info, 0, sizeof(info));
+ info.bits_per_sample = 16;
+ info.bytes_per_frame = 2;
+ info.frames_per_packet = 160;
+ info.samples_per_frame = 1;
+ info.samples_per_sec = 8000;
+
+ stream = pj_snd_open_player(-1, &info, &play_callback, NULL);
+ if (!stream)
+ return -1;
+
+ status = pj_snd_stream_start(stream);
+ if (status != 0)
+ goto on_error;
+
+ puts("Press <ENTER> to exit playing");
+ fgets(s, sizeof(s), stdin);
+
+ pj_snd_stream_stop(stream);
+ pj_snd_stream_close(stream);
+
+ fclose(fhnd);
+ return 0;
+
+on_error:
+ pj_snd_stream_stop(stream);
+ pj_snd_stream_close(stream);
+ return -1;
+}
+
+static int create_ses_by_remote_sdp(int local_port, char *sdp)
+{
+ pj_media_session_t *ses = NULL;
+ pjsdp_session_desc *sdp_ses;
+ pj_media_sock_info skinfo;
+ pj_pool_t *pool;
+ char s[4];
+ const pj_media_stream_info *info[2];
+ int i, count;
+
+ pool = pj_pool_create(pf, "sdp", 1024, 0, NULL);
+ if (!pool) {
+ PJ_LOG(1,(THIS_FILE, "Unable to create pool"));
+ return -1;
+ }
+
+ pj_memset(&skinfo, 0, sizeof(skinfo));
+ skinfo.rtp_sock = skinfo.rtcp_sock = pj_sock_socket(PJ_AF_INET, PJ_SOCK_DGRAM, 0, 0);
+ if (skinfo.rtp_sock == PJ_INVALID_SOCKET) {
+ PJ_LOG(1,(THIS_FILE, "Unable to create socket"));
+ goto on_error;
+ }
+
+ pj_sockaddr_init2(&skinfo.rtp_addr_name, "0.0.0.0", local_port);
+ if (pj_sock_bind(skinfo.rtp_sock, (struct pj_sockaddr*)&skinfo.rtp_addr_name, sizeof(pj_sockaddr_in)) != 0) {
+ PJ_LOG(1,(THIS_FILE, "Unable to bind socket"));
+ goto on_error;
+ }
+
+ sdp_ses = pjsdp_parse(sdp, strlen(sdp), pool);
+ if (!sdp_ses) {
+ PJ_LOG(1,(THIS_FILE, "Error parsing SDP"));
+ goto on_error;
+ }
+
+ ses = pj_media_session_create_from_sdp(mm, sdp_ses, &skinfo);
+ if (!ses) {
+ PJ_LOG(1,(THIS_FILE, "Unable to create session from SDP"));
+ goto on_error;
+ }
+
+ if (pj_media_session_activate(ses) != 0) {
+ PJ_LOG(1,(THIS_FILE, "Error activating session"));
+ goto on_error;
+ }
+
+ count = pj_media_session_enum_streams(ses, 2, info);
+ printf("\nDumping streams: \n");
+ for (i=0; i<count; ++i) {
+ const char *dir;
+ char *local_ip;
+
+ switch (info[i]->dir) {
+ case PJ_MEDIA_DIR_NONE:
+ dir = "- NONE -"; break;
+ case PJ_MEDIA_DIR_ENCODING:
+ dir = "SENDONLY"; break;
+ case PJ_MEDIA_DIR_DECODING:
+ dir = "RECVONLY"; break;
+ case PJ_MEDIA_DIR_ENCODING_DECODING:
+ dir = "SENDRECV"; break;
+ default:
+ dir = "?UNKNOWN"; break;
+ }
+
+ local_ip = pj_sockaddr_get_str_addr(&info[i]->sock_info.rtp_addr_name);
+
+ printf(" Stream %d: %.*s %s local=%s:%d remote=%.*s:%d\n",
+ i, info[i]->type.slen, info[i]->type.ptr,
+ dir,
+ local_ip, pj_sockaddr_get_port(&info[i]->sock_info.rtp_addr_name),
+ info[i]->rem_addr.slen, info[i]->rem_addr.ptr, info[i]->rem_port);
+ }
+
+ puts("Press <ENTER> to quit");
+ fgets(s, sizeof(s), stdin);
+
+ pj_media_session_destroy(ses);
+ pj_sock_close(skinfo.rtp_sock);
+ pj_pool_release(pool);
+
+ return 0;
+
+on_error:
+ if (ses)
+ pj_media_session_destroy(ses);
+ if (skinfo.rtp_sock != PJ_INVALID_SOCKET)
+ pj_sock_close(skinfo.rtp_sock);
+ if (pool)
+ pj_pool_release(pool);
+ return -1;
+}
+
+#if WRITE_ORIGINAL_PCM
+static pj_status_t convert(const char *src, const char *dst)
+{
+ char pcm[320];
+ char frame[160];
+ struct pj_audio_frame in, out;
+
+ fhnd_pcm = fopen(src, "rb");
+ if (!fhnd_pcm)
+ return -1;
+ fhnd = fopen(dst, "wb");
+ if (!fhnd)
+ return -1;
+
+ while (fread(pcm, 320, 1, fhnd_pcm) == 1) {
+
+ in.type = PJ_AUDIO_FRAME_AUDIO;
+ in.buf = pcm;
+ in.size = 320;
+ out.buf = frame;
+
+ if (codec->op->encode(codec, &in, 160, &out) != 0)
+ break;
+
+ if (fwrite(frame, out.size, 1, fhnd) != 1)
+ break;
+
+ }
+
+ fclose(fhnd);
+ fclose(fhnd_pcm);
+ return 0;
+}
+#endif
+
+static void usage(const char *exe)
+{
+ printf("Usage: %s <command> <file>\n", exe);
+ puts("where:");
+ puts(" <command> play|record|send|recv");
+}
+
+int main(int argc, char *argv[])
+{
+ if (argc < 2) {
+ usage(argv[0]);
+ return 1;
+ }
+
+ pj_init();
+
+ init();
+
+ if (stricmp(argv[1], "record")==0) {
+ record_file("FILE.PCM");
+ } else if (stricmp(argv[1], "play")==0) {
+ play_file("FILE.PCM");
+ } else if (stricmp(argv[1], "send")==0) {
+ create_ses_by_remote_sdp(4002, listener_sdp);
+ } else if (stricmp(argv[1], "recv")==0) {
+ create_ses_by_remote_sdp(4000, talker_sdp);
+ } else {
+ usage(argv[0]);
+ }
+ deinit();
+ return 0;
+}
diff --git a/pjmedia/src/test/jbuf_test.c b/pjmedia/src/test/jbuf_test.c
index d2ca95a5..e5c5eeaa 100644
--- a/pjmedia/src/test/jbuf_test.c
+++ b/pjmedia/src/test/jbuf_test.c
@@ -1,139 +1,160 @@
-/* $Id$
- *
- */
-#include <stdio.h>
-#include <ctype.h>
-#include <pjmedia/jbuf.h>
-#include <pj/pool.h>
-
-#define JB_MIN 1
-#define JB_MAX 8
-#define JB_BUF_SIZE 10
-
-#define REPORT
-//#define PRINT_COMMENT
-
-int jbuf_main(pj_pool_factory *pf)
-{
- pj_jitter_buffer jb;
- FILE *input = fopen("JBTEST.DAT", "rt");
- unsigned lastseq;
- void *data = "Hello world";
- char line[1024], *p;
- int lastget = 0, lastput = 0;
- pj_pool_t *pool;
-
- pj_init();
- pool = pj_pool_create(pf, "JBPOOL", 256*16, 256*16, NULL);
-
- pj_jb_init(&jb, pool, JB_MIN, JB_MAX, JB_BUF_SIZE);
-
- lastseq = 1;
-
- while ((p=fgets(line, sizeof(line), input)) != NULL) {
-
- while (*p && isspace(*p))
- ++p;
-
- if (!*p)
- continue;
-
- if (*p == '#') {
-#ifdef PRINT_COMMENT
- printf("\n%s", p);
-#endif
- continue;
- }
-
- pj_jb_reset(&jb);
-#ifdef REPORT
- printf( "Initial\t%c size=%d prefetch=%d level=%d\n",
- ' ', jb.lst.count, jb.prefetch, jb.level);
-#endif
-
- while (*p) {
- int c;
- unsigned seq = 0;
- void *thedata;
- int status = 1234;
-
- c = *p++;
- if (isspace(c))
- continue;
-
- if (c == '/') {
- char *end;
-
- printf("/*");
-
- do {
- putchar(*++p);
- } while (*p != '/');
-
- putchar('\n');
-
- c = *++p;
- end = p;
-
- }
-
- if (isspace(c))
- continue;
-
- if (isdigit(c)) {
- seq = c - '0';
- while (*p) {
- c = *p++;
-
- if (isspace(c))
- continue;
-
- if (!isdigit(c))
- break;
-
- seq = seq * 10 + c - '0';
- }
- }
-
- if (!*p)
- break;
-
- switch (toupper(c)) {
- case 'G':
- seq = -1;
- status = pj_jb_get(&jb, &seq, &thedata);
- lastget = seq;
- break;
- case 'P':
- if (seq == 0)
- seq = lastseq++;
- else
- lastseq = seq;
- status = pj_jb_put(&jb, seq, data);
- if (status == 0)
- lastput = seq;
- break;
- default:
- printf("Unknown character '%c'\n", c);
- break;
- }
-
-#ifdef REPORT
- printf("seq=%d\t%c rc=%d\tsize=%d\tpfch=%d\tlvl=%d\tmxl=%d\tdelay=%d\n",
- seq, toupper(c), status, jb.lst.count, jb.prefetch, jb.level, jb.max_level,
- (lastget>0 && lastput>0) ? lastput-lastget : -1);
-#endif
- }
- }
-
-#ifdef REPORT
- printf("0\t%c size=%d prefetch=%d level=%d\n",
- ' ', jb.lst.count, jb.prefetch, jb.level);
-#endif
-
- if (input != stdin)
- fclose(input);
-
- pj_pool_release(pool);
- return 0;
-}
+/* $Id$
+ *
+ */
+/*
+ * PJMEDIA - Multimedia over IP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include <stdio.h>
+#include <ctype.h>
+#include <pjmedia/jbuf.h>
+#include <pj/pool.h>
+
+#define JB_MIN 1
+#define JB_MAX 8
+#define JB_BUF_SIZE 10
+
+#define REPORT
+//#define PRINT_COMMENT
+
+int jbuf_main(pj_pool_factory *pf)
+{
+ pj_jitter_buffer jb;
+ FILE *input = fopen("JBTEST.DAT", "rt");
+ unsigned lastseq;
+ void *data = "Hello world";
+ char line[1024], *p;
+ int lastget = 0, lastput = 0;
+ pj_pool_t *pool;
+
+ pj_init();
+ pool = pj_pool_create(pf, "JBPOOL", 256*16, 256*16, NULL);
+
+ pj_jb_init(&jb, pool, JB_MIN, JB_MAX, JB_BUF_SIZE);
+
+ lastseq = 1;
+
+ while ((p=fgets(line, sizeof(line), input)) != NULL) {
+
+ while (*p && isspace(*p))
+ ++p;
+
+ if (!*p)
+ continue;
+
+ if (*p == '#') {
+#ifdef PRINT_COMMENT
+ printf("\n%s", p);
+#endif
+ continue;
+ }
+
+ pj_jb_reset(&jb);
+#ifdef REPORT
+ printf( "Initial\t%c size=%d prefetch=%d level=%d\n",
+ ' ', jb.lst.count, jb.prefetch, jb.level);
+#endif
+
+ while (*p) {
+ int c;
+ unsigned seq = 0;
+ void *thedata;
+ int status = 1234;
+
+ c = *p++;
+ if (isspace(c))
+ continue;
+
+ if (c == '/') {
+ char *end;
+
+ printf("/*");
+
+ do {
+ putchar(*++p);
+ } while (*p != '/');
+
+ putchar('\n');
+
+ c = *++p;
+ end = p;
+
+ }
+
+ if (isspace(c))
+ continue;
+
+ if (isdigit(c)) {
+ seq = c - '0';
+ while (*p) {
+ c = *p++;
+
+ if (isspace(c))
+ continue;
+
+ if (!isdigit(c))
+ break;
+
+ seq = seq * 10 + c - '0';
+ }
+ }
+
+ if (!*p)
+ break;
+
+ switch (toupper(c)) {
+ case 'G':
+ seq = -1;
+ status = pj_jb_get(&jb, &seq, &thedata);
+ lastget = seq;
+ break;
+ case 'P':
+ if (seq == 0)
+ seq = lastseq++;
+ else
+ lastseq = seq;
+ status = pj_jb_put(&jb, seq, data);
+ if (status == 0)
+ lastput = seq;
+ break;
+ default:
+ printf("Unknown character '%c'\n", c);
+ break;
+ }
+
+#ifdef REPORT
+ printf("seq=%d\t%c rc=%d\tsize=%d\tpfch=%d\tlvl=%d\tmxl=%d\tdelay=%d\n",
+ seq, toupper(c), status, jb.lst.count, jb.prefetch, jb.level, jb.max_level,
+ (lastget>0 && lastput>0) ? lastput-lastget : -1);
+#endif
+ }
+ }
+
+#ifdef REPORT
+ printf("0\t%c size=%d prefetch=%d level=%d\n",
+ ' ', jb.lst.count, jb.prefetch, jb.level);
+#endif
+
+ if (input != stdin)
+ fclose(input);
+
+ pj_pool_release(pool);
+ return 0;
+}
diff --git a/pjmedia/src/test/main.c b/pjmedia/src/test/main.c
index c6aeb9b2..06eedf70 100644
--- a/pjmedia/src/test/main.c
+++ b/pjmedia/src/test/main.c
@@ -1,27 +1,48 @@
-/* $Id$
- *
- */
-#include <pj/os.h>
-#include <pj/pool.h>
-#include <pjmedia/sound.h>
-
-pj_status_t session_test (pj_pool_factory *pf);
-pj_status_t rtp_test (pj_pool_factory *pf);
-pj_status_t sdp_test(pj_pool_factory *pf);
-int jbuf_main(pj_pool_factory *pf);
-
-int main()
-{
- pj_caching_pool caching_pool;
-
- pj_init();
- pj_caching_pool_init(&caching_pool, &pj_pool_factory_default_policy, 0);
-
- sdp_test (&caching_pool.factory);
- rtp_test(&caching_pool.factory);
- session_test (&caching_pool.factory);
- //jbuf_main(&caching_pool.factory);
-
- pj_caching_pool_destroy(&caching_pool);
- return 0;
-}
+/* $Id$
+ *
+ */
+/*
+ * PJMEDIA - Multimedia over IP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include <pj/os.h>
+#include <pj/pool.h>
+#include <pjmedia/sound.h>
+
+pj_status_t session_test (pj_pool_factory *pf);
+pj_status_t rtp_test (pj_pool_factory *pf);
+pj_status_t sdp_test(pj_pool_factory *pf);
+int jbuf_main(pj_pool_factory *pf);
+
+int main()
+{
+ pj_caching_pool caching_pool;
+
+ pj_init();
+ pj_caching_pool_init(&caching_pool, &pj_pool_factory_default_policy, 0);
+
+ sdp_test (&caching_pool.factory);
+ rtp_test(&caching_pool.factory);
+ session_test (&caching_pool.factory);
+ //jbuf_main(&caching_pool.factory);
+
+ pj_caching_pool_destroy(&caching_pool);
+ return 0;
+}
diff --git a/pjmedia/src/test/rtp_test.c b/pjmedia/src/test/rtp_test.c
index 702c6f32..d88d63a4 100644
--- a/pjmedia/src/test/rtp_test.c
+++ b/pjmedia/src/test/rtp_test.c
@@ -1,22 +1,43 @@
-/* $Id$
- *
- */
-#include <pjmedia/rtp.h>
-#include <stdio.h>
-
-int rtp_test()
-{
- pj_rtp_session rtp;
- FILE *fhnd = fopen("RTP.DAT", "wb");
- const void *rtphdr;
- int hdrlen;
-
- if (!fhnd)
- return -1;
-
- pj_rtp_session_init (&rtp, 4, 0x12345678);
- pj_rtp_encode_rtp (&rtp, 4, 0, 0, 160, &rtphdr, &hdrlen);
- fwrite (rtphdr, hdrlen, 1, fhnd);
- fclose(fhnd);
- return 0;
-}
+/* $Id$
+ *
+ */
+/*
+ * PJMEDIA - Multimedia over IP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include <pjmedia/rtp.h>
+#include <stdio.h>
+
+int rtp_test()
+{
+ pj_rtp_session rtp;
+ FILE *fhnd = fopen("RTP.DAT", "wb");
+ const void *rtphdr;
+ int hdrlen;
+
+ if (!fhnd)
+ return -1;
+
+ pj_rtp_session_init (&rtp, 4, 0x12345678);
+ pj_rtp_encode_rtp (&rtp, 4, 0, 0, 160, &rtphdr, &hdrlen);
+ fwrite (rtphdr, hdrlen, 1, fhnd);
+ fclose(fhnd);
+ return 0;
+}
diff --git a/pjmedia/src/test/sdptest.c b/pjmedia/src/test/sdptest.c
index 1b80e518..07c9a06b 100644
--- a/pjmedia/src/test/sdptest.c
+++ b/pjmedia/src/test/sdptest.c
@@ -1,106 +1,127 @@
-/* $Id$
- *
- */
-#include <pjmedia/sdp.h>
-#include <pj/os.h>
-#include <pj/pool.h>
-#include <stdio.h>
-#include <string.h>
-
-static char *sdp[] = {
- /*
- "v=0\r\n"
- "o=mhandley 2890844526 2890842807 IN IP4 126.16.64.4\r\n"
- "s=SDP Seminar\r\n"
- "i=A Seminar on the session description protocol\r\n"
- "u=http://www.cs.ucl.ac.uk/staff/M.Handley/sdp.03.ps\r\n"
- "e=mjh@isi.edu (Mark Handley)\r\n"
- "c=IN IP4 224.2.17.12/127\r\n"
- "t=2873397496 2873404696\r\n"
- "a=recvonly\r\n"
- "m=audio 49170 RTP/AVP 0\r\n"
- "m=video 51372 RTP/AVP 31\r\n"
- "m=application 32416 udp wb\r\n"
- "a=orient:portrait\r\n"
- "m=audio 49230 RTP/AVP 96 97 98\r\n"
- "a=rtpmap:96 L8/8000\r\n"
- "a=rtpmap:97 L16/8000\r\n"
- "a=rtpmap:98 L16/11025/2\r\n",
- */
- "v=0\r\n"
- "o=usera 2890844526 2890844527 IN IP4 alice.example.com\r\n"
- "s=\r\n"
- "c=IN IP4 alice.example.com\r\n"
- "t=0 0\r\n"
- "m=message 7394 msrp/tcp *\r\n"
- "a=accept-types: message/cpim text/plain text/html\r\n"
- "a=path:msrp://alice.example.com:7394/2s93i9;tcp\r\n"
-};
-
-static int sdp_perform_test(pj_pool_factory *pf)
-{
- pj_pool_t *pool;
- int inputlen, len;
- pjsdp_session_desc *ses;
- char buf[1500];
- enum { LOOP=1000000 };
- int i;
- pj_time_val start, end;
-
- printf("Parsing and printing %d SDP messages..\n", LOOP);
-
- pool = pj_pool_create(pf, "", 4096, 0, NULL);
- inputlen = strlen(sdp[0]);
- pj_gettimeofday(&start);
- for (i=0; i<LOOP; ++i) {
- ses = pjsdp_parse(sdp[0], inputlen, pool);
- len = pjsdp_print(ses, buf, sizeof(buf));
- buf[len] = '\0';
- pj_pool_reset(pool);
- }
- pj_gettimeofday(&end);
-
- printf("Original:\n%s\n", sdp[0]);
- printf("Parsed:\n%s\n", buf);
-
- PJ_TIME_VAL_SUB(end, start);
- printf("Time: %ld:%03lds\n", end.sec, end.msec);
-
- if (end.msec==0 && end.sec==0) end.msec=1;
- printf("Performance: %ld msg/sec\n", LOOP*1000/PJ_TIME_VAL_MSEC(end));
- puts("");
-
- pj_pool_release(pool);
- return 0;
-}
-
-static int sdp_conform_test(pj_pool_factory *pf)
-{
- pj_pool_t *pool;
- pjsdp_session_desc *ses;
- int i, len;
- char buf[1500];
-
- for (i=0; i<sizeof(sdp)/sizeof(sdp[0]); ++i) {
- pool = pj_pool_create(pf, "sdp", 4096, 0, NULL);
- ses = pjsdp_parse(sdp[i], strlen(sdp[i]), pool);
- len = pjsdp_print(ses, buf, sizeof(buf));
- buf[len] = '\0';
- printf("%s\n", buf);
- pj_pool_release(pool);
- }
-
- return 0;
-}
-
-pj_status_t sdp_test(pj_pool_factory *pf)
-{
- if (sdp_conform_test(pf) != 0)
- return -2;
-
- if (sdp_perform_test(pf) != 0)
- return -3;
-
- return 0;
-}
-
+/* $Id$
+ *
+ */
+/*
+ * PJMEDIA - Multimedia over IP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include <pjmedia/sdp.h>
+#include <pj/os.h>
+#include <pj/pool.h>
+#include <stdio.h>
+#include <string.h>
+
+static char *sdp[] = {
+ /*
+ "v=0\r\n"
+ "o=mhandley 2890844526 2890842807 IN IP4 126.16.64.4\r\n"
+ "s=SDP Seminar\r\n"
+ "i=A Seminar on the session description protocol\r\n"
+ "u=http://www.cs.ucl.ac.uk/staff/M.Handley/sdp.03.ps\r\n"
+ "e=mjh@isi.edu (Mark Handley)\r\n"
+ "c=IN IP4 224.2.17.12/127\r\n"
+ "t=2873397496 2873404696\r\n"
+ "a=recvonly\r\n"
+ "m=audio 49170 RTP/AVP 0\r\n"
+ "m=video 51372 RTP/AVP 31\r\n"
+ "m=application 32416 udp wb\r\n"
+ "a=orient:portrait\r\n"
+ "m=audio 49230 RTP/AVP 96 97 98\r\n"
+ "a=rtpmap:96 L8/8000\r\n"
+ "a=rtpmap:97 L16/8000\r\n"
+ "a=rtpmap:98 L16/11025/2\r\n",
+ */
+ "v=0\r\n"
+ "o=usera 2890844526 2890844527 IN IP4 alice.example.com\r\n"
+ "s=\r\n"
+ "c=IN IP4 alice.example.com\r\n"
+ "t=0 0\r\n"
+ "m=message 7394 msrp/tcp *\r\n"
+ "a=accept-types: message/cpim text/plain text/html\r\n"
+ "a=path:msrp://alice.example.com:7394/2s93i9;tcp\r\n"
+};
+
+static int sdp_perform_test(pj_pool_factory *pf)
+{
+ pj_pool_t *pool;
+ int inputlen, len;
+ pjsdp_session_desc *ses;
+ char buf[1500];
+ enum { LOOP=1000000 };
+ int i;
+ pj_time_val start, end;
+
+ printf("Parsing and printing %d SDP messages..\n", LOOP);
+
+ pool = pj_pool_create(pf, "", 4096, 0, NULL);
+ inputlen = strlen(sdp[0]);
+ pj_gettimeofday(&start);
+ for (i=0; i<LOOP; ++i) {
+ ses = pjsdp_parse(sdp[0], inputlen, pool);
+ len = pjsdp_print(ses, buf, sizeof(buf));
+ buf[len] = '\0';
+ pj_pool_reset(pool);
+ }
+ pj_gettimeofday(&end);
+
+ printf("Original:\n%s\n", sdp[0]);
+ printf("Parsed:\n%s\n", buf);
+
+ PJ_TIME_VAL_SUB(end, start);
+ printf("Time: %ld:%03lds\n", end.sec, end.msec);
+
+ if (end.msec==0 && end.sec==0) end.msec=1;
+ printf("Performance: %ld msg/sec\n", LOOP*1000/PJ_TIME_VAL_MSEC(end));
+ puts("");
+
+ pj_pool_release(pool);
+ return 0;
+}
+
+static int sdp_conform_test(pj_pool_factory *pf)
+{
+ pj_pool_t *pool;
+ pjsdp_session_desc *ses;
+ int i, len;
+ char buf[1500];
+
+ for (i=0; i<sizeof(sdp)/sizeof(sdp[0]); ++i) {
+ pool = pj_pool_create(pf, "sdp", 4096, 0, NULL);
+ ses = pjsdp_parse(sdp[i], strlen(sdp[i]), pool);
+ len = pjsdp_print(ses, buf, sizeof(buf));
+ buf[len] = '\0';
+ printf("%s\n", buf);
+ pj_pool_release(pool);
+ }
+
+ return 0;
+}
+
+pj_status_t sdp_test(pj_pool_factory *pf)
+{
+ if (sdp_conform_test(pf) != 0)
+ return -2;
+
+ if (sdp_perform_test(pf) != 0)
+ return -3;
+
+ return 0;
+}
+
diff --git a/pjmedia/src/test/session_test.c b/pjmedia/src/test/session_test.c
index cf13adab..cfb1ee2a 100644
--- a/pjmedia/src/test/session_test.c
+++ b/pjmedia/src/test/session_test.c
@@ -1,115 +1,136 @@
-/* $Id$
- *
- */
-#include <pjmedia/mediamgr.h>
-#include <pjmedia/session.h>
-#include <pj/sock.h>
-#include <pj/pool.h>
-#include <stdio.h>
-#include <pj/string.h>
-
-pj_status_t session_test (pj_pool_factory *pf)
-{
- pj_med_mgr_t *mm;
- pj_media_session_t *s1, *s2;
- pj_pool_t *pool;
- pjsdp_session_desc *sdp;
- pj_media_stream_info sd_info;
- char buf[1024];
- int len;
- pj_media_stream_stat tx_stat, rx_stat;
-
- pool = pj_pool_create(pf, "test", 4096, 1024, NULL);
-
- // Init media manager.
- mm = pj_med_mgr_create ( pf );
-
- // Create caller session.
- // THIS WILL DEFINITELY CRASH (NULL as argument)!
- s1 = pj_media_session_create (mm, NULL);
-
- // Set caller's media to send-only.
- sd_info.dir = PJ_MEDIA_DIR_ENCODING;
- pj_media_session_modify_stream (s1, 0, PJ_MEDIA_STREAM_MODIFY_DIR, &sd_info);
-
- // Create caller SDP.
- sdp = pj_media_session_create_sdp (s1, pool, 0);
- len = pjsdp_print (sdp, buf, sizeof(buf));
- buf[len] = '\0';
- printf("Caller's initial SDP:\n<BEGIN>\n%s\n<END>\n", buf);
-
- // Parse SDP from caller.
- sdp = pjsdp_parse (buf, len, pool);
-
- // Create callee session based on caller's SDP.
- // THIS WILL DEFINITELY CRASH (NULL as argument)!
- s2 = pj_media_session_create_from_sdp (mm, sdp, NULL);
-
- // Create callee SDP
- sdp = pj_media_session_create_sdp (s2, pool, 0);
- len = pjsdp_print (sdp, buf, sizeof(buf));
- buf[len] = '\0';
- printf("Callee's SDP:\n<BEGIN>\n%s\n<END>\n", buf);
-
- // Parse SDP from callee.
- sdp = pjsdp_parse (buf, len, pool);
-
- // Update caller
- pj_media_session_update (s1, sdp);
- sdp = pj_media_session_create_sdp (s1, pool, 0);
- pjsdp_print (sdp, buf, sizeof(buf));
- printf("Caller's SDP after update:\n<BEGIN>\n%s\n<END>\n", buf);
-
- // Now start media.
- pj_media_session_activate (s2);
- pj_media_session_activate (s1);
-
- // Wait
- for (;;) {
- int has_stat;
-
- printf("Enter q to exit, 1 or 2 to print statistics.\n");
- fgets (buf, 10, stdin);
- has_stat = 0;
-
- switch (buf[0]) {
- case 'q':
- case 'Q':
- goto done;
- break;
- case '1':
- pj_media_session_get_stat (s1, 0, &tx_stat, &rx_stat);
- has_stat = 1;
- break;
- case '2':
- pj_media_session_get_stat (s2, 0, &tx_stat, &rx_stat);
- has_stat = 1;
- break;
- }
-
- if (has_stat) {
- pj_media_stream_stat *stat[2] = { &tx_stat, &rx_stat };
- const char *statname[2] = { "TX", "RX" };
- int i;
-
- for (i=0; i<2; ++i) {
- printf("%s statistics:\n", statname[i]);
- printf(" Pkt TX=%d RX=%d\n", stat[i]->pkt_tx, stat[i]->pkt_rx);
- printf(" Octets TX=%d RX=%d\n", stat[i]->oct_tx, stat[i]->oct_rx);
- printf(" Jitter %d ms\n", stat[i]->jitter);
- printf(" Pkt lost %d\n", stat[i]->pkt_lost);
- }
- printf("\n");
- }
- }
-
-done:
-
- // Done.
- pj_pool_release (pool);
- pj_media_session_destroy (s2);
- pj_media_session_destroy (s1);
- pj_med_mgr_destroy (mm);
-
- return 0;
-}
+/* $Id$
+ *
+ */
+/*
+ * PJMEDIA - Multimedia over IP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include <pjmedia/mediamgr.h>
+#include <pjmedia/session.h>
+#include <pj/sock.h>
+#include <pj/pool.h>
+#include <stdio.h>
+#include <pj/string.h>
+
+pj_status_t session_test (pj_pool_factory *pf)
+{
+ pj_med_mgr_t *mm;
+ pj_media_session_t *s1, *s2;
+ pj_pool_t *pool;
+ pjsdp_session_desc *sdp;
+ pj_media_stream_info sd_info;
+ char buf[1024];
+ int len;
+ pj_media_stream_stat tx_stat, rx_stat;
+
+ pool = pj_pool_create(pf, "test", 4096, 1024, NULL);
+
+ // Init media manager.
+ mm = pj_med_mgr_create ( pf );
+
+ // Create caller session.
+ // THIS WILL DEFINITELY CRASH (NULL as argument)!
+ s1 = pj_media_session_create (mm, NULL);
+
+ // Set caller's media to send-only.
+ sd_info.dir = PJ_MEDIA_DIR_ENCODING;
+ pj_media_session_modify_stream (s1, 0, PJ_MEDIA_STREAM_MODIFY_DIR, &sd_info);
+
+ // Create caller SDP.
+ sdp = pj_media_session_create_sdp (s1, pool, 0);
+ len = pjsdp_print (sdp, buf, sizeof(buf));
+ buf[len] = '\0';
+ printf("Caller's initial SDP:\n<BEGIN>\n%s\n<END>\n", buf);
+
+ // Parse SDP from caller.
+ sdp = pjsdp_parse (buf, len, pool);
+
+ // Create callee session based on caller's SDP.
+ // THIS WILL DEFINITELY CRASH (NULL as argument)!
+ s2 = pj_media_session_create_from_sdp (mm, sdp, NULL);
+
+ // Create callee SDP
+ sdp = pj_media_session_create_sdp (s2, pool, 0);
+ len = pjsdp_print (sdp, buf, sizeof(buf));
+ buf[len] = '\0';
+ printf("Callee's SDP:\n<BEGIN>\n%s\n<END>\n", buf);
+
+ // Parse SDP from callee.
+ sdp = pjsdp_parse (buf, len, pool);
+
+ // Update caller
+ pj_media_session_update (s1, sdp);
+ sdp = pj_media_session_create_sdp (s1, pool, 0);
+ pjsdp_print (sdp, buf, sizeof(buf));
+ printf("Caller's SDP after update:\n<BEGIN>\n%s\n<END>\n", buf);
+
+ // Now start media.
+ pj_media_session_activate (s2);
+ pj_media_session_activate (s1);
+
+ // Wait
+ for (;;) {
+ int has_stat;
+
+ printf("Enter q to exit, 1 or 2 to print statistics.\n");
+ fgets (buf, 10, stdin);
+ has_stat = 0;
+
+ switch (buf[0]) {
+ case 'q':
+ case 'Q':
+ goto done;
+ break;
+ case '1':
+ pj_media_session_get_stat (s1, 0, &tx_stat, &rx_stat);
+ has_stat = 1;
+ break;
+ case '2':
+ pj_media_session_get_stat (s2, 0, &tx_stat, &rx_stat);
+ has_stat = 1;
+ break;
+ }
+
+ if (has_stat) {
+ pj_media_stream_stat *stat[2] = { &tx_stat, &rx_stat };
+ const char *statname[2] = { "TX", "RX" };
+ int i;
+
+ for (i=0; i<2; ++i) {
+ printf("%s statistics:\n", statname[i]);
+ printf(" Pkt TX=%d RX=%d\n", stat[i]->pkt_tx, stat[i]->pkt_rx);
+ printf(" Octets TX=%d RX=%d\n", stat[i]->oct_tx, stat[i]->oct_rx);
+ printf(" Jitter %d ms\n", stat[i]->jitter);
+ printf(" Pkt lost %d\n", stat[i]->pkt_lost);
+ }
+ printf("\n");
+ }
+ }
+
+done:
+
+ // Done.
+ pj_pool_release (pool);
+ pj_media_session_destroy (s2);
+ pj_media_session_destroy (s1);
+ pj_med_mgr_destroy (mm);
+
+ return 0;
+}