/* * Mobicents, Communications Middleware * * Copyright (c) 2008, Red Hat Middleware LLC or third-party * contributors as * indicated by the @author tags or express copyright attribution * statements applied by the authors. All third-party contributions are * distributed under license by Red Hat Middleware LLC. * * This copyrighted material is made available to anyone wishing to use, modify, * copy, or redistribute it subject to the terms and conditions of the GNU * Lesser General Public License, as published by the Free Software Foundation. * * This program 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 distribution; if not, write to: * Free Software Foundation, Inc. * 51 Franklin Street, Fifth Floor * * Boston, MA 02110-1301 USA */ package org.mobicents.media.server.impl.resource.phone; import java.io.IOException; import org.mobicents.media.Buffer; import org.mobicents.media.Format; import org.mobicents.media.server.impl.AbstractSink; import org.mobicents.media.server.impl.NotifyEventImpl; import org.mobicents.media.server.impl.resource.GoertzelFilter; import org.mobicents.media.server.spi.dsp.Codec; import org.mobicents.media.server.spi.events.NotifyEvent; /** * * @author kulikov */ public class PhoneSignalDetector extends AbstractSink { private final static int STATE_IDLE = 0; private final static int STATE_SIGNAL = 1; private final static int STATE_SILENCE = 2; private int POWER = 10000; private int state = STATE_IDLE; private final static double E = 100; private final static int PACKET_DURATION = 50; private final static Format[] FORMATS = new Format[]{Codec.LINEAR_AUDIO}; private int[] T; private int[] f; private int offset; private int toneDuration = PACKET_DURATION; private int N = 8 * toneDuration; private double scale = (double) toneDuration / (double) 1000; private GoertzelFilter[] freqFilters; private double[] signal; private double maxAmpl; private double threshold; private int level; private double p[]; private long startTime; private int count; private NotifyEvent event; public PhoneSignalDetector(String name) { super(name); signal = new double[N]; } public int getEventID() { return event != null ? event.getEventID() : 0; } public void setEventID(int eventID) { event = new NotifyEventImpl(this, eventID); } public void setPeriods(int[] T) { this.T = T; } public int[] getPeriods() { return T; } public void setFrequency(int[] f) { this.f = f; freqFilters = new GoertzelFilter[f.length]; p = new double[f.length]; for (int i = 0; i < f.length; i++) { freqFilters[i] = new GoertzelFilter(f[i], N, scale); } } public int[] getFrequency() { return f; } public void setVolume(int level) { this.level = level; threshold = Math.pow(Math.pow(10, level), 0.1) * Short.MAX_VALUE; } public int getVolume() { return level; } @Override public void onMediaTransfer(Buffer buffer) throws IOException { byte[] data = (byte[]) buffer.getData(); int M = buffer.getOffset() + buffer.getLength(); int k = buffer.getOffset(); while (k < M) { while (offset < N && k < M - 1) { double s = ((data[k++] & 0xff) | (data[k++] << 8)); double sa = Math.abs(s); if (sa > maxAmpl) { maxAmpl = sa; } signal[offset++] = s; } //if dtmf buffer full check signal if (offset == N) { offset = 0; //and if max amplitude of signal is greater theshold //try to detect tone. if (maxAmpl >= threshold) { maxAmpl = 0; getPower(freqFilters, signal, 0, p); if (isDetected()) { notifySignal(); } else { notifyNoSignal(); } } else { notifySilence(); } } } } private void notifySignal() { switch(state) { case STATE_IDLE : startTime = System.currentTimeMillis(); state = STATE_SIGNAL; break; case STATE_SILENCE : long now = System.currentTimeMillis(); long duration = now - startTime; if (Math.abs(duration - T[1] * 1000) < E) { state = STATE_SIGNAL; startTime = now; } else { count = 0; state = STATE_IDLE; } break; } } private void notifySilence() { switch (state) { case STATE_SIGNAL : long now = System.currentTimeMillis(); long duration = now - startTime; if (Math.abs(duration - T[0] * 1000) < E) { count++; if (count == 3) { state = STATE_IDLE; count = 0; sendEvent(event); } else { state = STATE_SILENCE; startTime = now; } } else { count = 0; state = STATE_IDLE; } break; } } private void notifyNoSignal() { switch (state) { case STATE_SIGNAL : count = 0; state = STATE_IDLE; break; } } private boolean isDetected() { for (double P : p) { if (P <= POWER ) { return false; } } return true; } private void getPower(GoertzelFilter[] filters, double[] data, int offset, double[] power) { for (int i = 0; i < filters.length; i++) { power[i] = filters[i].getPower(data, offset); } } public Format[] getFormats() { return FORMATS; } public boolean isAcceptable(Format format) { return format.matches(Codec.LINEAR_AUDIO); } }