/** * Copyright (C) 2010-2012 Regis Montoya (aka r3gis - www.r3gis.fr) * This file is part of CSipSimple. * * CSipSimple 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 3 of the License, or * (at your option) any later version. * If you own a pjsip commercial license you can also redistribute it * and/or modify it under the terms of the GNU Lesser General Public License * as an android library. * * CSipSimple 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 CSipSimple. If not, see <http://www.gnu.org/licenses/>. */ package com.csipsimple.pjsip; import com.csipsimple.utils.Log; import org.pjsip.pjsua.pj_pool_t; import org.pjsip.pjsua.pj_str_t; import org.pjsip.pjsua.pjmedia_port; import org.pjsip.pjsua.pjmedia_tone_desc; import org.pjsip.pjsua.pjmedia_tone_digit; import org.pjsip.pjsua.pjsua; import org.pjsip.pjsua.pjsuaConstants; import org.pjsip.pjsua.pjsua_call_info; /** * DTMF In band tone generator for a given call object * It creates it's own pool, media port, and can stream in. * */ public class PjStreamDialtoneGenerator { private static final String THIS_FILE = "PjStreamDialtoneGenerator"; private static String SUPPORTED_DTMF = "0123456789abcd*#"; private final int callId; private final boolean streamAsMicro; private pj_pool_t dialtonePool; private pjmedia_port dialtoneGen; private int dialtoneSlot = -1; public PjStreamDialtoneGenerator(int aCallId) { this(aCallId, true); } public PjStreamDialtoneGenerator(int aCallId, boolean onMicro) { callId = aCallId; streamAsMicro = onMicro; } /** * Start the tone generate. * This is automatically done by the send dtmf * @return the pjsip error code for creation */ private synchronized int startDialtoneGenerator() { pjsua_call_info info = new pjsua_call_info(); pjsua.call_get_info(callId, info); int status; dialtonePool = pjsua.pjsua_pool_create("tonegen-"+callId, 512, 512); pj_str_t name = pjsua.pj_str_copy("dialtoneGen"); long clockRate = 8000; long channelCount = 1; long samplesPerFrame = 160; long bitsPerSample = 16; long options = 0; int[] dialtoneSlotPtr = new int[1]; dialtoneGen = new pjmedia_port(); status = pjsua.pjmedia_tonegen_create2(dialtonePool, name, clockRate, channelCount, samplesPerFrame, bitsPerSample, options, dialtoneGen); if (status != pjsua.PJ_SUCCESS) { stopDialtoneGenerator(); return status; } status = pjsua.conf_add_port(dialtonePool, dialtoneGen, dialtoneSlotPtr); if (status != pjsua.PJ_SUCCESS) { stopDialtoneGenerator(); return status; } dialtoneSlot = dialtoneSlotPtr[0]; if(streamAsMicro) { int callConfSlot = info.getConf_slot(); status = pjsua.conf_connect(dialtoneSlot, callConfSlot); }else { status = pjsua.conf_connect(dialtoneSlot, 0); } if (status != pjsua.PJ_SUCCESS) { dialtoneSlot = -1; stopDialtoneGenerator(); return status; } return pjsua.PJ_SUCCESS; } /** * Stop the dialtone generator. * This has to be called manually when no more DTMF codes are to be send for the associated call */ public synchronized void stopDialtoneGenerator() { stopSending(); // Destroy the port if (dialtoneSlot != -1) { pjsua.conf_remove_port(dialtoneSlot); dialtoneSlot = -1; } if (dialtoneGen != null) { pjsua.pjmedia_port_destroy(dialtoneGen); dialtoneGen = null; } if (dialtonePool != null) { pjsua.pj_pool_release(dialtonePool); dialtonePool = null; } } /** * Send multiple tones. * @param dtmfChars tones list to send. * @return the pjsip status */ public synchronized int sendPjMediaDialTone(String dtmfChars) { int status = ensureDialtoneGen(); if(status != pjsua.PJ_SUCCESS) { return status; } stopSending(); for(int i = 0 ; i < dtmfChars.length(); i++ ) { char d = dtmfChars.charAt(i); if(SUPPORTED_DTMF.indexOf(d) == -1) { Log.w(THIS_FILE, "Unsupported DTMF char " + d); } else { // Found dtmf char, use digit api pjmedia_tone_digit[] tone = new pjmedia_tone_digit[1]; tone[0] = new pjmedia_tone_digit(); tone[0].setVolume((short) 0); tone[0].setOn_msec((short) 100); tone[0].setOff_msec((short) 200); tone[0].setDigit(d); pjsua.pjmedia_tonegen_play_digits(dialtoneGen, 1, tone, 0); } } return status; } /** * Start playback of a waiting tone. * This will create a thread looping until {@link #stopDialtoneGenerator()} called * * @return #PJ_SUCCESS if start done correctly */ public synchronized int startPjMediaWaitingTone() { int status = ensureDialtoneGen(); if (status != pjsua.PJ_SUCCESS) { return status; } stopSending(); // Found dtmf char, use digit api pjmedia_tone_desc[] tone = new pjmedia_tone_desc[1]; tone[0] = new pjmedia_tone_desc(); tone[0].setVolume((short) 0); // 0 means default tone[0].setOn_msec((short) 100); tone[0].setOff_msec((short) 1500); tone[0].setFreq1((short)440); tone[0].setFreq2((short)350); // Not sure about this one pjsua.pjmedia_tonegen_play(dialtoneGen, 1, tone, pjsuaConstants.PJMEDIA_TONEGEN_LOOP); return status; } private int ensureDialtoneGen() { if (dialtoneGen == null) { int status = startDialtoneGenerator(); if (status != pjsua.PJ_SUCCESS) { return -1; } } return pjsua.PJ_SUCCESS; } private void stopSending() { if (dialtoneGen != null) { pjsua.pjmedia_tonegen_stop(dialtoneGen); } } }