Output Volume

Discuss client development (or even MPD development if you feel so inclined), ask questions about the client libs, MPD feature requests from client developers, etc...
Post Reply
Bobboau
Posts: 3
Joined: September 8th, 2015, 12:04 pm

Output Volume

Post by Bobboau » September 8th, 2015, 12:20 pm

I would like to be able to set output volumes independently of each other. So if I have two outputs A and B I would like to be able to set B to be 50% via a client (as a protocol command I would presume). Ideally I would think this would work as if I set the normal volume to 50% and A was at 100% then the actual volume coming out of my two outputs would be A:50% and B:25%. I don't think there is a way to do this right now, am I wrong? is there a way? if not I would think adding an optional parameter to setvol or having a separate command (like maybe setoutputvol) which requires an output id in addition to a volume level would be a good candidate interface, but I have no idea how difficult that would be to implement and I would guess it would be pretty low on the priorities. I'm trying to setup a zoned sound system and this is really the biggest fly in my ointment at the moment, I can only seem to turn outputs on or off, but not control how loud they are in a direct manner (I can change volume system wide, and I can sort of stairstep relative volume, but this is far from ideal).

suggestions? thoughts?

max
Forum team
Posts: 922
Joined: January 15th, 2013, 3:43 pm

Re: Output Volume

Post by max » September 8th, 2015, 5:09 pm

There's no way to do that currently, but it would be a good feature.

Bobboau
Posts: 3
Joined: September 8th, 2015, 12:04 pm

Re: Output Volume

Post by Bobboau » October 3rd, 2015, 1:46 am

Well, I made a feature request in the bugtracker a while ago, with no response yet.

Just to see what would be involved I hacked the source a bit tonight, and made an implementation of this. From looking around the source I can bet I've missed some function call for notifying something somewhere or didn't use the right wrapping method, but at the very least I think it shows this is feasible.

Here's a patch if anyone is interested.
Maybe someone who knows what they are doing can tell me what I got wrong
(would have attached as a file, but I can't find an extension the forum software likes)

Code: Select all

diff --git a/src/command/AllCommands.cxx b/src/command/AllCommands.cxx
index 8e8865f..40e5e1f 100644
--- a/src/command/AllCommands.cxx
+++ b/src/command/AllCommands.cxx
@@ -134,6 +134,7 @@ static constexpr struct command commands[] = {
 	{ "next", PERMISSION_CONTROL, 0, 0, handle_next },
 	{ "notcommands", PERMISSION_NONE, 0, 0, handle_not_commands },
 	{ "outputs", PERMISSION_READ, 0, 0, handle_devices },
+	{ "outputvolume", PERMISSION_ADMIN, 2, 2, handle_outputvolume },
 	{ "password", PERMISSION_NONE, 1, 1, handle_password },
 	{ "pause", PERMISSION_CONTROL, 0, 1, handle_pause },
 	{ "ping", PERMISSION_NONE, 0, 0, handle_ping },
diff --git a/src/command/OutputCommands.cxx b/src/command/OutputCommands.cxx
index 7bbe5f9..654b8cc 100644
--- a/src/command/OutputCommands.cxx
+++ b/src/command/OutputCommands.cxx
@@ -83,3 +83,33 @@ handle_devices(Client &client, gcc_unused Request args, Response &r)
 	printAudioDevices(r, client.partition.outputs);
 	return CommandResult::OK;
 }
+
+CommandResult
+handle_outputvolume(Client &client, Request args, Response &r)
+{
+	assert(args.size == 2);
+	unsigned device;
+	if (!args.Parse(0, device, r))
+		return CommandResult::ERROR;
+
+	if (device > client.partition.outputs.Size()-1 || device < 0) {
+		r.Error(ACK_ERROR_NO_EXIST, "No such audio output");
+		return CommandResult::ERROR;
+	}
+
+	unsigned volume;
+	if (!args.Parse(1, volume, r))
+		return CommandResult::ERROR;
+
+	if (volume > 100 || volume < 0) {
+		r.Error(ACK_ERROR_NO_EXIST, "volume must be in the range 0 - 100");
+		return CommandResult::ERROR;
+	}
+
+	if (!audio_output_set_volume(client.partition.outputs, device, volume)) {
+		r.Error(ACK_ERROR_NO_EXIST, "could not set volume on the output");
+		return CommandResult::ERROR;
+	}
+
+	return CommandResult::OK;
+}
diff --git a/src/command/OutputCommands.hxx b/src/command/OutputCommands.hxx
index 3dd81bc..0673e59 100644
--- a/src/command/OutputCommands.hxx
+++ b/src/command/OutputCommands.hxx
@@ -38,4 +38,7 @@ handle_toggleoutput(Client &client, Request request, Response &response);
 CommandResult
 handle_devices(Client &client, Request request, Response &response);
 
+CommandResult
+handle_outputvolume(Client &client, Request request, Response &response);
+
 #endif
diff --git a/src/output/OutputCommand.cxx b/src/output/OutputCommand.cxx
index 83abcf2..7d0fedd 100644
--- a/src/output/OutputCommand.cxx
+++ b/src/output/OutputCommand.cxx
@@ -31,6 +31,8 @@
 #include "player/Control.hxx"
 #include "mixer/MixerControl.hxx"
 #include "Idle.hxx"
+#include "mixer/MixerInternal.hxx"
+#include "util/Error.hxx"
 
 extern unsigned audio_output_state_version;
 
@@ -104,3 +106,30 @@ audio_output_toggle_index(MultipleOutputs &outputs, unsigned idx)
 
 	return true;
 }
