All Downloads are FREE. Search and download functionalities are using the official Maven repository.

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