summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorsamr7 <samr7@126591fb-c623-4b62-a76d-97a8e4f34109>2008-11-25 23:02:33 +0000
committersamr7 <samr7@126591fb-c623-4b62-a76d-97a8e4f34109>2008-11-25 23:02:33 +0000
commit05075c366990733e6c0aced5f8559cfd932e2bd4 (patch)
tree1428963ff6497d486304f622d17f82a1a4aec858
parent4aa52837906f909d00095e61b9def7781b83640c (diff)
downloadnohands-05075c366990733e6c0aced5f8559cfd932e2bd4.tar.gz
Add the stream snoop filter.
Fix the audiofile endpoint to support output. Fix minor problems in SoundIoPump, and the ALSA and OSS endpoints, discovered while testing streaming from files. Remove all non-character usage of char, replace with (u)int8_t. Update documentation. git-svn-id: http://nohands.svn.sourceforge.net/svnroot/nohands/trunk@43 126591fb-c623-4b62-a76d-97a8e4f34109
-rw-r--r--data/hfpd-dbus-doc.cs87
-rw-r--r--hfpd/objects.cpp219
-rw-r--r--hfpd/objects.h36
-rw-r--r--hfpd/proto.h1
-rw-r--r--include/libhfp/events.h2
-rw-r--r--include/libhfp/soundio.h87
-rw-r--r--libhfp/soundio-alsa.cpp8
-rw-r--r--libhfp/soundio-oss.cpp33
-rw-r--r--libhfp/soundio-pump.cpp41
-rw-r--r--libhfp/soundio-util.cpp237
-rw-r--r--test/pumpunit.cpp81
11 files changed, 726 insertions, 106 deletions
diff --git a/data/hfpd-dbus-doc.cs b/data/hfpd-dbus-doc.cs
index 64b6bdb..ce31886 100644
--- a/data/hfpd-dbus-doc.cs
+++ b/data/hfpd-dbus-doc.cs
@@ -872,14 +872,41 @@ namespace net.sf.nohands.hfpd {
in bool initiate_connection);
/**
+ * @brief Start a WAV file audio stream
+ *
+ * Opens a .WAV file and initiates unidirectional
+ * streaming either from the file to the primary sound
+ * card or from the primary sound card to the file.
+ *
+ * This mode of streaming is useful for playing a
+ * ring tone, or for recording a voice note.
+ *
+ * @param[in] file_path Path to WAV file to be created
+ * and streamed to, or existing WAV file to be played
+ * back.
+ * @param[in] write Set to @c true to cause the file to
+ * be streamed into and created if necessary, @c false
+ * to play back from the file.
+ *
+ * If the local sound card is already streaming, or
+ * preparing to stream with an audio gateway, the
+ * operation in progress will be aborted similar to
+ * Stop().
+ *
+ * @throw net.sf.nohands.hfpd.Error Thrown on any
+ * sort of error, unspecific of the reason of failure.
+ */
+ public FileStart(in string file_path, in bool write);
+
+ /**
* @brief Start a loopback audio stream
*
* Starts streaming the input from the local sound card
- * into the output of the local sound card.
+ * to the output to the local sound card.
*
- * This mode of streaming is useful to test the local
- * sound card configuration and subjectively evaluate the
- * latency resulting from the buffering settings.
+ * This mode of streaming is useful for testing the local
+ * sound card configuration and to subjectively evaluate
+ * the latency resulting from the buffering settings.
*
* If the local sound card is already streaming, or
* preparing to stream with a different audio gateway
@@ -940,6 +967,42 @@ namespace net.sf.nohands.hfpd {
public MembufClear();
/**
+ * @brief Configure a WAV file to receive audio pump
+ * traffic
+ *
+ * The HFPD audio handling components support snooping
+ * the stream traffic to a WAV file. This can be used
+ * to create voice notes and recordings of telephony
+ * sessions. Each time the stream is started, if a snoop
+ * file is configured, it is created, or truncated if it
+ * already exists, and filled with stream data as it is
+ * received.
+ *
+ * The snoop file can be configured to receive audio data
+ * from one or both directions of the stream. If configured
+ * to receive both directions, the audio data will be
+ * mixed before being saved to the target file.
+ *
+ * @param[in] filename Path and name of snoop file. If
+ * this value is zero-length, snooping is disabled.
+ * @param[in] capture If @c true, data captured from the
+ * local sound card will be snooped.
+ * @param[in] playback If @c true, data played through the
+ * local sound card will be snooped.
+ *
+ * @note If the contents of the snoop file are to be
+ * preserved, the snoop file should be deconfigured after
+ * streaming has been stopped to avoid having the file
+ * overwritten the next time streaming is started.
+ * @note Check local laws before creating recordings of
+ * telephone calls.
+ *
+ * @sa SoundIo.SnoopFileName
+ */
+ public SetSnoopFile(in string filename,
+ in bool capture, in bool playback);
+
+ /**
* @brief State of the SoundIo object
*
* This property can be accessed using the
@@ -954,12 +1017,13 @@ namespace net.sf.nohands.hfpd {
* - 2 = Stopped, not streaming
* - 3 = Audio gateway configured but awaiting connection
* - 4 = Audio gateway connected and streaming
- * - 5 = Loopback endpoint streaming
- * - 6 = Memory buffer endpoint streaming
+ * - 5 = File endpoint streaming
+ * - 6 = Loopback endpoint streaming
+ * - 7 = Memory buffer endpoint streaming
*
* The state cannot be modified directly. Instead use the
- * methods Stop(), AudioGatewayStart(), LoopbackStart(),
- * and MembufStart().
+ * methods Stop(), AudioGatewayStart(), FileStart(),
+ * LoopbackStart(), and MembufStart().
*/
const byte State;
@@ -1006,6 +1070,13 @@ namespace net.sf.nohands.hfpd {
const variant AudioGateway;
/**
+ * @brief Target file for stream snooper
+ *
+ * See SetSnoopFile().
+ */
+ const string SnoopFileName;
+
+ /**
* @brief List of available local sound card drivers
*
* This property can be accessed using the
diff --git a/hfpd/objects.cpp b/hfpd/objects.cpp
index 4333e59..ba2d5ce 100644
--- a/hfpd/objects.cpp
+++ b/hfpd/objects.cpp
@@ -367,7 +367,7 @@ AudioState(void)
bool AudioGateway::
UpdateState(AudioGatewayState st)
{
- const unsigned char state = (char) st;
+ const uint8_t state = (uint8_t) st;
dbus_bool_t dc;
dc = ((st == HFPD_AG_DISCONNECTED) &&
@@ -388,7 +388,7 @@ UpdateState(AudioGatewayState st)
bool AudioGateway::
UpdateCallState(AudioGatewayCallState st)
{
- const unsigned char state = (char) st;
+ const uint8_t state = (uint8_t) st;
if ((st != m_call_state) &&
!SendSignalArgs(HFPD_AUDIOGATEWAY_INTERFACE_NAME,
"CallStateChanged",
@@ -403,7 +403,7 @@ UpdateCallState(AudioGatewayCallState st)
bool AudioGateway::
UpdateAudioState(AudioGatewayAudioState st)
{
- const unsigned char state = (char) st;
+ const uint8_t state = (uint8_t) st;
if ((st != m_audio_state) &&
!SendSignalArgs(HFPD_AUDIOGATEWAY_INTERFACE_NAME,
"AudioStateChanged",
@@ -813,21 +813,21 @@ CallTransfer(DBusMessage *msgp)
bool AudioGateway::
-GetState(DBusMessage *msgp, unsigned char &val)
+GetState(DBusMessage *msgp, uint8_t &val)
{
val = State();
return true;
}
bool AudioGateway::
-GetCallState(DBusMessage *msgp, unsigned char &val)
+GetCallState(DBusMessage *msgp, uint8_t &val)
{
val = CallState();
return true;
}
bool AudioGateway::
-GetAudioState(DBusMessage *msgp, unsigned char &val)
+GetAudioState(DBusMessage *msgp, uint8_t &val)
{
val = AudioState();
return true;
@@ -1856,14 +1856,14 @@ SetAutoRestart(DBusMessage *msgp, const bool &val, bool &doreply)
}
bool HandsFree::
-GetSecMode(DBusMessage *msgp, unsigned char &val)
+GetSecMode(DBusMessage *msgp, uint8_t &val)
{
val = m_hfp->GetSecMode();
return true;
}
bool HandsFree::
-SetSecMode(DBusMessage *msgp, const unsigned char &val, bool &doreply)
+SetSecMode(DBusMessage *msgp, const uint8_t &val, bool &doreply)
{
ErrorInfo error;
rfcomm_secmode_t old;
@@ -2157,7 +2157,8 @@ SoundIoObj(HandsFree *hfp)
m_state(HFPD_SIO_DECONFIGURED), m_state_sent(HFPD_SIO_DECONFIGURED),
m_ringtone(0), m_sigproc(0),
m_membuf(0), m_membuf_size(0),
- m_config(hfp->m_config)
+ m_config(hfp->m_config),
+ m_snoop(0), m_snoop_ep(0), m_snoop_filename(0)
{
}
@@ -2271,6 +2272,7 @@ failed:
void SoundIoObj::
Cleanup(void)
{
+ CleanupSnoop();
if (GetDbusSession()) {
GetDbusSession()->UnexportObject(this);
}
@@ -2284,6 +2286,24 @@ Cleanup(void)
}
}
+void SoundIoObj::
+CleanupSnoop(void)
+{
+ if (m_snoop) {
+ m_sound->RemoveFilter(m_snoop);
+ delete m_snoop;
+ m_snoop = 0;
+ }
+ if (m_snoop_ep) {
+ delete m_snoop_ep;
+ m_snoop_ep = 0;
+ }
+ if (m_snoop_filename) {
+ free(m_snoop_filename);
+ m_snoop_filename = 0;
+ }
+}
+
bool SoundIoObj::
UpdateState(SoundIoState st, ErrorInfo *reason)
{
@@ -2313,7 +2333,9 @@ UpdateState(SoundIoState st, ErrorInfo *reason)
return false;
}
- if (reason) {
+ if (reason &&
+ !reason->Matches(LIBHFP_ERROR_SUBSYS_SOUNDIO,
+ LIBHFP_ERROR_SOUNDIO_DATA_EXHAUSTED)) {
assert(st == HFPD_SIO_STOPPED);
/*
@@ -2344,7 +2366,9 @@ UpdateState(SoundIoState st, ErrorInfo *reason)
void SoundIoObj::
EpRelease(SoundIoState st, ErrorInfo *reason)
{
+ SoundIo *ep;
SoundIoFilter *fltp;
+ bool res;
if (st == HFPD_SIO_INVALID)
st = m_state;
@@ -2353,17 +2377,20 @@ EpRelease(SoundIoState st, ErrorInfo *reason)
case HFPD_SIO_STOPPED:
assert(!m_sound->IsStarted());
assert(!m_sound->GetSecondary());
+ assert(m_sound->IsDspEnabled());
assert(!m_bound_ag);
break;
case HFPD_SIO_AUDIOGATEWAY:
assert(m_bound_ag);
assert(m_sound->GetSecondary() == m_bound_ag->GetSoundIo());
+ assert(m_sound->IsDspEnabled());
m_sound->Stop();
m_sound->SetSecondary(0);
/* fall-thru */
case HFPD_SIO_AUDIOGATEWAY_CONNECTING:
assert(m_bound_ag);
assert(!m_sound->GetSecondary());
+ assert(m_sound->IsDspEnabled());
if (m_bound_ag->m_audio_bind == this)
m_bound_ag->m_audio_bind = 0;
m_bound_ag->GetSoundIo()->SndClose();
@@ -2371,6 +2398,17 @@ EpRelease(SoundIoState st, ErrorInfo *reason)
m_bound_ag->Put();
m_bound_ag = 0;
break;
+ case HFPD_SIO_FILE:
+ ep = m_sound->GetSecondary();
+ assert(ep);
+ m_sound->Stop();
+ m_sound->SetSecondary(0);
+ assert(!m_sound->IsDspEnabled());
+ res = m_sound->SetDspEnabled(true);
+ assert(res);
+ ep->SndClose();
+ delete ep;
+ break;
case HFPD_SIO_LOOPBACK:
assert(!m_sound->GetSecondary());
m_sound->Stop();
@@ -2484,6 +2522,57 @@ EpAudioGatewayComplete(AudioGateway *agp, ErrorInfo *error)
}
bool SoundIoObj::
+EpFile(const char *filename, bool writing, libhfp::ErrorInfo *error)
+{
+ static const SoundIoFormat file_out_fmt = {
+ SIO_PCM_S16_LE,
+ 8000,
+ 0,
+ 1,
+ 2
+ };
+
+ SoundIoFormat xfmt;
+ SoundIo *ep;
+ bool res;
+
+ ep = SoundIoCreateFileHandler(GetDi(), filename, writing, error);
+ if (!ep)
+ return false;
+
+ if (writing) {
+ memcpy(&xfmt, &file_out_fmt, sizeof(xfmt));
+ if (!ep->SndSetFormat(xfmt, error)) {
+ delete ep;
+ return false;
+ }
+ }
+
+ if (!ep->SndOpen(writing, !writing, error)) {
+ delete ep;
+ return false;
+ }
+
+ if (m_state != HFPD_SIO_STOPPED)
+ EpRelease();
+ assert(m_state == HFPD_SIO_STOPPED);
+ EpRelease(); /* Call again to run the assertions */
+
+ res = m_sound->SetDspEnabled(false);
+ assert(res);
+ res = m_sound->SetSecondary(ep);
+ assert(res);
+ if (!m_sound->Start(false, false, error)) {
+ GetDi()->LogWarn("Could not start stream in file %s mode",
+ writing ? "capture" : "playback");
+ EpRelease(HFPD_SIO_FILE);
+ return false;
+ }
+ (void) UpdateState(HFPD_SIO_FILE);
+ return true;
+}
+
+bool SoundIoObj::
EpLoopback(ErrorInfo *error)
{
if (m_state != HFPD_SIO_STOPPED)
@@ -2772,6 +2861,35 @@ AudioGatewayStart(DBusMessage *msgp)
}
bool SoundIoObj::
+FileStart(DBusMessage *msgp)
+{
+ DBusMessageIter mi;
+ char *filename;
+ dbus_bool_t for_write;
+ ErrorInfo error;
+ bool res;
+
+ res = dbus_message_iter_init(msgp, &mi);
+ assert(res);
+ assert(dbus_message_iter_get_arg_type(&mi) == DBUS_TYPE_STRING);
+ dbus_message_iter_get_basic(&mi, &filename);
+ res = dbus_message_iter_next(&mi);
+ assert(res);
+ assert(dbus_message_iter_get_arg_type(&mi) == DBUS_TYPE_BOOLEAN);
+ dbus_message_iter_get_basic(&mi, &for_write);
+
+ if (!EpFile(filename, for_write, &error))
+ return SendReplyErrorInfo(msgp, error);
+
+ if (!SendReplyArgs(msgp, DBUS_TYPE_INVALID)) {
+ EpRelease();
+ return false;
+ }
+
+ return true;
+}
+
+bool SoundIoObj::
LoopbackStart(DBusMessage *msgp)
{
ErrorInfo error;
@@ -2871,9 +2989,79 @@ MembufStart(DBusMessage *msgp)
return true;
}
+bool SoundIoObj::
+SetSnoopFile(DBusMessage *msgp)
+{
+ DBusMessageIter mi;
+ char *filename, *fncopy;
+ dbus_bool_t in, out;
+ ErrorInfo error;
+ SoundIo *ep;
+ SoundIoFilter *fltp;
+ bool res;
+
+ res = dbus_message_iter_init(msgp, &mi);
+ assert(res);
+ assert(dbus_message_iter_get_arg_type(&mi) == DBUS_TYPE_STRING);
+ dbus_message_iter_get_basic(&mi, &filename);
+ res = dbus_message_iter_next(&mi);
+ assert(res);
+ assert(dbus_message_iter_get_arg_type(&mi) == DBUS_TYPE_BOOLEAN);
+ dbus_message_iter_get_basic(&mi, &in);
+ res = dbus_message_iter_next(&mi);
+ assert(res);
+ assert(dbus_message_iter_get_arg_type(&mi) == DBUS_TYPE_BOOLEAN);
+ dbus_message_iter_get_basic(&mi, &out);
+
+ if (!filename || !filename[0]) {
+ if (!SendReplyArgs(msgp, DBUS_TYPE_INVALID))
+ return false;
+ CleanupSnoop();
+ return true;
+ }
+
+ fncopy = strdup(filename);
+ if (!fncopy)
+ return false;
+
+ ep = SoundIoCreateFileHandler(GetDi(), fncopy, true, &error);
+ if (!ep) {
+ free(fncopy);
+ return SendReplyErrorInfo(msgp, error);
+ }
+
+ fltp = SoundIoCreateSnooper(ep, in, out);
+ if (!fltp) {
+ delete ep;
+ free(fncopy);
+ return false;
+ }
+
+ if (!m_sound->AddBottom(fltp, &error)) {
+ delete fltp;
+ delete ep;
+ free(fncopy);
+ return SendReplyErrorInfo(msgp, error);
+ }
+
+ if (!SendReplyArgs(msgp, DBUS_TYPE_INVALID)) {
+ m_sound->RemoveFilter(fltp);
+ delete fltp;
+ delete ep;
+ free(fncopy);
+ return false;
+ }
+
+ CleanupSnoop();
+ m_snoop = fltp;
+ m_snoop_ep = ep;
+ m_snoop_filename = fncopy;
+ return true;
+}
+
bool SoundIoObj::
-GetState(DBusMessage *msgp, unsigned char &val)
+GetState(DBusMessage *msgp, uint8_t &val)
{
val = m_state;
return true;
@@ -2938,6 +3126,15 @@ SetMute(DBusMessage *msgp, const bool &val, bool &doreply)
}
bool SoundIoObj::
+GetSnoopFileName(DBusMessage *msgp, const char * &val)
+{
+ val = m_snoop_filename;
+ if (!val)
+ val = "";
+ return true;
+}
+
+bool SoundIoObj::
GetDriverName(DBusMessage *msgp, const char * &val)
{
val = m_sound->GetDriverName();
diff --git a/hfpd/objects.h b/hfpd/objects.h
index 9794e98..a9838c8 100644
--- a/hfpd/objects.h
+++ b/hfpd/objects.h
@@ -131,9 +131,9 @@ public:
bool CallTransfer(DBusMessage *msgp);
/* D-Bus Property related methods */
- bool GetState(DBusMessage *msgp, unsigned char &val);
- bool GetCallState(DBusMessage *msgp, unsigned char &val);
- bool GetAudioState(DBusMessage *msgp, unsigned char &val);
+ bool GetState(DBusMessage *msgp, uint8_t &val);
+ bool GetCallState(DBusMessage *msgp, uint8_t &val);
+ bool GetAudioState(DBusMessage *msgp, uint8_t &val);
bool GetClaimed(DBusMessage *msgp, bool &val);
bool GetVoluntaryDisconnect(DBusMessage *msgp, bool &val);
bool GetAddress(DBusMessage *msgp, const DbusProperty *propp,
@@ -183,11 +183,11 @@ static const DbusMethod g_AudioGateway_signals[] = {
* representation, so we use a raw get method.
*/
static const DbusProperty g_AudioGateway_properties[] = {
- DbusPropertyMarshallImmutable(unsigned char, State, AudioGateway,
+ DbusPropertyMarshallImmutable(uint8_t, State, AudioGateway,
GetState),
- DbusPropertyMarshallImmutable(unsigned char, CallState, AudioGateway,
+ DbusPropertyMarshallImmutable(uint8_t, CallState, AudioGateway,
GetCallState),
- DbusPropertyMarshallImmutable(unsigned char, AudioState, AudioGateway,
+ DbusPropertyMarshallImmutable(uint8_t, AudioState, AudioGateway,
GetAudioState),
DbusPropertyMarshallImmutable(bool, Claimed, AudioGateway,
GetClaimed),
@@ -293,8 +293,8 @@ public:
bool GetSystemState(DBusMessage *msgp, bool &val);
bool GetAutoRestart(DBusMessage *msgp, bool &val);
bool SetAutoRestart(DBusMessage *msgp, const bool &val, bool &doreply);
- bool GetSecMode(DBusMessage *msgp, unsigned char &val);
- bool SetSecMode(DBusMessage *msgp, const unsigned char &val,
+ bool GetSecMode(DBusMessage *msgp, uint8_t &val);
+ bool SetSecMode(DBusMessage *msgp, const uint8_t &val,
bool &doreply);
bool GetAcceptUnknown(DBusMessage *msgp, bool &val);
bool SetAcceptUnknown(DBusMessage *msgp, const bool &val,
@@ -352,7 +352,7 @@ static const DbusProperty g_HandsFree_properties[] = {
GetSystemState),
DbusPropertyMarshall(bool, AutoRestart, HandsFree,
GetAutoRestart, SetAutoRestart),
- DbusPropertyMarshall(unsigned char, SecMode, HandsFree,
+ DbusPropertyMarshall(uint8_t, SecMode, HandsFree,
GetSecMode, SetSecMode),
DbusPropertyMarshall(bool, AcceptUnknown, HandsFree,
GetAcceptUnknown, SetAcceptUnknown),
@@ -404,6 +404,10 @@ public:
AudioGateway *m_bound_ag;
+ libhfp::SoundIoFilter *m_snoop;
+ libhfp::SoundIo *m_snoop_ep;
+ char *m_snoop_filename;
+
libhfp::DispatchInterface *GetDi(void) const { return m_hf->GetDi(); }
@@ -415,6 +419,7 @@ public:
bool Init(DbusSession *dbusp);
void Cleanup(void);
+ void CleanupSnoop(void);
bool UpdateState(SoundIoState st, libhfp::ErrorInfo *reason = 0);
/*
@@ -427,6 +432,8 @@ public:
libhfp::ErrorInfo *error);
bool EpAudioGatewayComplete(AudioGateway *agp,
libhfp::ErrorInfo *error);
+ bool EpFile(const char *filename, bool writing,
+ libhfp::ErrorInfo *error);
bool EpLoopback(libhfp::ErrorInfo *error);
bool EpMembuf(bool in, bool out, libhfp::SoundIoFilter *fltp,
libhfp::ErrorInfo *error);
@@ -441,16 +448,19 @@ public:
bool ProbeDevices(DBusMessage *msgp);
bool Stop(DBusMessage *msgp);
bool AudioGatewayStart(DBusMessage *msgp);
+ bool FileStart(DBusMessage *msgp);
bool LoopbackStart(DBusMessage *msgp);
bool MembufClear(DBusMessage *msgp);
bool MembufStart(DBusMessage *msgp);
+ bool SetSnoopFile(DBusMessage *msgp);
/* D-Bus SoundIo property related methods */
- bool GetState(DBusMessage *msgp, unsigned char &val);
+ bool GetState(DBusMessage *msgp, uint8_t &val);
bool GetAudioGateway(DBusMessage *msgp, const DbusProperty *propp,
DBusMessageIter &mi);
bool GetMute(DBusMessage *msgp, bool &val);
bool SetMute(DBusMessage *msgp, const bool &val, bool &doreply);
+ bool GetSnoopFileName(DBusMessage *msgp, const char * &val);
bool GetDrivers(DBusMessage *msgp, const DbusProperty *propp,
DBusMessageIter &mi);
bool GetDriverName(DBusMessage *msgp, const char * &val);
@@ -494,9 +504,11 @@ static const DbusMethod g_SoundIo_methods[] = {
DbusMethodEntry(SoundIoObj, ProbeDevices, "s", "a(ss)"),
DbusMethodEntry(SoundIoObj, Stop, "", ""),
DbusMethodEntry(SoundIoObj, AudioGatewayStart, "ob", ""),
+ DbusMethodEntry(SoundIoObj, FileStart, "sb", ""),
DbusMethodEntry(SoundIoObj, LoopbackStart, "", ""),
DbusMethodEntry(SoundIoObj, MembufStart, "bbuu", ""),
DbusMethodEntry(SoundIoObj, MembufClear, "", ""),
+ DbusMethodEntry(SoundIoObj, SetSnoopFile, "sbb", ""),
{ 0, }
};
@@ -511,11 +523,13 @@ static const DbusMethod g_SoundIo_signals[] = {
};
static const DbusProperty g_SoundIo_properties[] = {
- DbusPropertyMarshallImmutable(unsigned char, State, SoundIoObj,
+ DbusPropertyMarshallImmutable(uint8_t, State, SoundIoObj,
GetState),
DbusPropertyRawImmutable("v", AudioGateway, SoundIoObj,
GetAudioGateway),
DbusPropertyMarshall(bool, Mute, SoundIoObj, GetMute, SetMute),
+ DbusPropertyMarshallImmutable(const char *, SnoopFileName, SoundIoObj,
+ GetSnoopFileName),
DbusPropertyRawImmutable("a(ss)", Drivers, SoundIoObj,
GetDrivers),
DbusPropertyMarshallImmutable(const char *, DriverName, SoundIoObj,
diff --git a/hfpd/proto.h b/hfpd/proto.h
index aa8a0e0..7f0fd14 100644
--- a/hfpd/proto.h
+++ b/hfpd/proto.h
@@ -74,6 +74,7 @@ enum SoundIoState {
HFPD_SIO_STOPPED,
HFPD_SIO_AUDIOGATEWAY_CONNECTING,
HFPD_SIO_AUDIOGATEWAY,
+ HFPD_SIO_FILE,
HFPD_SIO_LOOPBACK,
HFPD_SIO_MEMBUF,
};
diff --git a/include/libhfp/events.h b/include/libhfp/events.h
index 942b380..2e285c8 100644
--- a/include/libhfp/events.h
+++ b/include/libhfp/events.h
@@ -428,7 +428,7 @@ protected:
marshall_t m_marshall;
CallTarget *m_targ;
fake_mfp_t m_method;
- char m_save[4 * sizeof(void*)];
+ uint8_t m_save[4 * sizeof(void*)];
TRet InvokeRet(InArgset &ia) {
return (*m_marshall)(*(InArgset*)m_save, m_targ, m_method, ia);
diff --git a/include/libhfp/soundio.h b/include/libhfp/soundio.h
index e799189..99fb390 100644
--- a/include/libhfp/soundio.h
+++ b/include/libhfp/soundio.h
@@ -49,6 +49,8 @@ enum {
LIBHFP_ERROR_SOUNDIO_SYSCALL,
/** Error internal to audio module */
LIBHFP_ERROR_SOUNDIO_INTERNAL,
+ /** Function not supported */
+ LIBHFP_ERROR_SOUNDIO_NOT_SUPPORTED,
/** Device is already open */
LIBHFP_ERROR_SOUNDIO_ALREADY_OPEN,
/** Function not supported because endpoint does not have a clock */
@@ -723,18 +725,19 @@ extern SoundIoDeviceList *SoundIoGetDeviceListAlsa(ErrorInfo *error);
* The object can also operate in full-duplex mode, in which case it
* will present separate playback and capture buffers.
*
- * @param fmt Initial format to set on the object.
- * @param nsamps Buffer size to use when creating new buffers. To
+ * @param[in] fmt Initial format to set on the object.
+ * @param[in] nsamps Buffer size to use when creating new buffers. To
* convert from seconds, multiply the number of seconds by
* SoundIoFormat::samplerate.
*
* @return A newly constructed SoundIo memory buffer handler object
- * associated with @em filename, or NULL on failure.
+ * associated with @em filename, or @c 0 on memory allocation failure.
*
* @note This object was created to aid in system testing, to observe
* the effects of filters and signal processing settings.
*/
-SoundIo *SoundIoCreateMembuf(const SoundIoFormat *fmt, sio_sampnum_t nsamps);
+extern SoundIo *SoundIoCreateMembuf(const SoundIoFormat *fmt,
+ sio_sampnum_t nsamps);
/**
* @brief Construct a SoundIo object backed by a disk file
@@ -750,17 +753,21 @@ SoundIo *SoundIoCreateMembuf(const SoundIoFormat *fmt, sio_sampnum_t nsamps);
* For reading, SoundIo::SndGetFormat() will only report meaningful
* results after the file has been opened.
*
- * @param ei Pointer to dispatcher interface object for the environment.
+ * @param[in] ei Pointer to dispatcher interface object for the environment.
* This is used for logging errors.
- * @param filename Name of the audio file to associate the object with.
- * @param create Set to @em true to allow the file to be created if it
+ * @param[in] filename Name of the audio file to associate the object with.
+ * @param[in] create Set to @em true to allow the file to be created if it
* is opened for output and does not exist.
+ * @param[out] error Error information structure. If this method
+ * fails and returns @c 0, and @em error is not 0, @em error
+ * will be filled out with information on the cause of the failure.
*
* @return A newly constructed SoundIo disk file handler object
- * associated with @em filename, or NULL on failure.
+ * associated with @em filename, or @c 0 on failure.
*/
-SoundIo *SoundIoCreateFileHandler(DispatchInterface *ei,
- const char *filename, bool create);
+extern SoundIo *SoundIoCreateFileHandler(DispatchInterface *ei,
+ const char *filename, bool create,
+ ErrorInfo *error = 0);
/**
* @brief Audio Filtering and Signal Processing Interface
@@ -849,6 +856,48 @@ public:
SoundIoBuffer &dest) = 0;
};
+/**
+ * @brief Construct a stream snooping filter
+ * @ingroup soundio
+ *
+ * The stream snooper is an installable filter that outputs data
+ * passing through it to a slave SoundIo object. When the snooper is
+ * configured as an installed filter by SoundIoPump, the snooper will set
+ * the format of its slave to the configured streaming format and open the
+ * slave in sink mode. When the snooper is deconfigured, it will close
+ * the slave.
+ *
+ * If bidirectional snooping mode is set -- both the @em up and @em dn
+ * parameters are @em true, and the stream is operating in bidirectional
+ * mode, the output to the slave endpoint will be a mix of the packet data
+ * from both directions. Otherwise the output will be the packet data
+ * from one direction, or if not streaming in any configured directions,
+ * no output data. In the latter case, the slave endpoint will not
+ * even be opened.
+ *
+ * The intended use of the snooper object is for recording stream
+ * sessions to files. The intended slave endpoint is a file sink,
+ * e.g. one produced by SoundIoCreateFileHandler(). The use of a clocked
+ * endpoint as the slave is not recommended, as no rate matching is
+ * attempted by the snooper object.
+ *
+ * @param[in] target Slave endpoint to receive snooped sample data.
+ * @param[in] up Set to @c true to snoop audio data streaming upward,
+ * @c false to ignore data streaming upward.
+ * @param[in] dn Set to @c true to snoop audio data streaming downward,
+ * @c false to ignore data streaming downward.
+ *
+ * @return A newly constructed snoop filter configured with the target
+ * endpoint, or @c 0 on memory allocation failure.
+ *
+ * @note The snoop filter does not perform any life cycle management on
+ * the specified slave endpoint. Clients are responsible for managing the
+ * life cycle of the slave endpoint, and should take care to destroy the
+ * snoop filter before destroying the slave endpoint.
+ */
+extern SoundIoFilter *SoundIoCreateSnooper(SoundIo *target,
+ bool up = true, bool dn = true);
+
/**
* @brief Signal processing configuration for SoundIoFltSpeex
@@ -901,7 +950,7 @@ public:
* effective, this filter should either be the bottom-most filter, or
* no filters placed below it should alter the data stream.
*
- * @param ei Pointer to dispatcher interface object for the environment.
+ * @param[in] ei Pointer to dispatcher interface object for the environment.
* This is used for logging errors.
*
* To use this filter:
@@ -1056,7 +1105,7 @@ private:
bool bottom_roe, top_roe;
bool pump_down, pump_up;
bool warn_loss;
- char watchdog_strikes;
+ int8_t watchdog_strikes;
unsigned int watchdog_to;
sio_sampnum_t watchdog_min_progress;
sio_sampnum_t watchdog_max_progress;
@@ -1074,12 +1123,12 @@ private:
bool m_bottom_loss_tolerate, m_top_loss_tolerate;
/* For the watchdog */
- char m_bottom_strikes, m_top_strikes;
+ bool m_async_entered;
+ int8_t m_bottom_strikes, m_top_strikes;
+ int8_t m_bottom_in_strikes, m_top_in_strikes;
+ int8_t m_bottom_out_strikes, m_top_out_strikes;
sio_sampnum_t m_bottom_in_count, m_top_in_count;
sio_sampnum_t m_bottom_out_count, m_top_out_count;
- char m_bottom_in_strikes, m_top_in_strikes;
- char m_bottom_out_strikes, m_top_out_strikes;
- bool m_async_entered;
uint8_t m_bo_last[c_sampsize], m_bi_last[c_sampsize];
uint8_t m_to_last[c_sampsize], m_ti_last[c_sampsize];
@@ -1094,8 +1143,8 @@ private:
void DumpQueueState(bool start, bool top) const;
void AsyncProcess(SoundIo *subp, SoundIoQueueState &statep);
void AsyncStopped(SoundIo *subp, ErrorInfo &error);
- bool WatchdogThreshold(sio_sampnum_t &count, char &strikes,
- const char *name, ErrorInfo &error);
+ bool WatchdogThreshold(sio_sampnum_t &count, int8_t &strikes,
+ const char *name, ErrorInfo &error);
void Watchdog(TimerNotifier *notp);
void __Stop(ErrorInfo *reason = 0, SoundIo *offender = 0);
@@ -1641,7 +1690,7 @@ private:
Stats *m_history;
int m_history_count, m_history_pos;
- char m_pri_skew_strikes, m_sec_skew_strikes,
+ int8_t m_pri_skew_strikes, m_sec_skew_strikes,
m_endpoint_skew_strikes;
int m_stat_min_pri_duplex_skew;
diff --git a/libhfp/soundio-alsa.cpp b/libhfp/soundio-alsa.cpp
index 6c40fce..dfc2f78 100644
--- a/libhfp/soundio-alsa.cpp
+++ b/libhfp/soundio-alsa.cpp
@@ -829,6 +829,10 @@ public:
virtual ~SoundIoAlsaProc() {}
virtual bool SndOpen(bool play, bool capture, ErrorInfo *error) {
+ if (play)
+ m_alsa.m_play_props.bufsize = 0;
+ if (capture)
+ m_alsa.m_rec_props.bufsize = 0;
if (m_alsa.OpenDevice(SND_PCM_ACCESS_RW_INTERLEAVED,
play, capture, error)) {
m_play_nonblock = false;
@@ -851,6 +855,10 @@ public:
virtual bool SndSetFormat(SoundIoFormat &format, ErrorInfo *error) {
SndAsyncStop();
+ if (m_alsa.m_play_handle)
+ m_alsa.m_play_props.bufsize = 0;
+ if (m_alsa.m_rec_handle)
+ m_alsa.m_rec_props.bufsize = 0;
if (m_alsa.Reconfigure(&format,
SND_PCM_ACCESS_RW_INTERLEAVED, error)) {
OpenBuf();
diff --git a/libhfp/soundio-oss.cpp b/libhfp/soundio-oss.cpp
index 4d66708..b811dc2 100644
--- a/libhfp/soundio-oss.cpp
+++ b/libhfp/soundio-oss.cpp
@@ -72,6 +72,8 @@ class OssSoundIo : public SoundIoBufferBase {
bool m_play_nonblock;
bool m_rec_nonblock;
+ bool m_no_interrupts;
+
SoundIoFormat m_format;
sio_sampnum_t m_obuf_size;
@@ -347,7 +349,8 @@ public:
(void) SetupOss(m_play_fh, m_format, 0);
return false;
}
- if ((m_rec_fh != m_play_fh) &&
+ if ((m_rec_fh >= 0) &&
+ (m_rec_fh != m_play_fh) &&
!SetupOss(m_rec_fh, format, error)) {
(void) SetupOss(m_play_fh, m_format, 0);
(void) SetupOssPlayback(m_play_fh, m_format,0);
@@ -368,6 +371,9 @@ public:
int res;
ErrorInfo error;
+ if (m_rec_fh < 0)
+ return;
+
if (m_rec_nonblock != nonblock) {
if (!SetNonBlock(m_rec_fh, nonblock)) {
m_ei->LogWarn("OSS set rec nonblock: %s",
@@ -406,6 +412,9 @@ public:
int res;
ErrorInfo error;
+ if (m_play_fh < 0)
+ return;
+
if (m_play_nonblock != nonblock) {
if (!SetNonBlock(m_play_fh, nonblock)) {
m_ei->LogWarn("OSS set play nonblock "
@@ -445,6 +454,9 @@ public:
void AsyncProcess(SocketNotifier *notp, int fh) {
int delay = 0;
+ struct count_info count;
+ bool play_xrun = false;
+ bool rec_xrun = false;
if (m_play_fh >= 0) {
if (ioctl(m_play_fh, SNDCTL_DSP_GETODELAY,
@@ -455,10 +467,22 @@ public:
} else {
delay /= m_format.bytes_per_record;
}
+
+ if (ioctl(m_play_fh, SNDCTL_DSP_GETOPTR, &count) < 0) {
+ m_ei->LogWarn("OSS GETOPTR: %s",
+ strerror(errno));
+ } else if (!count.blocks && !m_no_interrupts) {
+ m_ei->LogWarn("*** OSS playback xrun ***");
+ play_xrun = true;
+ }
+
}
- SndPushInput(true);
- BufProcess(delay, false, false);
+ if (m_rec_fh >= 0)
+ SndPushInput(true);
+
+ m_no_interrupts = false;
+ BufProcess(delay, rec_xrun, play_xrun);
}
virtual bool SndAsyncStart(bool playback, bool capture,
@@ -497,13 +521,14 @@ public:
if (!m_not) {
m_not = m_ei->NewSocket(capture
? m_rec_fh
- : m_play_fh, false);
+ : m_play_fh, !capture);
if (!m_not) {
if (error)
error->SetNoMem();
return false;
}
m_not->Register(this, &OssSoundIo::AsyncProcess);
+ m_no_interrupts = true;
}
return true;
}
diff --git a/libhfp/soundio-pump.cpp b/libhfp/soundio-pump.cpp
index 5a9b318..fe9dabb 100644
--- a/libhfp/soundio-pump.cpp
+++ b/libhfp/soundio-pump.cpp
@@ -85,7 +85,7 @@ CopyIn(uint8_t *dest, SoundIoWorkingState *swsp, sio_sampnum_t nsamps)
{
sio_sampnum_t rem;
uint8_t *end;
- unsigned char bps = swsp->bpr;
+ uint8_t bps = swsp->bpr;
assert(nsamps);
if (swsp->in_buf.m_size) {
@@ -169,7 +169,7 @@ sio_sampnum_t SoundIoPump::
CopyOut(SoundIoWorkingState *dwsp, const uint8_t *src, sio_sampnum_t nsamps)
{
sio_sampnum_t rem;
- unsigned char bps = dwsp->bpr;
+ uint8_t bps = dwsp->bpr;
assert(nsamps);
@@ -239,7 +239,7 @@ CopyCross(SoundIoWorkingState *dwsp, SoundIoWorkingState *swsp,
sio_sampnum_t nsamps)
{
sio_sampnum_t rem, in_pad = 0;
- unsigned char bps = dwsp->bpr;
+ uint8_t bps = dwsp->bpr;
/* We maintain an in_pad count but do nothing with it */
@@ -325,7 +325,7 @@ sio_sampnum_t SoundIoPump::
OutputSilence(SoundIoWorkingState *dwsp, sio_sampnum_t nsamps)
{
sio_sampnum_t rem;
- unsigned char bps = dwsp->bpr;
+ uint8_t bps = dwsp->bpr;
uint8_t *buf, *end;
if (dwsp->out_buf.m_size) {
@@ -381,7 +381,7 @@ ProcessOneWay(SoundIoWorkingState *swsp, SoundIoWorkingState *dwsp,
SoundIoBuffer bufs, bufd, *bufp;
SoundIoFilter *fltp;
uint8_t *dibuf = NULL;
- unsigned char bps = dwsp->bpr;
+ uint8_t bps = dwsp->bpr;
/* Acquire a buffer from the source */
bufs.m_size = 0;
@@ -501,9 +501,9 @@ ProcessorLoop(SoundIoWorkingState &bws, SoundIoWorkingState &tws,
struct xfer_bound {
sio_sampnum_t lower;
sio_sampnum_t upper;
- char prio;
- char under_cost;
- char over_cost;
+ uint8_t prio;
+ uint8_t under_cost;
+ uint8_t over_cost;
};
sio_sampnum_t
@@ -715,6 +715,7 @@ AsyncProcess(SoundIo *subp, SoundIoQueueState &state)
m_config.bottom_in_max))
? (m_bottom_qs.in_queued - m_config.bottom_in_max) : 0;
bounds[ncopy].upper = (!m_config.bottom_async &&
+ m_bottom_qs.in_queued &&
(m_bottom_qs.in_queued <
m_config.filter_packet_samps))
? m_config.filter_packet_samps : m_bottom_qs.in_queued;
@@ -725,6 +726,8 @@ AsyncProcess(SoundIo *subp, SoundIoQueueState &state)
bounds[ncopy++].over_cost = 2;
bounds[ncopy].lower = (m_config.top_async &&
+ (m_config.bottom_async ||
+ m_bottom_qs.in_queued) &&
(m_top_qs.out_queued <
m_config.top_out_min))
? (m_config.top_out_min - m_top_qs.out_queued) : 0;
@@ -741,6 +744,7 @@ AsyncProcess(SoundIo *subp, SoundIoQueueState &state)
m_config.top_in_max))
? (m_top_qs.in_queued - m_config.top_in_max) : 0;
bounds[ncopy].upper = (!m_config.top_async &&
+ m_top_qs.in_queued &&
(m_top_qs.in_queued <
m_config.filter_packet_samps))
? m_config.filter_packet_samps : m_top_qs.in_queued;
@@ -748,6 +752,8 @@ AsyncProcess(SoundIo *subp, SoundIoQueueState &state)
bounds[ncopy].under_cost = 1;
bounds[ncopy++].over_cost = 2;
bounds[ncopy].lower = (m_config.bottom_async &&
+ (m_config.top_async ||
+ m_top_qs.in_queued) &&
(m_bottom_qs.out_queued <
m_config.bottom_out_min))
? (m_config.bottom_out_min -
@@ -951,8 +957,12 @@ done_copyback:
memcpy(m_ti_last, tws.in_silence, sizeof(m_ti_last));
memcpy(m_to_last, tws.out_silence, sizeof(m_to_last));
- /* If silence padding is required, do it */
- if (m_config.pump_up) {
+ /*
+ * If silence padding is required, do it
+ * Don't silence-pad if a remove-on-exhaust source is empty.
+ */
+ if (m_config.pump_up &&
+ (!m_config.bottom_roe || m_bottom_qs.in_queued)) {
nadj = m_top_qs.out_queued;
if (nadj < m_config.top_out_min) {
if (loss_debug && m_config.warn_loss) {
@@ -972,7 +982,8 @@ done_copyback:
did_loss = true;
}
}
- if (m_config.pump_down) {
+ if (m_config.pump_down &&
+ (!m_config.top_roe || m_top_qs.in_queued)) {
nadj = m_bottom_qs.out_queued;
if (nadj < m_config.bottom_out_min) {
if (loss_debug && m_config.warn_loss) {
@@ -1011,9 +1022,9 @@ done_copyback:
*/
if ((m_config.top_roe &&
(!m_config.pump_up || tws.out_drop) &&
- (!m_config.pump_down || tws.in_silencepad)) ||
+ (!m_config.pump_down || (!ncopy && m_bottom_qs.out_underflow))) ||
(m_config.bottom_roe &&
- (!m_config.pump_up || bws.in_silencepad) &&
+ (!m_config.pump_up || (!ncopy && m_top_qs.out_underflow)) &&
(!m_config.pump_down || bws.out_drop))) {
ErrorInfo error;
error.Set(LIBHFP_ERROR_SUBSYS_SOUNDIO,
@@ -1146,10 +1157,10 @@ AsyncStopped(SoundIo *subp, ErrorInfo &error)
}
bool SoundIoPump::
-WatchdogThreshold(sio_sampnum_t &count, char &strikes, const char *name,
+WatchdogThreshold(sio_sampnum_t &count, int8_t &strikes, const char *name,
ErrorInfo &error)
{
- char res = 0;
+ int8_t res = 0;
if (count < m_config.watchdog_min_progress) {
GetDi()->LogDebug("SoundIoPump: %s underprocessed (%d < %d)",
diff --git a/libhfp/soundio-util.cpp b/libhfp/soundio-util.cpp
index 5c2c520..6137497 100644
--- a/libhfp/soundio-util.cpp
+++ b/libhfp/soundio-util.cpp
@@ -432,6 +432,8 @@ public:
void SndClose(void) {
if (IsOpen()) {
+ if (m_write && m_offset)
+ (void) FlushPage();
afCloseFile(m_handle);
m_handle = AF_NULL_FILEHANDLE;
}
@@ -487,19 +489,35 @@ public:
afSeekFrame(m_handle, m_track, fc + samps);
}
+ bool FlushPage() {
+ int res;
+ if (m_offset) {
+ res = afWriteFrames(m_handle, m_track,
+ m_buf, m_offset);
+ if (res <= 0)
+ return false;
+ assert(res <= (int) m_offset);
+ }
+ m_offset = 0;
+ return true;
+ }
+
void SndGetOBuf(SoundIoBuffer &map) {
if (!IsOpen() || !m_write) {
map.m_size = 0;
return;
}
- /* TODO */
- abort();
+
+ if (!map.m_size || (map.m_size > (m_buf_size - m_offset)))
+ map.m_size = m_buf_size - m_offset;
+ map.m_data = m_buf + (m_offset * m_fmt.bytes_per_record);
}
void SndQueueOBuf(sio_sampnum_t samps) {
- assert(samps <= (m_length - m_offset));
- /* TODO */
- abort();
+ assert(samps <= (m_buf_size - m_offset));
+ m_offset += samps;
+ if (m_offset == m_buf_size)
+ (void) FlushPage();
}
void SndGetQueueState(SoundIoQueueState &qs) {
@@ -525,9 +543,8 @@ public:
void SndAsyncStop(void) {}
bool SndIsAsyncStarted(void) const { return false; }
- SoundIoAudioFile(DispatchInterface *ei,
- const char *filename, bool create)
- : m_ei(ei), m_filename(strdup(filename)), m_create(create),
+ SoundIoAudioFile(DispatchInterface *ei, char *filename, bool create)
+ : m_ei(ei), m_filename(filename), m_create(create),
m_handle(AF_NULL_FILEHANDLE), m_buf(0), m_buf_size(0) {
memset(&m_fmt, 0, sizeof(m_fmt));
}
@@ -543,17 +560,211 @@ public:
SoundIo *
SoundIoCreateFileHandler(DispatchInterface *ei,
- const char *filename, bool create)
+ const char *filename, bool create, ErrorInfo *error)
{
- SoundIo *siop = NULL;
- if (!filename) return NULL;
+ SoundIo *siop = 0;
+
+ if (!filename || !filename[0]) {
+ if (error)
+ error->Set(LIBHFP_ERROR_SUBSYS_EVENTS,
+ LIBHFP_ERROR_EVENTS_BAD_PARAMETER,
+ "Empty filename specified for audiofile");
+ return 0;
+ }
+
#if defined(USE_AUDIOFILE)
- siop = new SoundIoAudioFile(ei, filename, create);
-#endif
+ {
+ char *xfn;
+ xfn = strdup(filename);
+ if (!xfn) {
+ if (error)
+ error->SetNoMem();
+ return 0;
+ }
+ siop = new SoundIoAudioFile(ei, xfn, create);
+ if (!siop) {
+ free(xfn);
+ if (error)
+ error->SetNoMem();
+ }
+ }
return siop;
+#endif
+
+ if (error)
+ error->Set(LIBHFP_ERROR_SUBSYS_SOUNDIO,
+ LIBHFP_ERROR_SOUNDIO_NOT_SUPPORTED,
+ "Support for libaudiofile omitted");
+ return 0;
}
+class SoundIoSnooper : public SoundIoFilter {
+ SoundIo *m_output;
+ uint8_t *m_buf;
+ bool m_half;
+ SoundIoFormat m_fmt;
+ bool m_open;
+ bool m_no_up, m_no_dn;
+
+public:
+ SoundIoSnooper(SoundIo *target, bool up, bool dn)
+ : m_output(target), m_buf(0), m_half(false),
+ m_open(false), m_no_up(!up), m_no_dn(!dn) {}
+ ~SoundIoSnooper() {}
+
+ virtual bool FltPrepare(SoundIoFormat const &fmt,
+ bool up, bool dn, ErrorInfo *error) {
+ SoundIoFormat fmt_copy(fmt);
+
+ if (up && !m_no_up && dn && !m_no_dn) {
+ switch (fmt.sampletype) {
+ case SIO_PCM_U8:
+ case SIO_PCM_S16_LE:
+ break;
+ default:
+ if (error)
+ error->Set(
+ LIBHFP_ERROR_SUBSYS_SOUNDIO,
+ LIBHFP_ERROR_SOUNDIO_FORMAT_UNKNOWN,
+ "Format not recognized by "
+ "snooper");
+ return false;
+ }
+ m_buf = (uint8_t *) malloc(fmt.packet_samps *
+ fmt.bytes_per_record);
+ if (!m_buf) {
+ if (error)
+ error->SetNoMem();
+ return false;
+ }
+ }
+
+ if ((up && !m_no_up) || (dn && !m_no_dn)) {
+ if (!m_output->SndSetFormat(fmt_copy, error) ||
+ !m_output->SndOpen(true, false, error)) {
+ if (m_buf) {
+ free(m_buf);
+ m_buf = 0;
+ }
+ return false;
+ }
+ m_open = true;
+ }
+
+ m_fmt = fmt;
+ return true;
+ }
+
+ virtual void FltCleanup(void) {
+ if (m_open) {
+ m_output->SndClose();
+ m_open = false;
+ }
+ if (m_buf) {
+ free(m_buf);
+ m_buf = 0;
+ }
+ }
+
+ void MixBuffer(const uint8_t *buf, sio_sampnum_t size) {
+ size *= m_fmt.nchannels;
+
+ switch (m_fmt.sampletype) {
+ case SIO_PCM_U8: {
+ uint8_t *src = (uint8_t *) buf;
+ uint8_t *dest = (uint8_t *) m_buf;
+ int32_t tmp;
+ while (size) {
+ tmp = (((int) (*(src++)) - 128) +
+ ((int) (*dest) - 128));
+ if (tmp < -128)
+ tmp = -128;
+ if (tmp > 127)
+ tmp = 127;
+ *(dest++) = tmp + 128;
+ size--;
+ }
+ break;
+ }
+ case SIO_PCM_S16_LE: {
+ int16_t *src = (int16_t *) buf;
+ int16_t *dest = (int16_t *) m_buf;
+ int32_t tmp;
+ while (size) {
+ tmp = *(src++) + *dest;
+ if (tmp < -32768)
+ tmp = -32768;
+ if (tmp > 32767)
+ tmp = -32767;
+ *(dest++) = tmp;
+ size--;
+ }
+ break;
+ }
+ default:
+ abort();
+ }
+ }
+
+ void OutputBuffer(const uint8_t *buf, sio_sampnum_t size) {
+ SoundIoBuffer xbuf;
+ while (size) {
+ xbuf.m_size = size;
+ m_output->SndGetOBuf(xbuf);
+ if (!xbuf.m_size)
+ /* Uh-oh! */
+ return;
+
+ memcpy(xbuf.m_data,
+ buf,
+ xbuf.m_size * m_fmt.bytes_per_record);
+ m_output->SndQueueOBuf(xbuf.m_size);
+ size -= xbuf.m_size;
+ buf += (xbuf.m_size * m_fmt.bytes_per_record);
+ }
+ }
+
+ virtual SoundIoBuffer const *FltProcess(bool up,
+ SoundIoBuffer const &src,
+ SoundIoBuffer &dest) {
+ if (!m_buf) {
+ if ((up && !m_no_up) || (!up && !m_no_dn)) {
+ assert(m_open);
+ OutputBuffer(src.m_data, src.m_size);
+ }
+ return &src;
+ }
+
+ assert(!m_no_up && !m_no_dn);
+
+ if (!up) {
+ assert(!m_half);
+ memcpy(m_buf,
+ src.m_data,
+ src.m_size * m_fmt.bytes_per_record);
+ m_half = true;
+ return &src;
+ }
+
+ assert(m_half);
+ m_half = 0;
+ MixBuffer(src.m_data, src.m_size);
+ OutputBuffer(m_buf, src.m_size);
+ return &src;
+ }
+};
+
+SoundIoFilter *
+SoundIoCreateSnooper(SoundIo *target, bool up, bool dn)
+{
+ assert(target);
+ assert(up || dn);
+ return new SoundIoSnooper(target, up, dn);
+}
+
+
+
#if defined(USE_SPEEXDSP)
class SoundIoFltSpeexImpl : public SoundIoFltSpeex {
DispatchInterface *m_ei;
diff --git a/test/pumpunit.cpp b/test/pumpunit.cpp
index 976e11a..a260852 100644
--- a/test/pumpunit.cpp
+++ b/test/pumpunit.cpp
@@ -205,10 +205,15 @@ public:
fillme.m_data = m_sink_buf.GetSpace(nbytes);
}
- void CheckOBuf(const uint8_t *buf, size_t len) {
+ void CheckOBuf(const uint8_t *ibuf, size_t ilen) {
int subsamp, bpr, count = 0;
+ const uint8_t *buf;
+ size_t len;
bool last_mismatch = false;
+ int mismatch_count = 0;
+ buf = ibuf;
+ len = ilen;
bpr = m_fmt.bytes_per_record;
assert(!(len % bpr));
@@ -224,6 +229,7 @@ public:
}
m_sink_seq = buf[0];
last_mismatch = true;
+ mismatch_count++;
} else {
last_mismatch = false;
}
@@ -236,6 +242,7 @@ public:
"expect: 0x%02x got: 0x%02x\n",
m_name, subsamp, m_sink_seq,
buf[subsamp]);
+ mismatch_count++;
}
}
@@ -307,15 +314,56 @@ public:
};
+void
+run_test(SoundIoPump *pump, SoundIoTestEp *bot, SoundIoTestEp *top,
+ SoundIoTestEp *div)
+{
+ bool res;
+ int i;
+
+ res = bot->SndOpen(true, true);
+ assert(res);
+ res = top->SndOpen(true, true);
+ assert(res);
+ res = pump->Start();
+ assert(res);
+ assert(bot->SndIsAsyncStarted());
+
+ for (i = 0; i < 10000; i++) {
+ bot->FillOutput();
+ bot->ConsumeInput();
+ bot->DoAsync();
+
+ top->FillOutput();
+ top->ConsumeInput();
+ top->DoAsync();
+
+ if (div)
+ div->ConsumeInput();
+
+ assert(pump->IsStarted());
+ }
+
+ pump->Stop();
+ bot->SndClose();
+ top->SndClose();
+}
+
int
main(int argc, char **argv)
{
IndepEventDispatcher disp;
- SoundIoTestEp top("Top", 10000), bot("Bot", 10000);
+ SoundIoTestEp top("Top", 10000), bot("Bot", 10000), div("Div", 10000);
SoundIoPump pump(&disp, &bot);
+ SoundIoFilter *fltp, *snoopp;
SoundIoFormat xfmt;
bool res;
- int i;
+
+ fltp = SoundIoFltCreateDummy();
+ assert(fltp);
+
+ snoopp = SoundIoCreateSnooper(&div, true, false);
+ assert(snoopp);
xfmt.samplerate = 10000;
xfmt.sampletype = SIO_PCM_U8;
@@ -327,31 +375,16 @@ main(int argc, char **argv)
top.SndSetFormat(xfmt);
res = pump.SetTop(&top);
assert(res);
- res = bot.SndOpen(true, true);
- assert(res);
- res = top.SndOpen(true, true);
- assert(res);
- res = pump.Start();
- assert(res);
- assert(bot.SndIsAsyncStarted());
- for (i = 0; i < 10000; i++) {
+ res = pump.AddBottom(fltp);
+ assert(res);
- if (i == 309)
- printf("Hi\n");
+ run_test(&pump, &bot, &top, 0);
- bot.FillOutput();
- bot.ConsumeInput();
- bot.DoAsync();
-
- top.FillOutput();
- top.ConsumeInput();
- top.DoAsync();
-
- assert(pump.IsStarted());
- }
+ res = pump.AddBottom(snoopp);
+ assert(res);
- pump.Stop();
+ run_test(&pump, &bot, &top, &div);
return 0;
}