/* * Copyright (C) 2007 iptego GmbH * * This file is part of SEMS, a free SIP media server. * * SEMS is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * For a license to use the sems software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * SEMS is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #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, AmAudio* B, unsigned int s, double l, unsigned int flags) : A(A),B(B), s(s), l(l), mixing(false), next_start_ts_i(false), flags(flags) { } AmAudioMixIn::~AmAudioMixIn() { } int AmAudioMixIn::get(unsigned long long system_ts, unsigned char* buffer, int output_sample_rate, unsigned int nb_samples) { if (!mixing) { if (!next_start_ts_i) { next_start_ts_i = true; next_start_ts = IS_IMMEDIATE_START ? system_ts : system_ts + s*WALLCLOCK_RATE; } if (!sys_ts_less()(system_ts, next_start_ts)) { DBG("starting mix-in\n"); mixing = true; next_start_ts = system_ts + s*WALLCLOCK_RATE; } } if (NULL == A) return -1; B_mut.lock(); if (!mixing || NULL == B) { B_mut.unlock(); return A->get(system_ts, buffer, output_sample_rate, nb_samples); } else { if (l < 0.01) { // epsilon // only play back from B int res = B->get(system_ts, buffer, output_sample_rate, nb_samples); if (res <= 0) { // B empty res = A->get(system_ts, buffer, output_sample_rate, nb_samples); mixing = false; if (IS_ONLY_ONCE) { B = NULL; } else { AmAudioFile* B_file = dynamic_cast(B); if (NULL != B_file) { B_file->rewind(); } } } B_mut.unlock(); return res; } else { // mix the two int res = 0; short* pdest = (short*)buffer; // get audio from A int len = A->get(system_ts, (unsigned char*)mix_buf, output_sample_rate, nb_samples); if ((len<0) && !IS_FINISH_B_MIX) { // A finished B_mut.unlock(); return len; } for (int i=0; i<(PCM16_B2S(len)); i++) { pdest[i]=(short)(((double)mix_buf[i])*(1.0-l)); } res = len; // clear the rest unsigned int len_from_a = 0; if (res>0) len_from_a=(unsigned int)res; 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(system_ts, (unsigned char*)mix_buf, output_sample_rate, nb_samples); if (len<0) { // B finished mixing = false; if (IS_ONLY_ONCE) { B = NULL; } else { AmAudioFile* B_file = dynamic_cast(B); if (NULL != B_file) { B_file->rewind(); } } } else { 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; } } } int AmAudioMixIn::put(unsigned long long system_ts, unsigned char* buffer, int input_sample_rate, unsigned int size) { ERROR("writing not supported\n"); return -1; } void AmAudioMixIn::mixin(AmAudio* f) { B_mut.lock(); B = f; mixing = next_start_ts_i = false; /* so that mix in will re-start */ B_mut.unlock(); }