diff --git a/src/main/java/envoy/client/data/audio/AudioPlayer.java b/src/main/java/envoy/client/data/audio/AudioPlayer.java new file mode 100644 index 0000000..acdc576 --- /dev/null +++ b/src/main/java/envoy/client/data/audio/AudioPlayer.java @@ -0,0 +1,64 @@ +package envoy.client.data.audio; + +import javax.sound.sampled.*; + +import envoy.exception.EnvoyException; + +/** + * Plays back audio from a byte array. + *
+ * Project: envoy-client
+ * File: AudioPlayer.java
+ * Created: 05.07.2020
+ *
+ * @author Kai S. K. Engelbart
+ * @since Envoy Client v0.1-beta
+ */
+public final class AudioPlayer {
+
+ private final AudioFormat format;
+ private final DataLine.Info info;
+
+ private Clip clip;
+
+ /**
+ * Initializes the player with the default audio format.
+ *
+ * @since Envoy Client v0.1-beta
+ */
+ public AudioPlayer() { this(AudioRecorder.DEFAULT_AUDIO_FORMAT); }
+
+ /**
+ * Initializes the player with a given audio format.
+ *
+ * @param format the audio format to use
+ * @since Envoy Client v0.1-beta
+ */
+ public AudioPlayer(AudioFormat format) {
+ this.format = format;
+ info = new DataLine.Info(Clip.class, format);
+ }
+
+ /**
+ * @return {@code true} if audio play back is supported
+ * @since Envoy Client v0.1-beta
+ */
+ public boolean isSupported() { return AudioSystem.isLineSupported(info); }
+
+ /**
+ * Plays back an audio clip.
+ *
+ * @param data the data of the clip
+ * @throws EnvoyException if the play back failed
+ * @since Envoy Client v0.1-beta
+ */
+ public void play(byte[] data) throws EnvoyException {
+ try {
+ clip = (Clip) AudioSystem.getLine(info);
+ clip.open(format, data, 0, data.length);
+ clip.start();
+ } catch (LineUnavailableException e) {
+ throw new EnvoyException("Cannot play back audio", e);
+ }
+ }
+}
diff --git a/src/main/java/envoy/client/data/AudioRecorder.java b/src/main/java/envoy/client/data/audio/AudioRecorder.java
similarity index 59%
rename from src/main/java/envoy/client/data/AudioRecorder.java
rename to src/main/java/envoy/client/data/audio/AudioRecorder.java
index 96b08a0..b55da93 100644
--- a/src/main/java/envoy/client/data/AudioRecorder.java
+++ b/src/main/java/envoy/client/data/audio/AudioRecorder.java
@@ -1,4 +1,4 @@
-package envoy.client.data;
+package envoy.client.data.audio;
import java.io.IOException;
import java.nio.file.Files;
@@ -20,21 +20,49 @@ import envoy.exception.EnvoyException;
*/
public final class AudioRecorder {
- private final AudioFormat format;
+ /**
+ * The default audio format used for recording and play back.
+ *
+ * @since Envoy Client v0.1-beta
+ */
+ public static final AudioFormat DEFAULT_AUDIO_FORMAT = new AudioFormat(16000, 16, 1, true, false);
+
+ private final AudioFormat format;
+ private final DataLine.Info info;
- private DataLine.Info info;
private TargetDataLine line;
private Path tempFile;
- public AudioRecorder() { this(new AudioFormat(16000, 16, 1, true, false)); }
+ /**
+ * Initializes the recorder with the default audio format.
+ *
+ * @since Envoy Client v0.1-beta
+ */
+ public AudioRecorder() { this(DEFAULT_AUDIO_FORMAT); }
+ /**
+ * Initializes the recorder with a given audio format.
+ *
+ * @param format the audio format to use
+ * @since Envoy Client v0.1-beta
+ */
public AudioRecorder(AudioFormat format) {
this.format = format;
info = new DataLine.Info(TargetDataLine.class, format);
}
+ /**
+ * @return {@code true} if audio recording is supported
+ * @since Envoy Client v0.1-beta
+ */
public boolean isSupported() { return AudioSystem.isLineSupported(info); }
+ /**
+ * Starts the audio recording.
+ *
+ * @throws EnvoyException if starting the recording failed
+ * @since Envoy Client v0.1-beta
+ */
public void start() throws EnvoyException {
try {
@@ -54,6 +82,13 @@ public final class AudioRecorder {
}
}
+ /**
+ * Stops the recording.
+ *
+ * @return the finished recording
+ * @throws EnvoyException if finishing the recording failed
+ * @since Envoy Client v0.1-beta
+ */
public byte[] finish() throws EnvoyException {
try {
line.stop();
diff --git a/src/main/java/envoy/client/data/audio/package-info.java b/src/main/java/envoy/client/data/audio/package-info.java
new file mode 100644
index 0000000..3a172be
--- /dev/null
+++ b/src/main/java/envoy/client/data/audio/package-info.java
@@ -0,0 +1,11 @@
+/**
+ * Contains classes related to recording and playing back audio clips.
+ *
+ * Project: envoy-client
+ * File: package-info.java
+ * Created: 05.07.2020
+ *
+ * @author Kai S. K. Engelbart
+ * @since Envoy Client v0.1-beta
+ */
+package envoy.client.data.audio;
diff --git a/src/main/java/envoy/client/ui/AudioControl.java b/src/main/java/envoy/client/ui/AudioControl.java
new file mode 100644
index 0000000..698e315
--- /dev/null
+++ b/src/main/java/envoy/client/ui/AudioControl.java
@@ -0,0 +1,40 @@
+package envoy.client.ui;
+
+import javafx.scene.control.Button;
+import javafx.scene.layout.HBox;
+
+import envoy.client.data.audio.AudioPlayer;
+import envoy.exception.EnvoyException;
+
+/**
+ * Enables the play back of audio clips through a button.
+ *
+ * Project: envoy-client
+ * File: AudioControl.java
+ * Created: 05.07.2020
+ *
+ * @author Kai S. K. Engelbart
+ * @since Envoy Client v0.1-beta
+ */
+public final class AudioControl extends HBox {
+
+ private AudioPlayer player = new AudioPlayer();
+
+ /**
+ * Initializes the audio control.
+ *
+ * @param audioData the audio data to play.
+ * @since Envoy Client v0.1-beta
+ */
+ public AudioControl(byte[] audioData) {
+ var button = new Button("Play");
+ button.setOnAction(e -> {
+ try {
+ player.play(audioData);
+ } catch (EnvoyException ex) {
+
+ }
+ });
+ getChildren().add(button);
+ }
+}
diff --git a/src/main/java/envoy/client/ui/controller/ChatScene.java b/src/main/java/envoy/client/ui/controller/ChatScene.java
index 9dfcc25..38a5b9a 100644
--- a/src/main/java/envoy/client/ui/controller/ChatScene.java
+++ b/src/main/java/envoy/client/ui/controller/ChatScene.java
@@ -18,6 +18,7 @@ import javafx.scene.input.KeyEvent;
import javafx.scene.paint.Color;
import envoy.client.data.*;
+import envoy.client.data.audio.AudioRecorder;
import envoy.client.event.MessageCreationEvent;
import envoy.client.net.Client;
import envoy.client.net.WriteProxy;
diff --git a/src/main/java/envoy/client/ui/listcell/MessageControl.java b/src/main/java/envoy/client/ui/listcell/MessageControl.java
index d68acdd..7017f7c 100644
--- a/src/main/java/envoy/client/ui/listcell/MessageControl.java
+++ b/src/main/java/envoy/client/ui/listcell/MessageControl.java
@@ -9,7 +9,9 @@ import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.VBox;
+import envoy.client.ui.AudioControl;
import envoy.client.ui.IconUtil;
+import envoy.data.Attachment.AttachmentType;
import envoy.data.Message;
import envoy.data.Message.MessageStatus;
import envoy.data.User;
@@ -38,6 +40,11 @@ public class MessageControl extends VBox {
public MessageControl(Message message) {
// Creating the underlying VBox, the dateLabel and the textLabel
super(new Label(dateFormat.format(message.getCreationDate())));
+
+ // Voice attachment
+ if (message.hasAttachment() && message.getAttachment().getType() == AttachmentType.VOICE)
+ getChildren().add(new AudioControl(message.getAttachment().getData()));
+
final var textLabel = new Label(message.getText());
textLabel.setWrapText(true);
getChildren().add(textLabel);
diff --git a/src/main/resources/fxml/ChatScene.fxml b/src/main/resources/fxml/ChatScene.fxml
index d68b351..5638c7a 100644
--- a/src/main/resources/fxml/ChatScene.fxml
+++ b/src/main/resources/fxml/ChatScene.fxml
@@ -13,21 +13,35 @@
-