Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
M
mpd
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Registry
Registry
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Иван Мажукин
mpd
Commits
c16233fa
Commit
c16233fa
authored
Sep 26, 2019
by
Max Kellermann
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
add "moveoutput" command
parent
ac126ede
Hide whitespace changes
Inline
Side-by-side
Showing
9 changed files
with
157 additions
and
7 deletions
+157
-7
NEWS
NEWS
+1
-0
protocol.rst
doc/protocol.rst
+3
-0
AllCommands.cxx
src/command/AllCommands.cxx
+1
-0
PartitionCommands.cxx
src/command/PartitionCommands.cxx
+42
-0
PartitionCommands.hxx
src/command/PartitionCommands.hxx
+3
-0
Control.cxx
src/output/Control.cxx
+65
-7
Control.hxx
src/output/Control.hxx
+19
-0
MultipleOutputs.cxx
src/output/MultipleOutputs.cxx
+16
-0
MultipleOutputs.hxx
src/output/MultipleOutputs.hxx
+7
-0
No files found.
NEWS
View file @
c16233fa
...
@@ -3,6 +3,7 @@ ver 0.22 (not yet released)
...
@@ -3,6 +3,7 @@ ver 0.22 (not yet released)
- "findadd"/"searchadd"/"searchaddpl" support the "sort" and
- "findadd"/"searchadd"/"searchaddpl" support the "sort" and
"window" parameters
"window" parameters
- add command "readpicture" to download embedded pictures
- add command "readpicture" to download embedded pictures
- command "moveoutput" moves an output between partitions
* tags
* tags
- new tags "Grouping" (for ID3 "TIT1"), "Work" and "Conductor"
- new tags "Grouping" (for ID3 "TIT1"), "Work" and "Conductor"
* input
* input
...
...
doc/protocol.rst
View file @
c16233fa
...
@@ -1258,6 +1258,9 @@ client is assigned to one partition at a time.
...
@@ -1258,6 +1258,9 @@ client is assigned to one partition at a time.
:command:`newpartition {NAME}`
:command:`newpartition {NAME}`
Create a new partition.
Create a new partition.
:command:`moveoutput {OUTPUTNAME}`
Move an output to the current partition.
Audio output devices
Audio output devices
====================
====================
...
...
src/command/AllCommands.cxx
View file @
c16233fa
...
@@ -139,6 +139,7 @@ static constexpr struct command commands[] = {
...
@@ -139,6 +139,7 @@ static constexpr struct command commands[] = {
#endif
#endif
{
"move"
,
PERMISSION_CONTROL
,
2
,
2
,
handle_move
},
{
"move"
,
PERMISSION_CONTROL
,
2
,
2
,
handle_move
},
{
"moveid"
,
PERMISSION_CONTROL
,
2
,
2
,
handle_moveid
},
{
"moveid"
,
PERMISSION_CONTROL
,
2
,
2
,
handle_moveid
},
{
"moveoutput"
,
PERMISSION_ADMIN
,
1
,
1
,
handle_moveoutput
},
{
"newpartition"
,
PERMISSION_ADMIN
,
1
,
1
,
handle_newpartition
},
{
"newpartition"
,
PERMISSION_ADMIN
,
1
,
1
,
handle_newpartition
},
{
"next"
,
PERMISSION_CONTROL
,
0
,
0
,
handle_next
},
{
"next"
,
PERMISSION_CONTROL
,
0
,
0
,
handle_next
},
{
"notcommands"
,
PERMISSION_NONE
,
0
,
0
,
handle_not_commands
},
{
"notcommands"
,
PERMISSION_NONE
,
0
,
0
,
handle_not_commands
},
...
...
src/command/PartitionCommands.cxx
View file @
c16233fa
...
@@ -22,6 +22,7 @@
...
@@ -22,6 +22,7 @@
#include "Instance.hxx"
#include "Instance.hxx"
#include "Partition.hxx"
#include "Partition.hxx"
#include "IdleFlags.hxx"
#include "IdleFlags.hxx"
#include "output/Filtered.hxx"
#include "client/Client.hxx"
#include "client/Client.hxx"
#include "client/Response.hxx"
#include "client/Response.hxx"
#include "util/CharUtil.hxx"
#include "util/CharUtil.hxx"
...
@@ -113,3 +114,44 @@ handle_newpartition(Client &client, Request request, Response &response)
...
@@ -113,3 +114,44 @@ handle_newpartition(Client &client, Request request, Response &response)
return
CommandResult
::
OK
;
return
CommandResult
::
OK
;
}
}
CommandResult
handle_moveoutput
(
Client
&
client
,
Request
request
,
Response
&
response
)
{
const
char
*
output_name
=
request
[
0
];
auto
&
dest_partition
=
client
.
GetPartition
();
auto
*
existing_output
=
dest_partition
.
outputs
.
FindByName
(
output_name
);
if
(
existing_output
!=
nullptr
&&
!
existing_output
->
IsDummy
())
/* this output is already in the specified partition,
so nothing needs to be done */
return
CommandResult
::
OK
;
/* find the partition which owns this output currently */
auto
&
instance
=
client
.
GetInstance
();
for
(
auto
&
partition
:
instance
.
partitions
)
{
if
(
&
partition
==
&
dest_partition
)
continue
;
auto
*
output
=
partition
.
outputs
.
FindByName
(
output_name
);
if
(
output
==
nullptr
||
output
->
IsDummy
())
continue
;
const
bool
was_enabled
=
output
->
IsEnabled
();
if
(
existing_output
!=
nullptr
)
/* move the output back where it once was */
existing_output
->
ReplaceDummy
(
output
->
Steal
(),
was_enabled
);
else
/* add it to the output list */
dest_partition
.
outputs
.
Add
(
output
->
Steal
(),
was_enabled
);
instance
.
EmitIdle
(
IDLE_OUTPUT
);
return
CommandResult
::
OK
;
}
response
.
Error
(
ACK_ERROR_NO_EXIST
,
"No such output"
);
return
CommandResult
::
ERROR
;
}
src/command/PartitionCommands.hxx
View file @
c16233fa
...
@@ -35,4 +35,7 @@ handle_listpartitions(Client &client, Request request, Response &response);
...
@@ -35,4 +35,7 @@ handle_listpartitions(Client &client, Request request, Response &response);
CommandResult
CommandResult
handle_newpartition
(
Client
&
client
,
Request
request
,
Response
&
response
);
handle_newpartition
(
Client
&
client
,
Request
request
,
Response
&
response
);
CommandResult
handle_moveoutput
(
Client
&
client
,
Request
request
,
Response
&
response
);
#endif
#endif
src/output/Control.cxx
View file @
c16233fa
...
@@ -19,6 +19,7 @@
...
@@ -19,6 +19,7 @@
#include "Control.hxx"
#include "Control.hxx"
#include "Filtered.hxx"
#include "Filtered.hxx"
#include "Client.hxx"
#include "mixer/MixerControl.hxx"
#include "mixer/MixerControl.hxx"
#include "config/Block.hxx"
#include "config/Block.hxx"
#include "Log.hxx"
#include "Log.hxx"
...
@@ -31,7 +32,9 @@ static constexpr PeriodClock::Duration REOPEN_AFTER = std::chrono::seconds(10);
...
@@ -31,7 +32,9 @@ static constexpr PeriodClock::Duration REOPEN_AFTER = std::chrono::seconds(10);
AudioOutputControl
::
AudioOutputControl
(
std
::
unique_ptr
<
FilteredAudioOutput
>
_output
,
AudioOutputControl
::
AudioOutputControl
(
std
::
unique_ptr
<
FilteredAudioOutput
>
_output
,
AudioOutputClient
&
_client
)
noexcept
AudioOutputClient
&
_client
)
noexcept
:
output
(
std
::
move
(
_output
)),
client
(
_client
),
:
output
(
std
::
move
(
_output
)),
name
(
output
->
GetName
()),
client
(
_client
),
thread
(
BIND_THIS_METHOD
(
Task
))
thread
(
BIND_THIS_METHOD
(
Task
))
{
{
}
}
...
@@ -49,40 +52,86 @@ AudioOutputControl::Configure(const ConfigBlock &block)
...
@@ -49,40 +52,86 @@ AudioOutputControl::Configure(const ConfigBlock &block)
enabled
=
block
.
GetBlockValue
(
"enabled"
,
true
);
enabled
=
block
.
GetBlockValue
(
"enabled"
,
true
);
}
}
std
::
unique_ptr
<
FilteredAudioOutput
>
AudioOutputControl
::
Steal
()
noexcept
{
assert
(
!
IsDummy
());
/* close and disable the output */
{
std
::
unique_lock
<
Mutex
>
lock
(
mutex
);
if
(
really_enabled
&&
output
->
SupportsEnableDisable
())
CommandWait
(
lock
,
Command
::
DISABLE
);
enabled
=
really_enabled
=
false
;
}
/* stop the thread */
StopThread
();
/* now we can finally remove it */
const
std
::
lock_guard
<
Mutex
>
protect
(
mutex
);
return
std
::
exchange
(
output
,
nullptr
);
}
void
AudioOutputControl
::
ReplaceDummy
(
std
::
unique_ptr
<
FilteredAudioOutput
>
new_output
,
bool
_enabled
)
noexcept
{
assert
(
IsDummy
());
assert
(
new_output
);
{
const
std
::
lock_guard
<
Mutex
>
protect
(
mutex
);
output
=
std
::
move
(
new_output
);
enabled
=
_enabled
;
}
client
.
ApplyEnabled
();
}
const
char
*
const
char
*
AudioOutputControl
::
GetName
()
const
noexcept
AudioOutputControl
::
GetName
()
const
noexcept
{
{
return
output
->
GetName
();
return
name
.
c_str
();
}
}
const
char
*
const
char
*
AudioOutputControl
::
GetPluginName
()
const
noexcept
AudioOutputControl
::
GetPluginName
()
const
noexcept
{
{
return
output
->
GetPluginName
()
;
return
output
?
output
->
GetPluginName
()
:
"dummy"
;
}
}
const
char
*
const
char
*
AudioOutputControl
::
GetLogName
()
const
noexcept
AudioOutputControl
::
GetLogName
()
const
noexcept
{
{
assert
(
!
IsDummy
());
return
output
->
GetLogName
();
return
output
->
GetLogName
();
}
}
Mixer
*
Mixer
*
AudioOutputControl
::
GetMixer
()
const
noexcept
AudioOutputControl
::
GetMixer
()
const
noexcept
{
{
return
output
->
mixe
r
;
return
output
?
output
->
mixer
:
nullpt
r
;
}
}
const
std
::
map
<
std
::
string
,
std
::
string
>
const
std
::
map
<
std
::
string
,
std
::
string
>
AudioOutputControl
::
GetAttributes
()
const
noexcept
AudioOutputControl
::
GetAttributes
()
const
noexcept
{
{
return
output
->
GetAttributes
();
return
output
?
output
->
GetAttributes
()
:
std
::
map
<
std
::
string
,
std
::
string
>
{};
}
}
void
void
AudioOutputControl
::
SetAttribute
(
std
::
string
&&
name
,
std
::
string
&&
value
)
AudioOutputControl
::
SetAttribute
(
std
::
string
&&
attribute_name
,
std
::
string
&&
value
)
{
{
output
->
SetAttribute
(
std
::
move
(
name
),
std
::
move
(
value
));
if
(
!
output
)
throw
std
::
runtime_error
(
"Cannot set attribute on dummy output"
);
output
->
SetAttribute
(
std
::
move
(
attribute_name
),
std
::
move
(
value
));
}
}
bool
bool
...
@@ -137,6 +186,9 @@ AudioOutputControl::LockCommandWait(Command cmd) noexcept
...
@@ -137,6 +186,9 @@ AudioOutputControl::LockCommandWait(Command cmd) noexcept
void
void
AudioOutputControl
::
EnableAsync
()
AudioOutputControl
::
EnableAsync
()
{
{
if
(
!
output
)
return
;
if
(
!
thread
.
IsDefined
())
{
if
(
!
thread
.
IsDefined
())
{
if
(
!
output
->
SupportsEnableDisable
())
{
if
(
!
output
->
SupportsEnableDisable
())
{
/* don't bother to start the thread now if the
/* don't bother to start the thread now if the
...
@@ -155,6 +207,9 @@ AudioOutputControl::EnableAsync()
...
@@ -155,6 +207,9 @@ AudioOutputControl::EnableAsync()
void
void
AudioOutputControl
::
DisableAsync
()
noexcept
AudioOutputControl
::
DisableAsync
()
noexcept
{
{
if
(
!
output
)
return
;
if
(
!
thread
.
IsDefined
())
{
if
(
!
thread
.
IsDefined
())
{
if
(
!
output
->
SupportsEnableDisable
())
if
(
!
output
->
SupportsEnableDisable
())
really_enabled
=
false
;
really_enabled
=
false
;
...
@@ -234,6 +289,9 @@ AudioOutputControl::CloseWait(std::unique_lock<Mutex> &lock) noexcept
...
@@ -234,6 +289,9 @@ AudioOutputControl::CloseWait(std::unique_lock<Mutex> &lock) noexcept
{
{
assert
(
allow_play
);
assert
(
allow_play
);
if
(
IsDummy
())
return
;
if
(
output
->
mixer
!=
nullptr
)
if
(
output
->
mixer
!=
nullptr
)
mixer_auto_close
(
output
->
mixer
);
mixer_auto_close
(
output
->
mixer
);
...
...
src/output/Control.hxx
View file @
c16233fa
...
@@ -50,6 +50,13 @@ class AudioOutputControl {
...
@@ -50,6 +50,13 @@ class AudioOutputControl {
std
::
unique_ptr
<
FilteredAudioOutput
>
output
;
std
::
unique_ptr
<
FilteredAudioOutput
>
output
;
/**
/**
* A copy of FilteredAudioOutput::name which we need just in
* case this is a "dummy" output (output==nullptr) because
* this output has been moved to another partitioncommands.
*/
const
std
::
string
name
;
/**
* The PlayerControl object which "owns" this output. This
* The PlayerControl object which "owns" this output. This
* object is needed to signal command completion.
* object is needed to signal command completion.
*/
*/
...
@@ -256,6 +263,10 @@ public:
...
@@ -256,6 +263,10 @@ public:
gcc_pure
gcc_pure
Mixer
*
GetMixer
()
const
noexcept
;
Mixer
*
GetMixer
()
const
noexcept
;
bool
IsDummy
()
const
noexcept
{
return
!
output
;
}
/**
/**
* Caller must lock the mutex.
* Caller must lock the mutex.
*/
*/
...
@@ -294,6 +305,14 @@ public:
...
@@ -294,6 +305,14 @@ public:
return
last_error
;
return
last_error
;
}
}
/**
* Detach and return the #FilteredAudioOutput instance and,
* replacing it here with a "dummy" object.
*/
std
::
unique_ptr
<
FilteredAudioOutput
>
Steal
()
noexcept
;
void
ReplaceDummy
(
std
::
unique_ptr
<
FilteredAudioOutput
>
new_output
,
bool
_enabled
)
noexcept
;
void
StartThread
();
void
StartThread
();
/**
/**
...
...
src/output/MultipleOutputs.cxx
View file @
c16233fa
...
@@ -18,6 +18,7 @@
...
@@ -18,6 +18,7 @@
*/
*/
#include "MultipleOutputs.hxx"
#include "MultipleOutputs.hxx"
#include "Client.hxx"
#include "Filtered.hxx"
#include "Filtered.hxx"
#include "Defaults.hxx"
#include "Defaults.hxx"
#include "MusicPipe.hxx"
#include "MusicPipe.hxx"
...
@@ -143,6 +144,21 @@ MultipleOutputs::FindByName(const char *name) noexcept
...
@@ -143,6 +144,21 @@ MultipleOutputs::FindByName(const char *name) noexcept
}
}
void
void
MultipleOutputs
::
Add
(
std
::
unique_ptr
<
FilteredAudioOutput
>
output
,
bool
enable
)
noexcept
{
auto
&
client
=
GetAnyClient
();
// TODO: this operation needs to be protected with a mutex
outputs
.
emplace_back
(
std
::
make_unique
<
AudioOutputControl
>
(
std
::
move
(
output
),
client
));
outputs
.
back
()
->
LockSetEnabled
(
enable
);
client
.
ApplyEnabled
();
}
void
MultipleOutputs
::
EnableDisable
()
MultipleOutputs
::
EnableDisable
()
{
{
/* parallel execution */
/* parallel execution */
...
...
src/output/MultipleOutputs.hxx
View file @
c16233fa
...
@@ -119,6 +119,9 @@ public:
...
@@ -119,6 +119,9 @@ public:
return
FindByName
(
name
)
!=
nullptr
;
return
FindByName
(
name
)
!=
nullptr
;
}
}
void
Add
(
std
::
unique_ptr
<
FilteredAudioOutput
>
output
,
bool
enable
)
noexcept
;
void
SetReplayGainMode
(
ReplayGainMode
mode
)
noexcept
;
void
SetReplayGainMode
(
ReplayGainMode
mode
)
noexcept
;
/**
/**
...
@@ -153,6 +156,10 @@ public:
...
@@ -153,6 +156,10 @@ public:
void
SetSoftwareVolume
(
unsigned
volume
)
noexcept
;
void
SetSoftwareVolume
(
unsigned
volume
)
noexcept
;
private
:
private
:
AudioOutputClient
&
GetAnyClient
()
noexcept
{
return
outputs
.
front
()
->
GetClient
();
}
/**
/**
* Was Open() called successfully?
* Was Open() called successfully?
*
*
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment