
ua.mobius.media.server.impl.resource.dtmf.GeneratorImpl Maven / Gradle / Ivy
/*
* JBoss, Home of Professional Open Source
* Copyright 2011, Red Hat, Inc. and individual contributors
* by the @authors tag. See the copyright.txt in the distribution for a
* full listing of individual contributors.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
/*
* 15/07/13 - Change notice:
* This file has been modified by Mobius Software Ltd.
* For more information please visit http://www.mobius.ua
*/
package ua.mobius.media.server.impl.resource.dtmf;
import ua.mobius.media.ComponentType;
import ua.mobius.media.server.component.audio.AudioInput;
import ua.mobius.media.server.component.oob.OOBInput;
import ua.mobius.media.server.impl.AbstractCompoundSource;
import ua.mobius.media.server.scheduler.Scheduler;
import ua.mobius.media.server.spi.format.AudioFormat;
import ua.mobius.media.server.spi.format.FormatFactory;
import ua.mobius.media.server.spi.format.Formats;
import ua.mobius.media.server.spi.memory.ShortFrame;
import ua.mobius.media.server.spi.memory.ByteFrame;
import ua.mobius.media.server.spi.memory.ByteMemory;
import ua.mobius.media.server.spi.memory.ShortMemory;
import ua.mobius.media.server.spi.listener.Listeners;
import ua.mobius.media.server.spi.listener.TooManyListenersException;
import ua.mobius.media.server.spi.dtmf.DtmfGenerator;
import ua.mobius.media.server.spi.dtmf.DtmfGeneratorEvent;
import ua.mobius.media.server.spi.dtmf.DtmfGeneratorListener;
/**
* InbandGenerator generates Inband DTMF Tone only for uncompressed LINEAR
* codec. After creating instance of InbandGenerator, it needs to be initialized
* so that all the Tones are generated and kept ready for transmission once
* start is called.
*
* By default the Tone duration is 80ms. This is suited for Tone Detector who
* has Tone duration of greater than 40 and less than 80ms. For Tone Detector
* who's Tone duration is set greater than 80ms may fail to detect Tone
* generated by InbandGenerator(with duration 80ms). In that case increase the
* duration here too.
*
* @author yulian oifa
* @author amit bhayani
*/
public class GeneratorImpl extends AbstractCompoundSource implements DtmfGenerator {
private final static AudioFormat linear = FormatFactory.createAudioFormat("linear", 8000, 16, 1);
private long period = 20000000L;
private int packetSize = (int)(period / 1000000) * linear.getSampleRate()/1000 * linear.getSampleSize() / 8;
private final static Formats formats = new Formats();
static {
formats.add(linear);
}
public final static String[][] events = new String[][]{
{"1", "2", "3", "A"},
{"4", "5", "6", "B"},
{"7", "8", "9", "C"},
{"*", "0", "#", "D"}
};
private int[] lowFreq = new int[]{697, 770, 852, 941};
private int[] highFreq = new int[]{1209, 1336, 1477, 1633};
private String digit = null; // Min duration = 40ms and max = 500ms
private String oobDigit = null;
private int oobDigitValue=-1;
private int toneDuration = 50;
private Boolean growEndDuration=true;
private int endTonePackets=3;
private short A = Short.MAX_VALUE / 2;
private int volume = 0;
private int f1, f2;
private double dt;
private int pSize;
private double time = 0;
int index=0;
int eventDuration=0;
int oobVolume;
private AudioInput input;
private OOBInput oobInput;
private final Listeners listeners;
DtmfGeneratorEvent event=new DtmfGeneratorEvent(GeneratorImpl.this,DtmfGeneratorEvent.COMPLETED);
public GeneratorImpl(String name, Scheduler scheduler,Boolean growEndDuration,int endTonePackets) {
super(name, scheduler,scheduler.INPUT_QUEUE);
dt = 1.0 / linear.getSampleRate();
this.input=new AudioInput(ComponentType.DTMF_GENERATOR.getType(),packetSize);
this.connect(this.input);
this.oobInput=new OOBInput(ComponentType.DTMF_GENERATOR.getType());
this.connect(this.oobInput);
this.listeners = new Listeners();
this.growEndDuration=growEndDuration;
this.endTonePackets=endTonePackets;
}
public void addListener(final DtmfGeneratorListener listener)
{
try
{
listeners.add(listener);
}
catch(final TooManyListenersException ignored)
{
// This exception is never thrown by Listeners.add();
}
}
public void removeListener(final DtmfGeneratorListener listener)
{
listeners.remove(listener);
}
public void clearAllListeners()
{
listeners.clear();
}
public AudioInput getAudioInput()
{
return this.input;
}
public OOBInput getOOBInput()
{
return this.oobInput;
}
@Override
public void activate() {
if(oobDigit!=null) {
index=0;
start();
}
else if (digit != null) {
time = 0;
start();
}
}
public void setOOBDigit(String digit) {
if(digit.charAt(0)>='0' && digit.charAt(0)<='9')
oobDigitValue=(digit.charAt(0)-'0');
else if(digit.charAt(0)=='*')
oobDigitValue=10;
else if(digit.charAt(0)=='#')
oobDigitValue=11;
else if(digit.charAt(0)>='A' && digit.charAt(0)<='D')
oobDigitValue=12+digit.charAt(0)-'A';
else if(digit.charAt(0)>='a' && digit.charAt(0)<='d')
oobDigitValue=12+digit.charAt(0)-'a';
else
return;
index=0;
this.oobDigit=digit;
this.digit=null;
}
public void setDigit(String digit) {
this.oobDigit=null;
this.digit = digit;
this.time=0;
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 4; j++) {
if (events[i][j].equalsIgnoreCase(digit)) {
f1 = lowFreq[i];
f2 = highFreq[j];
}
}
}
}
@Override
public void completed()
{
super.completed();
listeners.dispatch(event);
}
public String getDigit() {
return this.digit;
}
public String getOOBDigit() {
return this.oobDigit;
}
public void setToneDuration(int duration) {
if (duration < 40) {
throw new IllegalArgumentException("Duration cannot be less than 40ms");
}
this.toneDuration = duration;
}
public int getToneDuration() {
return toneDuration;
}
public int getVolume() {
return this.volume;
}
public void setVolume(int volume) {
if (volume > 0) {
throw new IllegalArgumentException("Volume has to be negative value expressed in dBm0");
}
this.volume = volume;
A = (short) (Math.pow(Math.pow(10, volume), 0.1) * (Short.MAX_VALUE / 2));
}
private short getValue(double t) {
return (short) (A * (Math.sin(2 * Math.PI * f1 * t) + Math.sin(2 * Math.PI * f2 * t)));
}
public Formats getNativeFormats() {
return formats;
}
@Override
public ShortFrame evolve(long timestamp) {
if(digit==null)
return null;
if(time > (double) toneDuration / 1000.0)
return null;
int k = 0;
int frameSize = (int) ((double) 20 / 1000.0 / dt);
ShortFrame frame = ShortMemory.allocate(frameSize);
short[] data = frame.getData();
for (int i = 0; i < frameSize; i++)
data[i] = getValue(time + dt * i);
frame.setOffset(0);
frame.setLength(frameSize);
frame.setTimestamp(getMediaTime());
frame.setDuration(20000000L);
time += ((double) 20) / 1000.0;
if(time >= (double)toneDuration / 1000.0)
listeners.dispatch(event);
return frame;
}
@Override
public ByteFrame evolveOOB(long timestamp) {
if(oobDigit==null)
return null;
if(index > ((toneDuration / 20) + endTonePackets))
return null;
ByteFrame frame = ByteMemory.allocate(4);
byte[] data=frame.getData();
data[0]=(byte)oobDigitValue;
oobVolume=0-volume;
if(index > (toneDuration / 20))
{
//with end of event flag
data[1]=(byte)(0xBF & oobVolume);
if(growEndDuration)
eventDuration=(short)(160*index);
else
eventDuration=(short)((toneDuration/20)*160);
}
else
{
//without end of event flag
data[1]=(byte)(0x3F & oobVolume);
eventDuration=(short)(160*index);
}
data[2]=(byte)((eventDuration>>8) & 0xFF);
data[3]=(byte)(eventDuration & 0xFF);
frame.setOffset(0);
frame.setLength(4);
frame.setTimestamp(getMediaTime());
frame.setDuration(20000000L);
index++;
if(index == ((toneDuration / 20) + endTonePackets))
listeners.dispatch(event);
return frame;
}
@Override
public void deactivate() {
stop();
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy