Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.core.library.types.PercentType;

import io.reactivex.annotations.NonNull;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi, I just briefly looked into this and I am wondering why you import an additional annotation framework.
As the main class is annotated with @ NonNullByDefault, you may not need this anyway.
Did I overlook something?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@holgerfriedrich You are right, but it is a bad habit of making things explicit in method signatures that comes into play.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@kgoderis The import uses a different framework, I would expect org.eclipse.jdt.annotation.NonNull instead of io.reactivex.annotations.NonNull....

Copy link

Copilot AI Jul 26, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The import io.reactivex.annotations.NonNull is inconsistent with the existing annotation framework. The interface already uses @NonNullByDefault and should use JDT annotations (@NonNull) for consistency.

Suggested change
import io.reactivex.annotations.NonNull;
// Removed the unused import for io.reactivex.annotations.NonNull

Copilot uses AI. Check for mistakes.


/**
* This service provides functionality around audio services and is the central service to be used directly by others.
*
Expand All @@ -27,6 +29,7 @@
* @author Christoph Weitkamp - Added parameter to adjust the volume
* @author Wouter Born - Added methods for getting all sinks and sources
* @author Miguel Álvarez - Add record method
* @author Karel Goderis - Add multisink support
*/
@NonNullByDefault
public interface AudioManager {
Expand All @@ -51,6 +54,14 @@ public interface AudioManager {
*/
void play(@Nullable AudioStream audioStream, @Nullable String sinkId);

/**
* Plays the passed audio stream on the given set of sinks.
*
* @param audioStream The audio stream to play or null if streaming should be stopped
* @param sinkIds The set of the audio sink ids to use
*/
void play(@Nullable AudioStream audioStream, @NonNull Set<String> sinkIds);

/**
* Plays the passed audio stream on the given sink.
*
Expand All @@ -60,6 +71,15 @@ public interface AudioManager {
*/
void play(@Nullable AudioStream audioStream, @Nullable String sinkId, @Nullable PercentType volume);

/**
* Plays the passed audio stream on the given set of sinks.
*
* @param audioStream The audio stream to play or null if streaming should be stopped
* @param sinkIds The set of the audio sink ids to use
* @param volume The volume to be used or null if the default notification volume should be used
*/
void play(@Nullable AudioStream audioStream, Set<String> sinkIds, @Nullable PercentType volume);

/**
* Plays an audio file from the "sounds" folder using the default audio sink.
*
Expand All @@ -86,6 +106,15 @@ public interface AudioManager {
*/
void playFile(String fileName, @Nullable String sinkId) throws AudioException;

/**
* Plays an audio file from the "sounds" folder using the given audio sink.
*
* @param fileName The file from the "sounds" folder
* @param sinkIds The set of the audio sink ids to use
* @throws AudioException in case the file does not exist or cannot be opened
*/
void playFile(String fileName, @NonNull Set<String> sinkIds) throws AudioException;

/**
* Plays an audio file with the given volume from the "sounds" folder using the given audio sink.
*
Expand All @@ -96,6 +125,16 @@ public interface AudioManager {
*/
void playFile(String fileName, @Nullable String sinkId, @Nullable PercentType volume) throws AudioException;

/**
* Plays an audio file with the given volume from the "sounds" folder using the given audio sink.
*
* @param fileName The file from the "sounds" folder
* @param sinkIds The set of the audio sink ids to use
* @param volume The volume to be used or null if the default notification volume should be used
* @throws AudioException in case the file does not exist or cannot be opened
*/
void playFile(String fileName, @NonNull Set<String> sinkIds, @Nullable PercentType volume) throws AudioException;

/**
* Stream audio from the passed url using the default audio sink.
*
Expand All @@ -113,6 +152,15 @@ public interface AudioManager {
*/
void stream(@Nullable String url, @Nullable String sinkId) throws AudioException;

/**
* Stream audio from the passed url to the given set of sinks
*
* @param url The url to stream from or null if streaming should be stopped
* @param sinkIds The set of the audio sink ids to use
* @throws AudioException in case the url stream cannot be opened
*/
void stream(@Nullable String url, @NonNull Set<String> sinkIds) throws AudioException;

/**
* Parse and synthesize a melody and play it into the default sink.
*
Expand All @@ -138,6 +186,19 @@ public interface AudioManager {
*/
void playMelody(String melody, @Nullable String sinkId);

/**
* Parse and synthesize a melody and play it into the given sink.
*
* The melody should be a spaced separated list of note names or silences (character 0 or O).
* You can optionally add the character "'" to increase the note one octave.
* You can optionally add ":ms" where ms is an int value to customize the note/silence milliseconds duration
* (defaults to 200ms).
*
* @param melody The url to stream from or null if streaming should be stopped.
* @param sinkIds The set of the audio sink ids to use
*/
void playMelody(String melody, @NonNull Set<String> sinkIds);

/**
* Parse and synthesize a melody and play it into the given sink at the desired volume.
*
Expand All @@ -152,6 +213,20 @@ public interface AudioManager {
*/
void playMelody(String melody, @Nullable String sinkId, @Nullable PercentType volume);

/**
* Parse and synthesize a melody and play it into the given sink at the desired volume.
*
* The melody should be a spaced separated list of note names or silences (character 0 or O).
* You can optionally add the character "'" to increase the note one octave.
* You can optionally add ":ms" where ms is an int value to customize the note/silence milliseconds duration
* (defaults to 200ms).
*
* @param melody The url to stream from or null if streaming should be stopped.
* @param sinkIds The set of the audio sink ids to use
* @param volume The volume to be used or null if the default notification volume should be used
*/
void playMelody(String melody, @NonNull Set<String> sinkIds, @Nullable PercentType volume);

/**
* Record audio as a WAV file of the specified length to the sounds folder.
*
Expand Down Expand Up @@ -256,9 +331,10 @@ public interface AudioManager {
AudioSink getSink(@Nullable String sinkId);

/**
* Get a list of sink ids that match a given pattern
* Get a list of sink ids that match a given set of patterns
*
* @param pattern pattern to search, can include {@code *} and {@code ?} placeholders
* @param pattern patterns to search; patterns is a comma-separated list, whereby each can include {@code *} and
* {@code ?} placeholders, or can be literal string to designate a single sink
* @return ids of matching sinks
*/
Set<String> getSinkIds(String pattern);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Reference;

import io.reactivex.annotations.NonNull;
Copy link

Copilot AI Jul 26, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The import io.reactivex.annotations.NonNull is inconsistent with the existing annotation framework used in this codebase. Should use JDT annotations for consistency.

Suggested change
import io.reactivex.annotations.NonNull;
import org.eclipse.jdt.annotation.NonNull;

Copilot uses AI. Check for mistakes.


/**
* Console command extension for all audio features.
*
Expand Down Expand Up @@ -237,9 +239,7 @@ private void playMelodyOnSink(@Nullable String sinkId, String melody, @Nullable
}

private void playOnSinks(String pattern, String fileName, @Nullable PercentType volume, Console console) {
for (String sinkId : audioManager.getSinkIds(pattern)) {
playOnSink(sinkId, fileName, volume, console);
}
playOnSinks(audioManager.getSinkIds(pattern), fileName, volume, console);
}

private void playOnSink(@Nullable String sinkId, String fileName, @Nullable PercentType volume, Console console) {
Expand All @@ -251,6 +251,16 @@ private void playOnSink(@Nullable String sinkId, String fileName, @Nullable Perc
}
}

private void playOnSinks(@NonNull Set<String> sinkIds, String fileName, @Nullable PercentType volume,
Console console) {
try {
audioManager.playFile(fileName, sinkIds, volume);
} catch (AudioException e) {
console.println(Objects.requireNonNullElse(e.getMessage(),
String.format("An error occurred while playing '%s' on sinks '%s'", fileName, sinkIds)));
}
}

private void stream(String[] args, Console console) {
switch (args.length) {
case 1:
Expand Down
Loading
Loading