diff --git a/build.xml b/build.xml
index 516ed7bc3..62ec76c7f 100644
--- a/build.xml
+++ b/build.xml
@@ -653,8 +653,10 @@
-
+
+
+
@@ -997,6 +1001,8 @@
prefix="net/java/sip/communicator/impl/media"/>
+
@@ -1017,6 +1023,8 @@
prefix="net/java/sip/communicator/impl/media"/>
+
diff --git a/lib/felix.client.run.properties b/lib/felix.client.run.properties
index 62ffe9cd4..8c42b32c0 100644
--- a/lib/felix.client.run.properties
+++ b/lib/felix.client.run.properties
@@ -130,7 +130,6 @@ felix.auto.start.60= \
reference:file:sc-bundles/callhistory.jar \
reference:file:sc-bundles/filehistory.jar \
reference:file:sc-bundles/metahistory.jar \
- reference:file:sc-bundles/audionotifier.jar \
reference:file:sc-bundles/keybindings.jar \
reference:file:sc-bundles/notification.jar
diff --git a/lib/os-specific/linux/installer-exclude/jmf.jar b/lib/os-specific/linux/installer-exclude/jmf.jar
index 50fa7f011..8505b4bc8 100644
Binary files a/lib/os-specific/linux/installer-exclude/jmf.jar and b/lib/os-specific/linux/installer-exclude/jmf.jar differ
diff --git a/lib/os-specific/mac/installer-exclude/jmf.jar b/lib/os-specific/mac/installer-exclude/jmf.jar
index 406039d10..9345c520d 100644
Binary files a/lib/os-specific/mac/installer-exclude/jmf.jar and b/lib/os-specific/mac/installer-exclude/jmf.jar differ
diff --git a/lib/os-specific/solaris/installer-exclude/jmf.jar b/lib/os-specific/solaris/installer-exclude/jmf.jar
index bd86a16d5..3dad76dc0 100644
Binary files a/lib/os-specific/solaris/installer-exclude/jmf.jar and b/lib/os-specific/solaris/installer-exclude/jmf.jar differ
diff --git a/lib/os-specific/windows/installer-exclude/jmf.jar b/lib/os-specific/windows/installer-exclude/jmf.jar
index 55dea3c46..afbe92bdf 100644
Binary files a/lib/os-specific/windows/installer-exclude/jmf.jar and b/lib/os-specific/windows/installer-exclude/jmf.jar differ
diff --git a/resources/languages/resources.properties b/resources/languages/resources.properties
index 822955844..c35925bee 100644
--- a/resources/languages/resources.properties
+++ b/resources/languages/resources.properties
@@ -791,7 +791,10 @@ plugin.profiler.PLUGIN_NAME=Profiler4j
impl.media.configform.NO_DEVICE=
impl.media.configform.TITLE=Media
-impl.media.configform.AUDIO=&Audio:
+impl.media.configform.AUDIO=&Audio System:
+impl.media.configform.AUDIO_IN=Audio &In:
+impl.media.configform.AUDIO_OUT=Audio &Out:
+impl.media.configform.AUDIO_NOTIFY=&Notifications:
impl.media.configform.DOWN=&Down
impl.media.configform.ENCODINGS=&Encodings:
impl.media.configform.NO_PREVIEW=Preview
diff --git a/src/native/portaudio/net_java_sip_communicator_impl_media_protocol_portaudio_PortAudio.c b/src/native/portaudio/net_java_sip_communicator_impl_media_protocol_portaudio_PortAudio.c
index cb83342d5..92040cfca 100644
--- a/src/native/portaudio/net_java_sip_communicator_impl_media_protocol_portaudio_PortAudio.c
+++ b/src/native/portaudio/net_java_sip_communicator_impl_media_protocol_portaudio_PortAudio.c
@@ -166,11 +166,70 @@ Java_net_java_sip_communicator_impl_media_protocol_portaudio_PortAudio_Pa_1Write
(*env)->ReleaseByteArrayElements(env, buffer, data, 0);
- if (paNoError != errorCode)
+ if (paNoError != errorCode && errorCode != paOutputUnderflowed)
PortAudio_throwException(env, errorCode);
+
+/*
+ if(errorCode == paOutputUnderflowed)
+ {
+ printf("OutputUnderflowed\n");fflush(stdout);
+ }
+*/
}
}
+JNIEXPORT void JNICALL
+Java_net_java_sip_communicator_impl_media_protocol_portaudio_PortAudio_Pa_1ReadStream
+ (JNIEnv *env, jclass clazz, jlong stream, jbyteArray buffer, jlong frames)
+{
+ jbyte* data = (*env)->GetByteArrayElements(env, buffer, NULL);
+ PaError errorCode = Pa_ReadStream(
+ ((PortAudioStream *) stream)->stream,
+ data,
+ frames);
+ (*env)->ReleaseByteArrayElements(env, buffer, data, 0);
+
+ if (paNoError != errorCode && errorCode != paInputOverflowed)
+ PortAudio_throwException(env, errorCode);
+}
+
+JNIEXPORT jlong JNICALL
+Java_net_java_sip_communicator_impl_media_protocol_portaudio_PortAudio_Pa_1GetStreamReadAvailable
+ (JNIEnv *env, jclass clazz, jlong stream)
+{
+ return Pa_GetStreamReadAvailable(((PortAudioStream *) stream)->stream);
+}
+
+JNIEXPORT jlong JNICALL
+Java_net_java_sip_communicator_impl_media_protocol_portaudio_PortAudio_Pa_1GetStreamWriteAvailable
+ (JNIEnv *env, jclass clazz, jlong stream)
+{
+ return Pa_GetStreamWriteAvailable(((PortAudioStream *) stream)->stream);
+}
+
+JNIEXPORT jint JNICALL
+Java_net_java_sip_communicator_impl_media_protocol_portaudio_PortAudio_Pa_1GetSampleSize
+ (JNIEnv *env, jclass clazz, jlong format)
+{
+ return Pa_GetSampleSize(format);
+}
+
+JNIEXPORT jboolean JNICALL
+Java_net_java_sip_communicator_impl_media_protocol_portaudio_PortAudio_Pa_1IsFormatSupported
+ (JNIEnv *env, jclass clazz,
+ jlong inputParameters,
+ jlong outputParameters,
+ jdouble sampleRate)
+{
+ if(Pa_IsFormatSupported(
+ (PaStreamParameters *) inputParameters,
+ (PaStreamParameters *) outputParameters,
+ sampleRate) == paFormatIsSupported)
+ return JNI_TRUE;
+ else
+ return JNI_FALSE;
+}
+
JNIEXPORT jint JNICALL
Java_net_java_sip_communicator_impl_media_protocol_portaudio_PortAudio_PaDeviceInfo_1getMaxInputChannels(
JNIEnv *env, jclass clazz, jlong deviceInfo)
@@ -195,6 +254,13 @@ Java_net_java_sip_communicator_impl_media_protocol_portaudio_PortAudio_PaDeviceI
return name ? (*env)->NewStringUTF(env, name) : NULL;
}
+JNIEXPORT jdouble JNICALL
+Java_net_java_sip_communicator_impl_media_protocol_portaudio_PortAudio_PaDeviceInfo_1getDefaultSampleRate
+ (JNIEnv *env, jclass clazz, jlong deviceInfo)
+{
+ return ((PaDeviceInfo *) deviceInfo)->defaultSampleRate;
+}
+
JNIEXPORT jlong JNICALL
Java_net_java_sip_communicator_impl_media_protocol_portaudio_PortAudio_PaStreamParameters_1new(
JNIEnv *env,
@@ -211,7 +277,7 @@ Java_net_java_sip_communicator_impl_media_protocol_portaudio_PortAudio_PaStreamP
streamParameters->device = deviceIndex;
streamParameters->channelCount = channelCount;
streamParameters->sampleFormat = sampleFormat;
- streamParameters->suggestedLatency = 0;
+ streamParameters->suggestedLatency = 0,4;
streamParameters->hostApiSpecificStreamInfo = NULL;
}
return (jlong) streamParameters;
@@ -319,7 +385,7 @@ PortAudioStream_callback(
return paAbort;
}
- return
+ return
(*env)
->CallIntMethod(
env,
diff --git a/src/native/portaudio/net_java_sip_communicator_impl_media_protocol_portaudio_PortAudio.h b/src/native/portaudio/net_java_sip_communicator_impl_media_protocol_portaudio_PortAudio.h
index a4a3f43ef..b71b66e44 100644
--- a/src/native/portaudio/net_java_sip_communicator_impl_media_protocol_portaudio_PortAudio.h
+++ b/src/native/portaudio/net_java_sip_communicator_impl_media_protocol_portaudio_PortAudio.h
@@ -23,6 +23,45 @@ JNIEXPORT void JNICALL Java_net_java_sip_communicator_impl_media_protocol_portau
JNIEXPORT void JNICALL Java_net_java_sip_communicator_impl_media_protocol_portaudio_PortAudio_Pa_1WriteStream
(JNIEnv *, jclass, jlong, jbyteArray, jlong);
+/*
+ * Class: net_java_sip_communicator_impl_media_protocol_portaudio_PortAudio
+ * Method: Pa_ReadStream
+ * Signature: (J[BJ)V
+ */
+JNIEXPORT void JNICALL Java_net_java_sip_communicator_impl_media_protocol_portaudio_PortAudio_Pa_1ReadStream
+ (JNIEnv *, jclass, jlong, jbyteArray, jlong);
+
+/*
+ * Class: net_java_sip_communicator_impl_media_protocol_portaudio_PortAudio
+ * Method: Pa_GetStreamReadAvailable
+ * Signature: (J)J
+ */
+JNIEXPORT jlong JNICALL Java_net_java_sip_communicator_impl_media_protocol_portaudio_PortAudio_Pa_1GetStreamReadAvailable
+ (JNIEnv *, jclass, jlong);
+
+/*
+ * Class: net_java_sip_communicator_impl_media_protocol_portaudio_PortAudio
+ * Method: Pa_GetStreamWriteAvailable
+ * Signature: (J)J
+ */
+JNIEXPORT jlong JNICALL Java_net_java_sip_communicator_impl_media_protocol_portaudio_PortAudio_Pa_1GetStreamWriteAvailable
+ (JNIEnv *, jclass, jlong);
+
+/*
+ * Class: net_java_sip_communicator_impl_media_protocol_portaudio_PortAudio
+ * Method: Pa_GetSampleSize
+ * Signature: (J)I
+ */
+JNIEXPORT jint JNICALL Java_net_java_sip_communicator_impl_media_protocol_portaudio_PortAudio_Pa_1GetSampleSize
+ (JNIEnv *, jclass, jlong);
+
+/*
+ * Class: net_java_sip_communicator_impl_media_protocol_portaudio_PortAudio
+ * Method: Pa_IsFormatSupported
+ * Signature: (JJD)Z
+ */
+JNIEXPORT jboolean JNICALL Java_net_java_sip_communicator_impl_media_protocol_portaudio_PortAudio_Pa_1IsFormatSupported
+ (JNIEnv *, jclass, jlong, jlong, jdouble);
/*
* Class: net_java_sip_communicator_impl_media_protocol_portaudio_PortAudio
@@ -96,6 +135,14 @@ JNIEXPORT jint JNICALL Java_net_java_sip_communicator_impl_media_protocol_portau
JNIEXPORT jstring JNICALL Java_net_java_sip_communicator_impl_media_protocol_portaudio_PortAudio_PaDeviceInfo_1getName
(JNIEnv *, jclass, jlong);
+/*
+ * Class: net_java_sip_communicator_impl_media_protocol_portaudio_PortAudio
+ * Method: PaDeviceInfo_getDefaultSampleRate
+ * Signature: (J)D
+ */
+JNIEXPORT jdouble JNICALL Java_net_java_sip_communicator_impl_media_protocol_portaudio_PortAudio_PaDeviceInfo_1getDefaultSampleRate
+ (JNIEnv *, jclass, jlong);
+
/*
* Class: net_java_sip_communicator_impl_media_protocol_portaudio_PortAudio
* Method: PaStreamParameters_new
diff --git a/src/net/java/sip/communicator/impl/media/DeviceConfigurationComboBoxModel.java b/src/net/java/sip/communicator/impl/media/DeviceConfigurationComboBoxModel.java
index aa9206a44..74502815d 100644
--- a/src/net/java/sip/communicator/impl/media/DeviceConfigurationComboBoxModel.java
+++ b/src/net/java/sip/communicator/impl/media/DeviceConfigurationComboBoxModel.java
@@ -20,15 +20,31 @@
public class DeviceConfigurationComboBoxModel
implements ComboBoxModel
{
+ /**
+ * Encapsulates CaptureDeviceInfo
+ */
public static class CaptureDevice
{
+ /**
+ * Compares two CaptureDeviceInfo
+ * @param a
+ * @param b
+ * @return whether a is equal to b
+ */
public static boolean equals(CaptureDeviceInfo a, CaptureDeviceInfo b)
{
return (a == null) ? (b == null) : a.equals(b);
}
+ /**
+ * The encapsulated info.
+ */
public final CaptureDeviceInfo info;
+ /**
+ * Creates the wrapper.
+ * @param info the info object we wrap.
+ */
public CaptureDevice(CaptureDeviceInfo info)
{
this.info = info;
@@ -43,10 +59,31 @@ public String toString()
}
}
+ /**
+ * Type of the model - audio.
+ */
public static final int AUDIO = 1;
+ /**
+ * Type of the model - video.
+ */
public static final int VIDEO = 2;
+ /**
+ * Audio Capture Device.
+ */
+ public static final int AUDIO_CAPTURE = 3;
+
+ /**
+ * Audio playback device.
+ */
+ public static final int AUDIO_PLAYBACK = 4;
+
+ /**
+ * Audio device for notification sounds.
+ */
+ public static final int AUDIO_NOTIFY = 5;
+
private final DeviceConfiguration deviceConfiguration;
private CaptureDevice[] devices;
@@ -56,12 +93,19 @@ public String toString()
private final int type;
+ /**
+ * Creates device combobx model
+ * @param deviceConfiguration the current device configuration
+ * @param type the device - audio/video
+ */
public DeviceConfigurationComboBoxModel(
DeviceConfiguration deviceConfiguration, int type)
{
if (deviceConfiguration == null)
throw new IllegalArgumentException("deviceConfiguration");
- if ((type != AUDIO) && (type != VIDEO))
+ if ((type != AUDIO_CAPTURE) && (type != AUDIO_NOTIFY) &&
+ (type != AUDIO_PLAYBACK) &&
+ (type != AUDIO) && (type != VIDEO))
throw new IllegalArgumentException("type");
this.deviceConfiguration = deviceConfiguration;
@@ -77,6 +121,11 @@ public void addListDataListener(ListDataListener listener)
listeners.add(listener);
}
+ /**
+ * Change of the content.
+ * @param index0 from index.
+ * @param index1 to index.
+ */
protected void fireContentsChanged(int index0, int index1)
{
ListDataListener[] listeners =
@@ -101,12 +150,19 @@ private CaptureDevice[] getDevices()
if (devices != null)
return devices;
- DeviceConfiguration deviceConfiguration = getDeviceConfiguration();
+
CaptureDeviceInfo[] infos;
switch (type)
{
- case AUDIO:
- infos = deviceConfiguration.getAvailableAudioCaptureDevices();
+ case AUDIO_CAPTURE:
+ // supply only portaudio devices, as we are in case specifying
+ // capture devices available only for portaudio
+ infos = deviceConfiguration.getAvailableAudioCaptureDevices(
+ DeviceConfiguration.AUDIO_SYSTEM_PORTAUDIO);
+ break;
+ case AUDIO_NOTIFY:
+ case AUDIO_PLAYBACK:
+ infos = deviceConfiguration.getAvailableAudioPlaybackDevices();
break;
case VIDEO:
infos = deviceConfiguration.getAvailableVideoCaptureDevices();
@@ -125,20 +181,20 @@ private CaptureDevice[] getDevices()
return devices;
}
- public Object getElementAt(int index)
- {
- return getDevices()[index];
- }
-
private CaptureDevice getSelectedDevice()
{
- DeviceConfiguration deviceConfiguration = getDeviceConfiguration();
CaptureDeviceInfo info;
switch (type)
{
- case AUDIO:
+ case AUDIO_CAPTURE:
info = deviceConfiguration.getAudioCaptureDevice();
break;
+ case AUDIO_NOTIFY:
+ info = deviceConfiguration.getAudioNotifyDevice();
+ break;
+ case AUDIO_PLAYBACK:
+ info = deviceConfiguration.getAudioPlaybackDevice();
+ break;
case VIDEO:
info = deviceConfiguration.getVideoCaptureDevice();
break;
@@ -154,14 +210,28 @@ private CaptureDevice getSelectedDevice()
return null;
}
+ public Object getElementAt(int index)
+ {
+ if(type == AUDIO)
+ return deviceConfiguration.getAvailableAudioSystems()[index];
+ else
+ return getDevices()[index];
+ }
+
public Object getSelectedItem()
{
- return getSelectedDevice();
+ if(type == AUDIO)
+ return deviceConfiguration.getAudioSystem();
+ else
+ return getSelectedDevice();
}
public int getSize()
{
- return getDevices().length;
+ if(type == AUDIO)
+ return deviceConfiguration.getAvailableAudioSystems().length;
+ else
+ return getDevices().length;
}
public void removeListDataListener(ListDataListener listener)
@@ -184,9 +254,15 @@ private void setSelectedDevice(CaptureDevice device)
DeviceConfiguration deviceConfiguration = getDeviceConfiguration();
switch (type)
{
- case AUDIO:
+ case AUDIO_CAPTURE:
deviceConfiguration.setAudioCaptureDevice(device.info);
break;
+ case AUDIO_NOTIFY:
+ deviceConfiguration.setAudioNotifyDevice(device.info);
+ break;
+ case AUDIO_PLAYBACK:
+ deviceConfiguration.setAudioPlaybackDevice(device.info);
+ break;
case VIDEO:
deviceConfiguration.setVideoCaptureDevice(device.info);
break;
@@ -198,6 +274,17 @@ private void setSelectedDevice(CaptureDevice device)
public void setSelectedItem(Object item)
{
- setSelectedDevice((CaptureDevice) item);
+ if(type == AUDIO)
+ {
+ String systemName = (String)item;
+
+ if(!systemName.equals(deviceConfiguration.getAudioSystem()))
+ {
+ deviceConfiguration.setAudioSystem(systemName, null);
+ fireContentsChanged(-1, -1);
+ }
+ }
+ else
+ setSelectedDevice((CaptureDevice) item);
}
}
diff --git a/src/net/java/sip/communicator/impl/media/MediaConfigurationPanel.java b/src/net/java/sip/communicator/impl/media/MediaConfigurationPanel.java
index 351193121..3cc073578 100644
--- a/src/net/java/sip/communicator/impl/media/MediaConfigurationPanel.java
+++ b/src/net/java/sip/communicator/impl/media/MediaConfigurationPanel.java
@@ -16,6 +16,7 @@
import javax.swing.event.*;
import javax.swing.table.*;
+import net.java.sip.communicator.impl.media.device.*;
import net.java.sip.communicator.service.media.*;
import net.java.sip.communicator.service.resources.*;
import net.java.sip.communicator.util.*;
@@ -66,6 +67,9 @@ private static MediaServiceImpl getMediaService()
*/
private Player videoPlayerInPreview;
+ /**
+ * Creates the panel.
+ */
public MediaConfigurationPanel()
{
super(new GridLayout(0, 1, HGAP, VGAP));
@@ -90,12 +94,71 @@ private void controllerUpdateForPreview(ControllerEvent event,
}
}
+ private void createPortAudioControls(Container portAudioPanel)
+ {
+ portAudioPanel.add(new JLabel(getLabelText(
+ DeviceConfigurationComboBoxModel.AUDIO_CAPTURE)));
+ JComboBox captureCombo = new JComboBox();
+ captureCombo.setEditable(false);
+ captureCombo.setModel(
+ new DeviceConfigurationComboBoxModel(
+ mediaService.getDeviceConfiguration(),
+ DeviceConfigurationComboBoxModel.AUDIO_CAPTURE));
+ portAudioPanel.add(captureCombo);
+
+ portAudioPanel.add(new JLabel(getLabelText(
+ DeviceConfigurationComboBoxModel.AUDIO_PLAYBACK)));
+ JComboBox playbackCombo = new JComboBox();
+ playbackCombo.setEditable(false);
+ playbackCombo.setModel(
+ new DeviceConfigurationComboBoxModel(
+ mediaService.getDeviceConfiguration(),
+ DeviceConfigurationComboBoxModel.AUDIO_PLAYBACK));
+ portAudioPanel.add(playbackCombo);
+
+ portAudioPanel.add(new JLabel(getLabelText(
+ DeviceConfigurationComboBoxModel.AUDIO_NOTIFY)));
+ JComboBox notifyCombo = new JComboBox();
+ notifyCombo.setEditable(false);
+ notifyCombo.setModel(
+ new DeviceConfigurationComboBoxModel(
+ mediaService.getDeviceConfiguration(),
+ DeviceConfigurationComboBoxModel.AUDIO_NOTIFY));
+ portAudioPanel.add(notifyCombo);
+ }
+
private Component createControls(int type)
{
final JComboBox comboBox = new JComboBox();
comboBox.setEditable(false);
comboBox.setModel(new DeviceConfigurationComboBoxModel(mediaService
.getDeviceConfiguration(), type));
+ final Container portAudioPanel =
+ new TransparentPanel(new GridLayout(3, 2, HGAP, VGAP));
+ comboBox.addItemListener(new ItemListener() {
+
+ public void itemStateChanged(ItemEvent e)
+ {
+ if(e.getStateChange() == ItemEvent.SELECTED)
+ {
+ if(e.getItem().equals(
+ DeviceConfiguration.AUDIO_SYSTEM_PORTAUDIO))
+ {
+ createPortAudioControls(portAudioPanel);
+ }
+ else
+ {
+ portAudioPanel.removeAll();
+
+ revalidate();
+ repaint();
+ }
+ }
+ }
+ });
+ if(comboBox.getSelectedItem().equals(
+ DeviceConfiguration.AUDIO_SYSTEM_PORTAUDIO))
+ createPortAudioControls(portAudioPanel);
JLabel label = new JLabel(getLabelText(type));
label.setDisplayedMnemonic(getDisplayedMnemonic(type));
@@ -111,6 +174,13 @@ private Component createControls(int type)
firstConstraints.gridx = 1;
firstConstraints.weightx = 1;
firstContainer.add(comboBox, firstConstraints);
+
+ firstConstraints.gridx = 0;
+ firstConstraints.gridy = 1;
+ firstConstraints.weightx = 1;
+ firstConstraints.gridwidth = 2;
+ firstConstraints.insets = new Insets(VGAP, 0, 0, 0);
+ firstContainer.add(portAudioPanel, firstConstraints);
Container secondContainer =
new TransparentPanel(new GridLayout(1, 0, HGAP, VGAP));
@@ -392,6 +462,15 @@ private String getLabelText(int type)
case DeviceConfigurationComboBoxModel.AUDIO:
return MediaActivator.getResources().getI18NString(
"impl.media.configform.AUDIO");
+ case DeviceConfigurationComboBoxModel.AUDIO_CAPTURE:
+ return MediaActivator.getResources().getI18NString(
+ "impl.media.configform.AUDIO_IN");
+ case DeviceConfigurationComboBoxModel.AUDIO_NOTIFY:
+ return MediaActivator.getResources().getI18NString(
+ "impl.media.configform.AUDIO_NOTIFY");
+ case DeviceConfigurationComboBoxModel.AUDIO_PLAYBACK:
+ return MediaActivator.getResources().getI18NString(
+ "impl.media.configform.AUDIO_OUT");
case DeviceConfigurationComboBoxModel.VIDEO:
return MediaActivator.getResources().getI18NString(
"impl.media.configform.VIDEO");
diff --git a/src/net/java/sip/communicator/impl/media/MediaControl.java b/src/net/java/sip/communicator/impl/media/MediaControl.java
index f290e1aee..63b1d0df1 100644
--- a/src/net/java/sip/communicator/impl/media/MediaControl.java
+++ b/src/net/java/sip/communicator/impl/media/MediaControl.java
@@ -175,13 +175,18 @@ public void initialize( DeviceConfiguration deviceConfig,
{
public void propertyChange(PropertyChangeEvent evt)
{
- try
+ if(evt.getPropertyName().equals(
+ DeviceConfiguration.AUDIO_CAPTURE_DEVICE))
{
- initCaptureDevices();
- }
- catch (MediaException e)
- {
- logger.error("Cannot init capture devices after change", e);
+ try
+ {
+ initCaptureDevices();
+ }
+ catch (MediaException e)
+ {
+ logger.error(
+ "Cannot init capture devices after change", e);
+ }
}
}
});
diff --git a/src/net/java/sip/communicator/impl/media/MediaServiceImpl.java b/src/net/java/sip/communicator/impl/media/MediaServiceImpl.java
index 4400d5fa3..dda43227f 100644
--- a/src/net/java/sip/communicator/impl/media/MediaServiceImpl.java
+++ b/src/net/java/sip/communicator/impl/media/MediaServiceImpl.java
@@ -14,6 +14,8 @@
import net.java.sip.communicator.impl.media.codec.*;
import net.java.sip.communicator.impl.media.device.*;
+import net.java.sip.communicator.impl.media.notify.*;
+import net.java.sip.communicator.service.audionotifier.*;
import net.java.sip.communicator.service.media.*;
import net.java.sip.communicator.service.media.event.*;
import net.java.sip.communicator.service.protocol.*;
@@ -409,6 +411,9 @@ public void run()
defaultMediaControl.
initialize(deviceConfiguration, encodingConfiguration);
sdpFactory = SdpFactory.getInstance();
+
+ registerAudioNotifyService();
+
isStarted = true;
}
catch (Throwable ex)
@@ -423,6 +428,27 @@ public void run()
}
}
+ private void registerAudioNotifyService()
+ {
+ //Create the audio notifier service
+ AudioNotifierServiceImpl audioNotifier =
+ new AudioNotifierServiceImpl(deviceConfiguration);
+
+ audioNotifier.setMute(
+ !MediaActivator.getConfigurationService()
+ .getBoolean(
+ "net.java.sip.communicator.impl.sound.isSoundEnabled",
+ true));
+
+ MediaActivator.getBundleContext()
+ .registerService(
+ AudioNotifierService.class.getName(),
+ audioNotifier,
+ null);
+
+ logger.info("Audio Notifier Service ...[REGISTERED]");
+ }
+
/**
* A utility method that would block until the media service has been
* started or, in case it already is started, return immediately.
diff --git a/src/net/java/sip/communicator/impl/media/device/DeviceConfiguration.java b/src/net/java/sip/communicator/impl/media/device/DeviceConfiguration.java
index 5a3abd6b1..237bc4036 100644
--- a/src/net/java/sip/communicator/impl/media/device/DeviceConfiguration.java
+++ b/src/net/java/sip/communicator/impl/media/device/DeviceConfiguration.java
@@ -31,11 +31,25 @@ public class DeviceConfiguration
/**
* The name of the DeviceConfiguration property which
- * represents the device used by DeviceConfiguration for video
+ * represents the device used by DeviceConfiguration for audio
* capture.
*/
public static final String AUDIO_CAPTURE_DEVICE = "AUDIO_CAPTURE_DEVICE";
+ /**
+ * The name of the DeviceConfiguration property which
+ * represents the device used by DeviceConfiguration for audio
+ * playback.
+ */
+ public static final String AUDIO_PLAYBACK_DEVICE = "AUDIO_PLAYBACK_DEVICE";
+
+ /**
+ * The name of the DeviceConfiguration property which
+ * represents the device used by DeviceConfiguration for audio
+ * notify.
+ */
+ public static final String AUDIO_NOTIFY_DEVICE = "AUDIO_NOTIFY_DEVICE";
+
/**
* The name of the DeviceConfiguration property which
* represents the device used by DeviceConfiguration for video
@@ -43,9 +57,30 @@ public class DeviceConfiguration
*/
public static final String VIDEO_CAPTURE_DEVICE = "VIDEO_CAPTURE_DEVICE";
+ /**
+ * When audio is disabled the selected audio system is with name None.
+ */
+ public static final String AUDIO_SYSTEM_NONE = "None";
+
+ /**
+ * JavaSound sound system.
+ */
+ public static final String AUDIO_SYSTEM_JAVASOUND = "JavaSound";
+
+ /**
+ * PortAudio sound system.
+ */
+ public static final String AUDIO_SYSTEM_PORTAUDIO = "PortAudio";
+
private static final String PROP_AUDIO_DEVICE =
"net.java.sip.communicator.impl.media.audiodev";
+ private static final String PROP_AUDIO_PLAYBACK_DEVICE =
+ "net.java.sip.communicator.impl.media.audio.playbackdev";
+
+ private static final String PROP_AUDIO_NOTIFY_DEVICE =
+ "net.java.sip.communicator.impl.media.audio.notifydev";
+
private static final String PROP_AUDIO_DEVICE_IS_DISABLED =
"net.java.sip.communicator.impl.media.audiodevIsDisabled";
@@ -65,11 +100,19 @@ public class DeviceConfiguration
*/
private CaptureDeviceInfo audioCaptureDevice = null;
+ private CaptureDeviceInfo audioPlaybackDevice = null;
+
+ private CaptureDeviceInfo audioNotifyDevice = null;
+
/**
* The device that we'll be using for video capture.
*/
private CaptureDeviceInfo videoCaptureDevice;
+ private static Vector audioSystems = new Vector();
+
+ private String audioSystem = null;
+
/**
* Default constructor.
*/
@@ -109,11 +152,15 @@ private void extractConfiguredCaptureDevices()
CaptureDeviceInfo[] audioCaptureDevices =
getAvailableAudioCaptureDevices();
if (config.getBoolean(PROP_AUDIO_DEVICE_IS_DISABLED, false))
+ {
audioCaptureDevice = null;
+ audioSystem = AUDIO_SYSTEM_NONE;
+ }
else if (audioCaptureDevices.length < 1)
{
logger.warn("No Audio Device was found.");
audioCaptureDevice = null;
+ audioSystem = AUDIO_SYSTEM_NONE;
}
else
{
@@ -123,17 +170,33 @@ else if (audioCaptureDevices.length < 1)
String audioDevName = config.getString(PROP_AUDIO_DEVICE);
if(audioDevName == null)
- audioCaptureDevice = audioCaptureDevices[0];
+ {
+ // the default behaviour if nothing set is to use javasound
+ // this will also choose the capture device
+ setAudioSystem(AUDIO_SYSTEM_JAVASOUND, null);
+ }
else
{
for (CaptureDeviceInfo captureDeviceInfo : audioCaptureDevices)
{
if (audioDevName.equals(captureDeviceInfo.getName()))
{
- audioCaptureDevice = captureDeviceInfo;
+ setAudioSystem(getAudioSystem(captureDeviceInfo),
+ captureDeviceInfo);
break;
}
}
+
+ if(getAudioSystem() == null)
+ {
+ logger.warn("Computer sound config changed or " +
+ "there is a problem since last config was saved, " +
+ "will back to default javasound");
+ setAudioPlaybackDevice(null);
+ setAudioNotifyDevice(null);
+ setAudioCaptureDevice(null);
+ setAudioSystem(AUDIO_SYSTEM_JAVASOUND, null);
+ }
}
if (audioCaptureDevice != null)
logger.info("Found " + audioCaptureDevice.getName()
@@ -221,6 +284,56 @@ public CaptureDeviceInfo[] getAvailableAudioCaptureDevices()
return audioCaptureDevices.toArray(NO_CAPTURE_DEVICES);
}
+ /**
+ * Gets the list of audio capture devices which are available through this
+ * DeviceConfiguration, amongst which is
+ * {@link #getAudioCaptureDevice()} and represent acceptable values
+ * for {@link #setAudioCaptureDevice(CaptureDeviceInfo)}
+ *
+ * @param soundSystem
+ * filter capture devices only from the supplied audio system.
+ *
+ * @return an array of CaptureDeviceInfo describing the audio
+ * capture devices available through this
+ * DeviceConfiguration
+ */
+ public CaptureDeviceInfo[] getAvailableAudioCaptureDevices(String soundSystem)
+ {
+ String protocol = null;
+ if(soundSystem.equals(AUDIO_SYSTEM_JAVASOUND))
+ protocol = "javasound";
+ else if(soundSystem.equals(AUDIO_SYSTEM_PORTAUDIO))
+ protocol = "portaudio";
+
+ Vector res = new Vector();
+
+ if(protocol != null)
+ {
+ CaptureDeviceInfo[] all = getAvailableAudioCaptureDevices();
+ for(int i = 0; i < all.length; i++)
+ {
+ CaptureDeviceInfo cDeviceInfo = all[i];
+ if(cDeviceInfo.getLocator().getProtocol().equals(protocol))
+ {
+ res.add(cDeviceInfo);
+ }
+ }
+ }
+
+ return res.toArray(NO_CAPTURE_DEVICES);
+ }
+
+ /**
+ * Lists all the playback devices. These are only portaudio devices
+ * as we can only set particular device for playback when using portaudio.
+ *
+ * @return the devices that can be used for playback.
+ */
+ public CaptureDeviceInfo[] getAvailableAudioPlaybackDevices()
+ {
+ return PortAudioAuto.playbackDevices;
+ }
+
/**
* Gets the list of video capture devices which are available through this
* DeviceConfiguration, amongst which is
@@ -300,11 +413,14 @@ public void setAudioCaptureDevice(CaptureDeviceInfo device)
ConfigurationService config =
MediaActivator.getConfigurationService();
- config.setProperty(PROP_AUDIO_DEVICE_IS_DISABLED,
- audioCaptureDevice == null);
+
if (audioCaptureDevice != null)
+ {
config.setProperty(PROP_AUDIO_DEVICE, audioCaptureDevice
.getName());
+ }
+ else
+ config.setProperty(PROP_AUDIO_DEVICE, null);
firePropertyChange(AUDIO_CAPTURE_DEVICE, oldDevice, device);
}
@@ -329,4 +445,327 @@ public boolean isVideoCaptureSupported()
{
return this.videoCaptureDevice != null;
}
+
+ /**
+ * Return the installed Audio Systems.
+ * @return the audio systems names.
+ */
+ public String[] getAvailableAudioSystems()
+ {
+ return audioSystems.toArray(new String[0]);
+ }
+
+ /**
+ * Adds audio system.
+ * @param audioSystemName the name of the audio system.
+ */
+ public static void addAudioSystem(String audioSystemName)
+ {
+ audioSystems.add(audioSystemName);
+ }
+
+ /**
+ * The current selected audio system.
+ * @return the name of the current audio system.
+ */
+ public String getAudioSystem()
+ {
+ return audioSystem;
+ }
+
+ private String getAudioSystem(CaptureDeviceInfo cdi)
+ {
+ String res = null;
+ // Here we iterate over the available audio systems
+ // to be sure that the audio system
+ // is available and enabled on the system we are running on
+ if(cdi.getLocator().getProtocol().equals("javasound"))
+ {
+ Iterator iter = audioSystems.iterator();
+ while (iter.hasNext())
+ {
+ String asName = iter.next();
+ if(asName.equals(AUDIO_SYSTEM_JAVASOUND))
+ res = asName;
+ }
+ }
+ else if(cdi.getLocator().getProtocol().equals("portaudio"))
+ {
+ Iterator iter = audioSystems.iterator();
+ while (iter.hasNext())
+ {
+ String asName = iter.next();
+ if(asName.equals(AUDIO_SYSTEM_PORTAUDIO))
+ res = asName;
+ }
+ }
+
+ if(res == null)
+ res = AUDIO_SYSTEM_NONE;
+
+ return res;
+ }
+
+ /**
+ * Changes the current audio system.
+ * When javasound is selected we also change the capture device.
+ *
+ * @param name the name of the audio system.
+ * @param captureDevice the selected capture device, if is null we will
+ * choose a default one. Param used when first time initing and
+ * extracting config.
+ */
+ public void setAudioSystem(String name, CaptureDeviceInfo captureDevice)
+ {
+ ConfigurationService config = MediaActivator.getConfigurationService();
+
+ audioSystem = name;
+
+ if(name.equals(AUDIO_SYSTEM_NONE))
+ {
+ setAudioCaptureDevice(null);
+ setAudioNotifyDevice(null);
+ setAudioPlaybackDevice(null);
+ }
+ else if(name.equals(AUDIO_SYSTEM_JAVASOUND))
+ {
+ setAudioNotifyDevice(null);
+ setAudioPlaybackDevice(null);
+
+ // as there is only one device for javasound
+ // lets search for it
+ if(captureDevice != null)
+ setAudioCaptureDevice(captureDevice);
+ else
+ {
+ CaptureDeviceInfo[] audioCaptureDevices =
+ getAvailableAudioCaptureDevices();
+ for (CaptureDeviceInfo captureDeviceInfo : audioCaptureDevices)
+ {
+ if(captureDeviceInfo.getLocator().getProtocol().
+ equals("javasound"))
+ {
+ setAudioCaptureDevice(captureDeviceInfo);
+ break;
+ }
+ }
+ }
+
+ // if we have inited the audiocaptureDevice, it means javasound is
+ // available and everything is ok
+ if (audioCaptureDevice != null)
+ {
+ removePortAudioRenderer();
+ initJavaSoundRenderer();
+ }
+ }
+ else if(name.equals(AUDIO_SYSTEM_PORTAUDIO))
+ {
+ // changed to portaudio, so lets clear current device selection
+ // as we must select them
+ // if this is first time call devices will be already null
+ // and nothing will happen
+ setAudioCaptureDevice(null);
+ setAudioNotifyDevice(null);
+ setAudioPlaybackDevice(null);
+
+ // we don't save anything cause it will be saved
+ // when the devices are stored
+ // if nothing is set we consider it as not configured
+ // so when we restart we will end up with default config
+ // till restart will use latest config
+
+ // capture device is not null when we are called for the
+ // first time, we will also extract playback devices here
+ if(captureDevice != null)
+ {
+ setAudioCaptureDevice(captureDevice);
+
+ String audioDevName = config.getString(PROP_AUDIO_NOTIFY_DEVICE);
+ if(audioDevName != null)
+ {
+ for (CaptureDeviceInfo captureDeviceInfo :
+ PortAudioAuto.playbackDevices)
+ {
+ if (audioDevName.equals(captureDeviceInfo.getName()))
+ {
+ this.audioNotifyDevice = captureDeviceInfo;
+ break;
+ }
+ }
+ }
+
+ audioDevName = config.getString(PROP_AUDIO_PLAYBACK_DEVICE);
+ if(audioDevName != null)
+ {
+ for (CaptureDeviceInfo captureDeviceInfo :
+ PortAudioAuto.playbackDevices)
+ {
+ if (audioDevName.equals(captureDeviceInfo.getName()))
+ {
+ this.audioPlaybackDevice = captureDeviceInfo;
+ setDeviceToRenderer(audioPlaybackDevice);
+ removeJavaSoundRenderer();
+ break;
+ }
+ }
+ }
+ }
+
+ // return here to prevent clearing the last config that was saved
+ return;
+ }
+ else
+ {
+ // not expected behaviour
+ logger.error("Unknown audio system! Name:" + name);
+ audioSystem = null;
+ }
+
+ config.setProperty(PROP_AUDIO_DEVICE_IS_DISABLED,
+ audioCaptureDevice == null);
+ }
+
+ /**
+ * Installs the PortAudio Renderer
+ */
+ protected static void initPortAudioRenderer()
+ {
+ PlugInManager.addPlugIn(
+ "net.java.sip.communicator.impl.media.renderer.audio.PortAudioRenderer",
+ net.java.sip.communicator.impl.media.renderer.audio.
+ PortAudioRenderer.supportedInputFormats,
+ null,
+ PlugInManager.RENDERER);
+ }
+
+ private void removeJavaSoundRenderer()
+ {
+ PlugInManager.removePlugIn(
+ "com.sun.media.renderer.audio.JavaSoundRenderer",
+ PlugInManager.RENDERER);
+ }
+
+ private void removePortAudioRenderer()
+ {
+ PlugInManager.removePlugIn(
+ "net.java.sip.communicator.impl.media.renderer.audio.PortAudioRenderer",
+ PlugInManager.RENDERER);
+ }
+
+ private void initJavaSoundRenderer()
+ {
+ try
+ {
+ PlugInManager.addPlugIn(
+ "com.sun.media.renderer.audio.JavaSoundRenderer",
+ new com.sun.media.renderer.audio.JavaSoundRenderer()
+ .getSupportedInputFormats(),
+ null,
+ PlugInManager.RENDERER);
+ }
+ catch (Exception e)
+ {
+ // if class is missing
+ logger.error("Problem init javasound renderer", e);
+ }
+ }
+
+ private void setDeviceToRenderer(CaptureDeviceInfo devInfo)
+ {
+ // no need to change device to renderer it will not be used anyway
+ if(devInfo == null)
+ return;
+
+ try
+ {
+ net.java.sip.communicator.impl.media.renderer.audio.
+ PortAudioRenderer.setDevice(devInfo.getLocator());
+ }
+ catch (Exception e)
+ {
+ logger.error("error setting device to renderer", e);
+ }
+ }
+
+ /**
+ * @return the audioPlaybackDevice
+ */
+ public CaptureDeviceInfo getAudioPlaybackDevice()
+ {
+ return audioPlaybackDevice;
+ }
+
+ /**
+ * @return the audioNotifyDevice
+ */
+ public CaptureDeviceInfo getAudioNotifyDevice()
+ {
+ return audioNotifyDevice;
+ }
+
+ /**
+ * @param audioPlaybackDevice the audioPlaybackDevice to set
+ */
+ public void setAudioPlaybackDevice(CaptureDeviceInfo audioPlaybackDevice)
+ {
+ if(this.audioPlaybackDevice != audioPlaybackDevice)
+ {
+ CaptureDeviceInfo oldDev = this.audioPlaybackDevice;
+ this.audioPlaybackDevice = audioPlaybackDevice;
+ setDeviceToRenderer(audioPlaybackDevice);
+
+ // we changed playback device, so we are using portaudio
+ // lets use it, remove javasound renderer to be sure
+ // its not used anymore and install the portaudio one
+ removeJavaSoundRenderer();
+ initPortAudioRenderer();
+
+ ConfigurationService config =
+ MediaActivator.getConfigurationService();
+
+ if (audioPlaybackDevice != null)
+ {
+ config.setProperty(PROP_AUDIO_PLAYBACK_DEVICE,
+ audioPlaybackDevice.getName());
+
+ config.setProperty(PROP_AUDIO_DEVICE_IS_DISABLED, false);
+ }
+ else
+ config.setProperty(PROP_AUDIO_PLAYBACK_DEVICE, null);
+
+ firePropertyChange(AUDIO_PLAYBACK_DEVICE,
+ oldDev, audioPlaybackDevice);
+ }
+ }
+
+ /**
+ * @param audioNotifyDevice the audioNotifyDevice to set
+ */
+ public void setAudioNotifyDevice(CaptureDeviceInfo audioNotifyDevice)
+ {
+ if(this.audioNotifyDevice != audioNotifyDevice)
+ {
+ CaptureDeviceInfo oldDev = this.audioNotifyDevice;
+ this.audioNotifyDevice = audioNotifyDevice;
+
+ ConfigurationService config =
+ MediaActivator.getConfigurationService();
+
+ if (audioNotifyDevice != null)
+ {
+ config.setProperty(PROP_AUDIO_NOTIFY_DEVICE,
+ audioNotifyDevice.getName());
+
+ // atleast notify or playback must be set to consider
+ // portaudio for enabled
+ config.setProperty(PROP_AUDIO_DEVICE_IS_DISABLED, false);
+ }
+ else
+ config.setProperty(PROP_AUDIO_NOTIFY_DEVICE, null);
+
+ firePropertyChange(AUDIO_NOTIFY_DEVICE,
+ oldDev, audioNotifyDevice);
+ }
+ }
}
diff --git a/src/net/java/sip/communicator/impl/media/device/DirectSoundAuto.java b/src/net/java/sip/communicator/impl/media/device/DirectSoundAuto.java
deleted file mode 100644
index 1c33dafa3..000000000
--- a/src/net/java/sip/communicator/impl/media/device/DirectSoundAuto.java
+++ /dev/null
@@ -1,111 +0,0 @@
-/*
- * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client.
- *
- * Distributable under LGPL license.
- * See terms of license at gnu.org.
- *
- * File based on:
- * @(#)DirectSoundAuto.java 1.3 01/03/13
- * Copyright (c) 1999-2001 Sun Microsystems, Inc. All Rights Reserved.
- */
-package net.java.sip.communicator.impl.media.device;
-
-import java.util.*;
-import javax.media.*;
-import javax.media.format.*;
-
-import net.java.sip.communicator.util.*;
-
-public class DirectSoundAuto {
-
- private static final Logger logger = Logger.getLogger(DirectSoundAuto.class);
-
- private static final String detectClass = "com.sun.media.protocol.dsound.DSound";
- CaptureDeviceInfo[] devices = null;
-
- public static void main(String[] args) {
- new DirectSoundAuto();
- System.exit(0);
- }
-
- private boolean supports(AudioFormat af) {
- try {
- com.sun.media.protocol.dsound.DSound ds;
- ds = new com.sun.media.protocol.dsound.DSound(af, 1024);
- ds.open();
- ds.close();
- } catch (Exception e) {
- logger.error(e);
- return false;
- }
- return true;
- }
-
- @SuppressWarnings("unchecked") //legacy JMF code.
- public DirectSoundAuto() {
- boolean supported = false;
- // instance JavaSoundDetector to check is javasound's capture is availabe
- try {
- Class.forName(detectClass);
- supported = true;
- } catch (Throwable t) {
- supported = false;
- // t.printStackTrace();
- }
-
- logger.info("DirectSound Capture Supported = " + supported);
-
- if (supported) {
- // It's there, start to register JavaSound with CaptureDeviceManager
- Vector devices
- = (Vector)CaptureDeviceManager
- .getDeviceList(null).clone();
-
- // remove the old direct sound capturers
- String name;
- Enumeration enumeration = devices.elements();
- while (enumeration.hasMoreElements()) {
- CaptureDeviceInfo cdi = enumeration.nextElement();
- name = cdi.getName();
- if (name.startsWith(com.sun.media.protocol.dsound.DataSource.NAME))
- CaptureDeviceManager.removeDevice(cdi);
- }
- int LE = AudioFormat.LITTLE_ENDIAN;
- int SI = AudioFormat.SIGNED;
- int US = AudioFormat.UNSIGNED;
- int UN = AudioFormat.NOT_SPECIFIED;
- float [] Rates = new float[] {
- 48000, 44100, 32000, 22050, 16000, 11025, 8000
- };
- Vector formats = new Vector(4);
- for (int rateIndex = 0; rateIndex < Rates.length; rateIndex++) {
- float rate = Rates[rateIndex];
- AudioFormat af;
- af = new AudioFormat(AudioFormat.LINEAR, rate, 16, 2, LE, SI);
- if (supports(af)) formats.addElement(af);
- af = new AudioFormat(AudioFormat.LINEAR, rate, 16, 1, LE, SI);
- if (supports(af)) formats.addElement(af);
- af = new AudioFormat(AudioFormat.LINEAR, rate, 8, 2, UN, US);
- if (supports(af)) formats.addElement(af);
- af = new AudioFormat(AudioFormat.LINEAR, rate, 8, 1, UN, US);
- if (supports(af)) formats.addElement(af);
- }
-
- AudioFormat [] formatArray = new AudioFormat[formats.size()];
- for (int fa = 0; fa < formatArray.length; fa++)
- formatArray[fa] = formats.elementAt(fa);
-
- CaptureDeviceInfo cdi = new CaptureDeviceInfo(
- com.sun.media.protocol.dsound.DataSource.NAME,
- new MediaLocator("dsound://"),
- formatArray);
- CaptureDeviceManager.addDevice(cdi);
- try {
- CaptureDeviceManager.commit();
- logger.info("DirectSoundAuto: Committed ok");
- } catch (java.io.IOException ioe) {
- logger.error("DirectSoundAuto: error committing cdm");
- }
- }
- }
-}
diff --git a/src/net/java/sip/communicator/impl/media/device/JMFInit.java b/src/net/java/sip/communicator/impl/media/device/JMFInit.java
deleted file mode 100644
index 63d792da9..000000000
--- a/src/net/java/sip/communicator/impl/media/device/JMFInit.java
+++ /dev/null
@@ -1,357 +0,0 @@
-/*
- * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client.
- *
- * Distributable under LGPL license.
- * See terms of license at gnu.org.
- *
- * File based on:
- * @(#)JMFInit.java 1.14 03/04/30
- * Copyright (c) 1996-2002 Sun Microsystems, Inc. All rights reserved.
- */
-package net.java.sip.communicator.impl.media.device;
-
-import java.io.*;
-import java.util.*;
-import javax.media.*;
-import javax.media.format.*;
-
-import net.java.sip.communicator.util.*;
-
-
-public class JMFInit
- implements Runnable {
-
- private static final Logger logger = Logger.getLogger(JMFInit.class);
-
- public JMFInit() {
-
-// try {
-// Registry.commit();
-// }
-// catch (Exception e) {
-// logger.error("Failed to commit to JMFRegistry!", e );
-// }
-
-
- Thread detectThread = new Thread(this);
- detectThread.run();
-
-/*
- int slept = 0;
- while (!done && slept < 60 * 1000 * 2) {
- try {
- Thread.currentThread().sleep(500);
- }
- catch (InterruptedException ie) {
- }
- slept += 500;
- }
-
- if (!done) {
- console.error("Detection is taking too long! Aborting!");
- message("Detection is taking too long! Aborting!");
- }
-
- try {
- Thread.currentThread().sleep(2000);
- }
- catch (InterruptedException ie) {
- }
-*/
- }
-
- /**
- * Detect all capture devices
- */
- public void run() {
- detectDirectAudio();
- detectS8DirectAudio();
- detectCaptureDevices();
- }
-
- private void detectCaptureDevices() {
- // check if JavaSound capture is available
- logger.info("Looking for Audio capturer");
- Class> dsauto = null;
- try {
- dsauto = Class.forName(
- "net.java.sip.communicator.impl.media.device.DirectSoundAuto");
- dsauto.newInstance();
- logger.info("Finished detecting DirectSound capturer");
- }
- catch (ThreadDeath td) {
- throw td;
- }
- catch (Throwable t) {
- logger.warn("DirectSound capturer detection failed!", t);
- }
-
- Class> jsauto = null;
- try {
- jsauto = Class.forName(
- "net.java.sip.communicator.impl.media.device.JavaSoundAuto");
- jsauto.newInstance();
- logger.info("Finished detecting JavaSound capturer");
- }
- catch (ThreadDeath td) {
- throw td;
- }
- catch (Throwable t) {
- logger.warn("JavaSound capturer detection failed!", t);
- }
-
- // Check if VFWAuto or SunVideoAuto is available
- logger.info("Looking for video capture devices");
- Class> auto = null;
- Class> autoPlus = null;
- try {
- auto = Class.forName(
- "net.java.sip.communicator.impl.media.device.VFWAuto");
- }
- catch (Exception e) {
- logger.warn("VFWAuto capturer detection failed!", e);
- }
- if (auto == null) {
- try {
- auto = Class.forName(
- "net.java.sip.communicator.impl.media.device.SunVideoAuto");
- }
- catch (Exception ee) {
- logger.warn("SunVideoAuto capturer detection failed!", ee);
- }
- try {
- autoPlus = Class.forName(
- "net.java.sip.communicator.impl.media.device.SunVideoPlusAuto");
- }
- catch (Exception ee) {
- logger.warn("SunVideoPlusAuto capturer detection failed!", ee);
- }
- }
- if (auto == null) {
- try {
- auto = Class.forName(
- "net.java.sip.communicator.impl.media.device.V4LAuto");
- }
- catch (Exception ee) {
- logger.warn("V4lAuto capturer detection failed!", ee);
- }
- }
- try {
- auto.newInstance();
- if (autoPlus != null) {
- autoPlus.newInstance();
- }
- logger.info("Finished detecting video capture devices");
- }
- catch (ThreadDeath td) {
- throw td;
- }
- catch (Throwable t) {
- logger.error("Capture device detection failed!", t);
- }
- }
-
- @SuppressWarnings("unchecked") //legacy JMF code.
- private void detectDirectAudio() {
- Class> cls;
- int plType = PlugInManager.RENDERER;
- String dar = "com.sun.media.renderer.audio.DirectAudioRenderer";
- try {
- // Check if this is the Windows Performance Pack - hack
- cls = Class.forName(
- "net.java.sip.communicator.impl.media.device.VFWAuto");
- // Check if DS capture is supported, otherwise fail DS renderer
- // since NT doesn't have capture
- cls = Class.forName("com.sun.media.protocol.dsound.DSound");
- // Find the renderer class and instantiate it.
- cls = Class.forName(dar);
-
- Renderer rend = (Renderer) cls.newInstance();
- try {
- // Set the format and open the device
- AudioFormat af = new AudioFormat(AudioFormat.LINEAR,
- 44100, 16, 2);
- rend.setInputFormat(af);
- rend.open();
- Format[] inputFormats = rend.getSupportedInputFormats();
- // Register the device
- PlugInManager.addPlugIn(dar, inputFormats, new Format[0],
- plType);
- // Move it to the top of the list
- Vector rendList =
- PlugInManager.getPlugInList(null, null, plType);
- int listSize = rendList.size();
- if (rendList.elementAt(listSize - 1).equals(dar)) {
- rendList.removeElementAt(listSize - 1);
- rendList.insertElementAt(dar, 0);
- PlugInManager.setPlugInList(rendList, plType);
- PlugInManager.commit();
- //System.err.println("registered");
- }
- rend.close();
- }
- catch (Throwable t) {
- //System.err.println("Error " + t);
- }
- }
- catch (Throwable tt) {
- }
- }
-
- private void detectS8DirectAudio() {
- try
- {
- new S8DirectAudioAuto();
- }
- catch (Throwable tt)
- {
- }
- }
-
- /**
- * Runs JMFInit the first time the application is started so that capture
- * devices are properly detected and initialized by JMF.
- */
- public static void setupJMF()
- {
- try
- {
- logger.logEntry();
-
- // .jmf is the place where we store the jmf.properties file used
- // by JMF. if the directory does not exist or it does not contain
- // a jmf.properties file, or if the jmf.properties file has 0 length
- // then this is the first time we're running and should detect capture
- // devices
- String homeDir = System.getProperty("user.home");
- File jmfDir = new File(homeDir, ".jmf");
- String classpath = System.getProperty("java.class.path");
- classpath += System.getProperty("path.separator") +
- jmfDir.getAbsolutePath();
- System.setProperty("java.class.path", classpath);
-
- if (!jmfDir.exists())
- jmfDir.mkdir();
-
- File jmfProperties = new File(jmfDir, "jmf.properties");
-
- if (!jmfProperties.exists()) {
- try {
- jmfProperties.createNewFile();
- }
- catch (IOException ex) {
- logger.error(
- "Failed to create jmf.properties - " +
- jmfProperties.getAbsolutePath());
- }
- }
-
- //if we're running on linux checkout that libjmutil.so is where it
- //should be and put it there.
-// runLinuxPreInstall();
-
- if (jmfProperties.length() == 0) {
- new JMFInit();
- }
- }
- finally
- {
- logger.logExit();
- }
-
- }
-
-
-// private static void runLinuxPreInstall()
-// {
-// try {
-// logger.logEntry();
-//
-// if (Utils.getProperty("os.name") == null
-// || !Utils.getProperty("os.name").equalsIgnoreCase("Linux"))
-// return;
-//
-// try {
-// System.loadLibrary("jmv4l");
-// console.debug("Successfully loaded libjmv4l.so");
-// }
-// catch (UnsatisfiedLinkError err) {
-// console.debug("Failed to load libjmv4l.so. Will try and copy libjmutil.so", err);
-//
-// String destinationPathStr = Utils.getProperty("java.home")
-// + File.separator + "lib"
-// + File.separator + "i386";
-// String libjmutilFileStr = "libjmutil.so";
-//
-// try {
-// InputStream libIS =
-// MediaManager.class.getClassLoader().
-// getResourceAsStream(libjmutilFileStr);
-// File outFile = new File(destinationPathStr
-// +File.separator + libjmutilFileStr);
-//
-// //Check if file is already there - Ben Asselstine
-// if (outFile.exists()) {
-// //if we're here then libjmutil is already where it should be
-// // but yet we failed to load libjmv4l.
-// //so notify log and bail out
-// console.error(
-// "An error occurred while trying to load JMF. This "
-// +"error is probably due to a JMF installation problem. "
-// +"Please copy libjmutil.so to a location contained by "
-// + "$LD_LIBRARY_PATH and try again!",
-// err);
-// return;
-//
-// }
-//
-// outFile.createNewFile();
-//
-// console.debug("jmutil");
-//
-// FileOutputStream fileOS = new FileOutputStream(outFile);
-// int available = libIS.available();
-// byte[] bytes = new byte[libIS.available()];
-// int read = 0;
-// int i = 0;
-// for (i = 0; i playbackDevVector =
+ new Vector();
+
+ for (; deviceIndex < deviceCount; deviceIndex++)
+ {
+ long deviceInfo = PortAudio.Pa_GetDeviceInfo(deviceIndex);
+ int maxInputChannels =
+ PortAudio.PaDeviceInfo_getMaxInputChannels(deviceInfo);
+ int maxOutputChannels =
+ PortAudio.PaDeviceInfo_getMaxOutputChannels(deviceInfo);
- formats[0] = PortAudioStream.audioFormat;
+ CaptureDeviceInfo jmfInfo =
+ new CaptureDeviceInfo(
+ PortAudio.PaDeviceInfo_getName(deviceInfo),
+ new MediaLocator(
+ PortAudioStream.LOCATOR_PREFIX + deviceIndex),
+ PortAudioStream.getFormats());
- CaptureDeviceInfo jmfInfo =
- new CaptureDeviceInfo("portaudio:1",
- new MediaLocator("portaudio:#" + 1), formats);
+ if(maxInputChannels > 0)
+ {
+ CaptureDeviceManager.addDevice(jmfInfo);
+ }
- CaptureDeviceManager.addDevice(jmfInfo);
+ if(maxOutputChannels > 0)
+ {
+ playbackDevVector.add(jmfInfo);
+ }
+ }
+
+ playbackDevices = playbackDevVector.toArray(new CaptureDeviceInfo[0]);
CaptureDeviceManager.commit();
+
+ // Enables Portaudio Renderer
+ DeviceConfiguration.initPortAudioRenderer();
+
+ // now add it as available audio system to DeviceConfiguration
+ DeviceConfiguration.addAudioSystem(
+ DeviceConfiguration.AUDIO_SYSTEM_PORTAUDIO);
}
}
diff --git a/src/net/java/sip/communicator/impl/media/device/S8DirectAudioAuto.java b/src/net/java/sip/communicator/impl/media/device/S8DirectAudioAuto.java
deleted file mode 100644
index e28b6d4c5..000000000
--- a/src/net/java/sip/communicator/impl/media/device/S8DirectAudioAuto.java
+++ /dev/null
@@ -1,62 +0,0 @@
-package net.java.sip.communicator.impl.media.device;
-
-import java.util.*;
-
-import javax.media.*;
-
-import com.sun.media.*;
-
-/**
- * Probes for JMF Solaris 8 direct audio.
- *
- * @author Emil Ivov
- * @author Ken Larson
- */
-public class S8DirectAudioAuto
-{
- @SuppressWarnings("unchecked") //legacy JMF code.
- public S8DirectAudioAuto() throws Exception
- {
- Class> cls;
- int plType = PlugInManager.RENDERER;
- String dar = "com.sun.media.renderer.audio.DirectAudioRenderer";
-
- // Check if this is the solaris Performance Pack - hack
- cls = Class.forName(
- "net.java.sip.communicator.impl.media.device.SunVideoAuto");
-
- // Find the renderer class and instantiate it.
- cls = Class.forName(dar);
-
- Renderer rend = (Renderer) cls.newInstance();
-
- if (rend instanceof ExclusiveUse &&
- ! ( (ExclusiveUse) rend).isExclusive())
- {
- // sol8+, DAR supports mixing
- Vector rendList = PlugInManager.getPlugInList(null, null,
- plType);
- int listSize = rendList.size();
- boolean found = false;
- String rname = null;
-
- for (int i = 0; i < listSize; i++)
- {
- rname = rendList.elementAt(i);
- if (rname.equals(dar))
- { // DAR is in the registry
- found = true;
- rendList.removeElementAt(i);
- break;
- }
- }
-
- if (found)
- {
- rendList.insertElementAt(dar, 0);
- PlugInManager.setPlugInList(rendList, plType);
- PlugInManager.commit();
- }
- }
- }
-}
diff --git a/src/net/java/sip/communicator/impl/media/device/SunVideoAuto.java b/src/net/java/sip/communicator/impl/media/device/SunVideoAuto.java
deleted file mode 100644
index c387f09f2..000000000
--- a/src/net/java/sip/communicator/impl/media/device/SunVideoAuto.java
+++ /dev/null
@@ -1,206 +0,0 @@
-/*
- * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client.
- *
- * Distributable under LGPL license.
- * See terms of license at gnu.org.
- *
- * File based on:
- * @(#)SunVideoAuto.java 1.8 01/03/13
- * Copyright (c) 1999-2001 Sun Microsystems, Inc. All Rights Reserved.
- */
-package net.java.sip.communicator.impl.media.device;
-
-import java.io.*;
-import java.util.*;
-import javax.media.*;
-import javax.media.format.*;
-
-import java.awt.*;
-
-import net.java.sip.communicator.util.*;
-import com.sun.media.protocol.sunvideo.*;
-
-
-public class SunVideoAuto
-{
-
- private static final Logger logger = Logger.getLogger(SunVideoAuto.class);
-
- private static String DEVICE_PREFIX = "/dev/rtvc";
- private static String PROTOCOL = "sunvideo";
- private static String LOCATOR_PREFIX = PROTOCOL + "://";
- CaptureDeviceInfo [] devices = null;
- int currentID = -1;
-
- XILCapture xilCap;
-
- Vector formats = null;
-
- int [] ports = { 1, 2, 0 }; // most likely ports for a device
-
- int [] scales = { 2, 4, 1 }; // supported scales / sizes
-
- /**
- * Default constructor. Does nothing.
- */
- public SunVideoAuto()
- {
- }
-
- /**
- * Removes from the CaptureDeviceManager all currently detected vfw devices
- * and runs a new detection loop to rediscover those that are currently
- * available.
- *
- * @return the number of devices detected.
- */
- @SuppressWarnings("unchecked") //legacy JMF code.
- public int autoDetectDevices()
- {
- Vector devices
- = (Vector) CaptureDeviceManager.getDeviceList(null).clone();
- Enumeration enumeration = devices.elements();
- while (enumeration.hasMoreElements()) {
- CaptureDeviceInfo cdi = enumeration.nextElement();
- String devName = cdi.getLocator().getProtocol();
- if (devName.equals(PROTOCOL))
- CaptureDeviceManager.removeDevice(cdi);
- }
-
- int nDevices = 0;
- for (int i = 0; i < 7; i++) {
- File fl = new File(DEVICE_PREFIX + i);
- if (fl.exists()) {
- doDevice(i);
- nDevices++;
- }
- }
- try {
- CaptureDeviceManager.commit();
- } catch (java.io.IOException ioe) {
- logger.error("SunVideoAuto: error committing cdm", ioe);
- return 0;
- }
-
- return nDevices;
- }
-
- private void addFormat(Format fin)
- {
- Enumeration enumeration = formats.elements();
- while (enumeration.hasMoreElements()) {
- Format format = enumeration.nextElement();
- if (format.equals(fin))
- return;
- }
-
- //System.err.println("New format = " + fin);
- formats.addElement(fin);
- }
-
- private void doDevice(int index)
- {
-
- xilCap = new XILCapture(null);
- formats = new Vector();
- //boolean gotPort = false;
-
- if (!xilCap.connect(index)) {
- dummyDevice(index);
- return;
- }
-
-
- for (int i = 0; i < ports.length; i++) {
- if (xilCap.setPort(ports[i])) {
- getJpegFormats();
- getRGBFormats();
- }
- }
- xilCap.disconnect();
-
- if (formats.size() > 0)
- addDevice(index);
- else
- dummyDevice(index);
- }
-
-
- private void getRGBFormats()
- {
- if (!xilCap.setCompress("RGB"))
- return;
- for (int i = 0; i < scales.length; i++) {
- xilCap.setScale(scales[i]);
- // To get the real values, start the device
- if (xilCap.start()) {
- Dimension size = new Dimension(xilCap.getWidth(),
- xilCap.getHeight());
- int stride = xilCap.getLineStride();
- int maxbuf = stride * size.width;
- addFormat(new RGBFormat(size, maxbuf, byte[].class,
- 15f,
- 24,
- 3, 2, 1, 3, stride,
- Format.FALSE,
- Format.NOT_SPECIFIED));
- }
- xilCap.stop();
- }
- }
-
- private void getJpegFormats() {
- if (!xilCap.setCompress("Jpeg"))
- return;
- for (int i = 0; i < scales.length; i++) {
- xilCap.setScale(scales[i]);
- // To get the real values, start the device
- if (xilCap.start()) {
- Dimension size = new Dimension(xilCap.getWidth(),
- xilCap.getHeight());
- // approximate the max for high quality
- int maxbuf = 3 * size.width * size.height;
- addFormat(new VideoFormat(VideoFormat.JPEG, size, maxbuf,
- byte[].class, 15f));
- }
- xilCap.stop();
- }
- }
-
- private void dummyDevice(int index)
- {
- // Can't get to the device, use the barest formats
- addFormat(new VideoFormat(VideoFormat.JPEG));
- addFormat(new RGBFormat());
- addDevice(index);
- }
-
-
- private void addDevice(int index)
- {
-
- String name = "SunVideo device " + index;
- String locator = LOCATOR_PREFIX + index;
-
- Format [] farray = new Format[formats.size()];
- Enumeration enumeration = formats.elements();
-
- int i = 0;
- while (enumeration.hasMoreElements()) {
- Format format = enumeration.nextElement();
- farray[i++] = format;
- }
-
- CaptureDeviceInfo cdi = new CaptureDeviceInfo(name,
- new MediaLocator(locator), farray);
- CaptureDeviceManager.addDevice(cdi);
- }
-
- public static void main(String [] args)
- {
- SunVideoAuto sunVideoAuto = new SunVideoAuto();
- sunVideoAuto.autoDetectDevices();
- System.exit(0);
- }
-}
-
diff --git a/src/net/java/sip/communicator/impl/media/device/SunVideoPlusAuto.java b/src/net/java/sip/communicator/impl/media/device/SunVideoPlusAuto.java
deleted file mode 100644
index eafd7f892..000000000
--- a/src/net/java/sip/communicator/impl/media/device/SunVideoPlusAuto.java
+++ /dev/null
@@ -1,456 +0,0 @@
-/*
- * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client.
- *
- * Distributable under LGPL license.
- * See terms of license at gnu.org.
- *
- * File based on:
- * @(#)SunVideoPlusAuto.java 1.6 01/03/13
- * Copyright (c) 1999-2001 Sun Microsystems, Inc. All Rights Reserved.
- */
-package net.java.sip.communicator.impl.media.device;
-
-import java.io.*;
-import java.util.*;
-import javax.media.*;
-import javax.media.format.*;
-import javax.media.protocol.*;
-
-import java.awt.*;
-
-import com.sun.media.protocol.sunvideoplus.*;
-import net.java.sip.communicator.util.*;
-
-/**
- * SIP Communicator modifications.
- * @author Emil Ivov
- */
-public class SunVideoPlusAuto
-{
- private static final Logger logger
- = Logger.getLogger(SunVideoPlusAuto.class);
-
- private static String DEVICE_PREFIX = "/dev/o1k";
- private static String PROTOCOL = "sunvideoplus";
- private static String LOCATOR_PREFIX = PROTOCOL + "://";
-
- private static boolean DO_PAL = false;
-
- int currentID = -1;
-
- /**
- * Removes from the CaptureDeviceManager all currently detected sun video
- * devices and runs a new detection loop to rediscover those that are
- * currently available.
- *
- * @return the number of devices detected.
- */
- @SuppressWarnings("unchecked") //legacy JMF code.
- public int autoDetectDevices()
- {
- /*
- * First remove any old entries
- */
- Vector devices = (Vector) CaptureDeviceManager.
- getDeviceList(null).clone();
- Enumeration enumeration = devices.elements();
- while (enumeration.hasMoreElements())
- {
- CaptureDeviceInfo cdi = enumeration.nextElement();
- String devName = cdi.getLocator().getProtocol();
- if (devName.equals(PROTOCOL))
- CaptureDeviceManager.removeDevice(cdi);
- }
-
- int nDevices = 0;
- for (int i = 0; i < 10; i++)
- {
- File fl = new File(DEVICE_PREFIX + i);
- if (fl.exists())
- {
- if (DO_PAL)
- {
- generalDevice(i, "PAL");
- // If generating PAL, do both
- // Garbage collect to release the PAL datasource
- // otherwise it sometimes hangs before completing NTSC
- System.gc();
- generalDevice(i, "NTSC");
- }
- else
- {
- generalDevice(i, null);
- }
- // No longer generate specific configurations,
- // let capture preview handle selection.
- // doDevice(i);
- nDevices++;
- }
- }
-
- try
- {
- CaptureDeviceManager.commit();
- }
- catch (java.io.IOException ioe)
- {
- logger.error("SunVideoPlusAuto: error committing cdm", ioe);
- return 0;
- }
-
- return nDevices;
- }
-
- protected void generalDevice(int id, String signal)
- {
- // Add the general device
- javax.media.protocol.DataSource dsource = null;
- String url = LOCATOR_PREFIX + id;
- if (signal != null)
- url += "////" + signal.toLowerCase();
- try
- {
- dsource = Manager.createDataSource(new MediaLocator(url));
- }
- catch (Exception ex)
- {
- }
- if (dsource != null && dsource instanceof
- com.sun.media.protocol.sunvideoplus.DataSource)
- {
- CaptureDeviceInfo cdi = ( (CaptureDevice) dsource).
- getCaptureDeviceInfo();
- if (cdi != null)
- {
- String name = cdi.getName();
- if (signal == null)
- {
- CaptureDeviceManager.addDevice(cdi);
- }
- else
- {
- name = cdi.getName() + " (" + signal + ")";
- CaptureDeviceManager.addDevice(new CaptureDeviceInfo(name,
- cdi.getLocator(), cdi.getFormats()));
- }
- System.err.println("CaptureDeviceInfo = "
- + name + " "
- + cdi.getLocator());
- }
- dsource.disconnect();
- }
- }
-
- protected void doDevice(int id)
- {
- currentID = id;
- FormatSetup fd = new FormatSetup(currentID);
- Vector cdiv = fd.getDeviceInfo();
- if (cdiv != null && cdiv.size() > 0)
- {
- for (int i = 0; i < cdiv.size(); i++)
- {
- CaptureDeviceInfo cdi = cdiv.elementAt(i);
- // At the moment, the name and locator are identical
- System.err.println("CaptureDeviceInfo = "
- + cdi.getName());
-// System.err.println("CaptureDeviceInfo = "
-// + cdi.getName() + " "
-// + cdi.getLocator());
- }
- }
- }
-
- static class FormatSetup
- {
-
- int id;
-
- boolean fullVideo = false;
- boolean anyVideo = true;
-
- String sAnalog, sPort, sVideoFormat, sSize;
-
- Hashtable videoFormats = new Hashtable();
-
- OPICapture opiVidCap = null;
-
- public FormatSetup(int id)
- {
- this.id = id;
- opiVidCap = new OPICapture(null);
- if (!opiVidCap.connect(id))
- {
- throw new Error("Unable to connect to device");
- }
-
- }
-
- private void addVideoFormat(Format fin)
- {
- String sVideo = sPort + "/" + sVideoFormat + "/"
- + sSize + "/"
- + sAnalog;
- System.err.println("New format " + sVideo + " = " + fin);
- videoFormats.put(sVideo, fin);
- }
-
- public void mydispose()
- {
- opiVidCap.disconnect();
- System.err.println("Disconnected driver");
- }
-
- public void doFormat()
- {
- if (anyVideo)
- {
- doVideoFormats();
- }
- }
-
- public void doVideoFormats()
- {
- if (!anyVideo)
- {
- // add a dummy format entry
- videoFormats.put("off", new VideoFormat(VideoFormat.RGB));
- }
-
- sAnalog = "ntsc";
- if (DO_PAL)
- sAnalog = "pal";
- if (!opiVidCap.setSignal(sAnalog))
- {
- System.err.println("Video analog signal not recognized");
- return;
- }
- int port = 1;
- if (!opiVidCap.setPort(port))
- {
- System.err.println("Video source not recognized on port");
- return;
- }
- sPort = "" + port;
- opiVidCap.setScale(2);
- sSize = "cif";
- getVideoFormats();
- }
-
- private void getVideoFormats()
- {
- sVideoFormat = "h261";
- getH261Format();
- sVideoFormat = "h263";
- getH263Format();
- sVideoFormat = "jpeg";
- getJpegFormat();
- sVideoFormat = "rgb";
- getRGBFormat();
- sVideoFormat = "yuv";
- getYUVFormat();
- }
-
- private void getRGBFormat()
- {
- if (!opiVidCap.setCompress("RGB"))
- return;
- /*
- * If sizes are wanted, the only valid sizes are
- * NTSC
- * fcif (640 x 480)
- * cif (320 x 240)
- * qcif (160 x 120)
- * PAL
- * fcif (768 x 576)
- * cif (384 x 288)
- * qcif (192 x 144)
- */
- Dimension size = new Dimension(opiVidCap.getWidth(),
- opiVidCap.getHeight());
- addVideoFormat(new RGBFormat(size, Format.NOT_SPECIFIED,
- Format.byteArray,
- Format.NOT_SPECIFIED,
- 16,
- 0xF800, 0x7E0, 0x1F, 2,
- Format.NOT_SPECIFIED,
- Format.FALSE,
- Format.NOT_SPECIFIED));
- }
-
- private void getYUVFormat()
- {
- if (!opiVidCap.setCompress("YUV"))
- return;
- /*
- * If sizes are wanted, the only valid sizes are
- * NTSC
- * fcif (640 x 480)
- * cif (320 x 240)
- * qcif (160 x 120)
- * PAL
- * fcif (768 x 576)
- * cif (384 x 288)
- * qcif (192 x 144)
- *
- * The capture stream is actually interleaved YVYU format.
- * This is defined in the offset values below.
- */
- Dimension size = new Dimension(opiVidCap.getWidth(),
- opiVidCap.getHeight());
- addVideoFormat(new YUVFormat(size, Format.NOT_SPECIFIED,
- Format.byteArray,
- Format.NOT_SPECIFIED,
- YUVFormat.YUV_YUYV,
- Format.NOT_SPECIFIED,
- Format.NOT_SPECIFIED,
- 0, 3, 1));
- }
-
- private void getJpegFormat()
- {
- if (!opiVidCap.setCompress("Jpeg"))
- return;
- /*
- * If sizes are wanted, the only valid sizes are
- * NTSC
- * cif (320 x 240)
- * qcif (160 x 120)
- * PAL
- * cif (384 x 288)
- * qcif (192 x 144)
- */
- Dimension size = new Dimension(opiVidCap.getWidth(),
- opiVidCap.getHeight());
- addVideoFormat(new VideoFormat(VideoFormat.JPEG, size,
- Format.NOT_SPECIFIED,
- Format.byteArray,
- Format.NOT_SPECIFIED));
- }
-
- private void getH261Format()
- {
- if (!opiVidCap.setCompress("H261"))
- return;
- /*
- * If sizes are wanted, the only valid sizes are
- * cif (352 x 288)
- * qcif (176 x 144)
- */
- Dimension size = new Dimension(opiVidCap.getWidth(),
- opiVidCap.getHeight());
- addVideoFormat(new VideoFormat(VideoFormat.H261, size,
- Format.NOT_SPECIFIED,
- Format.byteArray,
- Format.NOT_SPECIFIED));
- }
-
- private void getH263Format()
- {
- if (!opiVidCap.setCompress("H263"))
- return;
- /*
- * If sizes are wanted, the only valid sizes are
- * cif (352 x 288)
- * qcif (176 x 144)
- */
- Dimension size = new Dimension(opiVidCap.getWidth(),
- opiVidCap.getHeight());
- addVideoFormat(new VideoFormat(VideoFormat.H263, size,
- Format.NOT_SPECIFIED,
- Format.byteArray,
- Format.NOT_SPECIFIED));
- }
-
- public void issueError(String err)
- {
- System.err.println(err);
- Toolkit.getDefaultToolkit().beep();
- }
-
- public Enumeration sortedFormats(Hashtable formats)
- {
- Vector sorted = new Vector();
- keyloop:for (Enumeration en = formats.keys();
- en.hasMoreElements(); )
- {
- String key = en.nextElement();
- for (int i = 0; i < sorted.size(); i++)
- {
- if (key.compareTo( sorted.elementAt(i)) < 0)
- {
- sorted.insertElementAt(key, i);
- continue keyloop;
- }
- }
- sorted.addElement(key);
- }
- return sorted.elements();
- }
-
- public Vector getDeviceInfo()
- {
- doFormat();
- mydispose();
-
- String locatorPrefix = LOCATOR_PREFIX + id;
- Vector devices = new Vector();
- if (anyVideo)
- {
-
- for (Enumeration ve = sortedFormats(videoFormats);
- ve.hasMoreElements(); )
- {
- String vKey = ve.nextElement();
- Format vForm = videoFormats.get(vKey);
- Format[] farray = null;
- farray = new Format[1];
- farray[0] = vForm;
- String name = locatorPrefix + "/" + vKey;
- CaptureDeviceInfo cdi = new CaptureDeviceInfo(name,
- new MediaLocator(name), farray);
- CaptureDeviceManager.addDevice(cdi);
- devices.addElement(cdi);
- }
- }
- return devices;
- }
-
- }
-
- public static void setPALSignal(boolean pal)
- {
- DO_PAL = pal;
- }
-
- public static void main(String[] args)
- {
- if (args.length > 0)
- {
- if (args.length > 1)
- {
- System.err.println(
- "Usage: java SunVideoPlusAuto [ ntsc | pal ]");
- System.exit(1);
- }
- if (args[0].equalsIgnoreCase("ntsc"))
- {
- SunVideoPlusAuto.setPALSignal(false);
- }
- else if (args[0].equalsIgnoreCase("pal"))
- {
- SunVideoPlusAuto.setPALSignal(true);
- }
- else
- {
- System.err.println(
- "Usage: java SunVideoPlusAuto [ ntsc | pal ]");
- System.exit(1);
- }
- }
- SunVideoPlusAuto sunVideoPlus = new SunVideoPlusAuto();
- sunVideoPlus.autoDetectDevices();
- System.exit(0);
- }
-}
-
diff --git a/src/net/java/sip/communicator/impl/media/device/V4LAuto.java b/src/net/java/sip/communicator/impl/media/device/V4LAuto.java
deleted file mode 100644
index 84ab8b5c2..000000000
--- a/src/net/java/sip/communicator/impl/media/device/V4LAuto.java
+++ /dev/null
@@ -1,110 +0,0 @@
-/*
- * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client.
- *
- * Distributable under LGPL license.
- * See terms of license at gnu.org.
- *
- * File based on:
- * @(#)V4LAuto.java 1.2 01/03/13
- * Copyright (c) 1999-2001 Sun Microsystems, Inc. All Rights Reserved.
- */
-package net.java.sip.communicator.impl.media.device;
-
-import java.util.*;
-
-import javax.media.*;
-
-import net.java.sip.communicator.util.*;
-
-import com.sun.media.protocol.v4l.*;
-
-/**
- * Probes for video capture devices present on linux systems.
- * @author Emil Ivov
- */
-public class V4LAuto {
-
- private static final Logger logger = Logger.getLogger(V4LAuto.class);
-
- /**
- * Removes from the CaptureDeviceManager all currently detected devices and
- * runs a new detection loop to rediscover those that are currently
- * available.
- *
- * @return the number of devices detected.
- */
- @SuppressWarnings("unchecked") //JMF legacy code
- public int autoDetectDevices()
- {
- Vector devices
- = (Vector) CaptureDeviceManager.getDeviceList(null).clone();
- Enumeration enumeration = devices.elements();
- while (enumeration.hasMoreElements())
- {
- CaptureDeviceInfo cdi = enumeration.nextElement();
- String name = cdi.getName();
- if (name.startsWith("v4l:"))
- CaptureDeviceManager.removeDevice(cdi);
- }
-
- int nDevices = 0;
- for (int i = 0; i < 10; i++)
- {
- CaptureDeviceInfo cdi = autoDetect(i);
- if (cdi != null)
- nDevices++;
- }
-
- return nDevices;
- }
- /**
- * Runs a device query for the capture card with the specified card number
- * and returns its corresponding CaptureDeviceInfo or null if detection
- * failed or nos such card exists.
- * @param cardNo the index of the card to discover.
- * @return the CaptureDeviceInfo corresponsing to the newly discovered
- * device.
- */
- protected CaptureDeviceInfo autoDetect(int cardNo)
- {
- CaptureDeviceInfo cdi = null;
- try
- {
- cdi = new V4LDeviceQuery();
-
- ((V4LDeviceQuery)cdi).sendQuery(cardNo);
- if ( cdi.getFormats() != null
- && cdi.getFormats().length > 0)
- {
- // Commit it to disk. Its a new device
- if (CaptureDeviceManager.addDevice(cdi))
- {
- logger.info("Added device " + cdi);
- CaptureDeviceManager.commit();
- }
- }
- }
- catch (Throwable thr)
- {
- logger.debug("No device for index "
- + cardNo + ". "
- + thr.getMessage());
- if (thr instanceof ThreadDeath)
- throw (ThreadDeath)thr;
- }
-
- return cdi;
- }
-
- /**
- * Test method, present for testing only.
- * @param args String[]
- */
- public static void main(String [] args)
- {
- V4LAuto auto = new V4LAuto();
- auto.autoDetectDevices();
- System.exit(0);
- }
-}
-
diff --git a/src/net/java/sip/communicator/impl/media/device/VFWAuto.java b/src/net/java/sip/communicator/impl/media/device/VFWAuto.java
deleted file mode 100644
index 450c538cc..000000000
--- a/src/net/java/sip/communicator/impl/media/device/VFWAuto.java
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client.
- *
- * Distributable under LGPL license.
- * See terms of license at gnu.org.
- *
- * File based on:
- * @(#)VFWAuto.java 1.2 01/03/13
- * Copyright (c) 1999-2001 Sun Microsystems, Inc. All Rights Reserved.
- */
-package net.java.sip.communicator.impl.media.device;
-
-import java.util.*;
-import javax.media.*;
-import com.sun.media.protocol.vfw.*;
-import net.java.sip.communicator.util.*;
-
-/**
- * SIP Communicator modifications
- * @author Emil Ivov
- */
-public class VFWAuto
-{
- private static final Logger logger
- = Logger.getLogger(VFWAuto.class);
-
- /**
- * Removes from the CaptureDeviceManager all currently detected vfw devices
- * and runs a new detection loop to rediscover those that are currently
- * available.
- *
- * @return the number of devices detected.
- */
- @SuppressWarnings("unchecked") // jmf legacy code
- public int autoDetectDevices()
- {
- Vector devices
- = (Vector) CaptureDeviceManager.getDeviceList(null).clone();
- Enumeration devicesEnum = devices.elements();
-
- while (devicesEnum.hasMoreElements())
- {
- CaptureDeviceInfo cdi = devicesEnum.nextElement();
- String name = cdi.getName();
- if (name.startsWith("vfw:"))
- CaptureDeviceManager.removeDevice(cdi);
- }
-
- int nDevices = 0;
- for (int i = 0; i < 10; i++)
- {
- String name = VFWCapture.capGetDriverDescriptionName(i);
- if (name != null && name.length() > 1)
- {
- logger.debug("Found device " + name);
- logger.debug("Querying device. Please wait...");
- com.sun.media.protocol.vfw.VFWSourceStream.autoDetect(i);
- nDevices++;
- }
- }
-
- return nDevices;
- }
-
- public static void main(String [] args)
- {
- VFWAuto vfwAuto = new VFWAuto();
- vfwAuto.autoDetectDevices();
- System.exit(0);
- }
-}
-
diff --git a/src/net/java/sip/communicator/impl/media/media.manifest.mf b/src/net/java/sip/communicator/impl/media/media.manifest.mf
index 430e71262..4d625ab6a 100644
--- a/src/net/java/sip/communicator/impl/media/media.manifest.mf
+++ b/src/net/java/sip/communicator/impl/media/media.manifest.mf
@@ -41,4 +41,5 @@ Export-Package: net.java.sip.communicator.service.media,
net.java.sip.communicator.service.media.event,
net.java.sip.communicator.impl.media,
net.java.sip.communicator.impl.media.device,
+ net.java.sip.communicator.service.audionotifier
Metadata-Location: /net/java/sip/communicator/impl/media/media.metadata.xml
diff --git a/src/net/java/sip/communicator/impl/media/notify/AudioNotifierServiceImpl.java b/src/net/java/sip/communicator/impl/media/notify/AudioNotifierServiceImpl.java
new file mode 100644
index 000000000..633b9f073
--- /dev/null
+++ b/src/net/java/sip/communicator/impl/media/notify/AudioNotifierServiceImpl.java
@@ -0,0 +1,159 @@
+/*
+ * SIP Communicator, 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.media.notify;
+
+import java.net.*;
+import java.util.*;
+
+import net.java.sip.communicator.impl.media.*;
+import net.java.sip.communicator.impl.media.device.*;
+import net.java.sip.communicator.service.audionotifier.*;
+
+/**
+ * The implementation of the AudioNotifierService.
+ *
+ * @author Yana Stamcheva
+ */
+public class AudioNotifierServiceImpl
+ implements AudioNotifierService
+{
+ private static final Map audioClips =
+ new HashMap();
+
+ private boolean isMute;
+
+ /**
+ * Device config to look for notify device.
+ */
+ private DeviceConfiguration deviceConfiguration;
+
+ /**
+ * Creates audio notify service.
+ * @param deviceConfiguration the device configuration.
+ */
+ public AudioNotifierServiceImpl(DeviceConfiguration deviceConfiguration)
+ {
+ this.deviceConfiguration = deviceConfiguration;
+ }
+
+ /**
+ * Creates an SCAudioClip from the given URI and adds it to the list of
+ * available audio-s.
+ *
+ * @param uri the path where the audio file could be found
+ */
+ public SCAudioClipImpl createAudio(String uri)
+ {
+ SCAudioClipImpl audioClip;
+
+ synchronized (audioClips)
+ {
+ if(audioClips.containsKey(uri))
+ {
+ audioClip = audioClips.get(uri);
+ }
+ else
+ {
+ URL url =
+ MediaActivator.getResources().getSoundURLForPath(uri);
+
+ if (url == null)
+ {
+ // Not found by the class loader. Perhaps it's a local file.
+ try
+ {
+ url = new URL(uri);
+ }
+ catch (MalformedURLException e)
+ {
+ //logger.error("The given uri could not be parsed.", e);
+ return null;
+ }
+ }
+
+ try
+ {
+ if(getDeviceConfiguration().getAudioSystem().equals(
+ DeviceConfiguration.AUDIO_SYSTEM_JAVASOUND))
+ {
+ audioClip = new JMFAudioClipImpl(url, this);
+ }
+ else if(getDeviceConfiguration().getAudioSystem().equals(
+ DeviceConfiguration.AUDIO_SYSTEM_PORTAUDIO))
+ {
+ audioClip = new PortAudioClipImpl(url, this);
+ }
+ else
+ return null;
+ }
+ catch (Throwable e)
+ {
+ // Cannot create audio to play
+ return null;
+ }
+
+ audioClips.put(uri, audioClip);
+ }
+ }
+
+ return audioClip;
+ }
+
+ /**
+ * Removes the given audio from the list of available audio clips.
+ *
+ * @param audioClip the audio to destroy
+ */
+ public void destroyAudio(SCAudioClip audioClip)
+ {
+ synchronized (audioClips) {
+ audioClips.remove(audioClip);
+ }
+ }
+
+ /**
+ * Enables or disables the sound in the application. If FALSE, we try to
+ * restore all looping sounds if any.
+ *
+ * @param isMute when TRUE disables the sound, otherwise enables the sound.
+ */
+ public void setMute(boolean isMute)
+ {
+ this.isMute = isMute;
+
+ for (SCAudioClipImpl audioClip : audioClips.values())
+ {
+ if (isMute)
+ {
+ audioClip.internalStop();
+ }
+ else if (audioClip.isLooping())
+ {
+ audioClip.playInLoop(audioClip.getLoopInterval());
+ }
+ }
+ }
+
+ /**
+ * Returns TRUE if the sound is currently disabled, FALSE otherwise.
+ * @return TRUE if the sound is currently disabled, FALSE otherwise
+ */
+ public boolean isMute()
+ {
+ return isMute;
+ }
+
+ /**
+ * The device configuration.
+ *
+ * @return the deviceConfiguration
+ */
+ public DeviceConfiguration getDeviceConfiguration()
+ {
+ return deviceConfiguration;
+ }
+}
diff --git a/src/net/java/sip/communicator/impl/media/notify/JMFAudioClipImpl.java b/src/net/java/sip/communicator/impl/media/notify/JMFAudioClipImpl.java
new file mode 100644
index 000000000..b277e3750
--- /dev/null
+++ b/src/net/java/sip/communicator/impl/media/notify/JMFAudioClipImpl.java
@@ -0,0 +1,201 @@
+/*
+ * SIP Communicator, 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.media.notify;
+
+import java.applet.*;
+import java.awt.event.*;
+import java.io.*;
+import java.lang.reflect.*;
+import java.net.*;
+import java.security.*;
+
+import javax.swing.*;
+
+import net.java.sip.communicator.service.audionotifier.*;
+
+/**
+ * Implementation of SCAudioClip.
+ *
+ * @author Yana Stamcheva
+ */
+public class JMFAudioClipImpl
+ extends SCAudioClipImpl
+ implements ActionListener
+
+{
+ private static Constructor acConstructor = null;
+
+ private final Timer playAudioTimer = new Timer(1000, null);
+
+ private final AudioClip audioClip;
+
+ private final AudioNotifierService audioNotifier;
+
+ /**
+ * Creates the audio clip and initialize the listener used from the
+ * loop timer.
+ *
+ * @param url the url pointing to the audio file
+ * @param audioNotifier the audio notify service
+ * @throws IOException cannot audio clip with supplied url.
+ */
+ public JMFAudioClipImpl(URL url, AudioNotifierService audioNotifier)
+ throws IOException
+ {
+ this.audioClip = createAppletAudioClip(url.openStream());
+ this.audioNotifier = audioNotifier;
+
+ this.playAudioTimer.addActionListener(this);
+ }
+
+ /**
+ * Plays this audio.
+ */
+ public void play()
+ {
+ if ((audioClip != null) && !audioNotifier.isMute())
+ audioClip.play();
+ }
+
+ /**
+ * Plays this audio in loop.
+ *
+ * @param interval the loop interval
+ */
+ public void playInLoop(int interval)
+ {
+ if(!audioNotifier.isMute())
+ {
+ if(interval == 0)
+ audioClip.loop();
+ else
+ {
+ //first play the audio and then start the timer and wait
+ audioClip.play();
+ playAudioTimer.setDelay(interval);
+ playAudioTimer.setRepeats(true);
+
+ playAudioTimer.start();
+ }
+ }
+
+ setLoopInterval(interval);
+ setIsLooping(true);
+ }
+
+ /**
+ * Stops this audio.
+ */
+ public void stop()
+ {
+ if (audioClip != null)
+ audioClip.stop();
+
+ if (isLooping())
+ {
+ playAudioTimer.stop();
+ setIsLooping(false);
+ }
+ }
+
+ /**
+ * Stops this audio without setting the isLooping property in the case of
+ * a looping audio. The AudioNotifier uses this method to stop the audio
+ * when setMute(true) is invoked. This allows us to restore all looping
+ * audios when the sound is restored by calling setMute(false).
+ */
+ public void internalStop()
+ {
+ if (audioClip != null)
+ audioClip.stop();
+
+ if (isLooping())
+ playAudioTimer.stop();
+ }
+
+ /**
+ * Creates an AppletAudioClip.
+ *
+ * @param inputstream the audio input stream
+ * @throws IOException
+ */
+ private static AudioClip createAppletAudioClip(InputStream inputstream)
+ throws IOException
+ {
+ if (acConstructor == null)
+ {
+ try
+ {
+ acConstructor
+ = AccessController.doPrivileged(
+ new PrivilegedExceptionAction>()
+ {
+ public Constructor run()
+ throws ClassNotFoundException,
+ NoSuchMethodException,
+ SecurityException
+ {
+ return createAcConstructor();
+ }
+ });
+ }
+ catch (PrivilegedActionException paex)
+ {
+ throw
+ new IOException(
+ "Failed to get AudioClip constructor: "
+ + paex.getException());
+ }
+ }
+
+ try
+ {
+ return acConstructor.newInstance(inputstream);
+ }
+ catch (Exception ex)
+ {
+ throw new IOException("Failed to construct the AudioClip: " + ex);
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ private static Constructor createAcConstructor()
+ throws ClassNotFoundException,
+ NoSuchMethodException,
+ SecurityException
+ {
+ Class> class1;
+ try
+ {
+ class1
+ = Class.forName(
+ "com.sun.media.sound.JavaSoundAudioClip",
+ true,
+ ClassLoader.getSystemClassLoader());
+ }
+ catch (ClassNotFoundException cnfex)
+ {
+ class1
+ = Class.forName("sun.audio.SunAudioClip", true, null);
+ }
+ return
+ (Constructor) class1.getConstructor(InputStream.class);
+ }
+
+ /**
+ * Plays an audio clip. Used in the playAudioTimer to play an audio in loop.
+ * @param e the event.
+ */
+ public void actionPerformed(ActionEvent e)
+ {
+ if (audioClip != null)
+ {
+ audioClip.stop();
+ audioClip.play();
+ }
+ }
+}
diff --git a/src/net/java/sip/communicator/impl/media/notify/PortAudioClipImpl.java b/src/net/java/sip/communicator/impl/media/notify/PortAudioClipImpl.java
new file mode 100644
index 000000000..b54277daa
--- /dev/null
+++ b/src/net/java/sip/communicator/impl/media/notify/PortAudioClipImpl.java
@@ -0,0 +1,204 @@
+/*
+ * SIP Communicator, 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.media.notify;
+
+import java.io.*;
+import java.net.*;
+
+import javax.sound.sampled.*;
+
+import net.java.sip.communicator.impl.media.protocol.portaudio.*;
+import net.java.sip.communicator.impl.media.device.*;
+import net.java.sip.communicator.util.*;
+
+/**
+ * Implementation of SCAudioClip using PortAudio.
+ *
+ * @author Damian Minkov
+ */
+public class PortAudioClipImpl
+ extends SCAudioClipImpl
+ implements PropertyChangeListener
+{
+ private static final Logger logger
+ = Logger.getLogger(PortAudioClipImpl.class);
+
+ private final AudioNotifierServiceImpl audioNotifier;
+
+ private Thread playThread = new Thread(new PlayThread());
+
+ private boolean started = false;
+
+ private long portAudioStream = 0;
+
+ private URL url = null;
+
+ /**
+ * Creates the audio clip and initialize the listener used from the
+ * loop timer.
+ *
+ * @param url the url pointing to the audio file
+ * @param audioNotifier the audio notify service
+ * @throws IOException cannot audio clip with supplied url.
+ */
+ public PortAudioClipImpl(URL url, AudioNotifierServiceImpl audioNotifier)
+ throws IOException
+ {
+ this.audioNotifier = audioNotifier;
+ this.url = url;
+
+ audioNotifier.getDeviceConfiguration().addPropertyChangeListener(this);
+ }
+
+ /**
+ * Plays this audio.
+ */
+ public void play()
+ {
+ if ((url != null) && !audioNotifier.isMute())
+ {
+ started = true;
+ playThread.start();
+ }
+ }
+
+ /**
+ * Plays this audio in loop.
+ *
+ * @param interval the loop interval
+ */
+ public void playInLoop(int interval)
+ {
+ setLoopInterval(interval);
+ setIsLooping(true);
+
+ if(!audioNotifier.isMute())
+ {
+ started = true;
+ playThread.start();
+ }
+ }
+
+ /**
+ * Stops this audio.
+ */
+ public void stop()
+ {
+ if (url != null)
+ started = false;
+ setIsLooping(false);
+ }
+
+ /**
+ * Stops this audio without setting the isLooping property in the case of
+ * a looping audio. The AudioNotifier uses this method to stop the audio
+ * when setMute(true) is invoked. This allows us to restore all looping
+ * audios when the sound is restored by calling setMute(false).
+ */
+ public void internalStop()
+ {
+ if (url != null)
+ started = false;
+ }
+
+ /**
+ * Notified when device configuration has changed.
+ * @param evt the event of the change
+ */
+ public void propertyChange(PropertyChangeEvent evt)
+ {
+ if(evt.getPropertyName().equals(
+ DeviceConfiguration.AUDIO_NOTIFY_DEVICE))
+ {
+ // make the stream 0, to be sure next time the new
+ // device will be used
+ portAudioStream = 0;
+ }
+ }
+
+ private class PlayThread
+ implements Runnable
+ {
+ byte[] buffer = new byte[1024];
+
+ public void run()
+ {
+ try
+ {
+ while(true)
+ {
+ if (portAudioStream == 0)
+ {
+ long streamParameters
+ = PortAudio.PaStreamParameters_new(
+ PortAudioStream.getDeviceIndexFromLocator(
+ audioNotifier.getDeviceConfiguration().
+ getAudioNotifyDevice().getLocator()),
+ 2,
+ PortAudio.SAMPLE_FORMAT_INT16);
+
+ portAudioStream
+ = PortAudio.Pa_OpenStream(
+ 0,
+ streamParameters,
+ 48000,
+ PortAudio.FRAMES_PER_BUFFER_UNSPECIFIED,
+ PortAudio.STREAM_FLAGS_NO_FLAG,
+ null);
+
+ PortAudio.Pa_StartStream(portAudioStream);
+ }
+
+ AudioInputStream audioStream =
+ AudioSystem.getAudioInputStream(url);
+
+ if(!started)
+ {
+ PortAudio.Pa_CloseStream(portAudioStream);
+ return;
+ }
+
+ int read = 0;
+ while((read = audioStream.read(buffer)) != -1)
+ {
+ PortAudio.Pa_WriteStream(
+ portAudioStream,
+ buffer,
+ buffer.length/audioStream.getFormat().getFrameSize());
+ }
+
+ if(!isLooping())
+ {
+ PortAudio.Pa_CloseStream(portAudioStream);
+ break;
+ }
+ else
+ {
+ Thread.sleep(getLoopInterval());
+ }
+ }
+ }
+ catch (PortAudioException e)
+ {
+ logger.error(
+ "Cannot open portaudio device for notifications", e);
+ }
+ catch (IOException e)
+ {
+ logger.error("Error reading from audio resource", e);
+ }
+ catch (InterruptedException e)
+ {
+ logger.error("Cannot wait the interval between plays", e);
+ }
+ catch (UnsupportedAudioFileException e)
+ {
+ logger.error("Unknown file format", e);
+ }
+ }
+ }
+}
diff --git a/src/net/java/sip/communicator/impl/media/notify/SCAudioClipImpl.java b/src/net/java/sip/communicator/impl/media/notify/SCAudioClipImpl.java
new file mode 100644
index 000000000..e925494fa
--- /dev/null
+++ b/src/net/java/sip/communicator/impl/media/notify/SCAudioClipImpl.java
@@ -0,0 +1,94 @@
+/*
+ * SIP Communicator, 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.media.notify;
+
+import net.java.sip.communicator.service.audionotifier.*;
+
+/**
+ * Common properties impl for SCAudioClip.
+ *
+ * @author Damian Minkov
+ */
+public abstract class SCAudioClipImpl
+ implements SCAudioClip
+{
+ private boolean isLooping;
+
+ private int loopInterval;
+
+ private boolean isInvalid;
+
+ /**
+ * Returns TRUE if this audio is invalid, FALSE otherwise.
+ *
+ * @return TRUE if this audio is invalid, FALSE otherwise
+ */
+ public boolean isInvalid()
+ {
+ return isInvalid;
+ }
+
+ /**
+ * Marks this audio as invalid or not.
+ *
+ * @param isInvalid TRUE to mark this audio as invalid, FALSE otherwise
+ */
+ public void setInvalid(boolean isInvalid)
+ {
+ this.setIsInvalid(isInvalid);
+ }
+
+ /**
+ * Returns TRUE if this audio is currently playing in loop, FALSE otherwise.
+ * @return TRUE if this audio is currently playing in loop, FALSE otherwise.
+ */
+ public boolean isLooping()
+ {
+ return isLooping;
+ }
+
+ /**
+ * Returns the loop interval if this audio is looping.
+ * @return the loop interval if this audio is looping
+ */
+ public int getLoopInterval()
+ {
+ return loopInterval;
+ }
+
+ /**
+ * @param isLooping the isLooping to set
+ */
+ public void setIsLooping(boolean isLooping)
+ {
+ this.isLooping = isLooping;
+ }
+
+ /**
+ * @param loopInterval the loopInterval to set
+ */
+ public void setLoopInterval(int loopInterval)
+ {
+ this.loopInterval = loopInterval;
+ }
+
+ /**
+ * @param isInvalid the isInvalid to set
+ */
+ public void setIsInvalid(boolean isInvalid)
+ {
+ this.isInvalid = isInvalid;
+ }
+
+ /**
+ * Stops this audio without setting the isLooping property in the case of
+ * a looping audio. The AudioNotifier uses this method to stop the audio
+ * when setMute(true) is invoked. This allows us to restore all looping
+ * audios when the sound is restored by calling setMute(false).
+ */
+ public abstract void internalStop();
+}
diff --git a/src/net/java/sip/communicator/impl/media/protocol/portaudio/DataSource.java b/src/net/java/sip/communicator/impl/media/protocol/portaudio/DataSource.java
index 179493550..846b21d13 100644
--- a/src/net/java/sip/communicator/impl/media/protocol/portaudio/DataSource.java
+++ b/src/net/java/sip/communicator/impl/media/protocol/portaudio/DataSource.java
@@ -12,10 +12,12 @@
import javax.media.protocol.*;
/**
+ * Portaudio datasource.
+ *
* @author Damian Minkov
*/
public class DataSource
- extends PushBufferDataSource
+ extends PullBufferDataSource
{
private boolean connected = false;
@@ -27,6 +29,7 @@ public class DataSource
/**
* Connect the datasource
+ * @throws IOException if we cannot initialize portaudio.
*/
public void connect()
throws IOException
@@ -80,7 +83,9 @@ public String getContentType()
/**
* Return required control from the Control[] array
- * if exists, that is
+ * if exists.
+ * @param controlType the control we are interested in.
+ * @return the object that implements the control, or null.
*/
public Object getControl(String controlType)
{
@@ -101,7 +106,7 @@ public Object getControl(String controlType)
/**
* Gives control information to the caller
- *
+ * @return the collection of object controls.
*/
public Object[] getControls()
{
@@ -120,22 +125,35 @@ public Time getDuration()
}
/**
- * Returns an array of PushBufferStream containing all the streams
+ * Returns an array of PullBufferStream containing all the streams
* i.e. only one in our case : only sound.
*
* If no stream actually exists, instantiate one on the fly.
*
* @return Array of one stream
*/
- public PushBufferStream[] getStreams()
+ public PullBufferStream[] getStreams()
{
- if (streams == null)
- streams = new PortAudioStream[] { new PortAudioStream() };
+ try
+ {
+ if (streams == null)
+ streams = new PortAudioStream[]
+ {new PortAudioStream(getLocator())};
+ }
+ catch (Exception e)
+ {
+ e.printStackTrace();
+ // if we cannot parse desired device we will not open a stream
+ // so there is no stream returned
+ streams = new PortAudioStream[0];
+ }
+
return streams;
}
/**
* Start the datasource and the underlying stream
+ * @throws IOException
*/
public void start()
throws IOException
@@ -146,21 +164,14 @@ public void start()
if (!connected)
throw new IOException("DataSource must be connected");
- try
- {
- streams[0].start();
- }
- catch (PortAudioException paex)
- {
- IOException ioex = new IOException();
- ioex.initCause(paex);
- throw ioex;
- }
+ streams[0].start();
+
started = true;
}
/**
* Stop the datasource and it's underlying stream
+ * @throws IOException
*/
public void stop()
throws IOException
@@ -168,16 +179,8 @@ public void stop()
if (!started)
return;
- try
- {
- streams[0].stop();
- }
- catch (PortAudioException paex)
- {
- IOException ioex = new IOException();
- ioex.initCause(paex);
- throw ioex;
- }
+ streams[0].stop();
+
started = false;
}
}
diff --git a/src/net/java/sip/communicator/impl/media/protocol/portaudio/PortAudio.java b/src/net/java/sip/communicator/impl/media/protocol/portaudio/PortAudio.java
index 35232979f..64081f69a 100644
--- a/src/net/java/sip/communicator/impl/media/protocol/portaudio/PortAudio.java
+++ b/src/net/java/sip/communicator/impl/media/protocol/portaudio/PortAudio.java
@@ -66,12 +66,29 @@ public static native void Pa_WriteStream(
long frames)
throws PortAudioException;
+ public static native void Pa_ReadStream(
+ long stream, byte[] buffer, long frames)
+ throws PortAudioException;
+
+ public static native long Pa_GetStreamReadAvailable(long stream);
+
+ public static native long Pa_GetStreamWriteAvailable(long stream);
+
+ public static native int Pa_GetSampleSize(long format);
+
+ public static native boolean Pa_IsFormatSupported(
+ long inputParameters,
+ long outputParameters,
+ double sampleRate);
+
public static native int PaDeviceInfo_getMaxInputChannels(long deviceInfo);
public static native int PaDeviceInfo_getMaxOutputChannels(long deviceInfo);
public static native String PaDeviceInfo_getName(long deviceInfo);
+ public static native double PaDeviceInfo_getDefaultSampleRate(long deviceInfo);
+
public static native long PaStreamParameters_new(
int deviceIndex,
int channelCount,
diff --git a/src/net/java/sip/communicator/impl/media/protocol/portaudio/PortAudioStream.java b/src/net/java/sip/communicator/impl/media/protocol/portaudio/PortAudioStream.java
index 7a589ff69..07ac1462e 100644
--- a/src/net/java/sip/communicator/impl/media/protocol/portaudio/PortAudioStream.java
+++ b/src/net/java/sip/communicator/impl/media/protocol/portaudio/PortAudioStream.java
@@ -14,22 +14,28 @@
import javax.media.format.*;
import javax.media.protocol.*;
+import net.java.sip.communicator.util.*;
+
/**
* @author Damian Minkov
*/
public class PortAudioStream
- implements PushBufferStream,
- PortAudioStreamCallback
+ implements PullBufferStream
{
-// private static final Logger logger
-// = Logger.getLogger(PortAudioStream.class);
+ private static final Logger logger =
+ Logger.getLogger(PortAudioStream.class);
+
+ /**
+ * The locatro prefix used when creating or parsing MediaLocators.
+ */
+ public static final String LOCATOR_PREFIX = "portaudio:#";
private final static ContentDescriptor cd =
new ContentDescriptor(ContentDescriptor.RAW);
- private BufferTransferHandler transferHandler;
+
private Control[] controls = new Control[0];
- public static AudioFormat audioFormat = new AudioFormat(
+ private static AudioFormat audioFormat = new AudioFormat(
AudioFormat.LINEAR,
44100,
16,
@@ -40,53 +46,47 @@ public class PortAudioStream
Format.NOT_SPECIFIED,
Format.byteArray);
+ private boolean started;
private long stream = 0;
- private ByteBuffer bufferToProcess = null;
-
private int seqNo = 0;
+ private int sampleSize = 1;
+
+ final private int deviceIndex;
+
/**
- * Returns the supported formats by this stream.
- * @return supported formats
+ * Creates new stream.
+ * @param locator the locator to extract the device index from it.
*/
- public Format getFormat()
+ public PortAudioStream(MediaLocator locator)
{
- return audioFormat;
+ this.deviceIndex = getDeviceIndexFromLocator(locator);
}
/**
- *
- * @param buffer
- * @throws IOException
+ * Return the formats supported by the datasource stream
+ * corresponding the maximum input channels.
+ *
+ * @return the supported formats.
*/
- public void read(Buffer buffer)
- throws IOException
+ public static Format[] getFormats()
{
- byte[] barr = new byte[bufferToProcess.remaining()];
-
- bufferToProcess.get(barr);
-
- buffer.setTimeStamp(System.nanoTime());
- buffer.setData(barr);
- buffer.setSequenceNumber(seqNo);
- buffer.setLength(barr.length);
- buffer.setFlags(0);
- buffer.setHeader(null);
- seqNo++;
+ return new Format[]{audioFormat};
}
/**
- *
- * @param transferHandler
+ * Returns the supported format by this stream.
+ * @return supported formats
*/
- public void setTransferHandler(BufferTransferHandler transferHandler)
+ public Format getFormat()
{
- this.transferHandler = transferHandler;
+ return audioFormat;
}
/**
* We are providing access to raw data
+ * @return RAW content descriptor.
*/
public ContentDescriptor getContentDescriptor()
{
@@ -95,6 +95,7 @@ public ContentDescriptor getContentDescriptor()
/**
* We are streaming.
+ * @return unknown content length.
*/
public long getContentLength()
{
@@ -103,7 +104,7 @@ public long getContentLength()
/**
* The stream never ends.
- *
+ * @return true if the end of the stream has been reached.
*/
public boolean endOfStream()
{
@@ -112,7 +113,7 @@ public boolean endOfStream()
/**
* Gives control information to the caller
- *
+ * @return no controls currently supported.
*/
public Object[] getControls()
{
@@ -121,7 +122,10 @@ public Object[] getControls()
/**
* Return required control from the Control[] array
- * if exists, that is
+ * if exists
+ *
+ * @param controlType the control class name.
+ * @return the object that implements the control, or null.
*/
public Object getControl(String controlType)
{
@@ -145,96 +149,130 @@ public Object getControl(String controlType)
}
}
- public void finishedCallback()
- {
- }
-
/**
* Starts the stream operation
*/
void start()
- throws PortAudioException
{
- if (this.stream != 0)
- throw new IllegalStateException("stream");
-
- long stream = createStream();
-
- try
- {
- PortAudio.Pa_StartStream(stream);
- this.stream = stream;
- }
- catch (PortAudioException startException)
+ synchronized (this)
{
+ this.started = true;
try
{
- PortAudio.Pa_CloseStream(stream);
+ PortAudio.Pa_StartStream(getStream());
}
- catch (PortAudioException closeException)
+ catch (PortAudioException paex)
{
- /*
- * We couldn't start the stream so we're closing it just to free
- * the native resources but if that fails as well, we cannot do
- * anything about it. Besides, we have to rethrow the exception
- * which was thrown on start.
- */
+ paex.printStackTrace();
}
-
- throw startException;
}
}
void stop()
- throws PortAudioException
{
- if (stream != 0)
+ synchronized (this)
{
- PortAudio.Pa_CloseStream(stream);
- stream = 0;
+ this.started = false;
+
+ try
+ {
+ PortAudio.Pa_CloseStream(getStream());
+ stream = 0;
+ }
+ catch (PortAudioException paex)
+ {
+ paex.printStackTrace();
+ }
}
}
- private long createStream()
+ private long getStream()
throws PortAudioException
{
- int deviceCount = PortAudio.Pa_GetDeviceCount();
- int deviceIndex = 0;
-
- for (; deviceIndex < deviceCount; deviceIndex++)
+ if (stream == 0)
{
- long deviceInfo = PortAudio.Pa_GetDeviceInfo(deviceIndex);
-
- if ((PortAudio.PaDeviceInfo_getMaxInputChannels(deviceInfo) == 2)
- && (PortAudio.PaDeviceInfo_getMaxOutputChannels(deviceInfo) == 0)
- && PortAudio.PaDeviceInfo_getName(deviceInfo)
- .contains("Analog"))
- break;
+ long streamParameters
+ = PortAudio.PaStreamParameters_new(
+ deviceIndex,
+ audioFormat.getChannels(),
+ PortAudio.SAMPLE_FORMAT_INT16);
+
+ stream
+ = PortAudio.Pa_OpenStream(
+ streamParameters,
+ 0,
+ audioFormat.getSampleRate(),
+ PortAudio.FRAMES_PER_BUFFER_UNSPECIFIED,
+ PortAudio.STREAM_FLAGS_NO_FLAG,
+ null);
+ sampleSize =
+ PortAudio.Pa_GetSampleSize(PortAudio.SAMPLE_FORMAT_INT16);
}
- long streamParameters
- = PortAudio.PaStreamParameters_new(
- deviceIndex,
- 1,
- PortAudio.SAMPLE_FORMAT_INT16);
-
- return
- PortAudio.Pa_OpenStream(
- streamParameters,
- 0,
- 44100,
- PortAudio.FRAMES_PER_BUFFER_UNSPECIFIED,
- PortAudio.STREAM_FLAGS_NO_FLAG,
- this);
+ return stream;
}
- public int callback(ByteBuffer input, ByteBuffer output)
+ /**
+ * Query if the next read will block.
+ * @return true if a read will block.
+ */
+ public boolean willReadBlock()
{
- bufferToProcess = input;
+ try
+ {
+ return PortAudio.Pa_GetStreamReadAvailable(getStream()) == 0;
+ }
+ catch (PortAudioException ex)
+ {
+ logger.error("Cannot get read available", ex);
+ return true;
+ }
+ }
- if (transferHandler != null)
- transferHandler.transferData(this);
+ /**
+ * Block and read a buffer from the stream.
+ * @param buffer should be non-null.
+ * @throws IOException Thrown if an error occurs while reading.
+ */
+ public synchronized void read(Buffer buffer)
+ throws IOException
+ {
+ if(!this.started)
+ return;
- return RESULT_CONTINUE;
+ try
+ {
+ int canread =
+ (int)PortAudio.Pa_GetStreamReadAvailable(getStream());
+ if(canread < 1)
+ canread = 512;
+
+ byte[] bytebuff = new byte[canread*sampleSize];
+
+ PortAudio.Pa_ReadStream(getStream(), bytebuff, canread);
+
+ buffer.setTimeStamp(System.nanoTime());
+ buffer.setData(bytebuff);
+ buffer.setSequenceNumber(seqNo);
+ buffer.setLength(bytebuff.length);
+ buffer.setFlags(0);
+ buffer.setHeader(null);
+ seqNo++;
+ }
+ catch (PortAudioException e)
+ {
+ logger.error("", e);
+ }
+ }
+
+ /**
+ * Extracts the device index from the locator.
+ * @param locator the locator containing the device index.
+ * @return the extracted device index.
+ */
+ public static int getDeviceIndexFromLocator(MediaLocator locator)
+ {
+ return Integer.parseInt(locator.toExternalForm().replace(
+ PortAudioStream.LOCATOR_PREFIX, ""));
}
}
diff --git a/src/net/java/sip/communicator/impl/media/renderer/audio/PortAudioRenderer.java b/src/net/java/sip/communicator/impl/media/renderer/audio/PortAudioRenderer.java
index f3ebbcf65..dd3040a40 100644
--- a/src/net/java/sip/communicator/impl/media/renderer/audio/PortAudioRenderer.java
+++ b/src/net/java/sip/communicator/impl/media/renderer/audio/PortAudioRenderer.java
@@ -12,6 +12,7 @@
import net.java.sip.communicator.impl.media.protocol.portaudio.*;
/**
+ * Portaudio renderer.
*
* @author Damian Minkov
*/
@@ -22,41 +23,55 @@ public class PortAudioRenderer
Logger.getLogger(PortAudioRenderer.class);
private static final String name = "PortAudio Renderer";
- public static Format[] supportedInputFormats = new Format[]
- {
- new AudioFormat(
+
+ private static AudioFormat audioFormat =
+ new AudioFormat(
AudioFormat.LINEAR,
- 44100,
+ 48000,
16,
- 1,
+ 2,
AudioFormat.LITTLE_ENDIAN,
AudioFormat.SIGNED,
16,
Format.NOT_SPECIFIED,
- Format.byteArray)
+ Format.byteArray);
+
+ /**
+ * The supported input formats.
+ */
+ public static Format[] supportedInputFormats = new Format[]
+ {
+ audioFormat
};
- protected Object [] controls = new Object[0];
+
+ private Object [] controls = new Object[0];
private AudioFormat inputFormat;
private long stream = 0;
- public PortAudioRenderer()
- {
- try
- {
- PortAudio.initialize();
- }
- catch (PortAudioException e)
- {
- logger.error("Cannot Initialize portaudio", e);
- }
- }
+ boolean started = false;
+
+ private WriterThread writerThread = null;
+ private final Object bufferSync = new Object();
+ private byte[] buffer = null;
+
+ private static int deviceIndex = -1;
+ /**
+ * Lists the input formats supported by this Renderer.
+ * @return An array of Format objects that represent
+ * the input formats supported by this Renderer.
+ */
public Format[] getSupportedInputFormats()
{
- return supportedInputFormats;
+ return new Format[]{audioFormat};
}
+ /**
+ * Sets the Format of the input data.
+ * @param format the format to set.
+ * @return The Format that was set.
+ */
public Format setInputFormat(Format format)
{
if(!(format instanceof AudioFormat))
@@ -69,10 +84,16 @@ public Format setInputFormat(Format format)
return inputFormat;
}
+ /**
+ * Initiates the rendering process.
+ * When start is called, the renderer begins rendering
+ * any data available in its internal buffers.
+ */
public void start()
{
try
- {
+ {
+ started = true;
PortAudio.Pa_StartStream(stream);
}
catch (PortAudioException e)
@@ -81,34 +102,52 @@ public void start()
}
}
+ /**
+ * Halts the rendering process.
+ */
public void stop()
{
- try
+ synchronized(bufferSync)
{
- PortAudio.Pa_CloseStream(stream);
- }
- catch (PortAudioException e)
- {
- logger.error("Closing portaudio stream failed", e);
+ try
+ {
+ PortAudio.Pa_CloseStream(stream);
+ writerThread = null;
+ started = false;
+ }
+ catch (PortAudioException e)
+ {
+ logger.error("Closing portaudio stream failed", e);
+ }
}
}
+ /**
+ * Processes the data and renders it
+ * to the output device represented by this Renderer.
+ * @param inputBuffer the input data.
+ * @return BUFFER_PROCESSED_OK if the processing is successful.
+ */
public int process(Buffer inputBuffer)
{
- try
+ synchronized(bufferSync)
{
byte[] buff = new byte[inputBuffer.getLength()];
- System.arraycopy(
- (byte[])inputBuffer.getData(),
- inputBuffer.getOffset(),
- buff,
- 0,
- buff.length);
- PortAudio.Pa_WriteStream(stream, buff, buff.length/2);
+ System.arraycopy(
+ (byte[])inputBuffer.getData(),
+ inputBuffer.getOffset(),
+ buff,
+ 0,
+ buff.length);
+
+ buffer = buff;
+ bufferSync.notifyAll();
}
- catch (PortAudioException e)
+
+ if(writerThread == null)
{
- logger.error("Error write to device!", e);
+ writerThread = new WriterThread();
+ writerThread.start();
}
return BUFFER_PROCESSED_OK;
@@ -116,12 +155,18 @@ public int process(Buffer inputBuffer)
/**
* Returns the name of the pluging.
+ * @return A String that contains the descriptive name of the plug-in.
*/
public String getName()
{
return name;
}
+ /**
+ * Opens the device and stream that we will use to render data.
+ * @throws ResourceUnavailableException If required resources cannot
+ * be opened/created.
+ */
public void open()
throws ResourceUnavailableException
{
@@ -129,31 +174,17 @@ public void open()
{
if (stream == 0)
{
- int deviceCount = PortAudio.Pa_GetDeviceCount();
- int deviceIndex = 0;
-
- for (; deviceIndex < deviceCount; deviceIndex++)
- {
- long deviceInfo = PortAudio.Pa_GetDeviceInfo(deviceIndex);
-
- if ((PortAudio.PaDeviceInfo_getMaxOutputChannels(deviceInfo) == 2)
- && (PortAudio.PaDeviceInfo_getMaxInputChannels(deviceInfo) == 2)
- && PortAudio.PaDeviceInfo_getName(deviceInfo)
- .contains("Analog"))
- break;
- }
-
long streamParameters
= PortAudio.PaStreamParameters_new(
deviceIndex,
- 1,
+ audioFormat.getChannels(),
PortAudio.SAMPLE_FORMAT_INT16);
stream
= PortAudio.Pa_OpenStream(
0,
streamParameters,
- 44100,
+ audioFormat.getSampleRate(),
PortAudio.FRAMES_PER_BUFFER_UNSPECIFIED,
PortAudio.STREAM_FLAGS_NO_FLAG,
null);
@@ -166,14 +197,14 @@ public void open()
}
/**
- *
+ * Closes the plug-in.
*/
public void close()
{
}
/**
- *
+ * Resets the state of the plug-in.
*/
public void reset()
{
@@ -181,7 +212,7 @@ public void reset()
/**
* Gives control information to the caller
- *
+ * @return the collection of object controls.
*/
public Object[] getControls()
{
@@ -190,7 +221,9 @@ public Object[] getControls()
/**
* Return required control from the Control[] array
- * if exists, that is
+ * if exists.
+ * @param controlType the control we are interested in.
+ * @return the object that implements the control, or null.
*/
public Object getControl(String controlType)
{
@@ -213,4 +246,60 @@ public Object getControl(String controlType)
return null;
}
}
+
+ /**
+ * Used to set the device index used by the renderer common for all
+ * instances of it.
+ * @param locator the locator containing the device index.
+ */
+ public static void setDevice(MediaLocator locator)
+ {
+ deviceIndex = PortAudioStream.getDeviceIndexFromLocator(locator);
+ }
+
+ /**
+ * Writes data to the portaudio stream. If there is no data for
+ * particular time write some silence so we wont hear some cracks in
+ * the sound.
+ */
+ private class WriterThread
+ extends Thread
+ {
+
+ public WriterThread()
+ {
+ setName("Portaudio Renderer");
+ }
+
+ public void run()
+ {
+ while(true)
+ {
+ try
+ {
+ synchronized(bufferSync)
+ {
+ // put some silence if there is no data
+ if(buffer == null)
+ {
+ buffer = new byte[1024];
+ }
+
+ PortAudio.Pa_WriteStream(
+ stream, buffer, buffer.length/4);
+
+ buffer = null;
+ bufferSync.wait(90);
+ }
+
+ if(!started)
+ return;
+ }
+ catch (Exception e)
+ {
+ logger.error("writing to stream", e);
+ }
+ }
+ }
+ }
}