[Pkg-voip-commits] r5365 - in /wengophone/trunk/debian: changelog patches/head/phapi-alsa-code-from-2.2.patch patches/series
cavedon-guest at alioth.debian.org
cavedon-guest at alioth.debian.org
Sun Mar 23 22:47:14 UTC 2008
Author: cavedon-guest
Date: Sun Mar 23 22:47:14 2008
New Revision: 5365
URL: http://svn.debian.org/wsvn/pkg-voip/?sc=1&rev=5365
Log:
Import the code for managing ALSA devices from the 2.2 branch. Closes: #471385
Added:
wengophone/trunk/debian/patches/head/phapi-alsa-code-from-2.2.patch
Modified:
wengophone/trunk/debian/changelog
wengophone/trunk/debian/patches/series
Modified: wengophone/trunk/debian/changelog
URL: http://svn.debian.org/wsvn/pkg-voip/wengophone/trunk/debian/changelog?rev=5365&op=diff
==============================================================================
--- wengophone/trunk/debian/changelog (original)
+++ wengophone/trunk/debian/changelog Sun Mar 23 22:47:14 2008
@@ -5,9 +5,11 @@
* Update package description. Closes: #463812
* Update Standards-Version to 3.7.3
* Fix build depends according to build-depends-on-1-revision lintian warning
- * Compiling with swscale support
-
- -- Ludovico Cavedon <cavedon at sssup.it> Thu, 20 Mar 2008 00:41:15 +0100
+ * Compiling with swscale support
+ * Import the code for managing ALSA devices from the 2.2 branch.
+ Closes: #471385
+
+ -- Ludovico Cavedon <cavedon at sssup.it> Sun, 23 Mar 2008 23:37:04 +0100
wengophone (2.1.2.dfsg0-2) unstable; urgency=low
Added: wengophone/trunk/debian/patches/head/phapi-alsa-code-from-2.2.patch
URL: http://svn.debian.org/wsvn/pkg-voip/wengophone/trunk/debian/patches/head/phapi-alsa-code-from-2.2.patch?rev=5365&op=file
==============================================================================
--- wengophone/trunk/debian/patches/head/phapi-alsa-code-from-2.2.patch (added)
+++ wengophone/trunk/debian/patches/head/phapi-alsa-code-from-2.2.patch Sun Mar 23 22:47:14 2008
@@ -1,0 +1,1722 @@
+Index: wengophone-2.1.2.dfsg0/wifo/phapi/phmedia-alsa.c
+===================================================================
+--- wengophone-2.1.2.dfsg0.orig/wifo/phapi/phmedia-alsa.c 2008-03-23 23:32:51.000000000 +0100
++++ wengophone-2.1.2.dfsg0/wifo/phapi/phmedia-alsa.c 2008-03-23 23:34:52.000000000 +0100
+@@ -1,5 +1,4 @@
+-/*
+- * The phmedia-alsa module implements interface to ALSA audio devices for phapi
++/* -*- Mode: C; tab-width:8; c-basic-offset:8; -*-
+ *
+ * Copyright (C) 2004 Vadim Lebedev <vadim at mbdsys.com>
+ * Copyright (C) 2006-2007 WENGO SAS
+@@ -15,586 +14,1200 @@
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+- * License along with dpkg; if not, write to the Free Software
++ * License along with wengophone; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
++ *
++ * 2008-03-23 Ludovico Cavedon <cavedon at sssup.it>
++ * - removed owpl logging code backporting it to branch 2.1
++ *
++ * 2007-09-27 Alec Leamas <nospam>
++ * - Documentation added
++ * - Hardware buffer limited to 3 periods
++ * - Support for silence_threshold and silence_size added (not used).
++ * - Now uses implicit (start_threshold) start instead of explicit.
++ * - Changed threading model, now uses internal threads.
++ * - Added handling of stereo devices which can't be opened in mono.
++ * - Added rebuffering support in playback_thread().
++ * - Fixed bug in snd_pcm_open, which was done in blocking mode
++ * (potential hang if device can't be opened).
++ * - Refactored alsa_dev_open fixed memory leaks.
++ * - Added function prototypes to avoid compiler warnings.
++ * - Removed DBG_DYNA_DRV references, uses std owplDebug calls instead.
++ * - Added statistics support.
++ * - Made lot's of literals to symbolic constants.
++ *
++ * BUGS
++ * - Since the device is held open by this driver, playing sounds
++ * through alsa_sndfile.cpp does not work on physical alsa devices.
++ * - Marked as FIXME here and there...
+ */
+
+-/** @see http://www.linuxjournal.com/article/6735 for an introduction to ALSA */
++/**
++ *
++ * @file phmedia-alsa.c
++ *
++ * The phmedia-alsa module implements interface to ALSA devices for phapi
++ *
++ * @see http://www.linuxjournal.com/article/6735 for an introduction to ALSA
++ * @see http://dev.openwengo.com/trac/openwengo/trac.fcgi/wiki/PhApiAudio [PHAPI]
++ * @see http://www.alsa-project.org/ [ALSA]
++ *
++ * The data transferred to/from the ALSA driver is organized in three levels:
++ *
++ * - The first level is the samplesize, the size of each sample. [PHAPI]
++ * defines each sample as a 16-bit, signed and little endian entity.
++ *
++ * - [ALSA] defines the frame, which is defined as a sample from each channel.
++ * Thus, in mono mode framesize == samplesize, in stereo
++ * framesize == 2 * samplesize.
++ *
++ * - Data is transferred to/from the driver in chunks, typically a 100-2000
++ * bytes. These chunks are described as 'frames' in [PHAPI] and as
++ * 'periods' in [ALSA]. For now, these chunks are mono streams of
++ * samples.
++ *
++ * [ALSA] defines the basic setup parameters:
++ * - The size of the hardware ring buffer. This is tradeoff between not
++ * not losing packets, and packets becoming to old to be
++ * usable in an oversized buffer.
++ * - The 'period' size, effectively the same as a 'chunk'.
++ * - The relations between audio format, channels and framesize.
++ * - The start threshold - a stream starts if the amount of buffered
++ * (output) or requested(input) data is equal to or exceeds this value.
++ * This drivers setup starts streams in this way.
++ * - The silence threshold and silence length: See ::SILENCE_THRESHOLD
++ *
++ * The fundamental requirement for the flow comes from the codecs, which emits
++ * and requires data at a specific rate. Wengophone's codecs has a sampling
++ * rate of 16 000 frames per second. This requirement is embodied in the
++ * parameters chunk_size and sample_rate to the open() function.
++ *
++ * Basic dependencies, with some real example data (see [ALSA], API
++ * reference, PCM section)
++ * - Sample rate = 16000 samples/sec.
++ * - Time for 1 sample = 1/rate s = 1000/rate ms: 1000/16000 ms.
++ * - Chunk(bytes, mono) = 640 bytes
++ * - Chunk(bytes, stereo) = 1280 bytes
++ * - Chunk(frames) = chunk(bytes)/sample_size/channels: 1280/2/2 = 320
++ * - Period size(frames) = 1 chunk = 320 frames.
++ * - Period time = chunk(frames)/sample rate: 320/16000 = 20 ms
++ * - Buffer size = periods per buffer * period size: 3 * 320 = 960 frames
++ * - Buffer time = buffer size(frames)/sample rate: 960/16000 = 60 ms
++ *
++ * Debugging tip: <i>cat /proc/asound/card0/pcm0p/sub0/hw_params</i>,
++ * <i>cat /proc/asound/card0/pcm0c/sub0/sw_params</i>. I bet your
++ * filenames are different.
++ *
++ */
++
++//FIXME: split in header + implementation file.
+
+ #ifdef ENABLE_ALSA
+
+ #define ALSA_PCM_NEW_HW_PARAMS_API
++#include <sys/time.h>
+ #include <alsa/asoundlib.h>
++#include <fcntl.h>
++#include <libgen.h>
++#include <pthread.h>
+
+ #include "phapi.h"
+ #include "phastream.h"
+ #include "phaudiodriver.h"
+ #include "phlog.h"
+
+-struct alsa_dev {
+- snd_pcm_t *ain, *aout;
+-};
++/** Preferred # channels (1-2). Wengophone always uses mono internally. */
++const int DEFAULT_CHANNELS = 1;
++
++/** Size of hw buffer (periods). */
++const unsigned BUFFER_PERIODS = 3;
++
++/**
++ *
++ * Iff I've understood this right: When the number of outgoing,
++ * buffered frames drops below this value, SILENZE_SIZE number
++ * of silence frames is added to the output - all this to make the
++ * expected underrun less noisy.
++ *
++ * SILENZE_SIZE should be <= SILENCE_THRESHOLD,
++ * or >= boundary (INT_MAX works fine). The latter case fills the
++ * buffer with silence.
++ *
++ * Beware: The [ALSA] docs for this are horrible, and I might have got
++ * all this wrong.
++ *
++ * Using internal threads and rebuffering support, this is not used.
++ *
++ * @see [ALSA], set_silence_threshold().
++ *
++ */
++const snd_pcm_uframes_t SILENCE_THRESHOLD = 0;
++
++/** Time (ms) we wait for a device in snd_pcm_wait(). */
++const unsigned SND_PCM_WAIT_MS = 500;
++
++/** @see SILENCE_THRESHOLD. */
++const snd_pcm_uframes_t SILENCE_SIZE = 0;
++
++/** Used format. */
++const snd_pcm_format_t PCM_FORMAT = SND_PCM_FORMAT_S16_LE;
++
++/** Suppress ALSA diagnostics on stdout/stderr (0/1). */
++const int ALSA_SILENT = 1;
++
++#define OUTPUT_LATENCY 100 /**< Buffering before stream start
++ as percent of a period. */
++#define INPUT_LATENCY 50 /**< Min size of requested data
++ which starts stream. */
++
++#define MONO_AVERAGE 1 /**< stereo2mono takes the average
++ of the two stereo channels. */
++
++#define PH_TRUE 1
++#define PH_FALSE 0
++typedef short ph_bool;
++
++/** Returns the alsa_drv_info* ptr stored as drvinfo in phastream_t. */
++#define DRV_INFO(x) ((alsa_drv_info *)((x)->drvinfo))
+
+-#define ADEV(x) ((struct alsa_dev *)((x)->drvinfo))
++/** Kludge to fix incompatibility between gcc 4.2 and alsa. */
++#define GCC_VERSION (__GNUC__ * 10 + __GNUC_MINOR__ )
+
+-void alsa_stream_start(phastream_t *as);
+-int alsa_stream_open(phastream_t *as, char *name, int rate, int framesize, ph_audio_cbk cbk);
+-int alsa_stream_write(phastream_t *as, void *buf, int len);
+-int alsa_stream_read(phastream_t *as, void *buf, int len);
+-int alsa_stream_get_out_space(phastream_t *as, int *used);
+-int alsa_stream_get_avail_data(phastream_t *as);
+-void alsa_stream_close(phastream_t *as);
+-int alsa_stream_get_fds(phastream_t *as, int fds[2]);
++#if GCC_VERSION > 41
+
++#undef snd_pcm_sw_params_alloca
++#define snd_pcm_sw_params_alloca(ptr) \
++do { \
++ *ptr = (snd_pcm_sw_params_t*) alloca(snd_pcm_sw_params_sizeof()); \
++ memset(*ptr, 0, snd_pcm_sw_params_sizeof()); \
++} while (0)
++
++#undef snd_pcm_hw_params_alloca
++#define snd_pcm_hw_params_alloca(ptr) \
++do { \
++ *ptr = (snd_pcm_hw_params_t*) alloca(snd_pcm_hw_params_sizeof()); \
++ memset(*ptr, 0, snd_pcm_hw_params_sizeof()); \
++} while (0)
++
++#undef snd_pcm_status_alloca
++#define snd_pcm_status_alloca(ptr) \
++do { \
++ *ptr = (snd_pcm_status_t *) alloca(snd_pcm_status_sizeof()); \
++ memset(*ptr, 0, snd_pcm_status_sizeof()); \
++} while (0)
++
++#endif //GCC_VERSION
++
++
++/**
++ * @name Driver entry points
++ *
++ * @{
++ *
++ */
++
++/**
++ *
++ * Implements ::ph_audio_open.
++ *
++ * @BUG No check if ain/aout has same channel count
++ *
++ */
++static int alsa_stream_open(phastream_t* as,
++ const char* name,
++ int wished_rate,
++ int chunk_size,
++ ph_audio_cbk cbk);
++
++/**
++ * Implements ::ph_audio_close
++ */
++static void alsa_stream_close(phastream_t *as);
++
++/**
++ * Implements ::ph_audio_start
++ */
++static void alsa_stream_start(phastream_t *as);
++
++/**
++ * Implements ::ph_audio_write
++ *
++ * Timing: On a AMD X2 4200+ each invocation is about 150 us.
++ *
++*/
++static int alsa_stream_write(phastream_t *as, void *buf, int len);
++
++/**
++ * Implements ::ph_audio_read
++ */
++static int alsa_stream_read(phastream_t *as, void *buf, int len);
++
++/**
++ * Implements ::ph_audio_get_avail_data
++ */
++static int alsa_stream_get_avail_data(phastream_t *as);
++
++/**
++ * Implements ::ph_audio_get_out_space.
++ */
++static int alsa_stream_get_out_space(phastream_t *as, int *used);
++
++/**
++ * Implements ::ph_audio_get_fds
++ */
++static int alsa_stream_get_fds(phastream_t *as, int fds[2]);
++/** Register the driver. */
+ void ph_alsa_driver_init(void);
+-snd_pcm_t *alsa_dev_open(char *name, int type, int wished_rate, int framesize, int latencymsecs, int *chosen_rate);
+-int alsa_dev_get_fd(snd_pcm_t *s);
+-int suspend(snd_pcm_t *handle);
+
++/*@}*/
++
++
++/** Description of one device (input or output). */
++struct _alsa_dev {
++ snd_pcm_t* pcm;
++ char* id; /**< id string e g "input". */
++ unsigned long bytes; /**< # bytes read/written. */
++ unsigned long rebuffered; /**< Resent (no input) bytes.*/
++ unsigned soft_errors; /**< recoverable errors. */
++ unsigned again_errors; /**< EAGAIN soft error. */
++ unsigned hard_errors; /**< Non-recoverable errors. */
++ pthread_t thread; /**< Thread in async mode. */
++ unsigned stop_thread; /**< Initially 0, set to 1 */
++ /** of main thread to stop */
++ /** child, set to 2 by */
++ /** child when child exits. */
++};
++
++typedef struct _alsa_dev alsa_dev;
++
++/** The driver info kept in the phastream->drv_info. */
++struct _alsa_drv_info {
++ alsa_dev input;
++ alsa_dev output;
++ unsigned channels; /**< Mono/stereo (1, 2). */
++ unsigned sample_size; /**< In bytes, 1.\ .\ 8. */
++ size_t chunk_size; /**< As to alsa_stream_open().*/
++ ph_audio_cbk callback; /**< Invoked by threads. */
++};
++
++typedef struct _alsa_drv_info alsa_drv_info;
++
++/** Argument when opening device. */
++struct _pcm_data {
++ snd_pcm_t* device;
++ unsigned channels;
++ unsigned rate;
++ ssize_t chunk_size; /**< # bytes (sic!) in each transfer. */
++ unsigned threshold;
++ /**< Delay before stream starts(frames)*/
++ unsigned buffer_periods; /**< Size of hw buffer, in periods. */
++ unsigned silence_threshold;
++ unsigned silence_size;
++};
++
++typedef struct _pcm_data pcm_data;
++
++/**
++ * The driver info registered by init()
++ */
+ struct ph_audio_driver ph_alsa_driver = {
+- "alsa",
+- PH_SNDDRVR_FDS,
+- 0,
+- alsa_stream_start,
+- alsa_stream_open,
+- alsa_stream_write,
+- alsa_stream_read,
+- alsa_stream_get_out_space,
+- alsa_stream_get_avail_data,
+- alsa_stream_close,
+- alsa_stream_get_fds,
+- NULL
++ "alsa",
++ PH_SNDDRVR_PLAY_CALLBACK | PH_SNDDRVR_REC_CALLBACK,
++ 0,
++ alsa_stream_start,
++ alsa_stream_open,
++ alsa_stream_write,
++ alsa_stream_read,
++ alsa_stream_get_out_space,
++ alsa_stream_get_avail_data,
++ alsa_stream_close,
++ alsa_stream_get_fds,
++ NULL
+ };
+
+-void ph_alsa_driver_init(void)
+-{
+- ph_register_audio_driver(&ph_alsa_driver);
++/**
++ * nanosleep(2) arg to relinquish CPU while waiting for IO, 5 ms.
++ */
++const struct timespec io_wait = { 0, 5 * 1000000 };
++
++/** Dump lot's of information about a pcm device. */
++static void dump_pcm_data( snd_pcm_t* handle, snd_pcm_hw_params_t *params);
++
++/**
++ * Open an ALSA device for input or output
++ *
++ * @param handle On exit, handle to device in state SND_PCM_STATE_OPEN.
++ *
++ * @param name Name of device. Might be the plain device name, or environment
++ * setting on the form "alsa:IN=inputdev OUT=outputdev".
++ * @param type indicates a playback or a recorder device.
++ *
++ * @return 0 on success, else ALSA error code.
++ *
++ */
++static int open_device( snd_pcm_t** handle,
++ const char* name,
++ snd_pcm_format_t type);
++
++/**
++ *
++ * Open and setup an ALSA device for input or output.
++ *
++ * @param name Plain ALSA device name e g "hw:0,0" or a string as present
++ * in environment e g "alsa:IN=hw:0,0 OUT=hw:1,0".
++ * @param pcm Device and device data. On successful exit, pcm->pcm is
++ * in state SND_PCM_STATE_PREPARE.
++ *
++ * @return 0 on success, else negative alsa error code.
++ *
++ */
++static int alsa_dev_open( const char* name,
++ snd_pcm_stream_t type,
++ pcm_data* pcm);
++
++/** Return size of hw buffer, in frames. */
++static snd_pcm_uframes_t
++pcm_data_get_buffer_size( snd_pcm_hw_params_t* params, pcm_data* pcm);
++
++/** Return size of one period, in frames. */
++
++static snd_pcm_sframes_t
++pcm_data_get_period_size( snd_pcm_hw_params_t* params, pcm_data* pcm);
++
++/** Return size of input/output threshold, in frames. */
++static snd_pcm_uframes_t pcm_data_get_threshold( pcm_data* pcm );
++
++static snd_pcm_uframes_t pcm_data_get_silence_threshold( pcm_data* pcm);
++
++static snd_pcm_uframes_t pcm_data_get_silence_size( pcm_data* pcm);
++
++/** printf-style sibling to LOG_ERROR. */
++#define ALSA_ERROR DBG_DYNA_AUDIO_DRV
++
++#define ALSA_DEBUG DBG_DYNA_AUDIO_DRV
++
++/** Non-blocking handling of ALSA error code, reset dev as required. */
++static void ph_handle_pcm_error( alsa_dev* device, int err );
++
++static snd_pcm_uframes_t
++wp_bytes_to_frames( snd_pcm_hw_params_t* params, size_t bytes);
++
++/** Set hw parameters for an alsa device. */
++static int set_hw_params( snd_pcm_stream_t type,
++ pcm_data* pcm,
++ snd_pcm_hw_params_t* hw_params);
++
++/** Set sw parameters for an alsa device. */
++static int set_sw_params( snd_pcm_stream_t type,
++ pcm_data* pcm,
++ snd_pcm_hw_params_t* hw_params );
++
++/**
++ * Initiate a new pcm_data instance.
++ *
++ * @see snd_pcm_sw_params_set_start_threshold.
++ * @param pcm Filled with data on exit.
++ * @param rate Sample rate (frames/sec).
++ * @param chunk_size Size of blocks read or written (bytes).
++ * @param latency Start threshold value as percent of a period.
++ */
++static void pcm_data_new( pcm_data* pcm,
++ unsigned rate,
++ unsigned chunk_size,
++ unsigned latency);
++
++/**
++ * Create a stereo buffer from a mono source.
++ *
++ * @param dest The destination stereo buffer, 2 * size big.
++ * @param src The source mono buffer
++ * @param size Size of source mono buffer (bytes).
++ * @param sample_size Size in bytes of each sample 1.\ .\ 8
++ *
++ */
++static void mono2stereo( char* dest,
++ char* src,
++ size_t size,
++ size_t sample_size);
++
++/**
++ *
++ * Read data from ALSA, support for alsa_stream_read.
++ *
++ * @param stream Data source (mic).
++ * @param buf Buffer for data, stereo or mono as required.
++ * @param len Buffer length = requested read size (bytes).
++ * @return # bytes read, <= len.
++ *
++ */
++static int stream_read( alsa_dev* input, void* buff, int len);
++
++/**
++ *
++ * Write data to ALSA, support for alsa_stream_write.
++ *
++ * @param output Where to send data.
++ * @param buff Stereo or mono data.
++ * @len length of buff (bytes).
++ * @return # bytes written.
++ *
++ */
++static int stream_write( alsa_dev* output, void *buf, int len);
++
++/**
++ *
++ * Create a mono buffer from a stereo source.
++ *
++ * @param src The stereo input buffer with signed 16-bit samples.
++ * @param dest The mono destination buffer.
++ * @param size Size of source stereo buffer (bytes).
++ * @param sample_size Size in bytes of each sample 1.\ .\ 8.
++ * @return Size of returned mono buffer (i e, size / 2 ).
++ *
++ */
++static size_t stereo2mono( char* dest,
++ char* src,
++ size_t size,
++ size_t sample_size);
++
++/** Shuffles data from ALSA to phapi. */
++static void* recorder_thread( void* arg) ;
++
++/** Shuffles data from phapi to ALSA. */
++static void* playback_thread( void* arg);
++
++static void dump_pcm_data( snd_pcm_t* handle,
++ snd_pcm_hw_params_t *params) {
++
++ int dir;
++ unsigned int val2;
++ unsigned int val;
++ snd_pcm_access_t access;
++ snd_pcm_subformat_t format;
++ snd_pcm_uframes_t uframe;
++ snd_pcm_uframes_t frames;
++
++ /* Display information about the PCM interface */
++ ALSA_DEBUG("PCM handle name = '%s'", snd_pcm_name(handle));
++ ALSA_DEBUG("PCM state = %s",
++ snd_pcm_state_name(snd_pcm_state(handle)));
++ snd_pcm_hw_params_get_access(params, &access);
++ ALSA_DEBUG("access type = %s", snd_pcm_access_name(access));
++ snd_pcm_hw_params_get_format(params, &dir);
++ ALSA_DEBUG("format = '%s' (%s)",
++ snd_pcm_format_name((snd_pcm_format_t)dir),
++ snd_pcm_format_description((snd_pcm_format_t)dir));
++ snd_pcm_hw_params_get_subformat(params, &format);
++ ALSA_DEBUG("subformat = '%s' (%s)",
++ snd_pcm_subformat_name(format),
++ snd_pcm_subformat_description(format));
++ snd_pcm_hw_params_get_channels(params, &val);
++ ALSA_DEBUG("channels = %d", val);
++ snd_pcm_hw_params_get_rate(params, &val, &dir);
++ ALSA_DEBUG("rate = %d bps", val);
++ snd_pcm_hw_params_get_period_time(params, &val, &dir);
++ ALSA_DEBUG("period time = %d us", val);
++ snd_pcm_hw_params_get_period_size(params, &frames, &dir);
++ ALSA_DEBUG("period size = %d frames", (int)frames);
++ snd_pcm_hw_params_get_buffer_time(params, &val, &dir);
++ ALSA_DEBUG("buffer time = %d us", val);
++ snd_pcm_hw_params_get_buffer_size(params, &uframe);
++ ALSA_DEBUG("buffer size = %d frames", (int)uframe);
++ snd_pcm_hw_params_get_periods(params, &val, &dir);
++ ALSA_DEBUG("periods per buffer = %d frames", val);
++ snd_pcm_hw_params_get_rate_numden(params, &val, &val2);
++ ALSA_DEBUG("exact rate = %d/%d bps", val, val2);
++ val = snd_pcm_hw_params_get_sbits(params);
++ ALSA_DEBUG("significant bits = %d", val);
++ snd_pcm_hw_params_get_tick_time(params, &val, &dir);
++ ALSA_DEBUG("tick time = %d us", val);
++ val = snd_pcm_hw_params_is_batch(params);
++ ALSA_DEBUG("is batch = %d", val);
++ val = snd_pcm_hw_params_is_block_transfer(params);
++ ALSA_DEBUG("is block transfer = %d", val);
++ val = snd_pcm_hw_params_is_double(params);
++ ALSA_DEBUG("is double = %d", val);
++ val = snd_pcm_hw_params_is_half_duplex(params);
++ ALSA_DEBUG("is half duplex = %d", val);
++ val = snd_pcm_hw_params_is_joint_duplex(params);
++ ALSA_DEBUG("is joint duplex = %d", val);
++ val = snd_pcm_hw_params_can_overrange(params);
++ ALSA_DEBUG("can overrange = %d", val);
++ val = snd_pcm_hw_params_can_mmap_sample_resolution(params);
++ ALSA_DEBUG("can mmap = %d", val);
++ val = snd_pcm_hw_params_can_pause(params);
++ ALSA_DEBUG("can pause = %d", val);
++ val = snd_pcm_hw_params_can_resume(params);
++ ALSA_DEBUG("can resume = %d", val);
++ val = snd_pcm_hw_params_can_sync_start(params);
++ ALSA_DEBUG("can sync start = %d", val);
+ }
+
+-snd_pcm_t *alsa_dev_open(char *name, int type, int wished_rate, int framesize, int latencymsecs, int *chosen_rate)
+-{
+- snd_pcm_t *handle;
+- snd_pcm_hw_params_t *params;
+- snd_pcm_sw_params_t *sparams;
+- unsigned int val;
+- snd_pcm_uframes_t frames;
+- int rc;
+- char *nmend = 0;
+- char *s = 0;
+- *chosen_rate = 0;
+-
+-#if ACTIVATE_DYNA_AUDIO_DRV_DBG
+- int dir;
+- unsigned int val2;
+- snd_pcm_access_t access;
+- snd_pcm_subformat_t format;
+- snd_pcm_uframes_t uframe;
+-#endif
++/** Register this driver. */
++void ph_alsa_driver_init( void) {
++ ph_register_audio_driver( &ph_alsa_driver);
++}
+
+- if (name == NULL)
+- {
+- DBG_DYNA_AUDIO_DRV("alsa_dev_open: name == NULL!\n");
+- return 0;
+- }
+-
+- if (!strncasecmp(name, "alsa:", 5))
+- {
+- name += 5;
+- }
+-
+- if (type == SND_PCM_STREAM_CAPTURE)
+- {
+- if ((s = strstr(name, "IN=")) != NULL)
+- {
+- nmend = strchr(name + 3, ' ');
+- if (nmend)
+- {
+- *nmend = 0;
+- }
+- name = s + 3;
+- }
+- }
+- else if (type == SND_PCM_STREAM_PLAYBACK)
+- {
+- if ((s = strstr(name, "OUT=")) != NULL)
+- {
+- nmend = strchr(name + 4, ' ');
+- if (nmend)
+- {
+- *nmend = 0;
+- }
+- name = s + 4;
+- }
+- }
+-
+- if (type == SND_PCM_STREAM_PLAYBACK)
+- {
+- DBG_DYNA_AUDIO_DRV("alsa_dev_open: SND_PCM_STREAM_PLAYBACK (name: %s, rate: %d, framesize: %d)\n", name, wished_rate, framesize);
+- }
+- else if (type == SND_PCM_STREAM_CAPTURE)
+- {
+- DBG_DYNA_AUDIO_DRV("alsa_dev_open: SND_PCM_STREAM_CAPTURE (name: %s, rate: %d, framesize: %d)\n", name, wished_rate, framesize);
+- }
+-
+- /* Open PCM device */
+- rc = snd_pcm_open(&handle, name, type, 0);
+-
+- /* restore the overwritten space */
+- if (nmend)
+- {
+- *nmend = ' ';
+- }
+-
+- if (rc < 0)
+- {
+- DBG_DYNA_AUDIO_DRV("failed to open pcm device: %s\n", snd_strerror(rc));
+- return 0;
+- }
+- else
+- {
+- DBG_DYNA_AUDIO_DRV("open pcm device: succes\n");
+- }
+-
+- /* Allocate a hardware parameters object. */
+- rc = snd_pcm_hw_params_malloc(¶ms);
+- if (rc < 0)
+- {
+- DBG_DYNA_AUDIO_DRV("cannot allocate hardware parameter structure: %s\n", snd_strerror(rc));
+- return 0;
+- }
+-
+- /* Fill it in with default values. */
+- snd_pcm_hw_params_any(handle, params);
+-
+- /* Set the desired hardware parameters. */
+- /* One channel (mono) */
+- rc = snd_pcm_hw_params_set_channels(handle, params, 1);
+- if (rc < 0)
+- {
+- DBG_DYNA_AUDIO_DRV("unable to set hw parameters(channels=1): %s\n", snd_strerror(rc));
+- /* two channel (stereo) */
+-#if 0
+- rc = snd_pcm_hw_params_set_channels(handle, params, 2);
+- if (rc < 0)
+- {
+- DBG_DYNA_AUDIO_DRV("unable to set hw parameters(channels=2): %s\n", snd_strerror(rc));
+- goto err;
+- }
+-#else
+- goto err;
+-#endif
+- }
++static void pcm_data_new( pcm_data* pcm,
++ unsigned rate,
++ unsigned chunk_size,
++ unsigned latency) {
++
++ memset( pcm, 0, sizeof( pcm_data));
++ pcm->channels = DEFAULT_CHANNELS;
++ pcm->rate = rate;
++ pcm->threshold = latency;
++ pcm->chunk_size = chunk_size;
++ pcm->buffer_periods = BUFFER_PERIODS;
++ pcm->silence_threshold = SILENCE_THRESHOLD;
++ pcm->silence_size = SILENCE_SIZE;
++}
+
+- /* Interleaved mode */
+- rc = snd_pcm_hw_params_set_access(handle, params, SND_PCM_ACCESS_RW_INTERLEAVED);
+- if (rc < 0)
+- {
+- DBG_DYNA_AUDIO_DRV("unable to set hw parameters(SND_PCM_ACCESS_RW_INTERLEAVED): %s\n", snd_strerror(rc));
+- goto err;
+- }
+-
+- /* Signed 16-bit little-endian format */
+- rc = snd_pcm_hw_params_set_format(handle, params, SND_PCM_FORMAT_S16_LE);
+- if (rc < 0)
+- {
+- DBG_DYNA_AUDIO_DRV("unable to set hw parameters(SND_PCM_FORMAT_S16_LE): %s\n", snd_strerror(rc));
+- goto err;
+- }
+-
+- /* Set sampling rate */
+- val = wished_rate;
+- rc = snd_pcm_hw_params_set_rate_near(handle, params, &val, 0);
+- if (rc < 0)
+- {
+- DBG_DYNA_AUDIO_DRV("unable to set hw parameters(rate=%d dir=%d): %s\n", val, dir, snd_strerror(rc));
+- goto err;
+- }
+- *chosen_rate = val;
+-
+- /* Set period size */
+- frames = framesize;
+- rc = snd_pcm_hw_params_set_period_size_near(handle, params, &frames, 0);
+- if (rc < 0)
+- {
+- DBG_DYNA_AUDIO_DRV("unable to set hw parameters(period_size=%d): %s\n", (int) frames, snd_strerror(rc));
+- goto err;
+- }
+-
+- /* Write the parameters to the driver */
+- rc = snd_pcm_hw_params(handle, params);
+- if (rc < 0)
+- {
+- DBG_DYNA_AUDIO_DRV("unable to set hw parameters: %s\n", snd_strerror(rc));
+- goto err;
+- }
+-
+- /* Set the non-blocking mode */
+- rc = snd_pcm_nonblock(handle, 1);
+- if (rc < 0)
+- {
+- DBG_DYNA_AUDIO_DRV("unable to set noblocking mode:%s\n", snd_strerror(rc));
+- goto err;
+- }
+-
+-#if ACTIVATE_DYNA_AUDIO_DRV_DBG
+- /* Display information about the PCM interface */
+- DBG_DYNA_AUDIO_DRV("PCM handle name = '%s'\n", snd_pcm_name(handle));
+- DBG_DYNA_AUDIO_DRV("PCM state = %s\n", snd_pcm_state_name(snd_pcm_state(handle)));
+- snd_pcm_hw_params_get_access(params, &access);
+- DBG_DYNA_AUDIO_DRV("access type = %s\n", snd_pcm_access_name(access));
+- snd_pcm_hw_params_get_format(params, &dir);
+- DBG_DYNA_AUDIO_DRV("format = '%s' (%s)\n", snd_pcm_format_name((snd_pcm_format_t)dir), snd_pcm_format_description((snd_pcm_format_t)dir));
+- snd_pcm_hw_params_get_subformat(params, &format);
+- DBG_DYNA_AUDIO_DRV("subformat = '%s' (%s)\n",
+- snd_pcm_subformat_name(format),
+- snd_pcm_subformat_description(format));
+- snd_pcm_hw_params_get_channels(params, &val);
+- DBG_DYNA_AUDIO_DRV("channels = %d\n", val);
+- snd_pcm_hw_params_get_rate(params, &val, &dir);
+- DBG_DYNA_AUDIO_DRV("rate = %d bps\n", val);
+- snd_pcm_hw_params_get_period_time(params, &val, &dir);
+- DBG_DYNA_AUDIO_DRV("period time = %d us\n", val);
+- snd_pcm_hw_params_get_period_size(params, &frames, &dir);
+- DBG_DYNA_AUDIO_DRV("period size = %d frames\n", (int)frames);
+- snd_pcm_hw_params_get_buffer_time(params, &val, &dir);
+- DBG_DYNA_AUDIO_DRV("buffer time = %d us\n", val);
+- snd_pcm_hw_params_get_buffer_size(params, &uframe);
+- DBG_DYNA_AUDIO_DRV("buffer size = %d frames\n", (int)uframe);
+- snd_pcm_hw_params_get_periods(params, &val, &dir);
+- DBG_DYNA_AUDIO_DRV("periods per buffer = %d frames\n", val);
+- snd_pcm_hw_params_get_rate_numden(params, &val, &val2);
+- DBG_DYNA_AUDIO_DRV("exact rate = %d/%d bps\n", val, val2);
+- val = snd_pcm_hw_params_get_sbits(params);
+- DBG_DYNA_AUDIO_DRV("significant bits = %d\n", val);
+- snd_pcm_hw_params_get_tick_time(params, &val, &dir);
+- DBG_DYNA_AUDIO_DRV("tick time = %d us\n", val);
+- val = snd_pcm_hw_params_is_batch(params);
+- DBG_DYNA_AUDIO_DRV("is batch = %d\n", val);
+- val = snd_pcm_hw_params_is_block_transfer(params);
+- DBG_DYNA_AUDIO_DRV("is block transfer = %d\n", val);
+- val = snd_pcm_hw_params_is_double(params);
+- DBG_DYNA_AUDIO_DRV("is double = %d\n", val);
+- val = snd_pcm_hw_params_is_half_duplex(params);
+- DBG_DYNA_AUDIO_DRV("is half duplex = %d\n", val);
+- val = snd_pcm_hw_params_is_joint_duplex(params);
+- DBG_DYNA_AUDIO_DRV("is joint duplex = %d\n", val);
+- val = snd_pcm_hw_params_can_overrange(params);
+- DBG_DYNA_AUDIO_DRV("can overrange = %d\n", val);
+- val = snd_pcm_hw_params_can_mmap_sample_resolution(params);
+- DBG_DYNA_AUDIO_DRV("can mmap = %d\n", val);
+- val = snd_pcm_hw_params_can_pause(params);
+- DBG_DYNA_AUDIO_DRV("can pause = %d\n", val);
+- val = snd_pcm_hw_params_can_resume(params);
+- DBG_DYNA_AUDIO_DRV("can resume = %d\n", val);
+- val = snd_pcm_hw_params_can_sync_start(params);
+- DBG_DYNA_AUDIO_DRV("can sync start = %d\n", val);
+-#endif
++static snd_pcm_sframes_t
++pcm_data_get_period_size( snd_pcm_hw_params_t* params, pcm_data* pcm) {
+
+- if (!latencymsecs)
+- {
+- snd_pcm_hw_params_free(params);
+- return handle;
+- }
+-
+- snd_pcm_sw_params_alloca(&sparams);
+-
+- /* retrieve the parameters from the driver */
+- rc = snd_pcm_sw_params_current(handle, sparams);
+- if (rc < 0)
+- {
+- DBG_DYNA_AUDIO_DRV("unable to get sw parameters: %s\n", snd_strerror(rc));
+- goto err;
+- }
+-
+- frames = (*chosen_rate) / 1000 * latencymsecs;
+- rc = snd_pcm_sw_params_set_start_threshold(handle, sparams, frames);
+- if (rc < 0)
+- {
+- DBG_DYNA_AUDIO_DRV("unable to set start threshold: %s\n", snd_strerror(rc));
+- goto err;
+- }
+-
+- /* Write the parameters to the driver */
+- rc = snd_pcm_sw_params(handle, sparams);
+- if (rc < 0)
+- {
+- DBG_DYNA_AUDIO_DRV("unable to set sw parameters: %s\n", snd_strerror(rc));
+- goto err;
+- }
+-
+- snd_pcm_hw_params_free(params);
+- //snd_pcm_sw_params_free(sparams);
+- return handle;
+-
+-err:
+- snd_pcm_hw_params_free(params);
+- //snd_pcm_sw_params_free(sparams);
+- snd_pcm_close(handle);
+- return 0;
++ return wp_bytes_to_frames( params, pcm->chunk_size);
+ }
+
+-int alsa_dev_get_fd(snd_pcm_t *s)
+-{
+- int err;
+- struct pollfd ufd;
++static snd_pcm_uframes_t
++pcm_data_get_buffer_size( snd_pcm_hw_params_t* params, pcm_data* pcm) {
+
+- if ((err = snd_pcm_poll_descriptors(s, &ufd, 1)) < 0)
+- {
+- DBG_DYNA_AUDIO_DRV( "Unable to obtain poll descriptors for device: %s\n", snd_strerror(err));
+- return -1;
+- }
++ return pcm_data_get_period_size( params, pcm) * pcm->buffer_periods;
++}
+
+- return ufd.fd;
++static snd_pcm_uframes_t pcm_data_get_threshold( pcm_data* pcm){
++
++ snd_pcm_uframes_t period =
++ snd_pcm_bytes_to_frames( pcm->device, pcm->chunk_size);
++ return ( pcm->threshold * period) / 100;
+ }
+
+-int alsa_stream_open(phastream_t *as, char *name, int wished_rate, int framesize, ph_audio_cbk cbk)
+-{
+- struct alsa_dev *ad = 0;
+- int mic_chosen_rate = 0;
+- int spk_chosen_rate = 0;
+-
+- DBG_DYNA_AUDIO_DRV("alsa_stream_open: (name: %s, rate: %d, framesize: %d)\n", name, wished_rate, framesize);
+- ad = calloc(sizeof(*ad), 1);
+- if (!ad)
+- {
+- return -PH_NORESOURCES;
+- }
+-
+- // 200ms may satisfied every sound card
+- ad->aout = alsa_dev_open(name, SND_PCM_STREAM_PLAYBACK, wished_rate, framesize / 2, 200, &spk_chosen_rate);
+- if (!ad->aout)
+- {
+- free(ad);
+- return -PH_NORESOURCES;
+- }
+-
+- ad->ain = alsa_dev_open(name, SND_PCM_STREAM_CAPTURE, wished_rate, framesize / 2, 0, &mic_chosen_rate);
+- if (!ad->ain)
+- {
+- // close the playback device if we fail to open capture device
+- snd_pcm_close(ad->aout);
+- free(ad);
+- return -PH_NORESOURCES;
+- }
+-
+- // TODO: refactor the actual_rate across all phaudio_driver backends
+- // it should be an OUT parameter in the dev_open prototype
+- // here, hopefully, both MIC and SPK devices have been opened with the same rate
+- // we use only the MIC rate
+- as->actual_rate = mic_chosen_rate;
+- DBG_DYNA_AUDIO_DRV("alsa_stream_open: chosen rate (freq)=(%d)\n",
+- as->actual_rate);
++static snd_pcm_uframes_t pcm_data_get_silence_size( pcm_data* pcm){
+
+- as->drvinfo = ad;
+- PH_SNDDRVR_USE();
++ return ( pcm->silence_size );
++}
++
++static snd_pcm_uframes_t pcm_data_get_silence_threshold( pcm_data* pcm){
+
+- return 0;
++ return ( pcm->silence_threshold );
+ }
+
+-void alsa_stream_close(phastream_t *as)
+-{
+- DBG_DYNA_AUDIO_DRV("alsa_stream_close\n");
+
+- if (!as->drvinfo)
+- {
+- DBG_DYNA_AUDIO_DRV("alsa stream already closed\n");
+- return;
+- }
++static int open_device( snd_pcm_t** handle,
++ const char* name,
++ snd_pcm_format_t type) {
++
++ const ph_bool input = ( type == SND_PCM_STREAM_CAPTURE);
++ const char* keyword = ( input ? "in" : "out");
++
++ char string[128];
++ char* strtok_ptr;
++ char* device = NULL;
++ int rc;
++
++ if ( name == NULL) {
++ ALSA_ERROR( "alsa_dev_open: name == NULL!");
++ return -1;
++ }
++ strncpy( string, name, sizeof( string));
++ device = strtok_r( string, ":", &strtok_ptr);
++ if( strcasecmp( device, "alsa") == 0) {
++ while( strcasecmp( device, keyword) != 0 && device != NULL) {
++ device = strtok_r( NULL, " =", &strtok_ptr);
++ }
++ if( device != NULL) {
++ device = strtok_r( NULL, " =", &strtok_ptr);
++ }
++ if( device == NULL) {
++ ALSA_ERROR( "Illegal device string: %s", name);
++ return -1;
++ }
++ }
++
++ rc = snd_pcm_open( handle, device, type, SND_PCM_NONBLOCK);
++ if (rc < 0){
++ ALSA_ERROR( "Cannot open pcm device %s: %s",
++ name, snd_strerror( rc));
++ return rc;
++ }
++ ALSA_DEBUG( "open pcm device %s for %s: OK",
++ name, input ? "input" : "output");
++ return 0;
++}
+
+- snd_pcm_drop(ADEV(as)->ain);
+- snd_pcm_drop(ADEV(as)->aout);
+- snd_pcm_close(ADEV(as)->ain);
+- snd_pcm_close(ADEV(as)->aout);
++static int set_hw_params( snd_pcm_stream_t type,
++ pcm_data* pcm,
++ snd_pcm_hw_params_t* params) {
++
++ snd_pcm_uframes_t frames;
++ int rc;
++
++ /* Fill it in with default values. */
++ snd_pcm_hw_params_any( pcm->device, params);
++
++ /* Set the desired hardware parameters. */
++ rc = snd_pcm_hw_params_set_channels_near( pcm->device,
++ params,
++ &pcm->channels);
++ if ( rc < 0) {
++ ALSA_ERROR("Unable to set hw parameters(channel): %s",
++ snd_strerror( rc));
++ return( rc );
++ }
++
++ if( pcm->channels == 2) {
++ // We will expand the mono chunk to a stereo chunk.
++ pcm->chunk_size *= 2;
++ }
++
++ rc = snd_pcm_hw_params_set_access( pcm->device,
++ params,
++ SND_PCM_ACCESS_RW_INTERLEAVED);
++ if( rc < 0) {
++ ALSA_ERROR(
++ "Set access SND_PCM_ACCESS_RW_INTERLEAVED: %s",
++ snd_strerror(rc));
++ return( rc );
++ }
++
++ /* Signed 16-bit little-endian format. */
++ rc = snd_pcm_hw_params_set_format( pcm->device,
++ params,
++ PCM_FORMAT);
++ if ( rc < 0) {
++ ALSA_ERROR( "Unable to set format %s: %s",
++ "SND_PCM_FORMAT_S16_LE",
++ snd_strerror( rc));
++ return( rc );
++ }
++ rc = snd_pcm_hw_params_set_rate_near( pcm->device,
++ params,
++ &pcm->rate,
++ 0);
++ if ( rc < 0) {
++ ALSA_ERROR( "Unable to set hw parameters: %s",
++ snd_strerror( rc));
++ return( rc );
++ }
++
++ frames = pcm_data_get_period_size( params, pcm);
++ rc = snd_pcm_hw_params_set_period_size_near( pcm->device,
++ params,
++ &frames,
++ 0);
++ if ( rc < 0) {
++ ALSA_ERROR(
++ "Unable to set hw params (period_size=%d): %s",
++ (int) frames, snd_strerror( rc));
++ return( rc );
++ }
++
++ frames = pcm_data_get_buffer_size( params, pcm);
++ rc = snd_pcm_hw_params_set_buffer_size_near( pcm->device,
++ params,
++ &frames);
++ if( rc < 0) {
++ ALSA_ERROR( "Unable to set hw buffer size: %s",
++ snd_strerror( rc));
++ return( rc );
++ }
++ /* Write the parameters to the driver */
++ rc = snd_pcm_hw_params( pcm->device, params);
++ if ( rc < 0) {
++ ALSA_ERROR( "Unable to set hw params: %s",
++ snd_strerror( rc));
++ return( rc );
++ }
+
+- free(as->drvinfo);
+- as->drvinfo = 0;
++ return( 0 );
+
+- PH_SNDDRVR_UNUSE();
+ }
+
+-void alsa_stream_start(phastream_t *as)
++static int set_sw_params( snd_pcm_stream_t type,
++ pcm_data* pcm,
++ snd_pcm_hw_params_t* hw_params )
+ {
+- snd_pcm_start(ADEV(as)->ain);
+- snd_pcm_start(ADEV(as)->aout);
++ int rc;
++ snd_pcm_sw_params_t *sparams = 0;
++ snd_pcm_uframes_t frames;
++
++ snd_pcm_sw_params_alloca( &sparams);
++
++ /* retrieve the parameters from the driver */
++ rc = snd_pcm_sw_params_current( pcm->device, sparams);
++ assert( rc == 0 );
++
++ frames = pcm_data_get_threshold( pcm);
++ rc = snd_pcm_sw_params_set_start_threshold( pcm->device,
++ sparams,
++ frames);
++ assert( rc == 0);
++
++ frames = pcm_data_get_period_size( hw_params, pcm);
++ rc = snd_pcm_sw_params_set_avail_min( pcm->device,
++ sparams,
++ frames);
++ assert( rc == 0);
++
++ if( type == SND_PCM_STREAM_PLAYBACK) {
++ frames = pcm_data_get_silence_threshold( pcm);
++ rc = snd_pcm_sw_params_set_silence_threshold( pcm->device,
++ sparams,
++ frames);
++ assert( rc == 0);
++
++ frames = pcm_data_get_silence_size( pcm);
++ rc = snd_pcm_sw_params_set_silence_size( pcm->device,
++ sparams,
++ frames);
++ assert( rc == 0);
++ }
++
++ /* Write the parameters to the driver */
++ rc = snd_pcm_sw_params( pcm->device, sparams);
++ if( rc < 0) {
++ ALSA_ERROR( "Unable to set sw parameters: %s",
++ snd_strerror( rc));
++ return( rc);
++ }
++ return( 0);
++
+ }
+
+-int suspend(snd_pcm_t *handle)
+-{
+- int res = 0;
++static snd_pcm_uframes_t
++wp_bytes_to_frames( snd_pcm_hw_params_t* params, size_t bytes) {
+
+- // wait until suspend flag is released
+- while ((res = snd_pcm_resume(handle)) == -EAGAIN)
+- {
+- sleep(1);
+- }
+- if (res < 0)
+- {
+- // failed to restart stream, let's try to repair
+- DBG_DYNA_AUDIO_DRV("failed restarting stream: %s\n", snd_strerror(res));
+- if ((res = snd_pcm_prepare(handle)) < 0)
+- {
+- DBG_DYNA_AUDIO_DRV("suspend error: %s\n", snd_strerror(res));
+- return 1;
+- }
+- }
+- return 0;
++ unsigned channels;
++ int res;
++ snd_pcm_format_t format;
++ int samplesize;
++
++ res = snd_pcm_hw_params_get_channels( params, &channels);
++ assert( res >= 0 && channels >= 1);
++ res = snd_pcm_hw_params_get_format( params, &format);
++ assert( res >= 0);
++ samplesize = snd_pcm_format_size( format, 1);
++ assert( samplesize > 0);
++ return bytes / (samplesize * channels);
+ }
+
+-int alsa_stream_write(phastream_t *as, void *buf, int len)
+-{
+- int res = 0;
+- int total = 0;
++static int alsa_dev_open( const char* name,
++ snd_pcm_stream_t type,
++ pcm_data* pcm) {
++
++ int rc;
++ snd_pcm_hw_params_t* hw_params;
++
++ snd_pcm_hw_params_alloca( &hw_params);
++
++ ALSA_DEBUG( "open_device: %s (name: %s, rate: %d, chunk: %d)",
++ type == SND_PCM_STREAM_CAPTURE ? "input" : "output",
++ name, pcm->rate, pcm->chunk_size);
++
++ rc = open_device( &pcm->device, name, type);
++ if( rc < 0 ) {
++ return ( rc);
++ };
++ rc = set_hw_params( type, pcm, hw_params );
++ snd_pcm_nonblock( pcm->device, 1);
++ dump_pcm_data( pcm->device, hw_params);
++ if( rc == 0) {
++ rc = set_sw_params( type, pcm, hw_params);
++ }
++
++ if( rc < 0 && pcm->device != 0) {
++ snd_pcm_close( pcm->device );
++ }
++ return rc;
++}
+
+- while(total < len / 2)
+- {
+- res = snd_pcm_writei(ADEV(as)->aout, buf, len / 2);
+- if (res < 0)
+- {
+- if (res == -EAGAIN)
+- {
+- DBG_DYNA_AUDIO_DRV("must wait: %s\n", snd_strerror(res));
+- if (snd_pcm_wait(ADEV(as)->aout, 1000) < 0)
+- {
+- DBG_DYNA_AUDIO_DRV("snd_pcm_wait failed: %s\n", snd_strerror(res));
+- return 0;
+- }
+- continue;
+- }
+- else if (res == -EPIPE)
+- {
+- DBG_DYNA_AUDIO_DRV("overrun: %s\n", snd_strerror(res));
+- // PCM device reached overrun, let's recover and try again
+- if (snd_pcm_prepare(ADEV(as)->aout) < 0)
+- {
+- DBG_DYNA_AUDIO_DRV("snd_pcm_prepare failed: %s\n", snd_strerror(res));
+- return 0;
+- }
+- continue;
+- }
+- else if (res == -ESTRPIPE)
+- {
+- // the system has suspended drivers, let's suspend and try again
+- DBG_DYNA_AUDIO_DRV("driver has been suspended: %s\n", snd_strerror(res));
+- if (suspend(ADEV(as)->aout))
+- {
+- DBG_DYNA_AUDIO_DRV("suspend failed\n");
+- return 0;
+- }
+- continue;
+- }
+- }
+- else
+- {
+-#if ACTIVATE_DYNA_AUDIO_DRV_DBG
+- if (res != len / 2)
+- {
+- // failed to write all data
+- DBG_DYNA_AUDIO_DRV("%d samples has been written instead of %d\n", res, len / 2);
+- }
+-#endif
+- total += res;
+- }
+- }
+
+- return total * 2;
++static int alsa_stream_open( phastream_t* as,
++ const char* name,
++ int wished_rate,
++ int chunk_size,
++ ph_audio_cbk cbk) {
++
++ alsa_drv_info* ad = 0;
++ pcm_data input_dev;
++ pcm_data output_dev;
++
++ ALSA_DEBUG(
++ "alsa_stream_open: (name: %s, rate: %d, chunk: %d)",
++ name, wished_rate, chunk_size);
++ ad = calloc( sizeof( alsa_drv_info), 1);
++ if ( !ad) {
++ return -PH_NORESOURCES;
++ }
++
++ pcm_data_new( &output_dev, wished_rate, chunk_size, OUTPUT_LATENCY);
++ alsa_dev_open(name, SND_PCM_STREAM_PLAYBACK, &output_dev);
++ ad->output.pcm = output_dev.device;
++ if ( !ad->output.pcm) {
++ free( ad);
++ return -PH_NOAUDIODEVICE;
++ }
++ ad->output.id = "output";
++
++ pcm_data_new( &input_dev, wished_rate, chunk_size, INPUT_LATENCY);
++ alsa_dev_open( name, SND_PCM_STREAM_CAPTURE, &input_dev);
++ ad->input.pcm = input_dev.device;
++ if ( !ad->input.pcm) {
++ // close the playback device if we fail to open capture device
++ snd_pcm_close( ad->output.pcm);
++ free( ad);
++ return -PH_NOAUDIODEVICE;
++ }
++ ad->input.id = "input";
++
++ if( input_dev.rate != output_dev.rate)
++ {
++ ALSA_ERROR( "Mic speed %d differs from speaker rate %d",
++ input_dev.rate, output_dev.rate);
++ ALSA_ERROR( "Lets try anyway, but this is scary...");
++ }
++
++ // TODO: refactor the actual_rate across all phaudio_driver backends
++ // it should be an OUT parameter in the dev_open prototype
++ // here, hopefully, both MIC and SPK devices have been opened with
++ // the same rate. We use only the MIC rate.
++
++ as->actual_rate = input_dev.rate;
++ ad->channels = input_dev.channels;
++ ad->sample_size = snd_pcm_format_size( PCM_FORMAT, 1);
++ ad->chunk_size = chunk_size;
++ ad->callback = cbk;
++ ALSA_DEBUG( "alsa_stream_open: chosen rate (freq)=(%d)",
++ as->actual_rate);
++ as->drvinfo = ad;
++ PH_SNDDRVR_USE();
++
++ return 0;
++ }
++
++static void alsa_stream_close( phastream_t *as) {
++
++ alsa_dev* input = &( DRV_INFO(as)->input);
++ alsa_dev* output = &( DRV_INFO(as)->output);
++
++ ALSA_DEBUG( "alsa_stream_close");
++ if ( !as->drvinfo){
++ ALSA_DEBUG( "ALSA streams already closed");
++ return;
++ }
++
++ ALSA_DEBUG( "Stop playback thread");
++ output->stop_thread = 1;
++ pthread_join( output->thread, NULL);
++ snd_pcm_drop( output->pcm);
++ snd_pcm_close( output->pcm);
++ ALSA_DEBUG(
++ "Output: (sent,rebuffered,again,soft,hard): %ld %ld %d, %d, %d",
++ output->bytes,
++ output->rebuffered,
++ output->again_errors,
++ output->soft_errors,
++ output->hard_errors);
++
++ ALSA_DEBUG( "Stop recorder thread");
++ input->stop_thread = 1;
++ pthread_join( input->thread, NULL);
++ snd_pcm_drop( input->pcm );
++ snd_pcm_close( input->pcm);
++ ALSA_DEBUG(
++ "Input: received %ld, errors(again, soft,hard) : %d, %d, %d",
++ input->bytes,
++ input->again_errors,
++ input->soft_errors,
++ input->hard_errors);
++
++ free( as->drvinfo);
++ as->drvinfo = 0;
++
++ PH_SNDDRVR_UNUSE();
++}
++
++static void alsa_stream_start( phastream_t *as) {
++
++ alsa_dev* output = &( DRV_INFO( as)->output);
++ alsa_dev* input = &( DRV_INFO( as)->input);
++ int rc;
++
++ // Output start automagically when the buffer exceeds threshold.
++ ALSA_DEBUG( "Streams ready to start");
++
++ output->stop_thread = 0;
++ rc = pthread_create( &output->thread,
++ NULL,
++ playback_thread,
++ (void*) as);
++ assert( rc == 0);
++ ALSA_DEBUG( "Playback thread started" );
++
++ input->stop_thread = 0;
++ rc = pthread_create( &input->thread,
++ NULL,
++ recorder_thread,
++ (void*) as);
++ assert( rc == 0);
++ ALSA_DEBUG( "Recorder thread started");
+ }
+
+-int alsa_stream_read(phastream_t *as, void *buf, int len)
++static void mono2stereo( char* dest,
++ char* src,
++ size_t size,
++ size_t sample_size) {
++
++ char* last_in_buffer = src + size - sample_size;
++ char* stereo = dest + 2 * size - sample_size;
++ char* mono;
++
++ for( mono = last_in_buffer; mono >= src; mono -= sample_size) {
++ memcpy( stereo, mono, sample_size) ;
++ stereo -= sample_size;
++ memcpy( stereo, mono, sample_size) ;
++ stereo -= sample_size;
++ }
++
++}
++
++static void ph_handle_pcm_error( alsa_dev* device, int err )
+ {
+- int res = 0;
+- int total = 0;
+
+- while(total < len / 2)
+- {
+- res = snd_pcm_readi(ADEV(as)->ain, buf, len / 2);
+- if (res < 0)
+- {
+- if (res == -EAGAIN)
+- {
+- DBG_DYNA_AUDIO_DRV("must wait: %s\n", snd_strerror(res));
+- if (snd_pcm_wait(ADEV(as)->ain, 1000) < 0)
+- {
+- DBG_DYNA_AUDIO_DRV("snd_pcm_wait failed: %s\n", snd_strerror(res));
+- return 0;
+- }
+- continue;
+- }
+- else if (res == -EPIPE)
+- {
+- // PCM device reached underrun, let's recover and try again
+- DBG_DYNA_AUDIO_DRV("underrun: %s\n", snd_strerror(res));
+- if (snd_pcm_prepare(ADEV(as)->ain) < 0)
+- {
+- DBG_DYNA_AUDIO_DRV("snd_pcm_prepare failed: %s\n", snd_strerror(res));
+- return 0;
+- }
+- continue;
+- }
+- else if (res == -ESTRPIPE)
+- {
+- // the system has suspended drivers, let's suspend and try again
+- DBG_DYNA_AUDIO_DRV("driver has been suspended: %s\n", snd_strerror(res));
+- if (suspend(ADEV(as)->ain))
+- {
+- DBG_DYNA_AUDIO_DRV("suspend failed\n");
+- return 0;
++ assert( err < 0 );
++ if( err == -EAGAIN){
++ device->again_errors += 1;
++ }
++ else {
++ device->soft_errors += 1;
++ err = snd_pcm_recover( device->pcm, err, ALSA_SILENT);
++ if( err < 0) {
++ ALSA_ERROR( "Can't restore ALSA %s: %s",
++ device->id, snd_strerror( err));
++ device->hard_errors += 1;
++ snd_pcm_prepare( device->pcm);
++ }
++ }
++}
++
++static int stream_write( alsa_dev* output, void *buf, int len){
++
++ snd_pcm_uframes_t frames;
++ int res = 0;
++ ssize_t bytes;
++
++
++ frames = snd_pcm_bytes_to_frames( output->pcm, len);
++ res = snd_pcm_writei( output->pcm, buf, frames);
++ if( res < 0 ){
++ ph_handle_pcm_error( output, res );
++ bytes = 0;
++ }
++ else{
++ bytes = snd_pcm_frames_to_bytes( output->pcm, res);
++ }
++ return bytes;
++}
++
++static int alsa_stream_write( phastream_t *as, void *buf, int len) {
++
++ char* ph_buf;
++ ssize_t bytes;
++ alsa_dev* output = &( DRV_INFO( as)->output);
++
++ assert( len >=0 && len <= (int) DRV_INFO( as)->chunk_size);
++
++ if( DRV_INFO( as)->channels == 1) {
++ bytes = stream_write( output, buf, len);
++ }
++ else {
++ ph_buf = alloca( len * 2);
++ mono2stereo( ph_buf, buf, len, DRV_INFO(as)->sample_size);
++ bytes = stream_write( output, ph_buf, len * 2) / 2;
++ }
++ output->bytes += bytes;
++
++ assert( bytes >= 0 &&
++ bytes <= (ssize_t) DRV_INFO( as)->chunk_size);
++ return bytes;
++}
++
++static size_t stereo2mono( char* dest_arg,
++ char* src_arg,
++ size_t size,
++ size_t sample_size)
++{
++ long val;
++ short* src = (short*) src_arg;
++ short* dest= (short*) dest_arg;
++
++ assert( sample_size == 2); // This only works for 16bit signed data.
++ assert( sizeof( short ) == 2);
++
++#ifdef MONO_AVERAGE
++ while( (char*) src < src_arg + size) {
++ val = *src++;
++ val += *src++;
++
++ *dest = (short)(val / 2);
++ dest++;
++ }
++#else
++ while( (char*) src < buf + size) {
++ *dest = *(src + (src % 4) / 2);
++ src += 2;
++ dest += 1;
+ }
+- continue;
+- }
+- }
+- else
+- {
+-#if ACTIVATE_DYNA_AUDIO_DRV_DBG
+- if (res != len / 2)
+- {
+- // failed to write all data
+- DBG_DYNA_AUDIO_DRV("%d samples has been written instead of %d\n", res, len / 2);
+- }
+ #endif
+- total += res;
+- break;
+- }
+- }
++ return size / 2;
++}
++
++static int stream_read(alsa_dev* input, void *buf, int len) {
+
+- return res * 2;
++ snd_pcm_uframes_t frames;
++ int res = 0;
++ unsigned bytes;
++
++ frames = snd_pcm_bytes_to_frames( input->pcm, len);
++ res = snd_pcm_readi( input->pcm, buf, frames);
++ if ( res == 0) {
++ ALSA_DEBUG( "Empty read!");
++ bytes = 0;
++ }
++ else if ( res < 0) {
++ ph_handle_pcm_error( input, res );
++ bytes = 0;
++ }
++ else{
++ bytes = snd_pcm_frames_to_bytes( input->pcm, res);
++ }
++ return bytes;
+ }
+
+-int alsa_stream_get_out_space(phastream_t *as, int *used)
+-{
+- snd_pcm_status_t *st;
+- snd_pcm_status_alloca(&st);
++static int alsa_stream_read( phastream_t *as, void *buf, int len) {
++
++ char* bigger_buf;
++ size_t bytes = len;
++ alsa_dev* input = &( DRV_INFO( as)->input);
++
++ assert( len <= (int) DRV_INFO( as)->chunk_size);
++
++ if( DRV_INFO( as)->channels == 1) {
++ bytes = stream_read( input, buf, len);
++ }
++ else {
++ assert( DRV_INFO( as)->channels == 2);
++ bigger_buf = alloca( len * 2);
++ bytes = stream_read( input, bigger_buf, len * 2);
++ bytes = stereo2mono( buf, bigger_buf, bytes, 2);
++ }
++ input->bytes += bytes;
++ assert( (int) bytes <= len);
++ return( bytes);
++}
+
+- if (snd_pcm_status(ADEV(as)->aout, st) < 0)
+- {
+- *used = 0;
+- return 320;
+- }
++static int alsa_stream_get_out_space( phastream_t *as, int *used) {
++ snd_pcm_status_t *st;
++ snd_pcm_uframes_t available;
++ snd_pcm_t* output = DRV_INFO( as)->output.pcm;
++
++ snd_pcm_status_alloca( &st);
++
++ if ( snd_pcm_status( output, st) < 0) {
++ *used = 0;
++ return 320; //FIXME! Whats this?!
++ }
++
++ *used = snd_pcm_frames_to_bytes( output,
++ snd_pcm_status_get_delay( st));
+
+- *used = 2 * (int) snd_pcm_status_get_delay(st);
+- return 2 * (int) snd_pcm_status_get_avail(st);
++ available = snd_pcm_status_get_avail( st);
++ ALSA_DEBUG( "Get_out_space, used: %d, available:%d",
++ *used, available);
++ return snd_pcm_frames_to_bytes( output, available);
+ }
+
+-int alsa_stream_get_avail_data(phastream_t *as)
+-{
+- snd_pcm_status_t *st;
+- snd_pcm_status_alloca(&st);
+
+- if (snd_pcm_status(ADEV(as)->ain, st) < 0)
+- {
+- return 0;
+- }
++static int alsa_stream_get_avail_data( phastream_t *as) {
+
+- return 2 * (int) snd_pcm_status_get_avail(st);
++ ALSA_ERROR( "alsa_stream_get_avail_data(): not implemented" );
++ return -1;
+ }
+
+-int alsa_stream_get_fds(phastream_t *as, int fds[2])
+-{
+- fds[0] = fds[1] = -1;
++static int alsa_stream_get_fds( phastream_t *as, int fds[2]) {
++
++ ALSA_ERROR( "alsa_stream_get_fds(): not implemented" );
++ return -1;
++}
++
++//
++// Timing: on a lightly loaded AMD x2 4200 I've measured
++// invocation of routines within +-3ms, without looking for
++// exceptions which sure are there.
++static void* playback_thread( void* arg) {
++
++ phastream_t* ps = (phastream_t*) arg;
++ alsa_dev* output = &( DRV_INFO( ps)->output);
++ const unsigned chunk_size = DRV_INFO( ps)->chunk_size;
++ int size;
++ unsigned sent;
++ char* buffer;
++ char* nxt_buff = buffer;
++ unsigned short prev_size = chunk_size;
++ int odd = 0;
++
++ buffer = alloca( 2 * chunk_size);
++ snd_pcm_format_set_silence( PCM_FORMAT, buffer, chunk_size);
++
++ while( !output->stop_thread) {
++ size = chunk_size;
++ odd = ( odd + 1) % 2;
++ nxt_buff = buffer + odd * chunk_size;
++ DRV_INFO( ps)->callback( ps, NULL, 0, nxt_buff, &size);
++ if( size == 0) {
++ size = prev_size;
++ odd = ( odd + 1) % 2;
++ nxt_buff = buffer + odd * chunk_size;
++ output->rebuffered += size;
++ }
++ else {
++ prev_size = size;
++ }
++ for( sent = 0; sent < (unsigned) size; ) {
++ sent += alsa_stream_write( ps,
++ nxt_buff + sent,
++ size - sent);
++ snd_pcm_wait( output->pcm, SND_PCM_WAIT_MS);
++ }
++ }
++ output->stop_thread++;
++ return( NULL);
++}
+
+- fds[0] = alsa_dev_get_fd(ADEV(as)->ain);
+- fds[1] = alsa_dev_get_fd(ADEV(as)->aout);
++static void* recorder_thread( void* arg) {
+
+- return 0;
++ phastream_t* ps = (phastream_t*) arg;
++ alsa_dev* input = &( DRV_INFO(ps)->input);
++ int res;
++ char* buffer;
++
++ buffer = alloca( DRV_INFO(ps)->chunk_size);
++
++ while( !input->stop_thread) {
++ snd_pcm_wait( input->pcm, SND_PCM_WAIT_MS);
++
++ res = alsa_stream_read( ps,
++ buffer,
++ DRV_INFO(ps)->chunk_size);
++ if( res <= 0) {
++ // Don't monopolize CPU if continous errors.
++ nanosleep( &io_wait, NULL);
++ continue;
++ }
++ DRV_INFO( ps)->callback(ps, buffer, res, NULL, 0);
++ }
++ input->stop_thread++;
++ return( NULL);
+ }
+
+ #endif //ENABLE_ALSA
Modified: wengophone/trunk/debian/patches/series
URL: http://svn.debian.org/wsvn/pkg-voip/wengophone/trunk/debian/patches/series?rev=5365&op=diff
==============================================================================
--- wengophone/trunk/debian/patches/series (original)
+++ wengophone/trunk/debian/patches/series Sun Mar 23 22:47:14 2008
@@ -1,5 +1,6 @@
head/alsa-plughw-default.patch
head/qt-fix-bad-window-size.patch
+head/phapi-alsa-code-from-2.2.patch
generic/qobjectthreadsafe-fix-qt42.patch
generic/cmake-fix-ENABLE_CRASHREPORT.patch
generic/cmake-static-coredumper.patch
More information about the Pkg-voip-commits
mailing list