mirror of
https://github.com/danieldemus/openhab-core.git
synced 2026-07-29 12:34:22 +02:00
Added new enhanced sound fragment, which brings mp3 support on top of ESH. (#63)
Remove the old audio action as all features should now be covered through ESH itself. Signed-off-by: Kai Kreuzer <kai@openhab.org>
This commit is contained in:
@@ -3,7 +3,6 @@
|
||||
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.7"/>
|
||||
<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
|
||||
<classpathentry kind="src" path="src/main/java"/>
|
||||
<classpathentry kind="lib" path="lib/jl1.0.1.jar"/>
|
||||
<classpathentry exported="true" kind="lib" path="lib/jackson-core-asl-1.9.2.jar"/>
|
||||
<classpathentry exported="true" kind="lib" path="lib/jackson-mapper-asl-1.9.2.jar"/>
|
||||
<classpathentry kind="output" path="target/classes"/>
|
||||
|
||||
@@ -43,7 +43,6 @@ Import-Package: com.google.common.base,
|
||||
org.eclipse.smarthome.ui.chart,
|
||||
org.eclipse.smarthome.ui.items,
|
||||
org.eclipse.xtext.xbase,
|
||||
org.openhab.io.multimedia.actions,
|
||||
org.openhab.library.tel.types,
|
||||
org.osgi.framework,
|
||||
org.osgi.service.cm,
|
||||
@@ -90,7 +89,6 @@ Export-Package: org.codehaus.jackson,
|
||||
org.openhab.core.transform.actions,
|
||||
org.openhab.core.types,
|
||||
org.openhab.io.console,
|
||||
org.openhab.io.multimedia.actions,
|
||||
org.openhab.io.multimedia.tts,
|
||||
org.openhab.io.net.actions,
|
||||
org.openhab.io.net.exec,
|
||||
@@ -101,8 +99,7 @@ Export-Package: org.codehaus.jackson,
|
||||
org.openhab.model.sitemap,
|
||||
org.openhab.ui.chart,
|
||||
org.openhab.ui.items
|
||||
Bundle-ClassPath: lib/jl1.0.1.jar,
|
||||
.,
|
||||
Bundle-ClassPath: .,
|
||||
lib/jackson-core-asl-1.9.2.jar,
|
||||
lib/jackson-mapper-asl-1.9.2.jar
|
||||
Service-Component: OSGI-INF/*.xml
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
|
||||
Copyright (c) 2015-2016 by the respective copyright holders.
|
||||
|
||||
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
|
||||
|
||||
-->
|
||||
<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" immediate="false" name="org.openhab.action.audio">
|
||||
<implementation class="org.openhab.io.multimedia.actions.AudioActionService"/>
|
||||
<reference bind="setVoiceManager" cardinality="1..1" interface="org.eclipse.smarthome.core.voice.VoiceManager" name="VoiceManager" policy="static" unbind="unsetVoiceManager"/>
|
||||
<service>
|
||||
<provide interface="org.eclipse.smarthome.model.script.engine.action.ActionService"/>
|
||||
</service>
|
||||
</scr:component>
|
||||
@@ -2,7 +2,5 @@ output.. = target/classes/
|
||||
bin.includes = META-INF/,\
|
||||
.,\
|
||||
lib/,\
|
||||
OSGI-INF/,\
|
||||
lib/jackson-core-asl-1.9.2.jar,\
|
||||
lib/jackson-mapper-asl-1.9.2.jar
|
||||
OSGI-INF/
|
||||
source.. = src/main/java/
|
||||
|
||||
-2
@@ -11,7 +11,6 @@ package org.openhab.core.compat1x.internal;
|
||||
import org.eclipse.smarthome.model.script.engine.ScriptEngine;
|
||||
import org.openhab.core.events.EventPublisher;
|
||||
import org.openhab.core.items.ItemRegistry;
|
||||
import org.openhab.io.multimedia.actions.Audio;
|
||||
import org.osgi.framework.BundleActivator;
|
||||
import org.osgi.framework.BundleContext;
|
||||
import org.osgi.util.tracker.ServiceTracker;
|
||||
@@ -59,7 +58,6 @@ public class CompatibilityActivator implements BundleActivator {
|
||||
itemRegistryTracker.close();
|
||||
eventPublisherTracker.close();
|
||||
scriptEngineTracker.close();
|
||||
Audio.playStream(null);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
-330
@@ -1,330 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2015-2016 by the respective copyright holders.
|
||||
*
|
||||
* 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.openhab.io.console;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import org.apache.commons.lang.ArrayUtils;
|
||||
import org.eclipse.smarthome.model.script.engine.Script;
|
||||
import org.eclipse.smarthome.model.script.engine.ScriptEngine;
|
||||
import org.eclipse.smarthome.model.script.engine.ScriptExecutionException;
|
||||
import org.eclipse.smarthome.model.script.engine.ScriptParsingException;
|
||||
import org.openhab.core.compat1x.internal.CompatibilityActivator;
|
||||
import org.openhab.core.events.EventPublisher;
|
||||
import org.openhab.core.items.Item;
|
||||
import org.openhab.core.items.ItemNotFoundException;
|
||||
import org.openhab.core.items.ItemNotUniqueException;
|
||||
import org.openhab.core.items.ItemRegistry;
|
||||
import org.openhab.core.types.Command;
|
||||
import org.openhab.core.types.State;
|
||||
import org.openhab.core.types.TypeParser;
|
||||
import org.openhab.io.multimedia.actions.Audio;
|
||||
|
||||
import com.google.common.base.Joiner;
|
||||
|
||||
/**
|
||||
* This class provides generic methods for handling console input (i.e. pure strings).
|
||||
*
|
||||
* @author Kai Kreuzer
|
||||
* @since 0.4.0
|
||||
*
|
||||
*/
|
||||
public class ConsoleInterpreter {
|
||||
|
||||
/**
|
||||
* This method simply takes a list of arguments, where the first one is treated
|
||||
* as the console command (such as "update", "send" etc.). The following entries
|
||||
* are then the arguments for this command.
|
||||
* If the command is unknown, the complete usage is printed to the console.
|
||||
*
|
||||
* @param args array which contains the console command and all its arguments
|
||||
* @param console the console for printing messages for the user
|
||||
*/
|
||||
static public void handleRequest(String[] args, Console console) {
|
||||
String arg = args[0];
|
||||
args = (String[]) ArrayUtils.remove(args, 0);
|
||||
if (arg.equals("items")) {
|
||||
ConsoleInterpreter.handleItems(args, console);
|
||||
} else if (arg.equals("send")) {
|
||||
ConsoleInterpreter.handleSend(args, console);
|
||||
} else if (arg.equals("update")) {
|
||||
ConsoleInterpreter.handleUpdate(args, console);
|
||||
} else if (arg.equals("status")) {
|
||||
ConsoleInterpreter.handleStatus(args, console);
|
||||
} else if (arg.equals("say")) {
|
||||
ConsoleInterpreter.handleSay(args, console);
|
||||
} else if (arg.equals(">")) {
|
||||
ConsoleInterpreter.handleScript(args, console);
|
||||
} else {
|
||||
console.printUsage(getUsage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method handles an update command.
|
||||
*
|
||||
* @param args array which contains the arguments for the update command
|
||||
* @param console the console for printing messages for the user
|
||||
*/
|
||||
static public void handleUpdate(String[] args, Console console) {
|
||||
ItemRegistry registry = CompatibilityActivator.itemRegistryTracker.getService();
|
||||
EventPublisher publisher = CompatibilityActivator.eventPublisherTracker.getService();
|
||||
if (publisher != null) {
|
||||
if (registry != null) {
|
||||
if (args.length > 0) {
|
||||
String itemName = args[0];
|
||||
try {
|
||||
Item item = registry.getItemByPattern(itemName);
|
||||
if (args.length > 1) {
|
||||
String stateName = args[1];
|
||||
State state = TypeParser.parseState(item.getAcceptedDataTypes(), stateName);
|
||||
if (state != null) {
|
||||
publisher.postUpdate(item.getName(), state);
|
||||
console.println("Update has been sent successfully.");
|
||||
} else {
|
||||
console.println(
|
||||
"Error: State '" + stateName + "' is not valid for item '" + itemName + "'");
|
||||
console.print("Valid data types are: ( ");
|
||||
for (Class<? extends State> acceptedType : item.getAcceptedDataTypes()) {
|
||||
console.print(acceptedType.getSimpleName() + " ");
|
||||
}
|
||||
console.println(")");
|
||||
}
|
||||
} else {
|
||||
console.printUsage(ConsoleInterpreter.getUpdateUsage());
|
||||
}
|
||||
} catch (ItemNotFoundException e) {
|
||||
console.println("Error: Item '" + itemName + "' does not exist.");
|
||||
} catch (ItemNotUniqueException e) {
|
||||
console.print("Error: Multiple items match this pattern: ");
|
||||
for (Item item : e.getMatchingItems()) {
|
||||
console.print(item.getName() + " ");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
console.printUsage(ConsoleInterpreter.getUpdateUsage());
|
||||
}
|
||||
} else {
|
||||
console.println("Sorry, no item registry service available!");
|
||||
}
|
||||
} else {
|
||||
console.println("Sorry, no event publisher service available!");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method handles a send command.
|
||||
*
|
||||
* @param args array which contains the arguments for the send command
|
||||
* @param console the console for printing messages for the user
|
||||
*/
|
||||
static public void handleSend(String[] args, Console console) {
|
||||
ItemRegistry registry = CompatibilityActivator.itemRegistryTracker.getService();
|
||||
EventPublisher publisher = CompatibilityActivator.eventPublisherTracker.getService();
|
||||
if (publisher != null) {
|
||||
if (registry != null) {
|
||||
if (args.length > 0) {
|
||||
String itemName = args[0];
|
||||
try {
|
||||
Item item = registry.getItemByPattern(itemName);
|
||||
if (args.length > 1) {
|
||||
String commandName = args[1];
|
||||
Command command = TypeParser.parseCommand(item.getAcceptedCommandTypes(), commandName);
|
||||
if (command != null) {
|
||||
publisher.sendCommand(itemName, command);
|
||||
console.println("Command has been sent successfully.");
|
||||
} else {
|
||||
console.println("Error: Command '" + commandName + "' is not valid for item '"
|
||||
+ itemName + "'");
|
||||
console.print("Valid command types are: ( ");
|
||||
for (Class<? extends Command> acceptedType : item.getAcceptedCommandTypes()) {
|
||||
console.print(acceptedType.getSimpleName() + " ");
|
||||
}
|
||||
console.println(")");
|
||||
}
|
||||
} else {
|
||||
console.printUsage(ConsoleInterpreter.getCommandUsage());
|
||||
}
|
||||
} catch (ItemNotFoundException e) {
|
||||
console.println("Error: Item '" + itemName + "' does not exist.");
|
||||
} catch (ItemNotUniqueException e) {
|
||||
console.print("Error: Multiple items match this pattern: ");
|
||||
for (Item item : e.getMatchingItems()) {
|
||||
console.print(item.getName() + " ");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
console.printUsage(ConsoleInterpreter.getCommandUsage());
|
||||
}
|
||||
} else {
|
||||
console.println("Sorry, no item registry service available!");
|
||||
}
|
||||
} else {
|
||||
console.println("Sorry, no event publisher service available!");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method handles an items command.
|
||||
*
|
||||
* @param args array which contains the arguments for the items command
|
||||
* @param console the console for printing messages for the user
|
||||
*/
|
||||
static public void handleItems(String[] args, Console console) {
|
||||
ItemRegistry registry = CompatibilityActivator.itemRegistryTracker.getService();
|
||||
if (registry != null) {
|
||||
String pattern = (args.length == 0) ? "*" : args[0];
|
||||
Collection<Item> items = registry.getItems(pattern);
|
||||
if (items.size() > 0) {
|
||||
for (Item item : items) {
|
||||
console.println(item.toString());
|
||||
}
|
||||
} else {
|
||||
console.println("No items found for this pattern.");
|
||||
}
|
||||
} else {
|
||||
console.println("Sorry, no item registry service available!");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method handles a status command.
|
||||
*
|
||||
* @param args array which contains the arguments for the status command
|
||||
* @param console the console for printing messages for the user
|
||||
*/
|
||||
static public void handleStatus(String[] args, Console console) {
|
||||
ItemRegistry registry = CompatibilityActivator.itemRegistryTracker.getService();
|
||||
if (registry != null) {
|
||||
if (args.length > 0) {
|
||||
String itemName = args[0];
|
||||
try {
|
||||
Item item = registry.getItemByPattern(itemName);
|
||||
console.println(item.getState().toString());
|
||||
} catch (ItemNotFoundException e) {
|
||||
console.println("Error: Item '" + itemName + "' does not exist.");
|
||||
} catch (ItemNotUniqueException e) {
|
||||
console.print("Error: Multiple items match this pattern: ");
|
||||
for (Item item : e.getMatchingItems()) {
|
||||
console.print(item.getName() + " ");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
console.printUsage(ConsoleInterpreter.getStatusUsage());
|
||||
}
|
||||
} else {
|
||||
console.println("Sorry, no item registry service available!");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method handles a say command.
|
||||
*
|
||||
* @param args array which contains the arguments for the status command
|
||||
* @param console the console for printing messages for the user
|
||||
*/
|
||||
static public void handleSay(String[] args, Console console) {
|
||||
StringBuilder msg = new StringBuilder();
|
||||
for (String word : args) {
|
||||
if (word.startsWith("%") && word.endsWith("%") && word.length() > 2) {
|
||||
String itemName = word.substring(1, word.length() - 1);
|
||||
ItemRegistry registry = CompatibilityActivator.itemRegistryTracker.getService();
|
||||
if (registry != null) {
|
||||
try {
|
||||
Item item = registry.getItemByPattern(itemName);
|
||||
msg.append(item.getState().toString());
|
||||
} catch (ItemNotFoundException e) {
|
||||
console.println("Error: Item '" + itemName + "' does not exist.");
|
||||
} catch (ItemNotUniqueException e) {
|
||||
console.print("Error: Multiple items match this pattern: ");
|
||||
for (Item item : e.getMatchingItems()) {
|
||||
console.print(item.getName() + " ");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
console.println("Sorry, no item registry service available!");
|
||||
}
|
||||
} else {
|
||||
msg.append(word);
|
||||
}
|
||||
msg.append(" ");
|
||||
}
|
||||
try {
|
||||
Audio.say(msg.toString());
|
||||
console.println("Said: " + msg);
|
||||
} catch (NoClassDefFoundError e) {
|
||||
// The dependency to the Audio class is optional, so we have to handle the case that it is not there
|
||||
console.println("Could not perform command as no TTS service is available.");
|
||||
}
|
||||
}
|
||||
|
||||
public static void handleScript(String[] args, Console console) {
|
||||
ScriptEngine scriptEngine = CompatibilityActivator.scriptEngineTracker.getService();
|
||||
if (scriptEngine != null) {
|
||||
String scriptString = Joiner.on(" ").join(args);
|
||||
Script script;
|
||||
try {
|
||||
script = scriptEngine.newScriptFromString(scriptString);
|
||||
Object result = script.execute();
|
||||
|
||||
if (result != null) {
|
||||
console.println(result.toString());
|
||||
} else {
|
||||
console.println("OK");
|
||||
}
|
||||
} catch (ScriptParsingException e) {
|
||||
console.println(e.getMessage());
|
||||
} catch (ScriptExecutionException e) {
|
||||
console.println(e.getMessage());
|
||||
}
|
||||
} else {
|
||||
console.println("Script engine is not available.");
|
||||
}
|
||||
}
|
||||
|
||||
/** returns a CR-separated list of usage texts for all available commands */
|
||||
private static String getUsage() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (String usage : ConsoleInterpreter.getUsages()) {
|
||||
sb.append(usage + "\n");
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/** returns an array of the usage texts for all available commands */
|
||||
static public String[] getUsages() {
|
||||
return new String[] { getUpdateUsage(), getCommandUsage(), getStatusUsage(), getItemsUsage(), getSayUsage(),
|
||||
getScriptUsage() };
|
||||
}
|
||||
|
||||
static public String getUpdateUsage() {
|
||||
return "update <item> <state> - sends a status update for an item";
|
||||
}
|
||||
|
||||
static public String getCommandUsage() {
|
||||
return "send <item> <command> - sends a command for an item";
|
||||
}
|
||||
|
||||
static public String getStatusUsage() {
|
||||
return "status <item> - shows the current status of an item";
|
||||
}
|
||||
|
||||
static public String getItemsUsage() {
|
||||
return "items [<pattern>] - lists names and types of all items matching the pattern";
|
||||
}
|
||||
|
||||
public static String getSayUsage() {
|
||||
return "say <sentence to say> - Says a message through TTS on the host machine";
|
||||
}
|
||||
|
||||
public static String getScriptUsage() {
|
||||
return "> <script to execute> - Executes a script";
|
||||
}
|
||||
|
||||
}
|
||||
-378
@@ -1,378 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2015-2016 by the respective copyright holders.
|
||||
*
|
||||
* 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.openhab.io.multimedia.actions;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.Socket;
|
||||
import java.net.URL;
|
||||
import java.net.URLConnection;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import javax.sound.sampled.AudioInputStream;
|
||||
import javax.sound.sampled.AudioSystem;
|
||||
import javax.sound.sampled.Clip;
|
||||
import javax.sound.sampled.FloatControl;
|
||||
import javax.sound.sampled.LineUnavailableException;
|
||||
import javax.sound.sampled.Mixer;
|
||||
import javax.sound.sampled.Port;
|
||||
import javax.sound.sampled.UnsupportedAudioFileException;
|
||||
|
||||
import org.apache.commons.collections.Closure;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.eclipse.smarthome.config.core.ConfigConstants;
|
||||
import org.openhab.core.library.types.PercentType;
|
||||
import org.openhab.core.scriptengine.action.ActionDoc;
|
||||
import org.openhab.core.scriptengine.action.ParamDoc;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javazoom.jl.decoder.JavaLayerException;
|
||||
import javazoom.jl.player.Player;
|
||||
|
||||
public class Audio {
|
||||
|
||||
private static final String SOUND_DIR = "sounds";
|
||||
private static final Logger logger = LoggerFactory.getLogger(Audio.class);
|
||||
|
||||
private static final Pattern plsStreamPattern = Pattern.compile("^File[0-9]=(.+)$");
|
||||
|
||||
private static Float macVolumeValue = null;
|
||||
|
||||
private static Player streamPlayer = null;
|
||||
|
||||
private static Socket shoutCastSocket = null;
|
||||
|
||||
@ActionDoc(text = "plays a sound from the sounds folder")
|
||||
static public void playSound(@ParamDoc(name = "filename", text = "the filename with extension") String filename) {
|
||||
try {
|
||||
InputStream is = new FileInputStream(
|
||||
ConfigConstants.getConfigFolder() + File.separator + SOUND_DIR + File.separator + filename);
|
||||
if (filename.toLowerCase().endsWith(".mp3")) {
|
||||
Player player = new Player(is);
|
||||
playInThread(player);
|
||||
} else {
|
||||
AudioInputStream ais = AudioSystem.getAudioInputStream(is);
|
||||
Clip clip = AudioSystem.getClip();
|
||||
clip.open(ais);
|
||||
playInThread(clip);
|
||||
}
|
||||
} catch (FileNotFoundException e) {
|
||||
logger.error("Cannot play sound '{}': {}", new Object[] { filename, e.getMessage() });
|
||||
} catch (JavaLayerException e) {
|
||||
logger.error("Cannot play sound '{}': {}", new Object[] { filename, e.getMessage() });
|
||||
} catch (UnsupportedAudioFileException e) {
|
||||
logger.error("Format of sound file '{}' is not supported: {}", new Object[] { filename, e.getMessage() });
|
||||
} catch (IOException e) {
|
||||
logger.error("Cannot play sound '{}': {}", new Object[] { filename, e.getMessage() });
|
||||
} catch (LineUnavailableException e) {
|
||||
logger.error("Cannot play sound '{}': {}", new Object[] { filename, e.getMessage() });
|
||||
}
|
||||
}
|
||||
|
||||
@ActionDoc(text = "plays an audio stream from an url")
|
||||
static public synchronized void playStream(
|
||||
@ParamDoc(name = "url", text = "the url of the audio stream") String url) {
|
||||
if (streamPlayer != null) {
|
||||
// if we are already playing a stream, stop it first
|
||||
streamPlayer.close();
|
||||
streamPlayer = null;
|
||||
}
|
||||
if (url == null) {
|
||||
// the call was only for stopping the currently playing stream
|
||||
return;
|
||||
}
|
||||
try {
|
||||
if (url.toLowerCase().endsWith(".m3u")) {
|
||||
InputStream is = new URL(url).openStream();
|
||||
String urls = IOUtils.toString(is);
|
||||
for (String line : urls.split("\n")) {
|
||||
if (!line.isEmpty() && !line.startsWith("#")) {
|
||||
url = line;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else if (url.toLowerCase().endsWith(".pls")) {
|
||||
InputStream is = new URL(url).openStream();
|
||||
String urls = IOUtils.toString(is);
|
||||
for (String line : urls.split("\n")) {
|
||||
if (!line.isEmpty() && line.startsWith("File")) {
|
||||
Matcher matcher = plsStreamPattern.matcher(line);
|
||||
if (matcher.find()) {
|
||||
url = matcher.group(1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
URL streamUrl = new URL(url);
|
||||
URLConnection connection = streamUrl.openConnection();
|
||||
InputStream is = null;
|
||||
if (connection.getContentType().equals("unknown/unknown")) {
|
||||
// Java does not parse non-standard headers used by SHOUTCast
|
||||
int port = streamUrl.getPort() > 0 ? streamUrl.getPort() : 80;
|
||||
// Manipulate User-Agent to receive a stream
|
||||
shoutCastSocket = new Socket(streamUrl.getHost(), port);
|
||||
|
||||
OutputStream os = shoutCastSocket.getOutputStream();
|
||||
String user_agent = "WinampMPEG/5.09";
|
||||
String req = "GET / HTTP/1.0\r\nuser-agent: " + user_agent
|
||||
+ "\r\nIcy-MetaData: 1\r\nConnection: keep-alive\r\n\r\n";
|
||||
os.write(req.getBytes());
|
||||
is = shoutCastSocket.getInputStream();
|
||||
} else {
|
||||
is = streamUrl.openStream();
|
||||
}
|
||||
if (is != null) {
|
||||
Player player = new Player(is);
|
||||
streamPlayer = player;
|
||||
playInThread(player);
|
||||
}
|
||||
} catch (JavaLayerException e) {
|
||||
logger.error("Cannot play stream '{}': JavaLayerException - {}", url, e.getMessage());
|
||||
} catch (MalformedURLException e) {
|
||||
logger.error("Cannot play stream '{}': MalformedURLException - {}", url, e.getMessage());
|
||||
} catch (IOException e) {
|
||||
logger.error("Cannot play stream '{}': {}", url, e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Says the given text..
|
||||
*
|
||||
* <p>
|
||||
* This method checks for registered TTS services. If there is a service
|
||||
* available for the current OS, this will be chosen. Otherwise, it
|
||||
* will pick a (the first) TTS service that is platform-independent.
|
||||
* </p>
|
||||
*
|
||||
* @param text the text to speak
|
||||
*/
|
||||
@ActionDoc(text = "says a given text through the default TTS service")
|
||||
static public void say(@ParamDoc(name = "text") Object text) {
|
||||
AudioActionService.voiceManager.say(text.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* Text-to-speech with a given voice.
|
||||
*
|
||||
* <p>
|
||||
* This method checks for registered TTS services. If there is a service
|
||||
* available for the current OS, this will be chosen. Otherwise, it
|
||||
* will pick a (the first) TTS service that is platform-independent.
|
||||
* </p>
|
||||
*
|
||||
* @param text the text to speak
|
||||
* @param voice the name of the voice to use or null, if the default voice should be used
|
||||
*/
|
||||
@ActionDoc(text = "says a given text through the default TTS service with a given voice")
|
||||
static public void say(@ParamDoc(name = "text") Object text, @ParamDoc(name = "voice") String voice) {
|
||||
AudioActionService.voiceManager.say(text.toString(), voice);
|
||||
}
|
||||
|
||||
/**
|
||||
* Text-to-speech with a given voice.
|
||||
*
|
||||
* <p>
|
||||
* This method checks for registered TTS services. If there is a service
|
||||
* available for the current OS, this will be chosen. Otherwise, it
|
||||
* will pick a (the first) TTS service that is platform-independent.
|
||||
* </p>
|
||||
*
|
||||
* @param text the text to speak
|
||||
* @param voice the name of the voice to use or null, if the default voice should be used
|
||||
* @param device the name of audio device to be used to play the audio or null, if the default output device should
|
||||
* be used
|
||||
*/
|
||||
@ActionDoc(text = "says a given text through the default TTS service with a given voice")
|
||||
static public void say(@ParamDoc(name = "text") Object text, @ParamDoc(name = "voice") String voice,
|
||||
@ParamDoc(name = "sink") String sink) {
|
||||
AudioActionService.voiceManager.say(text.toString(), voice, sink);
|
||||
}
|
||||
|
||||
@ActionDoc(text = "sets the master volume of the host")
|
||||
static public void setMasterVolume(
|
||||
@ParamDoc(name = "volume", text = "volume in the range [0,1]") final float volume) throws IOException {
|
||||
if (volume < 0 || volume > 1) {
|
||||
throw new IllegalArgumentException("Volume value must be in the range [0,1]!");
|
||||
}
|
||||
if (isMacOSX()) {
|
||||
macVolumeValue = volume;
|
||||
setMasterVolumeMac(volume * 100f);
|
||||
} else {
|
||||
setMasterVolumeJavaSound(volume);
|
||||
}
|
||||
}
|
||||
|
||||
@ActionDoc(text = "sets the master volume of the host")
|
||||
static public void setMasterVolume(@ParamDoc(name = "percent") final PercentType percent) throws IOException {
|
||||
setMasterVolume(percent.toBigDecimal().floatValue() / 100f);
|
||||
}
|
||||
|
||||
private static void setMasterVolumeMac(float volume) throws IOException {
|
||||
Runtime.getRuntime().exec(new String[] { "osascript", "-e", "set volume output volume " + volume });
|
||||
}
|
||||
|
||||
private static void setMasterVolumeJavaSound(final float volume) {
|
||||
runVolumeCommand(new Closure() {
|
||||
@Override
|
||||
public void execute(Object input) {
|
||||
FloatControl volumeControl = (FloatControl) input;
|
||||
volumeControl.setValue(volume);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@ActionDoc(text = "increases the master volume of the host")
|
||||
static public void increaseMasterVolume(@ParamDoc(name = "percent") final float percent) throws IOException {
|
||||
if (percent <= 0 || percent > 100) {
|
||||
throw new IllegalArgumentException("Percent must be in the range (0,100]!");
|
||||
}
|
||||
Float volume = getMasterVolume();
|
||||
if (volume == 0) {
|
||||
// as increasing 0 by x percent will still be 0, we have to set some initial positive value
|
||||
volume = 0.001f;
|
||||
}
|
||||
float newVolume = volume * (1f + percent / 100f);
|
||||
if (isMacOSX() && newVolume - volume < .01) {
|
||||
// the getMasterVolume() only returns integers, so we have to make sure that we
|
||||
// increase the volume level at least by 1%.
|
||||
newVolume += .01;
|
||||
}
|
||||
if (newVolume > 1) {
|
||||
newVolume = 1;
|
||||
}
|
||||
setMasterVolume(newVolume);
|
||||
}
|
||||
|
||||
@ActionDoc(text = "decreases the master volume of the host")
|
||||
static public void decreaseMasterVolume(@ParamDoc(name = "percent") final float percent) throws IOException {
|
||||
if (percent <= 0 || percent > 100) {
|
||||
throw new IllegalArgumentException("Percent must be in the range (0,100]!");
|
||||
}
|
||||
float volume = getMasterVolume();
|
||||
float newVolume = volume * (1f - percent / 100f);
|
||||
if (isMacOSX() && newVolume > 0 && volume - newVolume < .01) {
|
||||
// the getMasterVolume() only returns integers, so we have to make sure that we
|
||||
// decrease the volume level at least by 1%.
|
||||
newVolume -= .01;
|
||||
}
|
||||
if (newVolume < 0) {
|
||||
newVolume = 0;
|
||||
}
|
||||
setMasterVolume(newVolume);
|
||||
}
|
||||
|
||||
@ActionDoc(text = "gets the master volume of the host", returns = "volume as a float in the range [0,1]")
|
||||
static public float getMasterVolume() throws IOException {
|
||||
if (isMacOSX()) {
|
||||
return getMasterVolumeMac();
|
||||
} else {
|
||||
return getMasterVolumeJavaSound();
|
||||
}
|
||||
}
|
||||
|
||||
private static synchronized float getMasterVolumeMac() throws IOException {
|
||||
// we use a cache of the value as the script execution is pretty slow
|
||||
if (macVolumeValue == null) {
|
||||
Process p = Runtime.getRuntime()
|
||||
.exec(new String[] { "osascript", "-e", "output volume of (get volume settings)" });
|
||||
String value = IOUtils.toString(p.getInputStream()).trim();
|
||||
macVolumeValue = Float.valueOf(value) / 100f;
|
||||
}
|
||||
return macVolumeValue;
|
||||
}
|
||||
|
||||
private static float getMasterVolumeJavaSound() throws IOException {
|
||||
final Float[] volumes = new Float[1];
|
||||
runVolumeCommand(new Closure() {
|
||||
@Override
|
||||
public void execute(Object input) {
|
||||
FloatControl volumeControl = (FloatControl) input;
|
||||
volumes[0] = volumeControl.getValue();
|
||||
}
|
||||
});
|
||||
if (volumes[0] != null) {
|
||||
return volumes[0];
|
||||
} else {
|
||||
throw new IOException("Cannot determine master volume level");
|
||||
}
|
||||
}
|
||||
|
||||
private static void runVolumeCommand(Closure closure) {
|
||||
Mixer.Info[] infos = AudioSystem.getMixerInfo();
|
||||
for (Mixer.Info info : infos) {
|
||||
Mixer mixer = AudioSystem.getMixer(info);
|
||||
if (mixer.isLineSupported(Port.Info.SPEAKER)) {
|
||||
Port port;
|
||||
try {
|
||||
port = (Port) mixer.getLine(Port.Info.SPEAKER);
|
||||
port.open();
|
||||
if (port.isControlSupported(FloatControl.Type.VOLUME)) {
|
||||
FloatControl volume = (FloatControl) port.getControl(FloatControl.Type.VOLUME);
|
||||
closure.execute(volume);
|
||||
}
|
||||
port.close();
|
||||
} catch (LineUnavailableException e) {
|
||||
logger.error("Cannot access master volume control", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void playInThread(final Clip clip) {
|
||||
// run in new thread
|
||||
new Thread() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
clip.start();
|
||||
while (clip.isActive()) {
|
||||
sleep(1000L);
|
||||
}
|
||||
clip.close();
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e.getMessage());
|
||||
}
|
||||
}
|
||||
}.start();
|
||||
}
|
||||
|
||||
static private void playInThread(final Player player) {
|
||||
// run in new thread
|
||||
new Thread() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
player.play();
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e.getMessage());
|
||||
} finally {
|
||||
if (shoutCastSocket != null) {
|
||||
try {
|
||||
shoutCastSocket.close();
|
||||
} catch (IOException e) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}.start();
|
||||
}
|
||||
|
||||
private static boolean isMacOSX() {
|
||||
return System.getProperty("osgi.os").equals("macosx");
|
||||
}
|
||||
|
||||
}
|
||||
-35
@@ -1,35 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2015-2016 by the respective copyright holders.
|
||||
*
|
||||
* 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.openhab.io.multimedia.actions;
|
||||
|
||||
import org.eclipse.smarthome.core.voice.VoiceManager;
|
||||
import org.eclipse.smarthome.model.script.engine.action.ActionService;
|
||||
|
||||
public class AudioActionService implements ActionService {
|
||||
|
||||
public static VoiceManager voiceManager;
|
||||
|
||||
@Override
|
||||
public String getActionClassName() {
|
||||
return Audio.class.getCanonicalName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> getActionClass() {
|
||||
return Audio.class;
|
||||
}
|
||||
|
||||
protected void setVoiceManager(VoiceManager voiceManager) {
|
||||
AudioActionService.voiceManager = voiceManager;
|
||||
}
|
||||
|
||||
protected void unsetVoiceManager(VoiceManager voiceManager) {
|
||||
AudioActionService.voiceManager = null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<classpath>
|
||||
<classpathentry kind="src" path="src/main/java"/>
|
||||
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.7"/>
|
||||
<classpathentry kind="con" path="aQute.bnd.classpath.container"/>
|
||||
<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
|
||||
<classpathentry kind="lib" path="lib/jl1.0.1.jar"/>
|
||||
<classpathentry kind="output" path="target/classes"/>
|
||||
</classpath>
|
||||
@@ -0,0 +1,38 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<projectDescription>
|
||||
<name>org.openhab.io.sound</name>
|
||||
<comment></comment>
|
||||
<projects>
|
||||
</projects>
|
||||
<buildSpec>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.jdt.core.javabuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
<buildCommand>
|
||||
<name>bndtools.core.bndbuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.pde.ManifestBuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.pde.SchemaBuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.pde.ds.core.builder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
</buildSpec>
|
||||
<natures>
|
||||
<nature>org.eclipse.jdt.core.javanature</nature>
|
||||
<nature>org.eclipse.pde.PluginNature</nature>
|
||||
</natures>
|
||||
</projectDescription>
|
||||
@@ -0,0 +1,16 @@
|
||||
Manifest-Version: 1.0
|
||||
Bundle-ManifestVersion: 2
|
||||
Bundle-Name: openHAB Sound Support
|
||||
Bundle-SymbolicName: org.openhab.io.sound
|
||||
Bundle-Version: 2.0.0.qualifier
|
||||
Import-Package: org.apache.commons.collections,
|
||||
org.apache.commons.io,
|
||||
org.eclipse.smarthome.core.audio,
|
||||
org.eclipse.smarthome.core.common,
|
||||
org.slf4j
|
||||
Service-Component: OSGI-INF/*.xml
|
||||
Bundle-RequiredExecutionEnvironment: JavaSE-1.7
|
||||
Fragment-Host: org.eclipse.smarthome.io.javasound
|
||||
Bundle-ClassPath: lib/jl1.0.1.jar,
|
||||
.
|
||||
Bundle-ActivationPolicy: lazy
|
||||
@@ -0,0 +1,16 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
|
||||
Copyright (c) 2014-2016 by the respective copyright holders.
|
||||
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
|
||||
|
||||
-->
|
||||
<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" immediate="true" name="org.eclipse.smarthome.io.audio.enhancedjavasound">
|
||||
<implementation class="org.openhab.io.sound.internal.EnhancedJavaSoundAudioSink"/>
|
||||
<service>
|
||||
<provide interface="org.eclipse.smarthome.core.audio.AudioSink"/>
|
||||
</service>
|
||||
</scr:component>
|
||||
@@ -0,0 +1,5 @@
|
||||
bin.includes = META-INF/,\
|
||||
.,\
|
||||
OSGI-INF/,\
|
||||
lib/jl1.0.1.jar
|
||||
source.. = src/main/java
|
||||
@@ -0,0 +1,15 @@
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<packaging>eclipse-plugin</packaging>
|
||||
<parent>
|
||||
<groupId>org.openhab.core</groupId>
|
||||
<artifactId>pom-bundles</artifactId>
|
||||
<version>2.0.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>org.openhab.io.sound</artifactId>
|
||||
<name>openHAB Sound Support</name>
|
||||
|
||||
</project>
|
||||
+117
@@ -0,0 +1,117 @@
|
||||
/**
|
||||
* Copyright (c) 2014-2016 by the respective copyright holders.
|
||||
* 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.openhab.io.sound.internal;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.HashSet;
|
||||
import java.util.Locale;
|
||||
import java.util.Set;
|
||||
|
||||
import org.eclipse.smarthome.core.audio.AudioFormat;
|
||||
import org.eclipse.smarthome.core.audio.AudioStream;
|
||||
import org.eclipse.smarthome.core.audio.URLAudioStream;
|
||||
import org.eclipse.smarthome.core.audio.UnsupportedAudioFormatException;
|
||||
import org.eclipse.smarthome.io.javasound.internal.JavaSoundAudioSink;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javazoom.jl.decoder.JavaLayerException;
|
||||
import javazoom.jl.player.Player;
|
||||
|
||||
/**
|
||||
* This is an audio sink that is registered as a service, which can play mp3 files to the hosts outputs (e.g. speaker,
|
||||
* line-out).
|
||||
*
|
||||
* @author Karel Goderis - Initial contribution and API
|
||||
* @author Kai Kreuzer - Refactored and moved to openHAB
|
||||
*/
|
||||
public class EnhancedJavaSoundAudioSink extends JavaSoundAudioSink {
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(EnhancedJavaSoundAudioSink.class);
|
||||
|
||||
private static AudioFormat mp3 = new AudioFormat(AudioFormat.CONTAINER_NONE, AudioFormat.CODEC_MP3, null, null,
|
||||
null, null);
|
||||
private static AudioFormat wav = new AudioFormat(AudioFormat.CONTAINER_WAVE, AudioFormat.CODEC_PCM_SIGNED, null,
|
||||
null, null, null);
|
||||
private static HashSet<AudioFormat> supportedFormats = new HashSet<>();
|
||||
|
||||
static {
|
||||
supportedFormats.add(wav);
|
||||
supportedFormats.add(mp3);
|
||||
}
|
||||
|
||||
private static Player streamPlayer = null;
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return "enhancedjavasound";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getLabel(Locale locale) {
|
||||
return "System Speaker";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void process(final AudioStream audioStream) throws UnsupportedAudioFormatException {
|
||||
if (audioStream != null && audioStream.getFormat().getCodec() != AudioFormat.CODEC_MP3) {
|
||||
// we can only deal with mp3, so delegate the rest
|
||||
super.process(audioStream);
|
||||
} else {
|
||||
if (audioStream == null || audioStream instanceof URLAudioStream) {
|
||||
// we are dealing with an infinite stream here
|
||||
if (streamPlayer != null) {
|
||||
// if we are already playing a stream, stop it first
|
||||
streamPlayer.close();
|
||||
streamPlayer = null;
|
||||
}
|
||||
if (audioStream == null) {
|
||||
// the call was only for stopping the currently playing stream
|
||||
return;
|
||||
}
|
||||
}
|
||||
try (InputStream stream = audioStream) {
|
||||
Player player = new Player(stream);
|
||||
streamPlayer = player;
|
||||
playInThread(player);
|
||||
} catch (JavaLayerException | IOException e) {
|
||||
logger.error("An exception occurred while playing audio : '{}'", e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<AudioFormat> getSupportedFormats() {
|
||||
return supportedFormats;
|
||||
}
|
||||
|
||||
static private void playInThread(final Player player) {
|
||||
// run in new thread
|
||||
new Thread() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
player.play();
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e.getMessage());
|
||||
} finally {
|
||||
player.close();
|
||||
}
|
||||
}
|
||||
}.start();
|
||||
}
|
||||
|
||||
protected void deactivate() {
|
||||
if (streamPlayer != null) {
|
||||
// stop playing streams on shutdown
|
||||
streamPlayer.close();
|
||||
streamPlayer = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -23,6 +23,7 @@
|
||||
<module>org.openhab.io.transport.serial</module>
|
||||
<module>org.openhab.io.jetty.certificate</module>
|
||||
<module>org.openhab.io.rest.docs</module>
|
||||
<module>org.openhab.io.sound</module>
|
||||
<module>org.openhab.ui.dashboard</module>
|
||||
<module>org.openhab.ui.basicui</module>
|
||||
<module>org.openhab.ui.classicui</module>
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
<bundle prerequisite="true">mvn:org.apache.karaf.shell/org.apache.karaf.shell.core/${karaf.version}</bundle>
|
||||
<bundle prerequisite="true">mvn:org.apache.karaf.wrapper/org.apache.karaf.wrapper.core/${karaf.version}</bundle>
|
||||
<bundle>mvn:org.openhab.core/org.openhab.core.karaf/${project.version}</bundle>
|
||||
<bundle>mvn:org.openhab.core/org.openhab.io.sound/${project.version}</bundle>
|
||||
</feature>
|
||||
|
||||
<feature name="openhab-runtime-certificate" description="SSL Certificate Generator" version="${project.version}">
|
||||
|
||||
Reference in New Issue
Block a user