diff --git a/apps/examples/mixin_announce/Readme.mixin_announce b/apps/examples/mixin_announce/Readme.mixin_announce index 3b623ca9..ce22b6e0 100644 --- a/apps/examples/mixin_announce/Readme.mixin_announce +++ b/apps/examples/mixin_announce/Readme.mixin_announce @@ -6,20 +6,31 @@ that uses two AmAudio 'devices' (in the IVR: IvrAudioFile) as audio sources, plays the first one, and periodically mixes in the second one. The period, i.e. the time between two times mixing in the second audio, can be specified, as well as the level with which the second one is -mixed into the first. The last parameter, a boolean, specifies whether +mixed into the first. + +The last parameter, a boolean, specifies whether playback of the second audio should be finished if the first is finished while mixing in the second. + +With the mix_once and the mix_immediate it can be specified whether it should +be mixed in onyl once, and whether it should be mixed in immediately. +E.g. if there should be some prompt or info tone played into the background +of a call, mix_once=True and mix_immediate=True are useful. + If level == 0, playback of the first is not continued when playing the second, which means that it continues right where it was before playback of the second started. IVR usage: init(IvrAudioFile first, IvrAudioFile second, - int interval, float level, boolean finish) + int interval, float level, boolean finish, + boolean mix_once, boolean mix_immediate) C++ usage: AmAudioMixIn:: AmAudioMixIn(AmAudio* A, AmAudioFile* B, unsigned int interval, double level, - bool finish_b_while_mixing = false); + int flags); + + where flags definitions can be found in AmAudioMixIn.h diff --git a/apps/ivr/IvrAudioMixIn.cpp b/apps/ivr/IvrAudioMixIn.cpp index f7a6dd0a..f4971fbc 100644 --- a/apps/ivr/IvrAudioMixIn.cpp +++ b/apps/ivr/IvrAudioMixIn.cpp @@ -35,11 +35,11 @@ static PyObject* IvrAudioMixIn_init(IvrAudioMixIn* self, PyObject* args) AmAudioFile* b = NULL; int s; double l; - int finish = 0; + int finish = 0, mix_once=0, mix_immediate=0; PyObject *o_a, *o_b; - if(!PyArg_ParseTuple(args,"OOid|i", &o_a, &o_b, &s, &l, &finish)) + if(!PyArg_ParseTuple(args,"OOid|iii", &o_a, &o_b, &s, &l, &finish, &mix_once, &mix_immediate)) return NULL; if (o_a == Py_None) { @@ -69,7 +69,12 @@ static PyObject* IvrAudioMixIn_init(IvrAudioMixIn* self, PyObject* args) delete self->mix; } - self->mix = new AmAudioMixIn(a, b, s, l, finish); + int flags = 0; + if (finish) flags |=AUDIO_MIXIN_FINISH_B_MIX; + if (mix_once) flags |=AUDIO_MIXIN_ONCE; + if (mix_immediate) flags |=AUDIO_MIXIN_IMMEDIATE_START; + + self->mix = new AmAudioMixIn(a, b, s, l, flags); Py_INCREF(Py_None); return Py_None; diff --git a/core/AmAudioMixIn.cpp b/core/AmAudioMixIn.cpp index 55afe7c1..f8d0da93 100644 --- a/core/AmAudioMixIn.cpp +++ b/core/AmAudioMixIn.cpp @@ -28,32 +28,43 @@ #include "AmAudioMixIn.h" #include "SampleArray.h" +#define IS_FINISH_B_MIX (flags & AUDIO_MIXIN_FINISH_B_MIX) +#define IS_ONLY_ONCE (flags & AUDIO_MIXIN_ONCE) +#define IS_IMMEDIATE_START (flags & AUDIO_MIXIN_IMMEDIATE_START) + AmAudioMixIn::AmAudioMixIn(AmAudio* A, AmAudioFile* B, unsigned int s, double l, - bool finish_b_while_mixing) + unsigned int flags) : A(A),B(B), s(s), l(l), mixing(false), next_start_ts_i(false), - finish_b_while_mixing(finish_b_while_mixing) + flags(flags) { } + AmAudioMixIn::~AmAudioMixIn() { } int AmAudioMixIn::get(unsigned int user_ts, unsigned char* buffer, - unsigned int nb_samples) { + unsigned int nb_samples) { if (!mixing) { if (!next_start_ts_i) { next_start_ts_i = true; + next_start_ts = IS_IMMEDIATE_START ? + user_ts : user_ts + s*DEFAULT_SAMPLE_RATE; + } + if (!ts_less()(user_ts, next_start_ts)) { + DBG("starting mix-in\n"); + mixing = true; next_start_ts = user_ts + s*DEFAULT_SAMPLE_RATE; - } else { - if (ts_less()(next_start_ts, user_ts)) { - DBG("starting mix-in\n"); - mixing = true; - next_start_ts = user_ts + s*DEFAULT_SAMPLE_RATE; - } } } - if (!mixing) { + if (NULL == A) + return -1; + + B_mut.lock(); + + if (!mixing || NULL == B) { + B_mut.unlock(); return A->get(user_ts, buffer, nb_samples); } else { if (l < 0.01) { // epsilon @@ -62,8 +73,12 @@ int AmAudioMixIn::get(unsigned int user_ts, unsigned char* buffer, if (res <= 0) { // B empty res = A->get(user_ts, buffer, nb_samples); mixing = false; - B->rewind(); + if (IS_ONLY_ONCE) + B = NULL; + else + B->rewind(); } + B_mut.unlock(); return res; } else { // mix the two int res = 0; @@ -71,10 +86,11 @@ int AmAudioMixIn::get(unsigned int user_ts, unsigned char* buffer, // get audio from A int len = A->get(user_ts, (unsigned char*)mix_buf, nb_samples); - if ((len<0) && !finish_b_while_mixing) { // A finished + if ((len<0) && !IS_FINISH_B_MIX) { // A finished + B_mut.unlock(); return len; } - for (int i=0;i0) len_from_a=(unsigned int)res; - if (nb_samples<<1 != len_from_a) + if (PCM16_S2B(nb_samples) != len_from_a) memset((void*)&pdest[len_from_a>>1], 0, (nb_samples<<1) - len_from_a); - + // add audio from B len = B->get(user_ts, (unsigned char*)mix_buf, nb_samples); - if (len<0) { // B finished mixing = false; - B->rewind(); + + if (IS_ONLY_ONCE) + B = NULL; + else + B->rewind(); } else { - for (int i=0;ires) // audio from B is longer than from A - res = len; + for (int i=0; i<(PCM16_B2S(len)); i++) { + pdest[i]+=(short)(((double)mix_buf[i])*l); + } + if (len>res) // audio from B is longer than from A + res = len; + } + B_mut.unlock(); + + return res; } - return res; } } -} -int AmAudioMixIn::put(unsigned int user_ts, unsigned char* buffer, unsigned int size) { - ERROR("writing not supported\n"); - return -1; -} + int AmAudioMixIn::put(unsigned int user_ts, unsigned char* buffer, unsigned int size) { + ERROR("writing not supported\n"); + return -1; + } + + void AmAudioMixIn::mixin(AmAudioFile* f) { + B_mut.lock(); + B = f; + mixing = next_start_ts_i = false; /* so that mix in will re-start */ + B_mut.unlock(); + } + diff --git a/core/AmAudioMixIn.h b/core/AmAudioMixIn.h index fdb06a2c..3db82073 100644 --- a/core/AmAudioMixIn.h +++ b/core/AmAudioMixIn.h @@ -32,7 +32,7 @@ #include "AmAudioFile.h" -#define MAX_PACKETLENGTH_MS 30 +#define MAX_PACKETLENGTH_MS 80 #define MAX_BUF_SAMPLES SYSTEM_SAMPLERATE * MAX_PACKETLENGTH_MS / 1000 #define DEFAULT_SAMPLE_RATE SYSTEM_SAMPLERATE // eh... @@ -46,15 +46,21 @@ * playback of B started. * */ +#define AUDIO_MIXIN_FINISH_B_MIX 1 /* when A ends while mixing in B, end playback only after B has ended */ +#define AUDIO_MIXIN_ONCE 1 << 1 /* only mix in once */ +#define AUDIO_MIXIN_IMMEDIATE_START 1 << 2 /* start mixing in immediately, or wait s seconds before */ + class AmAudioMixIn : public AmAudio { AmAudio* A; AmAudioFile* B; unsigned int s; double l; - bool finish_b_while_mixing; + int flags; bool mixing; + AmMutex B_mut; + unsigned int next_start_ts; bool next_start_ts_i; @@ -64,8 +70,11 @@ class AmAudioMixIn : public AmAudio { public: AmAudioMixIn(AmAudio* A, AmAudioFile* B, unsigned int s, double l, - bool finish_b_while_mixing = false); + unsigned int flags = 0); ~AmAudioMixIn(); + + void mixin(AmAudioFile* f); + protected: // not used int read(unsigned int user_ts, unsigned int size){ return -1; }