diff --git a/app/alarm/audio-annunciator/.classpath b/app/alarm/audio-annunciator/.classpath
new file mode 100644
index 0000000000..948256b10b
--- /dev/null
+++ b/app/alarm/audio-annunciator/.classpath
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/alarm/audio-annunciator/build.xml b/app/alarm/audio-annunciator/build.xml
new file mode 100644
index 0000000000..019188b2fa
--- /dev/null
+++ b/app/alarm/audio-annunciator/build.xml
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/alarm/audio-annunciator/pom.xml b/app/alarm/audio-annunciator/pom.xml
new file mode 100644
index 0000000000..085313f76a
--- /dev/null
+++ b/app/alarm/audio-annunciator/pom.xml
@@ -0,0 +1,31 @@
+
+
+ 4.0.0
+
+ org.phoebus
+ app-alarm
+ 4.7.4-SNAPSHOT
+
+ app-alarm-audio-annunciator
+
+
+ 17
+ 17
+
+
+
+
+ org.phoebus
+ app-alarm-ui
+ 4.7.4-SNAPSHOT
+
+
+ org.openjfx
+ javafx-media
+ ${openjfx.version}
+
+
+
+
\ No newline at end of file
diff --git a/app/alarm/audio-annunciator/src/main/java/org/phoebus/applications/alarm/audio/annunciator/AudioAnnunciator.java b/app/alarm/audio-annunciator/src/main/java/org/phoebus/applications/alarm/audio/annunciator/AudioAnnunciator.java
new file mode 100644
index 0000000000..7d9600b967
--- /dev/null
+++ b/app/alarm/audio-annunciator/src/main/java/org/phoebus/applications/alarm/audio/annunciator/AudioAnnunciator.java
@@ -0,0 +1,84 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Oak Ridge National Laboratory.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+package org.phoebus.applications.alarm.audio.annunciator;
+
+import javafx.scene.media.AudioClip;
+import javafx.scene.media.Media;
+import javafx.scene.media.MediaPlayer;
+import javafx.util.Duration;
+import org.phoebus.applications.alarm.ui.annunciator.Annunciator;
+import org.phoebus.applications.alarm.ui.annunciator.AnnunciatorMessage;
+
+import java.util.List;
+
+/**
+ * Annunciator class. Uses Audio files to annunciate passed messages.
+ *
+ * @author Kunal Shroff
+ */
+@SuppressWarnings("nls")
+public class AudioAnnunciator implements Annunciator {
+ private final MediaPlayer alarmSound;
+ private final MediaPlayer minorAlarmSound;
+ private final MediaPlayer majorAlarmSound;
+ private final MediaPlayer invalidAlarmSound;
+ private final MediaPlayer undefinedAlarmSound;
+
+ /**
+ * Constructor
+ */
+ public AudioAnnunciator() {
+ alarmSound = new MediaPlayer(new Media(Preferences.alarm_sound_url));
+ minorAlarmSound = new MediaPlayer(new Media(Preferences.minor_alarm_sound_url));
+ majorAlarmSound = new MediaPlayer(new Media(Preferences.major_alarm_sound_url));
+ invalidAlarmSound = new MediaPlayer(new Media(Preferences.invalid_alarm_sound_url));
+ undefinedAlarmSound = new MediaPlayer(new Media(Preferences.undefined_alarm_sound_url));
+ // configure the media players for the different alarm sounds
+ List.of(alarmSound, minorAlarmSound, majorAlarmSound, invalidAlarmSound, undefinedAlarmSound)
+ .forEach(sound -> {
+ sound.setStopTime(Duration.seconds(Preferences.max_alarm_duration));
+ sound.setVolume(Preferences.volume);
+ });
+ }
+
+ /**
+ * Annunciate the message.
+ *
+ * @param message Message text
+ */
+ @Override
+ public void speak(final AnnunciatorMessage message) {
+ switch (message.severity) {
+ case MINOR -> speakAlone(minorAlarmSound);
+ case MAJOR -> speakAlone(majorAlarmSound);
+ case INVALID -> speakAlone(invalidAlarmSound);
+ case UNDEFINED -> speakAlone(undefinedAlarmSound);
+ default -> speakAlone(alarmSound);
+ }
+ }
+
+ synchronized private void speakAlone(MediaPlayer alarm) {
+ List.of(alarmSound, minorAlarmSound, majorAlarmSound, invalidAlarmSound, undefinedAlarmSound)
+ .forEach(sound -> {
+ sound.stop();
+ });
+ alarm.play();
+ }
+
+ /**
+ * Deallocates the voice.
+ */
+ @Override
+ public void shutdown() {
+ List.of(alarmSound, minorAlarmSound, majorAlarmSound, invalidAlarmSound, undefinedAlarmSound)
+ .forEach(sound -> {
+ sound.stop();
+ sound.dispose();
+ });
+ }
+}
diff --git a/app/alarm/audio-annunciator/src/main/java/org/phoebus/applications/alarm/audio/annunciator/Preferences.java b/app/alarm/audio-annunciator/src/main/java/org/phoebus/applications/alarm/audio/annunciator/Preferences.java
new file mode 100644
index 0000000000..cbbf2bf89e
--- /dev/null
+++ b/app/alarm/audio-annunciator/src/main/java/org/phoebus/applications/alarm/audio/annunciator/Preferences.java
@@ -0,0 +1,63 @@
+/*******************************************************************************
+ * Copyright (c) 2010-2022 Oak Ridge National Laboratory.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ ******************************************************************************/
+package org.phoebus.applications.alarm.audio.annunciator;
+
+
+import org.phoebus.framework.preferences.AnnotatedPreferences;
+import org.phoebus.framework.preferences.Preference;
+import org.phoebus.framework.preferences.PreferencesReader;
+
+/**
+ * Helper for reading preference settings
+ *
+ * @author Kunal Shroff
+ */
+@SuppressWarnings("nls")
+public class Preferences {
+
+ /**
+ * Setting
+ */
+ @Preference
+ public static String alarm_sound_url;
+ @Preference
+ public static String minor_alarm_sound_url;
+ @Preference
+ public static String major_alarm_sound_url;
+ @Preference
+ public static String invalid_alarm_sound_url;
+ @Preference
+ public static String undefined_alarm_sound_url;
+ @Preference
+ public static int volume;
+ @Preference
+ public static int max_alarm_duration;
+
+ static {
+ final PreferencesReader prefs = AnnotatedPreferences.initialize(AudioAnnunciator.class, Preferences.class, "/audio_annunciator_preferences.properties");
+ alarm_sound_url = useLocalResourceIfUnspecified(alarm_sound_url);
+ minor_alarm_sound_url = useLocalResourceIfUnspecified(minor_alarm_sound_url);
+ major_alarm_sound_url = useLocalResourceIfUnspecified(major_alarm_sound_url);
+ invalid_alarm_sound_url = useLocalResourceIfUnspecified(invalid_alarm_sound_url);
+ undefined_alarm_sound_url = useLocalResourceIfUnspecified(undefined_alarm_sound_url);
+ }
+
+ private static String useLocalResourceIfUnspecified(String alarmResource) {
+ if (alarmResource == null || alarmResource.isEmpty()) {
+ // If only the alarm sound url is set, in a case where we want to use the same alarm sound for all severties
+ if (!alarm_sound_url.isEmpty()) {
+ return alarm_sound_url;
+ } else {
+ return Preferences.class.getResource("/sounds/mixkit-classic-alarm-995.wav").toString();
+ }
+ } else {
+ return alarmResource;
+ }
+ }
+
+}
diff --git a/app/alarm/audio-annunciator/src/main/resources/META-INF/services/org.phoebus.applications.alarm.ui.annunciator.Annunciator b/app/alarm/audio-annunciator/src/main/resources/META-INF/services/org.phoebus.applications.alarm.ui.annunciator.Annunciator
new file mode 100644
index 0000000000..4377b88ff8
--- /dev/null
+++ b/app/alarm/audio-annunciator/src/main/resources/META-INF/services/org.phoebus.applications.alarm.ui.annunciator.Annunciator
@@ -0,0 +1 @@
+org.phoebus.applications.alarm.audio.annunciator.AudioAnnunciator
diff --git a/app/alarm/audio-annunciator/src/main/resources/audio_annunciator_preferences.properties b/app/alarm/audio-annunciator/src/main/resources/audio_annunciator_preferences.properties
new file mode 100644
index 0000000000..c8383b2611
--- /dev/null
+++ b/app/alarm/audio-annunciator/src/main/resources/audio_annunciator_preferences.properties
@@ -0,0 +1,23 @@
+# ----------------------------------------
+# Package org.phoebus.applications.alarm.audio.annunciator
+# ----------------------------------------
+
+# The audio annunciator will play the audio files specified for the associated alarm severity levels.
+# Currently supported formats are AIFF and WAV files.
+# examples of audio file URL's, they can be local or remote files.
+# file:/C:/tmp/audio/AudioFileWithWavFormat.wav
+# https://wavlist.com/wav/brass1.wav
+
+# default alarm sound, if we don't want severity specific sounds only setting this one preference is enough
+alarm_sound_url=
+
+minor_alarm_sound_url=
+major_alarm_sound_url=
+invalid_alarm_sound_url=
+undefined_alarm_sound_url=
+
+# audio clip volume (0-100)
+volume=100
+
+# max alarm Duration in seconds.
+max_alarm_duration=10
\ No newline at end of file
diff --git a/app/alarm/audio-annunciator/src/main/resources/sounds/mixkit-classic-alarm-995.wav b/app/alarm/audio-annunciator/src/main/resources/sounds/mixkit-classic-alarm-995.wav
new file mode 100644
index 0000000000..e1d7f42d5c
Binary files /dev/null and b/app/alarm/audio-annunciator/src/main/resources/sounds/mixkit-classic-alarm-995.wav differ
diff --git a/app/alarm/freetts-annunciator/.classpath b/app/alarm/freetts-annunciator/.classpath
new file mode 100644
index 0000000000..948256b10b
--- /dev/null
+++ b/app/alarm/freetts-annunciator/.classpath
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/alarm/freetts-annunciator/build.xml b/app/alarm/freetts-annunciator/build.xml
new file mode 100644
index 0000000000..8f2787706f
--- /dev/null
+++ b/app/alarm/freetts-annunciator/build.xml
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/alarm/freetts-annunciator/pom.xml b/app/alarm/freetts-annunciator/pom.xml
new file mode 100644
index 0000000000..58b9bbd018
--- /dev/null
+++ b/app/alarm/freetts-annunciator/pom.xml
@@ -0,0 +1,32 @@
+
+
+ 4.0.0
+
+ org.phoebus
+ app-alarm
+ 4.7.4-SNAPSHOT
+
+ app-alarm-freetts-annunciator
+
+
+ 17
+ 17
+
+
+
+
+ net.sf.sociaal
+ freetts
+ 1.2.2
+
+
+ org.phoebus
+ app-alarm-ui
+ 4.7.4-SNAPSHOT
+ compile
+
+
+
+
\ No newline at end of file
diff --git a/app/alarm/freetts-annunciator/src/main/java/org/phoebus/applications/alarm/freetts/annunciator/FreeTTSAnnunciator.java b/app/alarm/freetts-annunciator/src/main/java/org/phoebus/applications/alarm/freetts/annunciator/FreeTTSAnnunciator.java
new file mode 100644
index 0000000000..cc5d6674f8
--- /dev/null
+++ b/app/alarm/freetts-annunciator/src/main/java/org/phoebus/applications/alarm/freetts/annunciator/FreeTTSAnnunciator.java
@@ -0,0 +1,55 @@
+/*******************************************************************************
+ * Copyright (c) 2018 Oak Ridge National Laboratory.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+package org.phoebus.applications.alarm.freetts.annunciator;
+
+import com.sun.speech.freetts.Voice;
+import com.sun.speech.freetts.VoiceManager;
+import org.phoebus.applications.alarm.ui.annunciator.Annunciator;
+import org.phoebus.applications.alarm.ui.annunciator.AnnunciatorMessage;
+
+/**
+ * Annunciator class. Uses freeTTS to annunciate passed messages.
+ * @author Evan Smith, Kunal Shroff
+ */
+@SuppressWarnings("nls")
+public class FreeTTSAnnunciator implements Annunciator
+{
+ private final VoiceManager voiceManager;
+ private final Voice voice;
+ private static final String voice_name = "kevin16";
+
+ /** Constructor */
+ public FreeTTSAnnunciator()
+ {
+ // Define the voices directory.
+ System.setProperty("freetts.voices", "com.sun.speech.freetts.en.us.cmu_us_kal.KevinVoiceDirectory");
+ voiceManager = VoiceManager.getInstance();
+ voice = voiceManager.getVoice(voice_name);
+ voice.allocate();
+ }
+
+ /**
+ * Annunciate the message. Only returns once speaking finishes.
+ * @param message Message text
+ */
+ @Override
+ public void speak(final AnnunciatorMessage message)
+ {
+ if (null != message)
+ voice.speak(message.message);
+ }
+
+ /**
+ * Deallocates the voice.
+ */
+ @Override
+ public void shutdown()
+ {
+ voice.deallocate();
+ }
+}
diff --git a/app/alarm/freetts-annunciator/src/main/resources/META-INF/services/org.phoebus.applications.alarm.ui.annunciator.Annunciator b/app/alarm/freetts-annunciator/src/main/resources/META-INF/services/org.phoebus.applications.alarm.ui.annunciator.Annunciator
new file mode 100644
index 0000000000..06c0cfc350
--- /dev/null
+++ b/app/alarm/freetts-annunciator/src/main/resources/META-INF/services/org.phoebus.applications.alarm.ui.annunciator.Annunciator
@@ -0,0 +1 @@
+org.phoebus.applications.alarm.freetts.annunciator.FreeTTSAnnunciator
diff --git a/app/alarm/ui/src/test/java/org/phoebus/applications/alarm/VoiceDemo.java b/app/alarm/freetts-annunciator/src/test/java/org/phoebus/applications/alarm/freetts/annunciator/VoiceDemo.java
similarity index 94%
rename from app/alarm/ui/src/test/java/org/phoebus/applications/alarm/VoiceDemo.java
rename to app/alarm/freetts-annunciator/src/test/java/org/phoebus/applications/alarm/freetts/annunciator/VoiceDemo.java
index 49ba15fd7b..43e192e704 100644
--- a/app/alarm/ui/src/test/java/org/phoebus/applications/alarm/VoiceDemo.java
+++ b/app/alarm/freetts-annunciator/src/test/java/org/phoebus/applications/alarm/freetts/annunciator/VoiceDemo.java
@@ -5,7 +5,7 @@
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
******************************************************************************/
-package org.phoebus.applications.alarm;
+package org.phoebus.applications.alarm.freetts.annunciator;
import com.sun.speech.freetts.Voice;
import com.sun.speech.freetts.VoiceManager;
diff --git a/app/alarm/pom.xml b/app/alarm/pom.xml
index a5b1307493..71e072b45e 100644
--- a/app/alarm/pom.xml
+++ b/app/alarm/pom.xml
@@ -12,5 +12,7 @@
ui
logging-ui
datasource
+ freetts-annunciator
+ audio-annunciator
diff --git a/app/alarm/ui/pom.xml b/app/alarm/ui/pom.xml
index d36aa6b31f..de1c177062 100644
--- a/app/alarm/ui/pom.xml
+++ b/app/alarm/ui/pom.xml
@@ -44,10 +44,6 @@
app-alarm-model
4.7.4-SNAPSHOT
-
- net.sf.sociaal
- freetts
- 1.2.2
-
+
diff --git a/app/alarm/ui/src/main/java/org/phoebus/applications/alarm/ui/annunciator/Annunciator.java b/app/alarm/ui/src/main/java/org/phoebus/applications/alarm/ui/annunciator/Annunciator.java
index 2cda2a5163..8a4beff8d2 100644
--- a/app/alarm/ui/src/main/java/org/phoebus/applications/alarm/ui/annunciator/Annunciator.java
+++ b/app/alarm/ui/src/main/java/org/phoebus/applications/alarm/ui/annunciator/Annunciator.java
@@ -1,51 +1,16 @@
-/*******************************************************************************
- * Copyright (c) 2018 Oak Ridge National Laboratory.
- * All rights reserved. This program and the accompanying materials
- * are made available under the terms of the Eclipse Public License v1.0
- * which accompanies this distribution, and is available at
- * http://www.eclipse.org/legal/epl-v10.html
- *******************************************************************************/
package org.phoebus.applications.alarm.ui.annunciator;
-import com.sun.speech.freetts.Voice;
-import com.sun.speech.freetts.VoiceManager;
-
-/**
- * Annunciator class. Uses freeTTS to annunciate passed messages.
- * @author Evan Smith
- */
-@SuppressWarnings("nls")
-public class Annunciator
+public interface Annunciator
{
- private final VoiceManager voiceManager;
- private final Voice voice;
- private static final String voice_name = "kevin16";
-
- /** Constructor */
- public Annunciator()
- {
- // Define the voices directory.
- System.setProperty("freetts.voices", "com.sun.speech.freetts.en.us.cmu_us_kal.KevinVoiceDirectory");
- voiceManager = VoiceManager.getInstance();
- voice = voiceManager.getVoice(voice_name);
- voice.allocate();
- }
/**
* Annunciate the message. Only returns once speaking finishes.
* @param message Message text
*/
- public void speak(final String message)
- {
- if (null != message)
- voice.speak(message);
- }
+ void speak(final AnnunciatorMessage message);
/**
- * Deallocates the voice.
+ * Release resources that need to be cleaned on shutdown.
*/
- public void shutdown()
- {
- voice.deallocate();
- }
+ default void shutdown(){}
}
diff --git a/app/alarm/ui/src/main/java/org/phoebus/applications/alarm/ui/annunciator/AnnunciatorController.java b/app/alarm/ui/src/main/java/org/phoebus/applications/alarm/ui/annunciator/AnnunciatorController.java
index 22a9a12751..a550148a72 100644
--- a/app/alarm/ui/src/main/java/org/phoebus/applications/alarm/ui/annunciator/AnnunciatorController.java
+++ b/app/alarm/ui/src/main/java/org/phoebus/applications/alarm/ui/annunciator/AnnunciatorController.java
@@ -10,11 +10,14 @@
import java.time.Instant;
import java.util.ArrayList;
import java.util.List;
+import java.util.ServiceLoader;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.function.Consumer;
+import java.util.stream.Collectors;
import org.phoebus.applications.alarm.model.SeverityLevel;
+import org.phoebus.framework.adapter.AdapterFactory;
/** Controller class for an annunciator.
*
@@ -44,7 +47,8 @@ public class AnnunciatorController
private final BlockingQueue to_annunciate = new LinkedBlockingQueue<>();
- private final Annunciator annunciator = new Annunciator();
+ private final List annunciators;
+
private final Thread process_thread = new Thread(this::processMessages, "Annunciator");
// Muted _IS_ read from multiple threads, so it should always be fetched from memory.
@@ -59,6 +63,10 @@ public AnnunciatorController(final int threshold, final Consumer loader = ServiceLoader.load(Annunciator.class);
+ annunciators = loader.stream().map(ServiceLoader.Provider::get).collect(Collectors.toList());
+
// The thread should exit when requested by shutdown() call, but set to daemon so it dies
// when program closes regardless.
process_thread.setDaemon(true);
@@ -115,7 +123,9 @@ private void processMessages()
{
addToTable.accept(message);
if (! muted)
- annunciator.speak(message.message);
+ annunciators.stream().forEach(annunciator -> {
+ annunciator.speak(message);
+ });
}
}
else
@@ -130,7 +140,11 @@ private void processMessages()
{ // Annunciate if marked as stand out.
addToTable.accept(message);
if (! muted)
- annunciator.speak(message.message);
+ {
+ annunciators.stream().forEach(annunciator -> {
+ annunciator.speak(message);
+ });
+ }
}
else
{ // Increment count of non stand out messages.
@@ -144,7 +158,11 @@ private void processMessages()
final AnnunciatorMessage message = new AnnunciatorMessage(false, null, earliest, "There are " + flurry + " new messages");
addToTable.accept(message);
if (! muted)
- annunciator.speak(message.message);
+ {
+ annunciators.stream().forEach(annunciator -> {
+ annunciator.speak(message);
+ });
+ }
}
}
}
@@ -157,10 +175,13 @@ public void shutdown() throws InterruptedException
{
// Send magic message that wakes annunciatorThread and causes it to exit
to_annunciate.offer(LAST_MESSAGE);
+
// The thread should shutdown
process_thread.join(2000);
// Deallocate the annunciator's voice.
- annunciator.shutdown();
+ annunciators.stream().forEach(annunciator -> {
+ annunciator.shutdown();
+ });
}
}
diff --git a/phoebus-product/pom.xml b/phoebus-product/pom.xml
index 66b9b5b2e5..4815bb3803 100644
--- a/phoebus-product/pom.xml
+++ b/phoebus-product/pom.xml
@@ -186,6 +186,12 @@
app-alarm-ui
4.7.4-SNAPSHOT
+
+ org.phoebus
+ app-alarm-freetts-annunciator
+ 4.7.4-SNAPSHOT
+ true
+
org.phoebus
app-alarm-logging-ui