+
+bool
+audio_output_set_volume(MultipleOutputs &outputs, unsigned idx, unsigned volume)
+{
+	if (idx >= outputs.Size())
+		return false;
+
+	AudioOutput &ao = outputs.Get(idx);
+	if (ao.mixer) {
+		bool it_worked = mixer_set_volume(ao.mixer, volume, IgnoreError());
+		if (!it_worked) {
+			return false;
+		}
+	}
+	else {
+		return false;
+	}
+
+	idle_add(IDLE_MIXER);
+	idle_add(IDLE_OUTPUT);
+
+	ao.player_control->UpdateAudio();
+
+	++audio_output_state_version;
+
+	return true;
+}
diff --git a/src/output/OutputCommand.hxx b/src/output/OutputCommand.hxx
index 5b53cd1..e0918b2 100644
--- a/src/output/OutputCommand.hxx
+++ b/src/output/OutputCommand.hxx
@@ -50,4 +50,11 @@ audio_output_disable_index(MultipleOutputs &outputs, unsigned idx);
 bool
 audio_output_toggle_index(MultipleOutputs &outputs, unsigned idx);
 
+/**
+ * Sets the volume of an audio output.  Returns false if the specified output
+ * does not exist, or if it's volume cannot be set.
+ */
+bool
+audio_output_set_volume(MultipleOutputs &outputs, unsigned idx, unsigned volume);
+
 #endif
diff --git a/src/output/OutputPrint.cxx b/src/output/OutputPrint.cxx
index d2ddbbf..26916f9 100644
--- a/src/output/OutputPrint.cxx
+++ b/src/output/OutputPrint.cxx
@@ -27,6 +27,8 @@
 #include "MultipleOutputs.hxx"
 #include "Internal.hxx"
 #include "client/Response.hxx"
+#include "mixer/MixerInternal.hxx"
+#include "util/Error.hxx"
 
 void
 printAudioDevices(Response &r, const MultipleOutputs &outputs)
@@ -38,5 +40,15 @@ printAudioDevices(Response &r, const MultipleOutputs &outputs)
 			 "outputname: %s\n"
 			 "outputenabled: %i\n",
 			 i, ao.name, ao.enabled);
+
+		//if they have independent volume report it
+		if(ao.mixer)
+		{
+			int volume = ao.mixer->GetVolume(IgnoreError());
+			if(volume > -1) //-1 indicates an error state
+			{
+				r.Format("outputvolume: %i\n",volume);
+			}
+		}
 	}
 }

max
Forum team
Posts: 922
Joined: January 15th, 2013, 3:43 pm

Re: Output Volume

Post by max » October 16th, 2015, 3:33 pm

Submit this to the developer mailing list. On the forum, it's hard to review code and very cumbersome to apply a patch from.

andyclimb
Posts: 1
Joined: March 29th, 2016, 4:50 pm

Re: Output Volume

Post by andyclimb » March 29th, 2016, 4:54 pm

I came here specifically to look for / ask for this feature.. + 1 please

Post Reply