aboutsummaryrefslogtreecommitdiffstats
path: root/src/net/java/sip
diff options
context:
space:
mode:
authorVincent Lucas <chenzo@jitsi.org>2012-02-07 10:00:54 +0000
committerVincent Lucas <chenzo@jitsi.org>2012-02-07 10:00:54 +0000
commite0e1cc30dcb44a66630569aff98092b729dae807 (patch)
treec63a09ce2821858a5168279eb541881ab291d3d0 /src/net/java/sip
parenta72eb6baf4e054f868d6d065487d9c5d16106cc3 (diff)
downloadjitsi-e0e1cc30dcb44a66630569aff98092b729dae807.zip
jitsi-e0e1cc30dcb44a66630569aff98092b729dae807.tar.gz
jitsi-e0e1cc30dcb44a66630569aff98092b729dae807.tar.bz2
Adds in-band DTMF functionality.
Diffstat (limited to 'src/net/java/sip')
-rw-r--r--src/net/java/sip/communicator/impl/neomedia/AudioMediaStreamImpl.java16
-rw-r--r--src/net/java/sip/communicator/impl/neomedia/MediaServiceImpl.java5
-rw-r--r--src/net/java/sip/communicator/impl/neomedia/conference/AudioMixerPushBufferStream.java26
-rw-r--r--src/net/java/sip/communicator/impl/neomedia/conference/AudioMixingPushBufferDataSource.java49
-rw-r--r--src/net/java/sip/communicator/impl/neomedia/device/MediaDeviceSession.java19
-rw-r--r--src/net/java/sip/communicator/impl/neomedia/protocol/InbandDTMFDataSource.java25
-rw-r--r--src/net/java/sip/communicator/impl/neomedia/protocol/RewritablePullBufferDataSource.java (renamed from src/net/java/sip/communicator/impl/neomedia/protocol/MutePullBufferDataSource.java)58
-rw-r--r--src/net/java/sip/communicator/impl/neomedia/protocol/RewritablePushBufferDataSource.java (renamed from src/net/java/sip/communicator/impl/neomedia/protocol/MutePushBufferDataSource.java)167
-rw-r--r--src/net/java/sip/communicator/impl/neomedia/transform/dtmf/DtmfTransformEngine.java149
-rw-r--r--src/net/java/sip/communicator/impl/protocol/sip/OperationSetDTMFSipImpl.java61
-rw-r--r--src/net/java/sip/communicator/impl/protocol/sip/dtmf/DTMFInband.java30
-rw-r--r--src/net/java/sip/communicator/plugin/sipaccregwizz/ConnectionPanel.java59
-rw-r--r--src/net/java/sip/communicator/plugin/sipaccregwizz/SIPAccountRegistration.java40
-rw-r--r--src/net/java/sip/communicator/plugin/sipaccregwizz/SIPAccountRegistrationForm.java8
-rw-r--r--src/net/java/sip/communicator/plugin/sipaccregwizz/SIPAccountRegistrationWizard.java11
-rw-r--r--src/net/java/sip/communicator/service/neomedia/AudioMediaStream.java7
-rw-r--r--src/net/java/sip/communicator/service/neomedia/DTMFInbandTone.java424
17 files changed, 985 insertions, 169 deletions
diff --git a/src/net/java/sip/communicator/impl/neomedia/AudioMediaStreamImpl.java b/src/net/java/sip/communicator/impl/neomedia/AudioMediaStreamImpl.java
index c37e47b..1f2079a 100644
--- a/src/net/java/sip/communicator/impl/neomedia/AudioMediaStreamImpl.java
+++ b/src/net/java/sip/communicator/impl/neomedia/AudioMediaStreamImpl.java
@@ -291,6 +291,21 @@ public class AudioMediaStreamImpl
}
/**
+ * Adds a new inband DTMF tone to send.
+ *
+ * @param tone the DTMF tone to send.
+ */
+ public void addInbandDTMF(DTMFInbandTone tone)
+ {
+ MediaDeviceSession deviceSession = getDeviceSession();
+
+ if (deviceSession != null)
+ {
+ deviceSession.addDTMF(tone);
+ }
+ }
+
+ /**
* In addition to calling
* {@link MediaStreamImpl#addRTPExtension(byte, RTPExtension)}
* this method enables sending of CSRC audio levels. The reason we are
@@ -419,7 +434,6 @@ public class AudioMediaStreamImpl
if(dtmfTransfrmEngine != null)
{
- dtmfTransfrmEngine.stop();
dtmfTransfrmEngine = null;
}
}
diff --git a/src/net/java/sip/communicator/impl/neomedia/MediaServiceImpl.java b/src/net/java/sip/communicator/impl/neomedia/MediaServiceImpl.java
index 2ab59e3..3cf6627 100644
--- a/src/net/java/sip/communicator/impl/neomedia/MediaServiceImpl.java
+++ b/src/net/java/sip/communicator/impl/neomedia/MediaServiceImpl.java
@@ -1287,9 +1287,10 @@ public class MediaServiceImpl
(VideoMediaDeviceSession)dev.getSession();
DataSource ds = session.getCaptureDevice();
- if(ds instanceof MutePullBufferDataSource)
+ if(ds instanceof RewritablePullBufferDataSource)
{
- MutePullBufferDataSource ds2 = (MutePullBufferDataSource)ds;
+ RewritablePullBufferDataSource ds2 =
+ (RewritablePullBufferDataSource)ds;
ds = ds2.getWrappedDataSource();
}
diff --git a/src/net/java/sip/communicator/impl/neomedia/conference/AudioMixerPushBufferStream.java b/src/net/java/sip/communicator/impl/neomedia/conference/AudioMixerPushBufferStream.java
index 25635f1..08aaf95 100644
--- a/src/net/java/sip/communicator/impl/neomedia/conference/AudioMixerPushBufferStream.java
+++ b/src/net/java/sip/communicator/impl/neomedia/conference/AudioMixerPushBufferStream.java
@@ -1001,7 +1001,31 @@ class AudioMixerPushBufferStream
{
InputStreamDesc inputStreamDesc = inputStreams[i];
- if (outputDataSource.equals(inputStreamDesc.getOutputDataSource())
+ if(audioMixer.captureDevice instanceof
+ AudioMixingPushBufferDataSource
+ && inputStreamDesc.inputDataSourceDesc.inputDataSource ==
+ audioMixer.captureDevice
+ && outputDataSource.isSendingDTMF())
+ {
+ PushBufferStream inputStream
+ = (PushBufferStream) inputStreamDesc.getInputStream();
+ AudioFormat inputStreamFormat
+ = (AudioFormat) inputStream.getFormat();
+
+ double samplingFrequency = inputStreamFormat.getSampleRate();
+ int sampleSizeInBits = inputStreamFormat.getSampleSizeInBits();
+
+ // Generates the inband DTMF signal.
+ inputSamples[i] = outputDataSource.getNextToneSignal(
+ samplingFrequency,
+ sampleSizeInBits);
+ if(maxInputSampleCount < inputSamples[i].length)
+ {
+ maxInputSampleCount = inputSamples[i].length;
+ }
+ }
+ else if (outputDataSource.equals(
+ inputStreamDesc.getOutputDataSource())
|| (outputDataSourceIsMute
&& (inputStreamDesc
.inputDataSourceDesc
diff --git a/src/net/java/sip/communicator/impl/neomedia/conference/AudioMixingPushBufferDataSource.java b/src/net/java/sip/communicator/impl/neomedia/conference/AudioMixingPushBufferDataSource.java
index f94f451..b7587c6 100644
--- a/src/net/java/sip/communicator/impl/neomedia/conference/AudioMixingPushBufferDataSource.java
+++ b/src/net/java/sip/communicator/impl/neomedia/conference/AudioMixingPushBufferDataSource.java
@@ -8,6 +8,7 @@ package net.java.sip.communicator.impl.neomedia.conference;
import java.io.*;
import java.lang.reflect.*;
+import java.util.*;
import javax.media.*;
import javax.media.control.*;
@@ -15,6 +16,7 @@ import javax.media.protocol.*;
import net.java.sip.communicator.impl.neomedia.control.*;
import net.java.sip.communicator.impl.neomedia.protocol.*;
+import net.java.sip.communicator.service.neomedia.*;
import net.java.sip.communicator.util.*;
/**
@@ -27,7 +29,8 @@ import net.java.sip.communicator.util.*;
public class AudioMixingPushBufferDataSource
extends PushBufferDataSource
implements CaptureDevice,
- MuteDataSource
+ MuteDataSource,
+ InbandDTMFDataSource
{
/**
@@ -70,6 +73,11 @@ public class AudioMixingPushBufferDataSource
private boolean mute = false;
/**
+ * The tones to send via inband DTMF, if not empty.
+ */
+ private LinkedList<DTMFInbandTone> tones = new LinkedList<DTMFInbandTone>();
+
+ /**
* Initializes a new <tt>AudioMixingPushBufferDataSource</tt> instance which
* gives access to the result of the audio mixing performed by a specific
* <tt>AudioMixer</tt>.
@@ -363,4 +371,43 @@ public class AudioMixingPushBufferDataSource
this.mute = mute;
}
}
+
+ /**
+ * Adds a new inband DTMF tone to send.
+ *
+ * @param tone the DTMF tone to send.
+ */
+ public void addDTMF(DTMFInbandTone tone)
+ {
+ this.tones.add(tone);
+ }
+
+ /**
+ * Determines whether this <tt>DataSource</tt> sends a DTMF tone.
+ *
+ * @return <tt>true</tt> if this <tt>DataSource</tt> is sending a DTMF tone;
+ * otherwise, <tt>false</tt>.
+ */
+ public boolean isSendingDTMF()
+ {
+ return !this.tones.isEmpty();
+ }
+
+ /**
+ * Gets the next inband DTMF tone signal.
+ *
+ * @param samplingFrequency The sampling frequency (codec clock rate) in Hz
+ * of the stream which will encapsulate this signal.
+ * @param sampleSizeInBits The size of each sample (8 for a byte, 16 for a
+ * short and 32 for an int)
+ *
+ * @return The data array containing the DTMF signal.
+ */
+ public int[] getNextToneSignal(
+ double samplingFrequency,
+ int sampleSizeInBits)
+ {
+ DTMFInbandTone tone = tones.poll();
+ return tone.getAudioSamples(samplingFrequency, sampleSizeInBits);
+ }
}
diff --git a/src/net/java/sip/communicator/impl/neomedia/device/MediaDeviceSession.java b/src/net/java/sip/communicator/impl/neomedia/device/MediaDeviceSession.java
index d906509..a95b4f4 100644
--- a/src/net/java/sip/communicator/impl/neomedia/device/MediaDeviceSession.java
+++ b/src/net/java/sip/communicator/impl/neomedia/device/MediaDeviceSession.java
@@ -390,13 +390,13 @@ public class MediaDeviceSession
if (captureDevice instanceof PushBufferDataSource)
{
captureDevice
- = new MutePushBufferDataSource(
+ = new RewritablePushBufferDataSource(
(PushBufferDataSource) captureDevice);
}
else if (captureDevice instanceof PullBufferDataSource)
{
captureDevice
- = new MutePullBufferDataSource(
+ = new RewritablePullBufferDataSource(
(PullBufferDataSource) captureDevice);
}
}
@@ -1600,6 +1600,21 @@ public class MediaDeviceSession
}
/**
+ * Adds a new inband DTMF tone to send.
+ *
+ * @param tone the DTMF tone to send.
+ */
+ public void addDTMF(DTMFInbandTone tone)
+ {
+ DataSource captureDevice = this.captureDevice;
+
+ if (captureDevice instanceof InbandDTMFDataSource)
+ {
+ ((InbandDTMFDataSource) captureDevice).addDTMF(tone);
+ }
+ }
+
+ /**
* Adds a specific <tt>DataSource</tt> to the list of playbacks of
* <tt>ReceiveStream</tt>s and/or <tt>DataSource</tt>s performed by
* respective <tt>Player</tt>s on the <tt>MediaDevice</tt> represented by
diff --git a/src/net/java/sip/communicator/impl/neomedia/protocol/InbandDTMFDataSource.java b/src/net/java/sip/communicator/impl/neomedia/protocol/InbandDTMFDataSource.java
new file mode 100644
index 0000000..ae0f08c
--- /dev/null
+++ b/src/net/java/sip/communicator/impl/neomedia/protocol/InbandDTMFDataSource.java
@@ -0,0 +1,25 @@
+/*
+ * Jitsi, the OpenSource Java VoIP and Instant Messaging client.
+ *
+ * Distributable under LGPL license.
+ * See terms of license at gnu.org.
+ */
+package net.java.sip.communicator.impl.neomedia.protocol;
+
+import net.java.sip.communicator.service.neomedia.*;
+
+/**
+ * All datasources that support inband DTMF functionalities implement
+ * <tt>InbandDTMFDataSource</tt>.
+ *
+ * @author Vincent Lucas
+ */
+public interface InbandDTMFDataSource
+{
+ /**
+ * Adds a new inband DTMF tone to send.
+ *
+ * @param tone the DTMF tone to send.
+ */
+ public void addDTMF(DTMFInbandTone tone);
+}
diff --git a/src/net/java/sip/communicator/impl/neomedia/protocol/MutePullBufferDataSource.java b/src/net/java/sip/communicator/impl/neomedia/protocol/RewritablePullBufferDataSource.java
index 0a689ad..a5ac6cf 100644
--- a/src/net/java/sip/communicator/impl/neomedia/protocol/MutePullBufferDataSource.java
+++ b/src/net/java/sip/communicator/impl/neomedia/protocol/RewritablePullBufferDataSource.java
@@ -7,10 +7,13 @@
package net.java.sip.communicator.impl.neomedia.protocol;
import java.io.*;
+import java.util.*;
import javax.media.*;
import javax.media.protocol.*;
+import net.java.sip.communicator.service.neomedia.*;
+
/**
* Implements a <tt>PullBufferDataSource</tt> wrapper which provides mute
* support for the wrapped instance.
@@ -23,9 +26,10 @@ import javax.media.protocol.*;
* @author Damian Minkov
* @author Lubomir Marinov
*/
-public class MutePullBufferDataSource
+public class RewritablePullBufferDataSource
extends PullBufferDataSourceDelegate<PullBufferDataSource>
- implements MuteDataSource
+ implements MuteDataSource,
+ InbandDTMFDataSource
{
/**
* The indicator which determines whether this <tt>DataSource</tt> is mute.
@@ -33,13 +37,18 @@ public class MutePullBufferDataSource
private boolean mute;
/**
- * Initializes a new <tt>MutePullBufferDataSource</tt> instance which is to
- * provide mute support for a specific <tt>PullBufferDataSource</tt>.
+ * The tones to send via inband DTMF, if not empty.
+ */
+ private LinkedList<DTMFInbandTone> tones = new LinkedList<DTMFInbandTone>();
+
+ /**
+ * Initializes a new <tt>RewritablePullBufferDataSource</tt> instance which
+ * is to provide mute support for a specific <tt>PullBufferDataSource</tt>.
*
* @param dataSource the <tt>PullBufferDataSource</tt> the new instance is
* to provide mute support for
*/
- public MutePullBufferDataSource(PullBufferDataSource dataSource)
+ public RewritablePullBufferDataSource(PullBufferDataSource dataSource)
{
super(dataSource);
}
@@ -67,6 +76,27 @@ public class MutePullBufferDataSource
}
/**
+ * Adds a new inband DTMF tone to send.
+ *
+ * @param tone the DTMF tone to send.
+ */
+ public void addDTMF(DTMFInbandTone tone)
+ {
+ this.tones.add(tone);
+ }
+
+ /**
+ * Determines whether this <tt>DataSource</tt> sends a DTMF tone.
+ *
+ * @return <tt>true</tt> if this <tt>DataSource</tt> is sending a DTMF tone;
+ * otherwise, <tt>false</tt>.
+ */
+ public boolean isSendingDTMF()
+ {
+ return !this.tones.isEmpty();
+ }
+
+ /**
* Get wrapped DataSource.
*
* @return wrapped DataSource
@@ -130,18 +160,24 @@ public class MutePullBufferDataSource
/**
* Implements PullBufferStream#read(Buffer). If this instance is muted
- * (through its owning MutePullBufferDataSource), overwrites the data
- * read from the wrapped PullBufferStream with silence data.
- * @param buffer which data will be filled.
- * @throws IOException Thrown if an error occurs while reading.
+ * (through its owning RewritablePullBufferDataSource), overwrites the
+ * data read from the wrapped PullBufferStream with silence data.
+ * @param buffer which data will be filled. @throws IOException Thrown
+ * if an error occurs while reading.
*/
public void read(Buffer buffer)
throws IOException
{
stream.read(buffer);
- if (isMute())
- MutePushBufferDataSource.mute(buffer);
+ if (isSendingDTMF())
+ {
+ RewritablePushBufferDataSource.sendDTMF(buffer, tones.poll());
+ }
+ else if (isMute())
+ {
+ RewritablePushBufferDataSource.mute(buffer);
+ }
}
/**
diff --git a/src/net/java/sip/communicator/impl/neomedia/protocol/MutePushBufferDataSource.java b/src/net/java/sip/communicator/impl/neomedia/protocol/RewritablePushBufferDataSource.java
index c604c99..57fc4ed 100644
--- a/src/net/java/sip/communicator/impl/neomedia/protocol/MutePushBufferDataSource.java
+++ b/src/net/java/sip/communicator/impl/neomedia/protocol/RewritablePushBufferDataSource.java
@@ -7,11 +7,17 @@
package net.java.sip.communicator.impl.neomedia.protocol;
import java.io.*;
+import java.nio.ByteBuffer; // disambiguation.
+import java.nio.ByteOrder; // disambiguation.
+import java.nio.IntBuffer; // disambiguation.
import java.util.*;
import javax.media.*;
+import javax.media.format.*;
import javax.media.protocol.*;
+import net.java.sip.communicator.service.neomedia.*;
+
/**
* Implements a <tt>PushBufferDataSource</tt> wrapper which provides mute
* support for the wrapped instance.
@@ -23,9 +29,10 @@ import javax.media.protocol.*;
*
* @author Lyubomir Marinov
*/
-public class MutePushBufferDataSource
+public class RewritablePushBufferDataSource
extends PushBufferDataSourceDelegate<PushBufferDataSource>
- implements MuteDataSource
+ implements MuteDataSource,
+ InbandDTMFDataSource
{
/**
@@ -34,13 +41,18 @@ public class MutePushBufferDataSource
private boolean mute;
/**
- * Initializes a new <tt>MutePushBufferDataSource</tt> instance which is to
- * provide mute support for a specific <tt>PushBufferDataSource</tt>.
+ * The tones to send via inband DTMF, if not empty.
+ */
+ private LinkedList<DTMFInbandTone> tones = new LinkedList<DTMFInbandTone>();
+
+ /**
+ * Initializes a new <tt>RewritablePushBufferDataSource</tt> instance which
+ * is to provide mute support for a specific <tt>PushBufferDataSource</tt>.
*
* @param dataSource the <tt>PushBufferDataSource</tt> the new instance is
* to provide mute support for
*/
- public MutePushBufferDataSource(PushBufferDataSource dataSource)
+ public RewritablePushBufferDataSource(PushBufferDataSource dataSource)
{
super(dataSource);
}
@@ -124,6 +136,141 @@ public class MutePushBufferDataSource
}
/**
+ * Adds a new inband DTMF tone to send.
+ *
+ * @param tone the DTMF tone to send.
+ */
+ public void addDTMF(DTMFInbandTone tone)
+ {
+ this.tones.add(tone);
+ }
+
+ /**
+ * Determines whether this <tt>DataSource</tt> sends a DTMF tone.
+ *
+ * @return <tt>true</tt> if this <tt>DataSource</tt> is sending a DTMF tone;
+ * otherwise, <tt>false</tt>.
+ */
+ public boolean isSendingDTMF()
+ {
+ return !this.tones.isEmpty();
+ }
+
+ /**
+ * Replaces the media data contained in a specific <tt>Buffer</tt> with an
+ * inband DTMF tone signal.
+ *
+ * @param buffer the <tt>Buffer</tt> the data contained in which is to be
+ * replaced with the DTMF tone
+ * @param tone the <tt>DMFTTone</tt> to send via inband DTMF signal.
+ */
+ public static void sendDTMF(
+ Buffer buffer,
+ DTMFInbandTone tone)
+ {
+ Object data = buffer.getData();
+
+ // Send the inband DTMF tone only if the buffer contains audio data.
+ if (data != null && (buffer.getFormat() instanceof AudioFormat))
+ {
+ Class<?> dataClass = data.getClass();
+ double audioSample;
+ double amplitudeCoefficient;
+ int fromIndex = buffer.getOffset();
+
+ AudioFormat audioFormat = (AudioFormat) buffer.getFormat();
+ double samplingFrequency = audioFormat.getSampleRate();
+ int sampleSizeInBits = audioFormat.getSampleSizeInBits();
+
+ // Generates the inband DTMF signal.
+ int[] sampleData = tone.getAudioSamples(
+ samplingFrequency,
+ sampleSizeInBits);
+ IntBuffer sampleDataIntBuffer = IntBuffer.wrap(sampleData);
+
+ int toIndex = fromIndex +
+ sampleData.length * (sampleSizeInBits / 8);
+ ByteBuffer newData = ByteBuffer.allocate(toIndex);
+
+ // Prepares newData to be endian compliant with original buffer
+ // data.
+ if(audioFormat.getEndian() == AudioFormat.BIG_ENDIAN)
+ {
+ newData.order(ByteOrder.BIG_ENDIAN);
+ }
+ else
+ {
+ newData.order(ByteOrder.LITTLE_ENDIAN);
+ }
+
+ // Keeps data unchanged if storeed before the original buffer offset
+ // index.
+ // Takes care of original data array type (byte, short or int).
+ if (Format.byteArray.equals(dataClass))
+ {
+ newData.put(((byte[]) data), 0, fromIndex);
+ }
+ else if (Format.shortArray.equals(dataClass))
+ {
+ for(int i = 0; i < fromIndex; ++i)
+ {
+ newData.putShort(((short[]) data)[i]);
+ }
+ }
+ else if (Format.intArray.equals(dataClass))
+ {
+ for(int i = 0; i < fromIndex; ++i)
+ {
+ newData.putInt(((int[]) data)[i]);
+ }
+ }
+
+ // Copies inband DTMF singal into newData.
+ // Takes care of audio format encryption data type (byte, short or
+ // int).
+ switch (sampleSizeInBits)
+ {
+ case 8:
+ for(int i = 0; i < sampleData.length; ++i)
+ {
+ newData.put(((byte) sampleData[i]));
+ }
+ break;
+ case 16:
+ for(int i = 0; i < sampleData.length; ++i)
+ {
+ newData.putShort(((short) sampleData[i]));
+ }
+ break;
+ case 32:
+ for(int i = 0; i < sampleData.length; ++i)
+ {
+ newData.putInt(sampleData[i]);
+ }
+ break;
+ }
+
+ // Copies newData up to date into the original buffer.
+ // Takes care of original data array type (byte, short or int).
+ if (Format.byteArray.equals(dataClass))
+ {
+ buffer.setData(newData.array());
+ }
+ else if (Format.shortArray.equals(dataClass))
+ {
+ buffer.setData(newData.asShortBuffer().array());
+ }
+ else if (Format.intArray.equals(dataClass))
+ {
+ buffer.setData(newData.asIntBuffer().array());
+ }
+
+ // Updates the buffer length.
+ buffer.setLength(toIndex - fromIndex);
+ }
+ }
+
+ /**
* Implements a <tt>PushBufferStream</tt> wrapper which provides mute
* support for the wrapped instance.
*/
@@ -157,7 +304,7 @@ public class MutePushBufferDataSource
/**
* Implements {@link PushBufferStream#read(Buffer)}. If this instance is
- * muted (through its owning <tt>MutePushBufferDataSource</tt>),
+ * muted (through its owning <tt>RewritablePushBufferDataSource</tt>),
* overwrites the data read from the wrapped <tt>PushBufferStream</tt>
* with silence data.
*
@@ -171,8 +318,14 @@ public class MutePushBufferDataSource
{
stream.read(buffer);
- if (isMute())
+ if (isSendingDTMF())
+ {
+ sendDTMF(buffer, tones.poll());
+ }
+ else if (isMute())
+ {
mute(buffer);
+ }
}
/**
diff --git a/src/net/java/sip/communicator/impl/neomedia/transform/dtmf/DtmfTransformEngine.java b/src/net/java/sip/communicator/impl/neomedia/transform/dtmf/DtmfTransformEngine.java
index 6728c2f..510d7c3 100644
--- a/src/net/java/sip/communicator/impl/neomedia/transform/dtmf/DtmfTransformEngine.java
+++ b/src/net/java/sip/communicator/impl/neomedia/transform/dtmf/DtmfTransformEngine.java
@@ -81,11 +81,6 @@ public class DtmfTransformEngine
DTMFTone.DTMF_SHARP, DTMFTone.DTMF_STAR};
/**
- * The dispatcher that is delivering tones to the media steam.
- */
- private DTMFDispatcher dtmfDispatcher = null;
-
- /**
* The status that this engine is currently in.
*/
private ToneTransmissionState toneTransmissionState
@@ -186,13 +181,7 @@ public class DtmfTransformEngine
if(currentDtmfPayload == pkt.getPayloadType())
{
DtmfRawPacket p = new DtmfRawPacket(pkt);
-
- if (dtmfDispatcher == null)
- {
- dtmfDispatcher = new DTMFDispatcher();
- new Thread(dtmfDispatcher).start();
- }
- dtmfDispatcher.addTonePacket(p);
+ this.addTonePacket(p);
// ignore received dtmf packets
// if jmf receive change in rtp payload stops reception
@@ -327,133 +316,33 @@ public class DtmfTransformEngine
}
/**
- * Stops threads that this transform engine is using for even delivery.
+ * A packet that we should convert to tone and deliver
+ * to our media stream and its listeners in a separate thread.
+ *
+ * @param p the packet we will convert and deliver.
*/
- public void stop()
+ private void addTonePacket(DtmfRawPacket p)
{
- if(dtmfDispatcher != null)
- dtmfDispatcher.stop();
+ DTMFTone tone = getToneFromPacket(p);
+ boolean toEnd = p.isEnd();
+
+ mediaStream.fireDTMFEvent(tone, toEnd);
}
/**
- * A simple thread that waits for new tones to be reported from incoming
- * RTP packets and then delivers them to the <tt>AudioMediaStream</tt>
- * associated with this engine. The reason we need to do this in a separate
- * thread is of course the time sensitive nature of incoming RTP packets.
+ * Maps DTMF packet codes to our DTMFTone objects.
+ * @param p the packet
+ * @return the corresponding tone.
*/
- private class DTMFDispatcher
- implements Runnable
+ private DTMFTone getToneFromPacket(DtmfRawPacket p)
{
- /** Indicates whether this thread is supposed to be running */
- private boolean isRunning = false;
-
- /** The tone that we last received from the reverseTransform thread*/
- private DTMFTone lastReceivedTone = null;
-
- /** The tone that we last received from the reverseTransform thread*/
- private DTMFTone lastReportedTone = null;
-
- /**
- * Have we received end of the currently started tone.
- */
- private boolean toEnd = false;
-
- /**
- * Waits for new tone to be reported via the <tt>addTonePacket()</tt>
- * method and then delivers them to the <tt>AudioMediaStream</tt> that
- * we are associated with.
- */
- public void run()
- {
- isRunning = true;
-
- DTMFTone temp = null;
-
- while(isRunning)
- {
- synchronized(this)
- {
- if(lastReceivedTone == null)
- {
- try
- {
- wait();
- }
- catch (InterruptedException ie) {}
- }
-
- temp = lastReceivedTone;
- // make lastReportedLevels to null
- // so we will wait for the next tone on next iteration
- lastReceivedTone = null;
- }
-
- if(temp != null
- && ((lastReportedTone == null && !toEnd)
- || (lastReportedTone != null && toEnd)))
- {
- //now notify our listener
- if (mediaStream != null)
- {
- mediaStream.fireDTMFEvent(temp, toEnd);
- if(toEnd)
- lastReportedTone = null;
- else
- lastReportedTone = temp;
- toEnd = false;
- }
- }
- }
- }
-
- /**
- * A packet that we should convert to tone and deliver
- * to our media stream and its listeners in a separate thread.
- *
- * @param p the packet we will convert and deliver.
- */
- public void addTonePacket(DtmfRawPacket p)
+ for (int i = 0; i < supportedTones.length; i++)
{
- synchronized(this)
- {
- this.lastReceivedTone = getToneFromPacket(p);
- this.toEnd = p.isEnd();
-
- notifyAll();
- }
+ DTMFTone t = supportedTones[i];
+ if(t.getCode() == p.getCode())
+ return t;
}
- /**
- * Causes our run method to exit so that this thread would stop
- * handling levels.
- */
- public void stop()
- {
- synchronized(this)
- {
- this.lastReceivedTone = null;
- isRunning = false;
-
- notifyAll();
- }
- }
-
- /**
- * Maps DTMF packet codes to our DTMFTone objects.
- * @param p the packet
- * @return the corresponding tone.
- */
- private DTMFTone getToneFromPacket(DtmfRawPacket p)
- {
- for (int i = 0; i < supportedTones.length; i++)
- {
- DTMFTone t = supportedTones[i];
- if(t.getCode() == p.getCode())
- return t;
- }
-
- return null;
- }
+ return null;
}
-
}
diff --git a/src/net/java/sip/communicator/impl/protocol/sip/OperationSetDTMFSipImpl.java b/src/net/java/sip/communicator/impl/protocol/sip/OperationSetDTMFSipImpl.java
index 9bd2878..92773d6 100644
--- a/src/net/java/sip/communicator/impl/protocol/sip/OperationSetDTMFSipImpl.java
+++ b/src/net/java/sip/communicator/impl/protocol/sip/OperationSetDTMFSipImpl.java
@@ -24,6 +24,11 @@ public class OperationSetDTMFSipImpl
implements OperationSetDTMF
{
/**
+ * The DTMF method used to send tones.
+ */
+ private String dtmfMethod = null;
+
+ /**
* DTMF mode sending DTMF as sip info.
*/
private final DTMFInfo dtmfModeInfo;
@@ -34,14 +39,27 @@ public class OperationSetDTMFSipImpl
private final DTMF4733 dtmfModeRFC4733;
/**
+ * DTMF sending inband tones into the audio stream.
+ */
+ private final DTMFInband dtmfModeInband;
+
+ /**
* Constructor.
*
* @param pps the SIP Protocol provider service
*/
public OperationSetDTMFSipImpl(ProtocolProviderServiceSipImpl pps)
{
+ AccountID accountID = pps.getAccountID();
+ this.dtmfMethod = accountID.getAccountPropertyString("DTMF_METHOD");
+ if(dtmfMethod == null)
+ {
+ accountID.putAccountProperty("DTMF_METHOD", "RFC4733 / SIP-INFO");
+ }
+
dtmfModeInfo = new DTMFInfo(pps);
dtmfModeRFC4733 = new DTMF4733();
+ dtmfModeInband = new DTMFInband();
}
/**
@@ -72,16 +90,27 @@ public class OperationSetDTMFSipImpl
CallPeerSipImpl cp = (CallPeerSipImpl) (callPeer);
- if(isRFC4733Active(cp))
+ if(this.dtmfMethod == null ||
+ this.dtmfMethod.equals("RFC4733 / SIP-INFO"))
{
- dtmfModeRFC4733.startSendingDTMF(
- ((AudioMediaStream)cp.getMediaHandler()
- .getStream(MediaType.AUDIO)),
- tone);
+ if(isRFC4733Active(cp))
+ {
+ dtmfModeRFC4733.startSendingDTMF(
+ ((AudioMediaStream)cp.getMediaHandler()
+ .getStream(MediaType.AUDIO)),
+ tone);
+ }
+ else
+ {
+ dtmfModeInfo.startSendingDTMF(cp, tone);
+ }
}
else
{
- dtmfModeInfo.startSendingDTMF(cp, tone);
+ dtmfModeInband.addInbandDTMF(
+ ((AudioMediaStream)cp.getMediaHandler()
+ .getStream(MediaType.AUDIO)),
+ tone);
}
}
@@ -103,15 +132,19 @@ public class OperationSetDTMFSipImpl
CallPeerSipImpl cp = (CallPeerSipImpl) (callPeer);
- if(isRFC4733Active(cp))
- {
- dtmfModeRFC4733.stopSendingDTMF(
- ((AudioMediaStream)cp.getMediaHandler()
- .getStream(MediaType.AUDIO)));
- }
- else
+ if(this.dtmfMethod == null ||
+ this.dtmfMethod.equals("RFC4733 / SIP-INFO"))
{
- dtmfModeInfo.stopSendingDTMF(cp);
+ if(isRFC4733Active(cp))
+ {
+ dtmfModeRFC4733.stopSendingDTMF(
+ ((AudioMediaStream)cp.getMediaHandler()
+ .getStream(MediaType.AUDIO)));
+ }
+ else
+ {
+ dtmfModeInfo.stopSendingDTMF(cp);
+ }
}
}
diff --git a/src/net/java/sip/communicator/impl/protocol/sip/dtmf/DTMFInband.java b/src/net/java/sip/communicator/impl/protocol/sip/dtmf/DTMFInband.java
new file mode 100644
index 0000000..f3c7533
--- /dev/null
+++ b/src/net/java/sip/communicator/impl/protocol/sip/dtmf/DTMFInband.java
@@ -0,0 +1,30 @@
+/*
+ * Jitsi, the OpenSource Java VoIP and Instant Messaging client.
+ *
+ * Distributable under LGPL license.
+ * See terms of license at gnu.org.
+ */
+package net.java.sip.communicator.impl.protocol.sip.dtmf;
+
+import net.java.sip.communicator.service.neomedia.*;
+
+/**
+ * Sending DTMFs inband into the audio stream.
+ *
+ * @author Vincent Lucas
+ */
+public class DTMFInband
+{
+ /**
+ * Adds a new inband DTMF tone to send.
+ *
+ * @param audioStream The stream of this call.
+ * @param tone The tone (audio signal) to send via the audioStream.
+ */
+ public void addInbandDTMF(
+ AudioMediaStream audioStream,
+ net.java.sip.communicator.service.protocol.DTMFTone tone)
+ {
+ audioStream.addInbandDTMF(DTMFInbandTone.mapTone(tone));
+ }
+}
diff --git a/src/net/java/sip/communicator/plugin/sipaccregwizz/ConnectionPanel.java b/src/net/java/sip/communicator/plugin/sipaccregwizz/ConnectionPanel.java
index c1c3b21..c18e140 100644
--- a/src/net/java/sip/communicator/plugin/sipaccregwizz/ConnectionPanel.java
+++ b/src/net/java/sip/communicator/plugin/sipaccregwizz/ConnectionPanel.java
@@ -54,6 +54,13 @@ public class ConnectionPanel
private JTextField keepAliveIntervalValue = new JTextField();
+ private JComboBox dtmfMethodBox
+ = new JComboBox(new Object []
+ {
+ "RFC4733 / SIP-INFO",
+ "INBAND"
+ });
+
private boolean isServerOverridden = false;
private SIPAccountRegistrationForm regform;
@@ -195,8 +202,12 @@ public class ConnectionPanel
voicemailField.setText(regform.getRegistration().getVoicemailURI());
mainPanel.add(Box.createVerticalStrut(5));
+ mainPanel.add(createDTMFPanel());
+
+ mainPanel.add(Box.createVerticalStrut(5));
mainPanel.add(voicemailPanel);
+
this.add(mainPanel, BorderLayout.NORTH);
}
@@ -309,6 +320,24 @@ public class ConnectionPanel
}
/**
+ * Creates the DTMF panel.
+ * @return the created DTMF panel
+ */
+ private Component createDTMFPanel()
+ {
+ JPanel dtmfPanel = new TransparentPanel(new BorderLayout(10, 10));
+ JLabel dtmfMethodLabel = new JLabel(
+ Resources.getString("plugin.sipaccregwizz.DTMF_METHOD"));
+ dtmfPanel.add(dtmfMethodLabel, BorderLayout.WEST);
+
+ dtmfMethodBox.setSelectedItem(
+ regform.getRegistration().getDefaultDTMFMethod());
+ dtmfPanel.add(dtmfMethodBox, BorderLayout.CENTER);
+
+ return dtmfPanel;
+ }
+
+ /**
* Returns the server address.
* @return the server address
*/
@@ -491,6 +520,36 @@ public class ConnectionPanel
}
/**
+ * Returns the DTMF method.
+ * @return the DTMF method
+ */
+ String getDTMFMethod()
+ {
+ Object selItem = dtmfMethodBox.getSelectedItem();
+
+ if(selItem != null)
+ return selItem.toString();
+ else
+ return null;
+ }
+
+ /**
+ * Sets the DTMF method.
+ * @param dtmfMethod the DTMF method
+ */
+ void setDTMFMethod(String dtmfMethod)
+ {
+ if(dtmfMethod == null)
+ {
+ dtmfMethodBox.setSelectedItem(0);
+ }
+ else
+ {
+ dtmfMethodBox.setSelectedItem(dtmfMethod);
+ }
+ }
+
+ /**
* Returns the voicemail URI.
* @return the voicemail URI.
*/
diff --git a/src/net/java/sip/communicator/plugin/sipaccregwizz/SIPAccountRegistration.java b/src/net/java/sip/communicator/plugin/sipaccregwizz/SIPAccountRegistration.java
index a35ceac..85b6d6a 100644
--- a/src/net/java/sip/communicator/plugin/sipaccregwizz/SIPAccountRegistration.java
+++ b/src/net/java/sip/communicator/plugin/sipaccregwizz/SIPAccountRegistration.java
@@ -26,6 +26,8 @@ public class SIPAccountRegistration
public static String DEFAULT_KEEP_ALIVE_INTERVAL = "25";
+ private String defaultDTMFMethod = "RFC4733 / SIP-INFO";
+
private String id;
private String password;
@@ -74,6 +76,8 @@ public class SIPAccountRegistration
private String keepAliveInterval = DEFAULT_KEEP_ALIVE_INTERVAL;
+ private String dtmfMethod = null;
+
private String defaultDomain = null;
private boolean xCapEnable = false;
@@ -431,6 +435,26 @@ public class SIPAccountRegistration
}
/**
+ * Returns the DTMF method.
+ *
+ * @return the DTMF method.
+ */
+ public String getDTMFMethod()
+ {
+ return dtmfMethod;
+ }
+
+ /**
+ * Sets the DTMF method.
+ *
+ * @param dtmfMethod the DTMF method to set
+ */
+ public void setDTMFMethod(String dtmfMethod)
+ {
+ this.dtmfMethod = dtmfMethod;
+ }
+
+ /**
* If default call encryption is enabled
*
* @return If default call encryption is enabled
@@ -538,6 +562,22 @@ public class SIPAccountRegistration
}
/**
+ * @return the defaultDTMFMethod
+ */
+ public String getDefaultDTMFMethod()
+ {
+ return defaultDTMFMethod;
+ }
+
+ /**
+ * @param defaultDTMFMethod the defaultDTMFMethod to set
+ */
+ public void setDefaultDTMFMethod(String defaultDTMFMethod)
+ {
+ this.defaultDTMFMethod = defaultDTMFMethod;
+ }
+
+ /**
* @return the defaultTransport
*/
public String getDefaultTransport()
diff --git a/src/net/java/sip/communicator/plugin/sipaccregwizz/SIPAccountRegistrationForm.java b/src/net/java/sip/communicator/plugin/sipaccregwizz/SIPAccountRegistrationForm.java
index e8c8eb9..43091c2 100644
--- a/src/net/java/sip/communicator/plugin/sipaccregwizz/SIPAccountRegistrationForm.java
+++ b/src/net/java/sip/communicator/plugin/sipaccregwizz/SIPAccountRegistrationForm.java
@@ -278,6 +278,9 @@ public class SIPAccountRegistrationForm
registration.setKeepAliveInterval(
connectionPanel.getKeepAliveInterval());
+ registration.setDTMFMethod(
+ connectionPanel.getDTMFMethod());
+
SIPAccRegWizzActivator.getUIService().getAccountRegWizardContainer()
.setBackButtonEnabled(true);
@@ -362,6 +365,9 @@ public class SIPAccountRegistrationForm
accountID.getAccountPropertyString(
ProtocolProviderFactory.KEEP_ALIVE_INTERVAL);
+ String dtmfMethod =
+ accountID.getAccountPropertyString("DTMF_METHOD");
+
String voicemailURI = accountID.getAccountPropertyString(
ProtocolProviderFactory.VOICEMAIL_URI);
@@ -424,6 +430,8 @@ public class SIPAccountRegistrationForm
connectionPanel.setKeepAliveMethod(keepAliveMethod);
connectionPanel.setKeepAliveInterval(keepAliveInterval);
+ connectionPanel.setDTMFMethod(dtmfMethod);
+
if (voicemailURI != null && voicemailURI.length() > 0)
connectionPanel.setVoicemailURI(voicemailURI);
diff --git a/src/net/java/sip/communicator/plugin/sipaccregwizz/SIPAccountRegistrationWizard.java b/src/net/java/sip/communicator/plugin/sipaccregwizz/SIPAccountRegistrationWizard.java
index 20eb967..b019797 100644
--- a/src/net/java/sip/communicator/plugin/sipaccregwizz/SIPAccountRegistrationWizard.java
+++ b/src/net/java/sip/communicator/plugin/sipaccregwizz/SIPAccountRegistrationWizard.java
@@ -262,6 +262,10 @@ public class SIPAccountRegistrationWizard
Resources.getString("plugin.sipaccregwizz.KEEP_ALIVE_INTERVAL"),
registration.getKeepAliveInterval());
+ summaryTable.put(
+ Resources.getString("plugin.sipaccregwizz.DTMF_METHOD"),
+ registration.getDTMFMethod());
+
if (registration.isXCapEnable() || registration.isXiVOEnable())
{
summaryTable.put("XCAP " + Resources.getString(
@@ -485,6 +489,13 @@ public class SIPAccountRegistrationWizard
accountProperties.put(ProtocolProviderFactory.KEEP_ALIVE_INTERVAL,
registration.getKeepAliveInterval());
+ if(registration.getDTMFMethod() != null)
+ accountProperties.put("DTMF_METHOD",
+ registration.getDTMFMethod());
+ else
+ accountProperties.put("DTMF_METHOD",
+ registration.getDefaultDTMFMethod());
+
accountProperties.put("XIVO_ENABLE",
Boolean.toString(registration.isXiVOEnable()));
accountProperties.put("XCAP_ENABLE",
diff --git a/src/net/java/sip/communicator/service/neomedia/AudioMediaStream.java b/src/net/java/sip/communicator/service/neomedia/AudioMediaStream.java
index 17dfdf5..841aaa5 100644
--- a/src/net/java/sip/communicator/service/neomedia/AudioMediaStream.java
+++ b/src/net/java/sip/communicator/service/neomedia/AudioMediaStream.java
@@ -85,4 +85,11 @@ public interface AudioMediaStream
* @param listener the listener that we'd like to unregister
*/
public void removeDTMFListener(DTMFListener listener);
+
+ /**
+ * Adds a new inband DTMF tone to send.
+ *
+ * @param tone the DTMF tone to send.
+ */
+ public void addInbandDTMF(DTMFInbandTone tone);
}
diff --git a/src/net/java/sip/communicator/service/neomedia/DTMFInbandTone.java b/src/net/java/sip/communicator/service/neomedia/DTMFInbandTone.java
new file mode 100644
index 0000000..c265563
--- /dev/null
+++ b/src/net/java/sip/communicator/service/neomedia/DTMFInbandTone.java
@@ -0,0 +1,424 @@
+/*
+ * Jitsi, the OpenSource Java VoIP and Instant Messaging client.
+ *
+ * Distributable under LGPL license.
+ * See terms of license at gnu.org.
+ */
+package net.java.sip.communicator.service.neomedia;
+
+/**
+ * Manages the geneation of the inband DMTF signal. A signal is identified by a
+ * value (1, 2, 3, 4, 5, 6, 7, 8, 9, *, #, A, B, C and D) and each signal is
+ * producted by the composition of 2 frequencies (as defined below).
+ * (cf. ITU recommendation Q.23)
+ *
+ * +------------------------------------------------+
+ * | | 1209 Hz | 1336 Hz | 1477 Hz | 1633 Hz |
+ * +------------------------------------------------+
+ * | 697 Hz | 1 | 2 | 3 | A |
+ * | 770 Hz | 4 | 5 | 6 | B |
+ * | 852 Hz | 7 | 8 | 9 | C |
+ * | 941 Hz | * | 0 | # | D |
+ * +------------------------------------------------+
+ *
+ * @author Vincent Lucas
+ */
+
+public class DTMFInbandTone
+{
+ /**
+ * The first set of frequencies in Hz which composes an inband DTMF.
+ */
+ private static final double[] frequencyList1 =
+ new double[]
+ {
+ 697.0, 770.0, 852.0, 941.0
+ };
+
+ /**
+ * The second set of frequencies in Hz which composes an inband DTMF.
+ */
+ private static final double[] frequencyList2 =
+ new double[]
+ {
+ 1209.0, 1336.0, 1477.0, 1633.0
+ };
+
+ /**
+ * The "0" DTMF Inband Tone.
+ */
+ public static final DTMFInbandTone DTMF_INBAND_0 = new DTMFInbandTone("0",
+ frequencyList1[3],
+ frequencyList2[1]);
+
+ /**
+ * The "1" DTMF Inband Tone.
+ */
+ public static final DTMFInbandTone DTMF_INBAND_1 = new DTMFInbandTone("1",
+ frequencyList1[0],
+ frequencyList2[0]);
+
+ /**
+ * The "2" DTMF Inband Tone.
+ */
+ public static final DTMFInbandTone DTMF_INBAND_2 = new DTMFInbandTone("2",
+ frequencyList1[0],
+ frequencyList2[1]);
+
+ /**
+ * The "3" DTMF Inband Tone.
+ */
+ public static final DTMFInbandTone DTMF_INBAND_3 = new DTMFInbandTone("3",
+ frequencyList1[0],
+ frequencyList2[2]);
+
+ /**
+ * The "4" DTMF Inband Tone.
+ */
+ public static final DTMFInbandTone DTMF_INBAND_4 = new DTMFInbandTone("4",
+ frequencyList1[1],
+ frequencyList2[0]);
+
+ /**
+ * The "5" DTMF Inband Tone.
+ */
+ public static final DTMFInbandTone DTMF_INBAND_5 = new DTMFInbandTone("5",
+ frequencyList1[1],
+ frequencyList2[1]);
+
+ /**
+ * The "6" DTMF Inband Tone.
+ */
+ public static final DTMFInbandTone DTMF_INBAND_6 = new DTMFInbandTone("6",
+ frequencyList1[1],
+ frequencyList2[2]);
+
+ /**
+ * The "7" DTMF Inband Tone.
+ */
+ public static final DTMFInbandTone DTMF_INBAND_7 = new DTMFInbandTone("7",
+ frequencyList1[2],
+ frequencyList2[0]);
+
+ /**
+ * The "8" DTMF Inband Tone.
+ */
+ public static final DTMFInbandTone DTMF_INBAND_8 = new DTMFInbandTone("8",
+ frequencyList1[2],
+ frequencyList2[1]);
+
+ /**
+ * The "9" DTMF Inband Tone.
+ */
+ public static final DTMFInbandTone DTMF_INBAND_9 = new DTMFInbandTone("9",
+ frequencyList1[2],
+ frequencyList2[2]);
+
+ /**
+ * The "*" DTMF Inband Tone.
+ */
+ public static final DTMFInbandTone DTMF_INBAND_STAR =
+ new DTMFInbandTone("*",
+ frequencyList1[3],
+ frequencyList2[0]);
+
+ /**
+ * The "#" DTMF Inband Tone.
+ */
+ public static final DTMFInbandTone DTMF_INBAND_SHARP =
+ new DTMFInbandTone("#",
+ frequencyList1[3],
+ frequencyList2[2]);
+
+ /**
+ * The "A" DTMF Inband Tone.
+ */
+ public static final DTMFInbandTone DTMF_INBAND_A = new DTMFInbandTone("A",
+ frequencyList1[0],
+ frequencyList2[3]);
+
+ /**
+ * The "B" DTMF Inband Tone.
+ */
+ public static final DTMFInbandTone DTMF_INBAND_B = new DTMFInbandTone("B",
+ frequencyList1[1],
+ frequencyList2[3]);
+
+ /**
+ * The "C" DTMF Inband Tone.
+ */
+ public static final DTMFInbandTone DTMF_INBAND_C = new DTMFInbandTone("C",
+ frequencyList1[2],
+ frequencyList2[3]);
+
+ /**
+ * The "D" DTMF Inband Tone.
+ */
+ public static final DTMFInbandTone DTMF_INBAND_D = new DTMFInbandTone("D",
+ frequencyList1[3],
+ frequencyList2[3]);
+
+ /**
+ * The default duration of an inband DTMF tone in ms.
+ * 50 ms c.f.
+ * http://nemesis.lonestar.org/reference/telecom/signaling/dtmf.html
+ * which cites the norm ANSI T1.401-1988 (but anavailable for me).
+ * But when testing it at 50 ms, the asterisk servers miss some DTMF tone
+ * impulses. Thus, set it up 150 ms.
+ */
+ private static final int toneDuration = 150;
+
+ /**
+ * The default duration of an inband DTMF tone in ms.
+ * 45 ms c.f.
+ * http://nemesis.lonestar.org/reference/telecom/signaling/dtmf.html
+ * which cites the norm ANSI T1.401-1988 (but anavailable for me).
+ * Moreover the minimum duty cycle (signal tone + silence) for
+ * ANSI-compliance shall be greater or equal to 100 ms.
+ */
+ private static final int interDigitInterval = 45;
+
+ /**
+ * The value which identifies the current inband tone. Available values are
+ * (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, *, #, A, B, C and D).
+ */
+ private String value;
+
+ /**
+ * The first frequency which composes the current inband tone.
+ */
+ private double frequency1;
+
+ /**
+ * The second frequency which composes the current inband tone.
+ */
+ private double frequency2;
+
+ /**
+ * Creates a new instance of an inband tone. The value given is the main
+ * identifier which determines which are the two freequencies to used to
+ * generate this tone.
+ *
+ * @param value The identifier of the tone. Available values are (0, 1, 2,
+ * 3, 4, 5, 6, 7, 8, 9, *, #, A, B, C and D).
+ * @param frequency1 The first frequence which composes the tone. Available
+ * values corresponds to DTMFInbandTone.frequencyList1.
+ * @param frequency2 The second frequence which composes the tone. Available
+ * values corresponds to DTMFInbandTone.frequencyList2.
+ */
+ public DTMFInbandTone(String value, double frequency1, double frequency2)
+ {
+ this.value = value;
+ this.frequency1 = frequency1;
+ this.frequency2 = frequency2;
+ }
+
+ /**
+ * Returns this tone value as a string representation.
+ *
+ * @return this tone value.
+ */
+ public String getValue()
+ {
+ return this.value;
+ }
+
+ /**
+ * Returns the first frequency coded by this tone.
+ *
+ * @return the first frequency coded by this tone.
+ */
+ private double getFrequency1()
+ {
+ return this.frequency1;
+ }
+
+ /**
+ * Returns the second frequency coded by this tone.
+ *
+ * @return the second frequency coded by this tone.
+ */
+ private double getFrequency2()
+ {
+ return this.frequency2;
+ }
+
+ /**
+ * Generates a sample for the current tone signal.
+ *
+ * @param samplingFrequency The sampling frequency (codec clock rate) in Hz
+ * of the stream which will encapsulate this signal.
+ * @param sampleNumber The sample number of this signal to be produced. The
+ * sample number corresponds to the abscissa of the signal function.
+ *
+ * @return the sample generated. This sample corresponds to the ordinate of
+ * the signal function
+ */
+ public double getAudioSampleContinuous(
+ double samplingFrequency,
+ int sampleNumber)
+ {
+ double u1 = 2.0 * Math.PI * this.frequency1 / samplingFrequency;
+ double u2 = 2.0 * Math.PI * this.frequency2 / samplingFrequency;
+ double audioSample;
+
+ // The signal generated is composed of 2 sinusoidal signals, which
+ // ampltudes is between -1 and 1 (included).
+ audioSample = Math.sin(u1 * sampleNumber) * 0.5 +
+ Math.sin(u2 * sampleNumber) * 0.5;
+
+ return audioSample;
+ }
+
+ /**
+ * Generates a sample for the current tone signal converted into a discrete
+ * signal.
+ *
+ * @param samplingFrequency The sampling frequency (codec clock rate) in Hz
+ * of the stream which will encapsulate this signal.
+ * @param sampleNumber The sample number of this signal to be produced. The
+ * sample number corresponds to the abscissa of the signal function.
+ * @param sampleSizeInBits The size of each sample (8 for a byte, 16 for a
+ * short and 32 for an int)
+ *
+ * @return the sample generated. This sample corresponds to the ordinate of
+ * the signal function
+ */
+ public int getAudioSampleDiscrete(
+ double samplingFrequency,
+ int sampleNumber,
+ int sampleSizeInBits)
+ {
+ // If the param sampleSizeInBits is equal to 8, then its corresponds to
+ // a Java byte which is 8 bits signed type which contains a number
+ // between -128 and 127. Thus, as the result of the continuous function
+ // is between -1 and 1, we multiply each audioSample by 127. This
+ // generates a signal between -127 and 127.
+ // Same operation if sampleSizeInBits is equal to 16, this function
+ // generates a signal between -32767 and 32767.
+ // As well as if sampleSizeInBits is equal to 32, this function
+ // generates a signal between -2147483647 and 2147483647.
+ double amplitudeCoefficient = Math.pow(2.0, sampleSizeInBits - 1) - 1.0;
+ double audioSampleContinuous;
+ int audioSampleDiscrete;
+
+ audioSampleContinuous = this.getAudioSampleContinuous(
+ samplingFrequency,
+ sampleNumber);
+ audioSampleDiscrete = (int)
+ (audioSampleContinuous * amplitudeCoefficient);
+
+ return audioSampleDiscrete;
+ }
+
+ /**
+ * Generates a signal sample for the current tone signal and stores it into
+ * the byte data array.
+ *
+ * @param samplingFrequency The sampling frequency (codec clock rate) in Hz
+ * of the stream which will encapsulate this signal.
+ * @param sampleSizeInBits The size of each sample (8 for a byte, 16 for a
+ * short and 32 for an int)
+ *
+ * @return The data array containing the DTMF signal.
+ */
+ public int[] getAudioSamples(
+ double samplingFrequency,
+ int sampleSizeInBits)
+ {
+ int sampleNumber = 0;
+
+ int nbToneSamples = ((int) (samplingFrequency / 1000.0)) *
+ DTMFInbandTone.toneDuration;
+ int nbInterDigitSamples = ((int) (samplingFrequency / 1000.0)) *
+ DTMFInbandTone.interDigitInterval;
+
+ int[] sampleData =
+ new int[nbInterDigitSamples + nbToneSamples + nbInterDigitSamples];
+
+ while(sampleNumber < nbInterDigitSamples)
+ {
+ sampleData[sampleNumber] = 0;
+ ++sampleNumber;
+ }
+ while(sampleNumber < nbInterDigitSamples + nbToneSamples)
+ {
+ sampleData[sampleNumber] = getAudioSampleDiscrete(
+ samplingFrequency,
+ sampleNumber,
+ sampleSizeInBits);
+
+ ++sampleNumber;
+ }
+ while(sampleNumber < sampleData.length)
+ {
+ sampleData[sampleNumber] = 0;
+ ++sampleNumber;
+ }
+
+ return sampleData;
+ }
+
+ /**
+ * Maps between protocol and media inband DTMF objects.
+ * @param tone The DTMF tone as defined in the service protocol, which is
+ * only composed by a value as its identifier.
+ *
+ * @return the corresponding DTMF tone which contains a value as an
+ * identifier and two frequencies composing the inband tone.
+ */
+ public static DTMFInbandTone
+ mapTone(net.java.sip.communicator.service.protocol.DTMFTone tone)
+ {
+ if(tone.equals(
+ net.java.sip.communicator.service.protocol.DTMFTone.DTMF_0))
+ return DTMFInbandTone.DTMF_INBAND_0;
+ else if(tone.equals(
+ net.java.sip.communicator.service.protocol.DTMFTone.DTMF_1))
+ return DTMFInbandTone.DTMF_INBAND_1;
+ else if(tone.equals(
+ net.java.sip.communicator.service.protocol.DTMFTone.DTMF_2))
+ return DTMFInbandTone.DTMF_INBAND_2;
+ else if(tone.equals(
+ net.java.sip.communicator.service.protocol.DTMFTone.DTMF_3))
+ return DTMFInbandTone.DTMF_INBAND_3;
+ else if(tone.equals(
+ net.java.sip.communicator.service.protocol.DTMFTone.DTMF_4))
+ return DTMFInbandTone.DTMF_INBAND_4;
+ else if(tone.equals(
+ net.java.sip.communicator.service.protocol.DTMFTone.DTMF_5))
+ return DTMFInbandTone.DTMF_INBAND_5;
+ else if(tone.equals(
+ net.java.sip.communicator.service.protocol.DTMFTone.DTMF_6))
+ return DTMFInbandTone.DTMF_INBAND_6;
+ else if(tone.equals(
+ net.java.sip.communicator.service.protocol.DTMFTone.DTMF_7))
+ return DTMFInbandTone.DTMF_INBAND_7;
+ else if(tone.equals(
+ net.java.sip.communicator.service.protocol.DTMFTone.DTMF_8))
+ return DTMFInbandTone.DTMF_INBAND_8;
+ else if(tone.equals(
+ net.java.sip.communicator.service.protocol.DTMFTone.DTMF_9))
+ return DTMFInbandTone.DTMF_INBAND_9;
+ else if(tone.equals(
+ net.java.sip.communicator.service.protocol.DTMFTone.DTMF_A))
+ return DTMFInbandTone.DTMF_INBAND_A;
+ else if(tone.equals(
+ net.java.sip.communicator.service.protocol.DTMFTone.DTMF_B))
+ return DTMFInbandTone.DTMF_INBAND_B;
+ else if(tone.equals(
+ net.java.sip.communicator.service.protocol.DTMFTone.DTMF_C))
+ return DTMFInbandTone.DTMF_INBAND_C;
+ else if(tone.equals(
+ net.java.sip.communicator.service.protocol.DTMFTone.DTMF_D))
+ return DTMFInbandTone.DTMF_INBAND_D;
+ else if(tone.equals(
+ net.java.sip.communicator.service.protocol.DTMFTone.DTMF_SHARP))
+ return DTMFInbandTone.DTMF_INBAND_SHARP;
+ else if(tone.equals(
+ net.java.sip.communicator.service.protocol.DTMFTone.DTMF_STAR))
+ return DTMFInbandTone.DTMF_INBAND_STAR;
+
+ return null;
+ }
+}