From a90f58afe0f54aa8dc7476c58cfcb19671d49338 Mon Sep 17 00:00:00 2001 From: kske Date: Sun, 5 Jul 2020 12:04:25 +0200 Subject: [PATCH] Add Audio Playback Capability * Add envoy.client.data.audio package * Move AudioRecorder to the audio package * Add AudioPlayer class * Add AudioControl class that acts as a small media player * Display the audio control in message controls that contain voice messages --- .../envoy/client/data/audio/AudioPlayer.java | 64 ++++++++ .../data/{ => audio}/AudioRecorder.java | 43 +++++- .../envoy/client/data/audio/package-info.java | 11 ++ .../java/envoy/client/ui/AudioControl.java | 40 +++++ .../envoy/client/ui/controller/ChatScene.java | 1 + .../client/ui/listcell/MessageControl.java | 7 + src/main/resources/fxml/ChatScene.fxml | 145 ++++++++++++------ 7 files changed, 260 insertions(+), 51 deletions(-) create mode 100644 src/main/java/envoy/client/data/audio/AudioPlayer.java rename src/main/java/envoy/client/data/{ => audio}/AudioRecorder.java (59%) create mode 100644 src/main/java/envoy/client/data/audio/package-info.java create mode 100644 src/main/java/envoy/client/ui/AudioControl.java 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 @@ - + - - + + - - - - - - + + + + + + - + @@ -37,12 +51,14 @@ - + -