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
f5a923b9
Commit
f5a923b9
authored
Jan 27, 2014
by
Max Kellermann
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
OutputAll: convert to class, move instance to class Partition
Another big chunk of code for multi-player support.
parent
36bab6ef
Expand all
Show whitespace changes
Inline
Side-by-side
Showing
25 changed files
with
805 additions
and
309 deletions
+805
-309
Makefile.am
Makefile.am
+2
-2
Main.cxx
src/Main.cxx
+2
-4
Partition.cxx
src/Partition.cxx
+1
-0
Partition.hxx
src/Partition.hxx
+5
-2
PlayerControl.cxx
src/PlayerControl.cxx
+4
-2
PlayerControl.hxx
src/PlayerControl.hxx
+5
-1
PlayerThread.cxx
src/PlayerThread.cxx
+23
-23
StateFile.cxx
src/StateFile.cxx
+3
-3
OtherCommands.cxx
src/command/OtherCommands.cxx
+5
-3
OutputCommands.cxx
src/command/OutputCommands.cxx
+6
-10
PlayerCommands.cxx
src/command/PlayerCommands.cxx
+3
-5
MixerAll.cxx
src/mixer/MixerAll.cxx
+35
-55
MixerAll.hxx
src/mixer/MixerAll.hxx
+0
-64
Volume.cxx
src/mixer/Volume.cxx
+15
-11
Volume.hxx
src/mixer/Volume.hxx
+7
-3
MultipleOutputs.cxx
src/output/MultipleOutputs.cxx
+477
-0
MultipleOutputs.hxx
src/output/MultipleOutputs.hxx
+163
-66
OutputAll.cxx
src/output/OutputAll.cxx
+0
-0
OutputCommand.cxx
src/output/OutputCommand.cxx
+20
-26
OutputCommand.hxx
src/output/OutputCommand.hxx
+5
-3
OutputPrint.cxx
src/output/OutputPrint.cxx
+5
-7
OutputPrint.hxx
src/output/OutputPrint.hxx
+2
-2
OutputState.cxx
src/output/OutputState.cxx
+7
-12
OutputState.hxx
src/output/OutputState.hxx
+4
-2
run_output.cxx
test/run_output.cxx
+6
-3
No files found.
Makefile.am
View file @
f5a923b9
...
...
@@ -961,7 +961,7 @@ OUTPUT_API_SRC = \
src/output/OutputAPI.hxx
\
src/output/OutputInternal.hxx
\
src/output/OutputList.cxx src/output/OutputList.hxx
\
src/output/
OutputAll.cxx src/output/OutputAll
.hxx
\
src/output/
MultipleOutputs.cxx src/output/MultipleOutputs
.hxx
\
src/output/OutputThread.cxx src/output/OutputThread.hxx
\
src/output/OutputError.cxx src/output/OutputError.hxx
\
src/output/OutputControl.cxx src/output/OutputControl.hxx
\
...
...
@@ -985,7 +985,7 @@ MIXER_API_SRC = \
src/mixer/MixerList.hxx
\
src/mixer/MixerControl.cxx src/mixer/MixerControl.hxx
\
src/mixer/MixerType.cxx src/mixer/MixerType.hxx
\
src/mixer/MixerAll.cxx
src/mixer/MixerAll.hxx
\
src/mixer/MixerAll.cxx
\
src/mixer/MixerInternal.hxx
libmixer_plugins_a_SOURCES
=
\
...
...
src/Main.cxx
View file @
f5a923b9
...
...
@@ -37,7 +37,6 @@
#include "command/AllCommands.hxx"
#include "Partition.hxx"
#include "mixer/Volume.hxx"
#include "output/OutputAll.hxx"
#include "tag/TagConfig.hxx"
#include "ReplayGainConfig.hxx"
#include "Idle.hxx"
...
...
@@ -458,7 +457,7 @@ int mpd_main(int argc, char *argv[])
initialize_decoder_and_player
();
volume_init
();
initAudioConfig
();
audio_output_all_init
(
instance
->
partition
->
pc
);
instance
->
partition
->
outputs
.
Configure
(
instance
->
partition
->
pc
);
client_manager_init
();
replay_gain_global_init
();
...
...
@@ -500,7 +499,7 @@ int mpd_main(int argc, char *argv[])
return
EXIT_FAILURE
;
}
audio_output_all_set_replay_gain_m
ode
(
replay_gain_get_real_mode
(
instance
->
partition
->
playlist
.
queue
.
random
));
instance
->
partition
->
outputs
.
SetReplayGainM
ode
(
replay_gain_get_real_mode
(
instance
->
partition
->
playlist
.
queue
.
random
));
if
(
config_get_bool
(
CONF_AUTO_UPDATE
,
false
))
{
#ifdef ENABLE_INOTIFY
...
...
@@ -567,7 +566,6 @@ int mpd_main(int argc, char *argv[])
playlist_list_global_finish
();
input_stream_global_finish
();
audio_output_all_finish
();
mapper_finish
();
delete
instance
->
partition
;
command_finish
();
...
...
src/Partition.cxx
View file @
f5a923b9
...
...
@@ -20,6 +20,7 @@
#include "config.h"
#include "Partition.hxx"
#include "DetachedSong.hxx"
#include "output/MultipleOutputs.hxx"
void
Partition
::
DatabaseModified
()
...
...
src/Partition.hxx
View file @
f5a923b9
...
...
@@ -21,9 +21,11 @@
#define MPD_PARTITION_HXX
#include "Playlist.hxx"
#include "output/MultipleOutputs.hxx"
#include "PlayerControl.hxx"
struct
Instance
;
class
MultipleOutputs
;
/**
* A partition of the Music Player Daemon. It is a separate unit with
...
...
@@ -34,6 +36,8 @@ struct Partition {
struct
playlist
playlist
;
MultipleOutputs
outputs
;
PlayerControl
pc
;
Partition
(
Instance
&
_instance
,
...
...
@@ -41,8 +45,7 @@ struct Partition {
unsigned
buffer_chunks
,
unsigned
buffered_before_play
)
:
instance
(
_instance
),
playlist
(
max_length
),
pc
(
buffer_chunks
,
buffered_before_play
)
{
}
pc
(
outputs
,
buffer_chunks
,
buffered_before_play
)
{}
void
ClearQueue
()
{
playlist
.
Clear
(
pc
);
...
...
src/PlayerControl.cxx
View file @
f5a923b9
...
...
@@ -26,9 +26,11 @@
#include <assert.h>
PlayerControl
::
PlayerControl
(
unsigned
_buffer_chunks
,
PlayerControl
::
PlayerControl
(
MultipleOutputs
&
_outputs
,
unsigned
_buffer_chunks
,
unsigned
_buffered_before_play
)
:
buffer_chunks
(
_buffer_chunks
),
:
outputs
(
_outputs
),
buffer_chunks
(
_buffer_chunks
),
buffered_before_play
(
_buffered_before_play
),
command
(
PlayerCommand
::
NONE
),
state
(
PlayerState
::
STOP
),
...
...
src/PlayerControl.hxx
View file @
f5a923b9
...
...
@@ -29,6 +29,7 @@
#include <stdint.h>
class
MultipleOutputs
;
class
DetachedSong
;
enum
class
PlayerState
:
uint8_t
{
...
...
@@ -91,6 +92,8 @@ struct player_status {
};
struct
PlayerControl
{
MultipleOutputs
&
outputs
;
unsigned
buffer_chunks
;
unsigned
int
buffered_before_play
;
...
...
@@ -170,7 +173,8 @@ struct PlayerControl {
*/
bool
border_pause
;
PlayerControl
(
unsigned
buffer_chunks
,
PlayerControl
(
MultipleOutputs
&
_outputs
,
unsigned
buffer_chunks
,
unsigned
buffered_before_play
);
~
PlayerControl
();
...
...
src/PlayerThread.cxx
View file @
f5a923b9
...
...
@@ -28,7 +28,7 @@
#include "system/FatalError.hxx"
#include "CrossFade.hxx"
#include "PlayerControl.hxx"
#include "output/
OutputAll
.hxx"
#include "output/
MultipleOutputs
.hxx"
#include "tag/Tag.hxx"
#include "Idle.hxx"
#include "GlobalEvents.hxx"
...
...
@@ -125,7 +125,7 @@ class Player {
/**
* The time stamp of the chunk most recently sent to the
* output thread. This attribute is only used if
*
audio_output_all_get_elapsed_t
ime() didn't return a usable
*
MultipleOutputs::GetElapsedT
ime() didn't return a usable
* value; the output thread can estimate the elapsed time more
* precisely.
*/
...
...
@@ -228,8 +228,8 @@ private:
bool
WaitForDecoder
();
/**
* Wrapper for
audio_output_all_open(). Upon failure, it pauses the
* player.
* Wrapper for
MultipleOutputs::Open(). Upon failure, it
* p
auses the p
layer.
*
* @return true on success
*/
...
...
@@ -393,7 +393,7 @@ Player::OpenOutput()
pc
.
state
==
PlayerState
::
PAUSE
);
Error
error
;
if
(
audio_output_all_o
pen
(
play_audio_format
,
buffer
,
error
))
{
if
(
pc
.
outputs
.
O
pen
(
play_audio_format
,
buffer
,
error
))
{
output_open
=
true
;
paused
=
false
;
...
...
@@ -444,7 +444,7 @@ Player::CheckDecoderStartup()
pc
.
Unlock
();
if
(
output_open
&&
!
audio_output_all_w
ait
(
pc
,
1
))
!
pc
.
outputs
.
W
ait
(
pc
,
1
))
/* the output devices havn't finished playing
all chunks yet - wait for that */
return
true
;
...
...
@@ -504,7 +504,7 @@ Player::SendSilence()
memset
(
chunk
->
data
,
0
,
chunk
->
length
);
Error
error
;
if
(
!
audio_output_all_p
lay
(
chunk
,
error
))
{
if
(
!
pc
.
outputs
.
P
lay
(
chunk
,
error
))
{
LogError
(
error
);
buffer
.
Return
(
chunk
);
return
false
;
...
...
@@ -582,7 +582,7 @@ Player::SeekDecoder()
/* re-fill the buffer after seeking */
buffering
=
true
;
audio_output_all_c
ancel
();
pc
.
outputs
.
C
ancel
();
return
true
;
}
...
...
@@ -599,7 +599,7 @@ Player::ProcessCommand()
case
PlayerCommand
:
:
UPDATE_AUDIO
:
pc
.
Unlock
();
audio_output_all_enable_d
isable
();
pc
.
outputs
.
EnableD
isable
();
pc
.
Lock
();
pc
.
CommandFinished
();
break
;
...
...
@@ -618,7 +618,7 @@ Player::ProcessCommand()
paused
=
!
paused
;
if
(
paused
)
{
audio_output_all_p
ause
();
pc
.
outputs
.
P
ause
();
pc
.
Lock
();
pc
.
state
=
PlayerState
::
PAUSE
;
...
...
@@ -669,11 +669,11 @@ Player::ProcessCommand()
case
PlayerCommand
:
:
REFRESH
:
if
(
output_open
&&
!
paused
)
{
pc
.
Unlock
();
audio_output_all_c
heck
();
pc
.
outputs
.
C
heck
();
pc
.
Lock
();
}
pc
.
elapsed_time
=
audio_output_all_get_elapsed_t
ime
();
pc
.
elapsed_time
=
pc
.
outputs
.
GetElapsedT
ime
();
if
(
pc
.
elapsed_time
<
0.0
)
pc
.
elapsed_time
=
elapsed_time
;
...
...
@@ -733,7 +733,7 @@ play_chunk(PlayerControl &pc,
/* send the chunk to the audio outputs */
if
(
!
audio_output_all_p
lay
(
chunk
,
error
))
if
(
!
pc
.
outputs
.
P
lay
(
chunk
,
error
))
return
false
;
pc
.
total_play_time
+=
(
double
)
chunk
->
length
/
...
...
@@ -744,7 +744,7 @@ play_chunk(PlayerControl &pc,
inline
bool
Player
::
PlayNextChunk
()
{
if
(
!
audio_output_all_w
ait
(
pc
,
64
))
if
(
!
pc
.
outputs
.
W
ait
(
pc
,
64
))
/* the output pipe is still large enough, don't send
another chunk */
return
true
;
...
...
@@ -883,7 +883,7 @@ Player::SongBorder()
ReplacePipe
(
dc
.
pipe
);
audio_output_all_song_b
order
();
pc
.
outputs
.
SongB
order
();
if
(
!
WaitForDecoder
())
return
false
;
...
...
@@ -933,7 +933,7 @@ Player::Run()
pc
.
command
==
PlayerCommand
::
EXIT
||
pc
.
command
==
PlayerCommand
::
CLOSE_AUDIO
)
{
pc
.
Unlock
();
audio_output_all_c
ancel
();
pc
.
outputs
.
C
ancel
();
break
;
}
...
...
@@ -949,7 +949,7 @@ Player::Run()
/* not enough decoded buffer space yet */
if
(
!
paused
&&
output_open
&&
audio_output_all_c
heck
()
<
4
&&
pc
.
outputs
.
C
heck
()
<
4
&&
!
SendSilence
())
break
;
...
...
@@ -1029,7 +1029,7 @@ Player::Run()
to the audio output */
PlayNextChunk
();
}
else
if
(
audio_output_all_c
heck
()
>
0
)
{
}
else
if
(
pc
.
outputs
.
C
heck
()
>
0
)
{
/* not enough data from decoder, but the
output thread is still busy, so it's
okay */
...
...
@@ -1054,7 +1054,7 @@ Player::Run()
if
(
pipe
->
IsEmpty
())
{
/* wait for the hardware to finish
playback */
audio_output_all_d
rain
();
pc
.
outputs
.
D
rain
();
break
;
}
}
else
if
(
output_open
)
{
...
...
@@ -1130,7 +1130,7 @@ player_task(void *arg)
case
PlayerCommand
:
:
STOP
:
pc
.
Unlock
();
audio_output_all_c
ancel
();
pc
.
outputs
.
C
ancel
();
pc
.
Lock
();
/* fall through */
...
...
@@ -1145,7 +1145,7 @@ player_task(void *arg)
case
PlayerCommand
:
:
CLOSE_AUDIO
:
pc
.
Unlock
();
audio_output_all_r
elease
();
pc
.
outputs
.
R
elease
();
pc
.
Lock
();
pc
.
CommandFinished
();
...
...
@@ -1156,7 +1156,7 @@ player_task(void *arg)
case
PlayerCommand
:
:
UPDATE_AUDIO
:
pc
.
Unlock
();
audio_output_all_enable_d
isable
();
pc
.
outputs
.
EnableD
isable
();
pc
.
Lock
();
pc
.
CommandFinished
();
break
;
...
...
@@ -1166,7 +1166,7 @@ player_task(void *arg)
dc
.
Quit
();
audio_output_all_c
lose
();
pc
.
outputs
.
C
lose
();
player_command_finished
(
pc
);
return
;
...
...
src/StateFile.cxx
View file @
f5a923b9
...
...
@@ -75,7 +75,7 @@ StateFile::Write()
}
save_sw_volume_state
(
fp
);
audio_output_state_save
(
fp
);
audio_output_state_save
(
fp
,
partition
.
outputs
);
playlist_state_save
(
fp
,
partition
.
playlist
,
partition
.
pc
);
fclose
(
fp
);
...
...
@@ -99,8 +99,8 @@ StateFile::Read()
const
char
*
line
;
while
((
line
=
file
.
ReadLine
())
!=
NULL
)
{
success
=
read_sw_volume_state
(
line
)
||
audio_output_state_read
(
line
)
||
success
=
read_sw_volume_state
(
line
,
partition
.
outputs
)
||
audio_output_state_read
(
line
,
partition
.
outputs
)
||
playlist_state_restore
(
line
,
file
,
partition
.
playlist
,
partition
.
pc
);
if
(
!
success
)
...
...
src/command/OtherCommands.cxx
View file @
f5a923b9
...
...
@@ -45,6 +45,7 @@
#include "db/PlaylistVector.hxx"
#include "client/ClientFile.hxx"
#include "client/Client.hxx"
#include "Partition.hxx"
#include "Idle.hxx"
#include <assert.h>
...
...
@@ -254,7 +255,7 @@ handle_setvol(Client &client, gcc_unused int argc, char *argv[])
return
CommandResult
::
ERROR
;
}
success
=
volume_level_change
(
level
);
success
=
volume_level_change
(
client
.
partition
.
outputs
,
level
);
if
(
!
success
)
{
command_error
(
client
,
ACK_ERROR_SYSTEM
,
"problems setting volume"
);
...
...
@@ -276,7 +277,7 @@ handle_volume(Client &client, gcc_unused int argc, char *argv[])
return
CommandResult
::
ERROR
;
}
const
int
old_volume
=
volume_level_get
();
const
int
old_volume
=
volume_level_get
(
client
.
partition
.
outputs
);
if
(
old_volume
<
0
)
{
command_error
(
client
,
ACK_ERROR_SYSTEM
,
"No mixer"
);
return
CommandResult
::
ERROR
;
...
...
@@ -288,7 +289,8 @@ handle_volume(Client &client, gcc_unused int argc, char *argv[])
else
if
(
new_volume
>
100
)
new_volume
=
100
;
if
(
new_volume
!=
old_volume
&&
!
volume_level_change
(
new_volume
))
{
if
(
new_volume
!=
old_volume
&&
!
volume_level_change
(
client
.
partition
.
outputs
,
new_volume
))
{
command_error
(
client
,
ACK_ERROR_SYSTEM
,
"problems setting volume"
);
return
CommandResult
::
ERROR
;
...
...
src/command/OutputCommands.cxx
View file @
f5a923b9
...
...
@@ -23,18 +23,17 @@
#include "output/OutputCommand.hxx"
#include "protocol/Result.hxx"
#include "protocol/ArgParser.hxx"
#include "client/Client.hxx"
#include "Partition.hxx"
CommandResult
handle_enableoutput
(
Client
&
client
,
gcc_unused
int
argc
,
char
*
argv
[])
{
unsigned
device
;
bool
ret
;
if
(
!
check_unsigned
(
client
,
&
device
,
argv
[
1
]))
return
CommandResult
::
ERROR
;
ret
=
audio_output_enable_index
(
device
);
if
(
!
ret
)
{
if
(
!
audio_output_enable_index
(
client
.
partition
.
outputs
,
device
))
{
command_error
(
client
,
ACK_ERROR_NO_EXIST
,
"No such audio output"
);
return
CommandResult
::
ERROR
;
...
...
@@ -47,13 +46,10 @@ CommandResult
handle_disableoutput
(
Client
&
client
,
gcc_unused
int
argc
,
char
*
argv
[])
{
unsigned
device
;
bool
ret
;
if
(
!
check_unsigned
(
client
,
&
device
,
argv
[
1
]))
return
CommandResult
::
ERROR
;
ret
=
audio_output_disable_index
(
device
);
if
(
!
ret
)
{
if
(
!
audio_output_disable_index
(
client
.
partition
.
outputs
,
device
))
{
command_error
(
client
,
ACK_ERROR_NO_EXIST
,
"No such audio output"
);
return
CommandResult
::
ERROR
;
...
...
@@ -69,7 +65,7 @@ handle_toggleoutput(Client &client, gcc_unused int argc, char *argv[])
if
(
!
check_unsigned
(
client
,
&
device
,
argv
[
1
]))
return
CommandResult
::
ERROR
;
if
(
!
audio_output_toggle_index
(
device
))
{
if
(
!
audio_output_toggle_index
(
client
.
partition
.
outputs
,
device
))
{
command_error
(
client
,
ACK_ERROR_NO_EXIST
,
"No such audio output"
);
return
CommandResult
::
ERROR
;
...
...
@@ -82,7 +78,7 @@ CommandResult
handle_devices
(
Client
&
client
,
gcc_unused
int
argc
,
gcc_unused
char
*
argv
[])
{
printAudioDevices
(
client
);
printAudioDevices
(
client
,
client
.
partition
.
outputs
);
return
CommandResult
::
OK
;
}
src/command/PlayerCommands.cxx
View file @
f5a923b9
...
...
@@ -25,7 +25,6 @@
#include "db/update/UpdateGlue.hxx"
#include "client/Client.hxx"
#include "mixer/Volume.hxx"
#include "output/OutputAll.hxx"
#include "Partition.hxx"
#include "protocol/Result.hxx"
#include "protocol/ArgParser.hxx"
...
...
@@ -140,7 +139,7 @@ handle_status(Client &client,
COMMAND_STATUS_PLAYLIST_LENGTH
": %i
\n
"
COMMAND_STATUS_MIXRAMPDB
": %f
\n
"
COMMAND_STATUS_STATE
": %s
\n
"
,
volume_level_get
(),
volume_level_get
(
client
.
partition
.
outputs
),
playlist
.
GetRepeat
(),
playlist
.
GetRandom
(),
playlist
.
GetSingle
(),
...
...
@@ -277,7 +276,7 @@ handle_random(Client &client, gcc_unused int argc, char *argv[])
return
CommandResult
::
ERROR
;
client
.
partition
.
SetRandom
(
status
);
audio_output_all_set_replay_gain_m
ode
(
replay_gain_get_real_mode
(
client
.
partition
.
GetRandom
()));
client
.
partition
.
outputs
.
SetReplayGainM
ode
(
replay_gain_get_real_mode
(
client
.
partition
.
GetRandom
()));
return
CommandResult
::
OK
;
}
...
...
@@ -379,8 +378,7 @@ handle_replay_gain_mode(Client &client,
return
CommandResult
::
ERROR
;
}
audio_output_all_set_replay_gain_mode
(
replay_gain_get_real_mode
(
client
.
playlist
.
queue
.
random
));
client
.
partition
.
outputs
.
SetReplayGainMode
(
replay_gain_get_real_mode
(
client
.
playlist
.
queue
.
random
));
return
CommandResult
::
OK
;
}
...
...
src/mixer/MixerAll.cxx
View file @
f5a923b9
...
...
@@ -18,11 +18,10 @@
*/
#include "config.h"
#include "
MixerAll
.hxx"
#include "
output/MultipleOutputs
.hxx"
#include "MixerControl.hxx"
#include "MixerInternal.hxx"
#include "MixerList.hxx"
#include "output/OutputAll.hxx"
#include "output/OutputInternal.hxx"
#include "pcm/Volume.hxx"
#include "util/Error.hxx"
...
...
@@ -34,39 +33,33 @@
static
constexpr
Domain
mixer_domain
(
"mixer"
);
static
int
output_mixer_get_volume
(
unsigned
i
)
output_mixer_get_volume
(
const
audio_output
&
ao
)
{
struct
audio_output
*
output
;
int
volume
;
assert
(
i
<
audio_output_count
());
output
=
audio_output_get
(
i
);
if
(
!
output
->
enabled
)
if
(
!
ao
.
enabled
)
return
-
1
;
Mixer
*
mixer
=
output
->
mixer
;
Mixer
*
mixer
=
ao
.
mixer
;
if
(
mixer
==
nullptr
)
return
-
1
;
Error
error
;
volume
=
mixer_get_volume
(
mixer
,
error
);
int
volume
=
mixer_get_volume
(
mixer
,
error
);
if
(
volume
<
0
&&
error
.
IsDefined
())
FormatError
(
error
,
"Failed to read mixer for '%s'"
,
output
->
name
);
ao
.
name
);
return
volume
;
}
int
mixer_all_get_volume
(
void
)
MultipleOutputs
::
GetVolume
()
const
{
unsigned
count
=
audio_output_count
(),
ok
=
0
;
int
volume
,
total
=
0
;
unsigned
ok
=
0
;
int
total
=
0
;
for
(
unsigned
i
=
0
;
i
<
count
;
i
++
)
{
volume
=
output_mixer_get_volume
(
i
);
for
(
auto
ao
:
outputs
)
{
int
volume
=
output_mixer_get_volume
(
*
ao
);
if
(
volume
>=
0
)
{
total
+=
volume
;
++
ok
;
...
...
@@ -80,59 +73,47 @@ mixer_all_get_volume(void)
}
static
bool
output_mixer_set_volume
(
unsigned
i
,
unsigned
volume
)
output_mixer_set_volume
(
audio_output
&
ao
,
unsigned
volume
)
{
struct
audio_output
*
output
;
bool
success
;
assert
(
i
<
audio_output_count
());
assert
(
volume
<=
100
);
output
=
audio_output_get
(
i
);
if
(
!
output
->
enabled
)
if
(
!
ao
.
enabled
)
return
false
;
Mixer
*
mixer
=
output
->
mixer
;
Mixer
*
mixer
=
ao
.
mixer
;
if
(
mixer
==
nullptr
)
return
false
;
Error
error
;
success
=
mixer_set_volume
(
mixer
,
volume
,
error
);
bool
success
=
mixer_set_volume
(
mixer
,
volume
,
error
);
if
(
!
success
&&
error
.
IsDefined
())
FormatError
(
error
,
"Failed to set mixer for '%s'"
,
output
->
name
);
ao
.
name
);
return
success
;
}
bool
mixer_all_set_v
olume
(
unsigned
volume
)
MultipleOutputs
::
SetV
olume
(
unsigned
volume
)
{
bool
success
=
false
;
unsigned
count
=
audio_output_count
();
assert
(
volume
<=
100
);
for
(
unsigned
i
=
0
;
i
<
count
;
i
++
)
success
=
output_mixer_set_volume
(
i
,
volume
)
bool
success
=
false
;
for
(
auto
ao
:
outputs
)
success
=
output_mixer_set_volume
(
*
ao
,
volume
)
||
success
;
return
success
;
}
static
int
output_mixer_get_software_volume
(
unsigned
i
)
output_mixer_get_software_volume
(
const
audio_output
&
ao
)
{
struct
audio_output
*
output
;
assert
(
i
<
audio_output_count
());
output
=
audio_output_get
(
i
);
if
(
!
output
->
enabled
)
if
(
!
ao
.
enabled
)
return
-
1
;
Mixer
*
mixer
=
output
->
mixer
;
Mixer
*
mixer
=
ao
.
mixer
;
if
(
mixer
==
nullptr
||
!
mixer
->
IsPlugin
(
software_mixer_plugin
))
return
-
1
;
...
...
@@ -140,13 +121,13 @@ output_mixer_get_software_volume(unsigned i)
}
int
mixer_all_get_software_volume
(
void
)
MultipleOutputs
::
GetSoftwareVolume
()
const
{
unsigned
count
=
audio_output_count
(),
ok
=
0
;
int
volume
,
total
=
0
;
unsigned
ok
=
0
;
int
total
=
0
;
for
(
unsigned
i
=
0
;
i
<
count
;
i
++
)
{
volume
=
output_mixer_get_software_volume
(
i
);
for
(
auto
ao
:
outputs
)
{
int
volume
=
output_mixer_get_software_volume
(
*
ao
);
if
(
volume
>=
0
)
{
total
+=
volume
;
++
ok
;
...
...
@@ -160,16 +141,15 @@ mixer_all_get_software_volume(void)
}
void
mixer_all_set_software_v
olume
(
unsigned
volume
)
MultipleOutputs
::
SetSoftwareV
olume
(
unsigned
volume
)
{
unsigned
count
=
audio_output_count
();
assert
(
volume
<=
PCM_VOLUME_1
);
for
(
unsigned
i
=
0
;
i
<
count
;
i
++
)
{
struct
audio_output
*
output
=
audio_output_get
(
i
);
if
(
output
->
mixer
!=
nullptr
&&
output
->
mixer
->
plugin
==
&
software_mixer_plugin
)
mixer_set_volume
(
output
->
mixer
,
volume
,
IgnoreError
());
for
(
auto
ao
:
outputs
)
{
const
auto
mixer
=
ao
->
mixer
;
if
(
mixer
!=
nullptr
&&
mixer
->
plugin
==
&
software_mixer_plugin
)
mixer_set_volume
(
mixer
,
volume
,
IgnoreError
());
}
}
src/mixer/MixerAll.hxx
deleted
100644 → 0
View file @
36bab6ef
/*
* Copyright (C) 2003-2014 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
/** \file
*
* Functions which affect the mixers of all audio outputs.
*/
#ifndef MPD_MIXER_ALL_HXX
#define MPD_MIXER_ALL_HXX
#include "Compiler.h"
/**
* Returns the average volume of all available mixers (range 0..100).
* Returns -1 if no mixer can be queried.
*/
gcc_pure
int
mixer_all_get_volume
(
void
);
/**
* Sets the volume on all available mixers.
*
* @param volume the volume (range 0..100)
* @return true on success, false on failure
*/
bool
mixer_all_set_volume
(
unsigned
volume
);
/**
* Similar to mixer_all_get_volume(), but gets the volume only for
* software mixers. See #software_mixer_plugin. This function fails
* if no software mixer is configured.
*/
gcc_pure
int
mixer_all_get_software_volume
(
void
);
/**
* Similar to mixer_all_set_volume(), but sets the volume only for
* software mixers. See #software_mixer_plugin. This function cannot
* fail, because the underlying software mixers cannot fail either.
*/
void
mixer_all_set_software_volume
(
unsigned
volume
);
#endif
src/mixer/Volume.cxx
View file @
f5a923b9
...
...
@@ -19,7 +19,7 @@
#include "config.h"
#include "Volume.hxx"
#include "
MixerAll
.hxx"
#include "
output/MultipleOutputs
.hxx"
#include "Idle.hxx"
#include "GlobalEvents.hxx"
#include "util/StringUtil.hxx"
...
...
@@ -59,36 +59,40 @@ void volume_init(void)
GlobalEvents
::
Register
(
GlobalEvents
::
MIXER
,
mixer_event_callback
);
}
int
volume_level_get
(
void
)
int
volume_level_get
(
const
MultipleOutputs
&
outputs
)
{
if
(
last_hardware_volume
>=
0
&&
!
hardware_volume_clock
.
CheckUpdate
(
1000
))
/* throttle access to hardware mixers */
return
last_hardware_volume
;
last_hardware_volume
=
mixer_all_get_v
olume
();
last_hardware_volume
=
outputs
.
GetV
olume
();
return
last_hardware_volume
;
}
static
bool
software_volume_change
(
unsigned
volume
)
static
bool
software_volume_change
(
MultipleOutputs
&
outputs
,
unsigned
volume
)
{
assert
(
volume
<=
100
);
volume_software_set
=
volume
;
mixer_all_set_software_v
olume
(
volume
);
outputs
.
SetSoftwareV
olume
(
volume
);
return
true
;
}
static
bool
hardware_volume_change
(
unsigned
volume
)
static
bool
hardware_volume_change
(
MultipleOutputs
&
outputs
,
unsigned
volume
)
{
/* reset the cache */
last_hardware_volume
=
-
1
;
return
mixer_all_set_v
olume
(
volume
);
return
outputs
.
SetV
olume
(
volume
);
}
bool
volume_level_change
(
unsigned
volume
)
bool
volume_level_change
(
MultipleOutputs
&
outputs
,
unsigned
volume
)
{
assert
(
volume
<=
100
);
...
...
@@ -96,11 +100,11 @@ bool volume_level_change(unsigned volume)
idle_add
(
IDLE_MIXER
);
return
hardware_volume_change
(
volume
);
return
hardware_volume_change
(
outputs
,
volume
);
}
bool
read_sw_volume_state
(
const
char
*
line
)
read_sw_volume_state
(
const
char
*
line
,
MultipleOutputs
&
outputs
)
{
char
*
end
=
nullptr
;
long
int
sv
;
...
...
@@ -111,7 +115,7 @@ read_sw_volume_state(const char *line)
line
+=
sizeof
(
SW_VOLUME_STATE
)
-
1
;
sv
=
strtol
(
line
,
&
end
,
10
);
if
(
*
end
==
0
&&
sv
>=
0
&&
sv
<=
100
)
software_volume_change
(
sv
);
software_volume_change
(
outputs
,
sv
);
else
FormatWarning
(
volume_domain
,
"Can't parse software volume: %s"
,
line
);
...
...
src/mixer/Volume.hxx
View file @
f5a923b9
...
...
@@ -24,15 +24,19 @@
#include <stdio.h>
class
MultipleOutputs
;
void
volume_init
(
void
);
gcc_pure
int
volume_level_get
(
void
);
int
volume_level_get
(
const
MultipleOutputs
&
outputs
);
bool
volume_level_change
(
unsigned
volume
);
bool
volume_level_change
(
MultipleOutputs
&
outputs
,
unsigned
volume
);
bool
read_sw_volume_state
(
const
char
*
line
);
read_sw_volume_state
(
const
char
*
line
,
MultipleOutputs
&
outputs
);
void
save_sw_volume_state
(
FILE
*
fp
);
...
...
src/output/MultipleOutputs.cxx
0 → 100644
View file @
f5a923b9
/*
* Copyright (C) 2003-2014 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "config.h"
#include "MultipleOutputs.hxx"
#include "PlayerControl.hxx"
#include "OutputInternal.hxx"
#include "OutputControl.hxx"
#include "OutputError.hxx"
#include "MusicBuffer.hxx"
#include "MusicPipe.hxx"
#include "MusicChunk.hxx"
#include "system/FatalError.hxx"
#include "util/Error.hxx"
#include "config/ConfigData.hxx"
#include "config/ConfigGlobal.hxx"
#include "config/ConfigOption.hxx"
#include "notify.hxx"
#include <assert.h>
#include <string.h>
MultipleOutputs
::
MultipleOutputs
()
:
buffer
(
nullptr
),
pipe
(
nullptr
),
elapsed_time
(
-
1
)
{
}
MultipleOutputs
::~
MultipleOutputs
()
{
for
(
auto
i
:
outputs
)
{
audio_output_disable
(
i
);
audio_output_finish
(
i
);
}
}
static
audio_output
*
LoadOutput
(
PlayerControl
&
pc
,
const
config_param
&
param
)
{
Error
error
;
audio_output
*
output
=
audio_output_new
(
param
,
pc
,
error
);
if
(
output
==
nullptr
)
{
if
(
param
.
line
>
0
)
FormatFatalError
(
"line %i: %s"
,
param
.
line
,
error
.
GetMessage
());
else
FatalError
(
error
);
}
return
output
;
}
void
MultipleOutputs
::
Configure
(
PlayerControl
&
pc
)
{
const
config_param
*
param
=
nullptr
;
while
((
param
=
config_get_next_param
(
CONF_AUDIO_OUTPUT
,
param
))
!=
nullptr
)
{
auto
output
=
LoadOutput
(
pc
,
*
param
);
if
(
FindByName
(
output
->
name
)
!=
nullptr
)
FormatFatalError
(
"output devices with identical "
"names: %s"
,
output
->
name
);
outputs
.
push_back
(
output
);
}
if
(
outputs
.
empty
())
{
/* auto-detect device */
const
config_param
empty
;
auto
output
=
LoadOutput
(
pc
,
empty
);
outputs
.
push_back
(
output
);
}
}
audio_output
*
MultipleOutputs
::
FindByName
(
const
char
*
name
)
const
{
for
(
auto
i
:
outputs
)
if
(
strcmp
(
i
->
name
,
name
)
==
0
)
return
i
;
return
nullptr
;
}
void
MultipleOutputs
::
EnableDisable
()
{
for
(
auto
ao
:
outputs
)
{
bool
enabled
;
ao
->
mutex
.
lock
();
enabled
=
ao
->
really_enabled
;
ao
->
mutex
.
unlock
();
if
(
ao
->
enabled
!=
enabled
)
{
if
(
ao
->
enabled
)
audio_output_enable
(
ao
);
else
audio_output_disable
(
ao
);
}
}
}
bool
MultipleOutputs
::
AllFinished
()
const
{
for
(
auto
ao
:
outputs
)
{
const
ScopeLock
protect
(
ao
->
mutex
);
if
(
audio_output_is_open
(
ao
)
&&
!
audio_output_command_is_finished
(
ao
))
return
false
;
}
return
true
;
}
void
MultipleOutputs
::
WaitAll
()
{
while
(
!
AllFinished
())
audio_output_client_notify
.
Wait
();
}
void
MultipleOutputs
::
AllowPlay
()
{
for
(
auto
ao
:
outputs
)
audio_output_allow_play
(
ao
);
}
static
void
audio_output_reset_reopen
(
struct
audio_output
*
ao
)
{
const
ScopeLock
protect
(
ao
->
mutex
);
ao
->
fail_timer
.
Reset
();
}
void
MultipleOutputs
::
ResetReopen
()
{
for
(
auto
ao
:
outputs
)
audio_output_reset_reopen
(
ao
);
}
bool
MultipleOutputs
::
Update
()
{
bool
ret
=
false
;
if
(
!
input_audio_format
.
IsDefined
())
return
false
;
for
(
auto
ao
:
outputs
)
ret
=
audio_output_update
(
ao
,
input_audio_format
,
*
pipe
)
||
ret
;
return
ret
;
}
void
MultipleOutputs
::
SetReplayGainMode
(
ReplayGainMode
mode
)
{
for
(
auto
ao
:
outputs
)
audio_output_set_replay_gain_mode
(
ao
,
mode
);
}
bool
MultipleOutputs
::
Play
(
music_chunk
*
chunk
,
Error
&
error
)
{
assert
(
buffer
!=
nullptr
);
assert
(
pipe
!=
nullptr
);
assert
(
chunk
!=
nullptr
);
assert
(
chunk
->
CheckFormat
(
input_audio_format
));
if
(
!
Update
())
{
/* TODO: obtain real error */
error
.
Set
(
output_domain
,
"Failed to open audio output"
);
return
false
;
}
pipe
->
Push
(
chunk
);
for
(
auto
ao
:
outputs
)
audio_output_play
(
ao
);
return
true
;
}
bool
MultipleOutputs
::
Open
(
const
AudioFormat
audio_format
,
MusicBuffer
&
_buffer
,
Error
&
error
)
{
bool
ret
=
false
,
enabled
=
false
;
assert
(
buffer
==
nullptr
||
buffer
==
&
_buffer
);
assert
((
pipe
==
nullptr
)
==
(
buffer
==
nullptr
));
buffer
=
&
_buffer
;
/* the audio format must be the same as existing chunks in the
pipe */
assert
(
pipe
==
nullptr
||
pipe
->
CheckFormat
(
audio_format
));
if
(
pipe
==
nullptr
)
pipe
=
new
MusicPipe
();
else
/* if the pipe hasn't been cleared, the the audio
format must not have changed */
assert
(
pipe
->
IsEmpty
()
||
audio_format
==
input_audio_format
);
input_audio_format
=
audio_format
;
ResetReopen
();
EnableDisable
();
Update
();
for
(
auto
ao
:
outputs
)
{
if
(
ao
->
enabled
)
enabled
=
true
;
if
(
ao
->
open
)
ret
=
true
;
}
if
(
!
enabled
)
error
.
Set
(
output_domain
,
"All audio outputs are disabled"
);
else
if
(
!
ret
)
/* TODO: obtain real error */
error
.
Set
(
output_domain
,
"Failed to open audio output"
);
if
(
!
ret
)
/* close all devices if there was an error */
Close
();
return
ret
;
}
/**
* Has the specified audio output already consumed this chunk?
*/
gcc_pure
static
bool
chunk_is_consumed_in
(
const
struct
audio_output
*
ao
,
gcc_unused
const
MusicPipe
*
pipe
,
const
struct
music_chunk
*
chunk
)
{
if
(
!
ao
->
open
)
return
true
;
if
(
ao
->
chunk
==
nullptr
)
return
false
;
assert
(
chunk
==
ao
->
chunk
||
pipe
->
Contains
(
ao
->
chunk
));
if
(
chunk
!=
ao
->
chunk
)
{
assert
(
chunk
->
next
!=
nullptr
);
return
true
;
}
return
ao
->
chunk_finished
&&
chunk
->
next
==
nullptr
;
}
bool
MultipleOutputs
::
IsChunkConsumed
(
const
music_chunk
*
chunk
)
const
{
for
(
auto
ao
:
outputs
)
{
const
ScopeLock
protect
(
ao
->
mutex
);
if
(
!
chunk_is_consumed_in
(
ao
,
pipe
,
chunk
))
return
false
;
}
return
true
;
}
inline
void
MultipleOutputs
::
ClearTailChunk
(
gcc_unused
const
struct
music_chunk
*
chunk
,
bool
*
locked
)
{
assert
(
chunk
->
next
==
nullptr
);
assert
(
pipe
->
Contains
(
chunk
));
for
(
unsigned
i
=
0
,
n
=
outputs
.
size
();
i
!=
n
;
++
i
)
{
audio_output
*
ao
=
outputs
[
i
];
/* this mutex will be unlocked by the caller when it's
ready */
ao
->
mutex
.
lock
();
locked
[
i
]
=
ao
->
open
;
if
(
!
locked
[
i
])
{
ao
->
mutex
.
unlock
();
continue
;
}
assert
(
ao
->
chunk
==
chunk
);
assert
(
ao
->
chunk_finished
);
ao
->
chunk
=
nullptr
;
}
}
unsigned
MultipleOutputs
::
Check
()
{
const
struct
music_chunk
*
chunk
;
bool
is_tail
;
struct
music_chunk
*
shifted
;
bool
locked
[
outputs
.
size
()];
assert
(
buffer
!=
nullptr
);
assert
(
pipe
!=
nullptr
);
while
((
chunk
=
pipe
->
Peek
())
!=
nullptr
)
{
assert
(
!
pipe
->
IsEmpty
());
if
(
!
IsChunkConsumed
(
chunk
))
/* at least one output is not finished playing
this chunk */
return
pipe
->
GetSize
();
if
(
chunk
->
length
>
0
&&
chunk
->
times
>=
0.0
)
/* only update elapsed_time if the chunk
provides a defined value */
elapsed_time
=
chunk
->
times
;
is_tail
=
chunk
->
next
==
nullptr
;
if
(
is_tail
)
/* this is the tail of the pipe - clear the
chunk reference in all outputs */
ClearTailChunk
(
chunk
,
locked
);
/* remove the chunk from the pipe */
shifted
=
pipe
->
Shift
();
assert
(
shifted
==
chunk
);
if
(
is_tail
)
/* unlock all audio outputs which were locked
by clear_tail_chunk() */
for
(
unsigned
i
=
0
,
n
=
outputs
.
size
();
i
!=
n
;
++
i
)
if
(
locked
[
i
])
outputs
[
i
]
->
mutex
.
unlock
();
/* return the chunk to the buffer */
buffer
->
Return
(
shifted
);
}
return
0
;
}
bool
MultipleOutputs
::
Wait
(
PlayerControl
&
pc
,
unsigned
threshold
)
{
pc
.
Lock
();
if
(
Check
()
<
threshold
)
{
pc
.
Unlock
();
return
true
;
}
pc
.
Wait
();
pc
.
Unlock
();
return
Check
()
<
threshold
;
}
void
MultipleOutputs
::
Pause
()
{
Update
();
for
(
auto
ao
:
outputs
)
audio_output_pause
(
ao
);
WaitAll
();
}
void
MultipleOutputs
::
Drain
()
{
for
(
auto
ao
:
outputs
)
audio_output_drain_async
(
ao
);
WaitAll
();
}
void
MultipleOutputs
::
Cancel
()
{
/* send the cancel() command to all audio outputs */
for
(
auto
ao
:
outputs
)
audio_output_cancel
(
ao
);
WaitAll
();
/* clear the music pipe and return all chunks to the buffer */
if
(
pipe
!=
nullptr
)
pipe
->
Clear
(
*
buffer
);
/* the audio outputs are now waiting for a signal, to
synchronize the cleared music pipe */
AllowPlay
();
/* invalidate elapsed_time */
elapsed_time
=
-
1.0
;
}
void
MultipleOutputs
::
Close
()
{
for
(
auto
ao
:
outputs
)
audio_output_close
(
ao
);
if
(
pipe
!=
nullptr
)
{
assert
(
buffer
!=
nullptr
);
pipe
->
Clear
(
*
buffer
);
delete
pipe
;
pipe
=
nullptr
;
}
buffer
=
nullptr
;
input_audio_format
.
Clear
();
elapsed_time
=
-
1.0
;
}
void
MultipleOutputs
::
Release
()
{
for
(
auto
ao
:
outputs
)
audio_output_release
(
ao
);
if
(
pipe
!=
nullptr
)
{
assert
(
buffer
!=
nullptr
);
pipe
->
Clear
(
*
buffer
);
delete
pipe
;
pipe
=
nullptr
;
}
buffer
=
nullptr
;
input_audio_format
.
Clear
();
elapsed_time
=
-
1.0
;
}
void
MultipleOutputs
::
SongBorder
()
{
/* clear the elapsed_time pointer at the beginning of a new
song */
elapsed_time
=
0.0
;
}
src/output/
OutputAll
.hxx
→
src/output/
MultipleOutputs
.hxx
View file @
f5a923b9
...
...
@@ -26,58 +26,92 @@
#ifndef OUTPUT_ALL_H
#define OUTPUT_ALL_H
#include "AudioFormat.hxx"
#include "ReplayGainInfo.hxx"
#include "Compiler.h"
#include <vector>
#include <assert.h>
struct
AudioFormat
;
class
MusicBuffer
;
class
MusicPipe
;
struct
music_chunk
;
struct
PlayerControl
;
struct
audio_output
;
class
Error
;
/**
* Global initialization: load audio outputs from the configuration
* file and initialize them.
class
MultipleOutputs
{
std
::
vector
<
audio_output
*>
outputs
;
AudioFormat
input_audio_format
;
/**
* The #MusicBuffer object where consumed chunks are returned.
*/
void
audio_output_all_init
(
PlayerControl
&
pc
);
MusicBuffer
*
buffer
;
/**
* Global finalization: free memory occupied by audio outputs. All
/**
* The #MusicPipe object which feeds all audio outputs. It is
* filled by audio_output_all_play().
*/
void
audio_output_all_finish
(
void
);
MusicPipe
*
pipe
;
/**
* The "elapsed_time" stamp of the most recently finished
* chunk.
*/
float
elapsed_time
;
public
:
/**
* Load audio outputs from the configuration file and
* initialize them.
*/
MultipleOutputs
();
~
MultipleOutputs
();
void
Configure
(
PlayerControl
&
pc
);
/**
* Returns the total number of audio output devices, including those
* who
are disabled right now.
/**
* Returns the total number of audio output devices, including
* those which
are disabled right now.
*/
gcc_const
unsigned
int
audio_output_count
(
void
);
gcc_pure
unsigned
Size
()
const
{
return
outputs
.
size
();
}
/**
/**
* Returns the "i"th audio output device.
*/
gcc_const
struct
audio_output
*
audio_output_get
(
unsigned
i
);
const
audio_output
&
Get
(
unsigned
i
)
const
{
assert
(
i
<
Size
());
/**
* Returns the audio output device with the specified name. Returns
* NULL if the name does not exist.
return
*
outputs
[
i
];
}
audio_output
&
Get
(
unsigned
i
)
{
assert
(
i
<
Size
());
return
*
outputs
[
i
];
}
/**
* Returns the audio output device with the specified name.
* Returns nullptr if the name does not exist.
*/
gcc_pure
struct
audio_output
*
audio_output_find
(
const
char
*
name
);
gcc_pure
audio_output
*
FindByName
(
const
char
*
name
)
const
;
/**
/**
* Checks the "enabled" flag of all audio outputs, and if one has
* changed, commit the change.
*/
void
audio_output_all_enable_disable
(
void
);
void
EnableDisable
();
/**
/**
* Opens all audio outputs which are not disabled.
*
* @param audio_format the preferred audio format
...
...
@@ -85,28 +119,23 @@ audio_output_all_enable_disable(void);
* should be returned
* @return true on success, false on failure
*/
bool
audio_output_all_open
(
AudioFormat
audio_format
,
MusicBuffer
&
buffer
,
bool
Open
(
const
AudioFormat
audio_format
,
MusicBuffer
&
_buffer
,
Error
&
error
);
/**
/**
* Closes all audio outputs.
*/
void
audio_output_all_close
(
void
);
void
Close
();
/**
* Closes all audio outputs. Outputs with the "always_on" flag are
*
put into pause mode.
/**
* Closes all audio outputs. Outputs with the "always_on"
* flag are
put into pause mode.
*/
void
audio_output_all_release
(
void
);
void
Release
();
void
audio_output_all_set_replay_gain_mode
(
ReplayGainMode
mode
);
void
SetReplayGainMode
(
ReplayGainMode
mode
);
/**
/**
* Enqueue a #music_chunk object for playing, i.e. pushes it to a
* #MusicPipe.
*
...
...
@@ -114,19 +143,17 @@ audio_output_all_set_replay_gain_mode(ReplayGainMode mode);
* @return true on success, false if no audio output was able to play
* (all closed then)
*/
bool
audio_output_all_play
(
music_chunk
*
chunk
,
Error
&
error
);
bool
Play
(
music_chunk
*
chunk
,
Error
&
error
);
/**
/**
* Checks if the output devices have drained their music pipe, and
* returns the consumed music chunks to the #music_buffer.
*
* @return the number of chunks to play left in the #MusicPipe
*/
unsigned
audio_output_all_check
(
void
);
unsigned
Check
();
/**
/**
* Checks if the size of the #MusicPipe is below the #threshold. If
* not, it attempts to synchronize with all output threads, and waits
* until another #music_chunk is finished.
...
...
@@ -134,41 +161,111 @@ audio_output_all_check(void);
* @param threshold the maximum number of chunks in the pipe
* @return true if there are less than #threshold chunks in the pipe
*/
bool
audio_output_all_wait
(
PlayerControl
&
pc
,
unsigned
threshold
);
bool
Wait
(
PlayerControl
&
pc
,
unsigned
threshold
);
/**
/**
* Puts all audio outputs into pause mode. Most implementations will
* simply close it then.
*/
void
audio_output_all_pause
(
void
);
void
Pause
();
/**
/**
* Drain all audio outputs.
*/
void
audio_output_all_drain
(
void
);
void
Drain
();
/**
/**
* Try to cancel data which may still be in the device's buffers.
*/
void
audio_output_all_cancel
(
void
);
void
Cancel
();
/**
/**
* Indicate that a new song will begin now.
*/
void
audio_output_all_song_border
(
void
);
void
SongBorder
();
/**
/**
* Returns the "elapsed_time" stamp of the most recently finished
* chunk. A negative value is returned when no chunk has been
* finished yet.
*/
gcc_pure
float
audio_output_all_get_elapsed_time
(
void
);
gcc_pure
float
GetElapsedTime
()
const
{
return
elapsed_time
;
}
/**
* Returns the average volume of all available mixers (range
* 0..100). Returns -1 if no mixer can be queried.
*/
gcc_pure
int
GetVolume
()
const
;
/**
* Sets the volume on all available mixers.
*
* @param volume the volume (range 0..100)
* @return true on success, false on failure
*/
bool
SetVolume
(
unsigned
volume
);
/**
* Similar to GetVolume(), but gets the volume only for
* software mixers. See #software_mixer_plugin. This
* function fails if no software mixer is configured.
*/
gcc_pure
int
GetSoftwareVolume
()
const
;
/**
* Similar to SetVolume(), but sets the volume only for
* software mixers. See #software_mixer_plugin. This
* function cannot fail, because the underlying software
* mixers cannot fail either.
*/
void
SetSoftwareVolume
(
unsigned
volume
);
private
:
/**
* Determine if all (active) outputs have finished the current
* command.
*/
gcc_pure
bool
AllFinished
()
const
;
void
WaitAll
();
/**
* Signals all audio outputs which are open.
*/
void
AllowPlay
();
/**
* Resets the "reopen" flag on all audio devices. MPD should
* immediately retry to open the device instead of waiting for
* the timeout when the user wants to start playback.
*/
void
ResetReopen
();
/**
* Opens all output devices which are enabled, but closed.
*
* @return true if there is at least open output device which
* is open
*/
bool
Update
();
/**
* Has this chunk been consumed by all audio outputs?
*/
bool
IsChunkConsumed
(
const
music_chunk
*
chunk
)
const
;
/**
* There's only one chunk left in the pipe (#pipe), and all
* audio outputs have consumed it already. Clear the
* reference.
*/
void
ClearTailChunk
(
const
struct
music_chunk
*
chunk
,
bool
*
locked
);
};
#endif
src/output/OutputAll.cxx
deleted
100644 → 0
View file @
36bab6ef
This diff is collapsed.
Click to expand it.
src/output/OutputCommand.cxx
View file @
f5a923b9
...
...
@@ -26,7 +26,7 @@
#include "config.h"
#include "OutputCommand.hxx"
#include "
OutputAll
.hxx"
#include "
MultipleOutputs
.hxx"
#include "OutputInternal.hxx"
#include "PlayerControl.hxx"
#include "mixer/MixerControl.hxx"
...
...
@@ -35,21 +35,19 @@
extern
unsigned
audio_output_state_version
;
bool
audio_output_enable_index
(
unsigned
idx
)
audio_output_enable_index
(
MultipleOutputs
&
outputs
,
unsigned
idx
)
{
struct
audio_output
*
ao
;
if
(
idx
>=
audio_output_count
())
if
(
idx
>=
outputs
.
Size
())
return
false
;
a
o
=
audio_output_g
et
(
idx
);
if
(
ao
->
enabled
)
a
udio_output
&
ao
=
outputs
.
G
et
(
idx
);
if
(
ao
.
enabled
)
return
true
;
ao
->
enabled
=
true
;
ao
.
enabled
=
true
;
idle_add
(
IDLE_OUTPUT
);
ao
->
player_control
->
UpdateAudio
();
ao
.
player_control
->
UpdateAudio
();
++
audio_output_state_version
;
...
...
@@ -57,27 +55,25 @@ audio_output_enable_index(unsigned idx)
}
bool
audio_output_disable_index
(
unsigned
idx
)
audio_output_disable_index
(
MultipleOutputs
&
outputs
,
unsigned
idx
)
{
struct
audio_output
*
ao
;
if
(
idx
>=
audio_output_count
())
if
(
idx
>=
outputs
.
Size
())
return
false
;
a
o
=
audio_output_g
et
(
idx
);
if
(
!
ao
->
enabled
)
a
udio_output
&
ao
=
outputs
.
G
et
(
idx
);
if
(
!
ao
.
enabled
)
return
true
;
ao
->
enabled
=
false
;
ao
.
enabled
=
false
;
idle_add
(
IDLE_OUTPUT
);
Mixer
*
mixer
=
ao
->
mixer
;
Mixer
*
mixer
=
ao
.
mixer
;
if
(
mixer
!=
nullptr
)
{
mixer_close
(
mixer
);
idle_add
(
IDLE_MIXER
);
}
ao
->
player_control
->
UpdateAudio
();
ao
.
player_control
->
UpdateAudio
();
++
audio_output_state_version
;
...
...
@@ -85,26 +81,24 @@ audio_output_disable_index(unsigned idx)
}
bool
audio_output_toggle_index
(
unsigned
idx
)
audio_output_toggle_index
(
MultipleOutputs
&
outputs
,
unsigned
idx
)
{
struct
audio_output
*
ao
;
if
(
idx
>=
audio_output_count
())
if
(
idx
>=
outputs
.
Size
())
return
false
;
a
o
=
audio_output_g
et
(
idx
);
const
bool
enabled
=
ao
->
enabled
=
!
ao
->
enabled
;
a
udio_output
&
ao
=
outputs
.
G
et
(
idx
);
const
bool
enabled
=
ao
.
enabled
=
!
ao
.
enabled
;
idle_add
(
IDLE_OUTPUT
);
if
(
!
enabled
)
{
Mixer
*
mixer
=
ao
->
mixer
;
Mixer
*
mixer
=
ao
.
mixer
;
if
(
mixer
!=
nullptr
)
{
mixer_close
(
mixer
);
idle_add
(
IDLE_MIXER
);
}
}
ao
->
player_control
->
UpdateAudio
();
ao
.
player_control
->
UpdateAudio
();
++
audio_output_state_version
;
...
...
src/output/OutputCommand.hxx
View file @
f5a923b9
...
...
@@ -27,25 +27,27 @@
#ifndef MPD_OUTPUT_COMMAND_HXX
#define MPD_OUTPUT_COMMAND_HXX
class
MultipleOutputs
;
/**
* Enables an audio output. Returns false if the specified output
* does not exist.
*/
bool
audio_output_enable_index
(
unsigned
idx
);
audio_output_enable_index
(
MultipleOutputs
&
outputs
,
unsigned
idx
);
/**
* Disables an audio output. Returns false if the specified output
* does not exist.
*/
bool
audio_output_disable_index
(
unsigned
idx
);
audio_output_disable_index
(
MultipleOutputs
&
outputs
,
unsigned
idx
);
/**
* Toggles an audio output. Returns false if the specified output
* does not exist.
*/
bool
audio_output_toggle_index
(
unsigned
idx
);
audio_output_toggle_index
(
MultipleOutputs
&
outputs
,
unsigned
idx
);
#endif
src/output/OutputPrint.cxx
View file @
f5a923b9
...
...
@@ -24,22 +24,20 @@
#include "config.h"
#include "OutputPrint.hxx"
#include "
OutputAll
.hxx"
#include "
MultipleOutputs
.hxx"
#include "OutputInternal.hxx"
#include "client/Client.hxx"
void
printAudioDevices
(
Client
&
client
)
printAudioDevices
(
Client
&
client
,
const
MultipleOutputs
&
outputs
)
{
const
unsigned
n
=
audio_output_count
();
for
(
unsigned
i
=
0
;
i
<
n
;
++
i
)
{
const
struct
audio_output
*
ao
=
audio_output_get
(
i
);
for
(
unsigned
i
=
0
,
n
=
outputs
.
Size
();
i
!=
n
;
++
i
)
{
const
audio_output
&
ao
=
outputs
.
Get
(
i
);
client_printf
(
client
,
"outputid: %i
\n
"
"outputname: %s
\n
"
"outputenabled: %i
\n
"
,
i
,
ao
->
name
,
ao
->
enabled
);
i
,
ao
.
name
,
ao
.
enabled
);
}
}
src/output/OutputPrint.hxx
View file @
f5a923b9
/*
* Copyright (C) 2003-2014 The Music Player Daemon Project
* http://www.musicpd.org
...
...
@@ -27,8 +26,9 @@
#define MPD_OUTPUT_PRINT_HXX
class
Client
;
class
MultipleOutputs
;
void
printAudioDevices
(
Client
&
client
);
printAudioDevices
(
Client
&
client
,
const
MultipleOutputs
&
outputs
);
#endif
src/output/OutputState.cxx
View file @
f5a923b9
...
...
@@ -24,7 +24,7 @@
#include "config.h"
#include "OutputState.hxx"
#include "
OutputAll
.hxx"
#include "
MultipleOutputs
.hxx"
#include "OutputInternal.hxx"
#include "OutputError.hxx"
#include "Log.hxx"
...
...
@@ -38,27 +38,22 @@
unsigned
audio_output_state_version
;
void
audio_output_state_save
(
FILE
*
fp
)
audio_output_state_save
(
FILE
*
fp
,
const
MultipleOutputs
&
outputs
)
{
unsigned
n
=
audio_output_count
();
assert
(
n
>
0
);
for
(
unsigned
i
=
0
;
i
<
n
;
++
i
)
{
const
struct
audio_output
*
ao
=
audio_output_get
(
i
);
for
(
unsigned
i
=
0
,
n
=
outputs
.
Size
();
i
!=
n
;
++
i
)
{
const
audio_output
&
ao
=
outputs
.
Get
(
i
);
fprintf
(
fp
,
AUDIO_DEVICE_STATE
"%d:%s
\n
"
,
ao
->
enabled
,
ao
->
name
);
ao
.
enabled
,
ao
.
name
);
}
}
bool
audio_output_state_read
(
const
char
*
line
)
audio_output_state_read
(
const
char
*
line
,
MultipleOutputs
&
outputs
)
{
long
value
;
char
*
endptr
;
const
char
*
name
;
struct
audio_output
*
ao
;
if
(
!
StringStartsWith
(
line
,
AUDIO_DEVICE_STATE
))
return
false
;
...
...
@@ -74,7 +69,7 @@ audio_output_state_read(const char *line)
return
true
;
name
=
endptr
+
1
;
a
o
=
audio_output_find
(
name
);
a
udio_output
*
ao
=
outputs
.
FindByName
(
name
);
if
(
ao
==
NULL
)
{
FormatDebug
(
output_domain
,
"Ignoring device state for '%s'"
,
name
);
...
...
src/output/OutputState.hxx
View file @
f5a923b9
...
...
@@ -27,11 +27,13 @@
#include <stdio.h>
class
MultipleOutputs
;
bool
audio_output_state_read
(
const
char
*
line
);
audio_output_state_read
(
const
char
*
line
,
MultipleOutputs
&
outputs
);
void
audio_output_state_save
(
FILE
*
fp
);
audio_output_state_save
(
FILE
*
fp
,
const
MultipleOutputs
&
outputs
);
/**
* Generates a version number for the current state of the audio
...
...
test/run_output.cxx
View file @
f5a923b9
...
...
@@ -74,8 +74,10 @@ find_named_config_block(ConfigOption option, const char *name)
return
NULL
;
}
PlayerControl
::
PlayerControl
(
gcc_unused
unsigned
_buffer_chunks
,
gcc_unused
unsigned
_buffered_before_play
)
{}
PlayerControl
::
PlayerControl
(
gcc_unused
MultipleOutputs
&
_outputs
,
gcc_unused
unsigned
_buffer_chunks
,
gcc_unused
unsigned
_buffered_before_play
)
:
outputs
(
_outputs
)
{}
PlayerControl
::~
PlayerControl
()
{}
static
struct
audio_output
*
...
...
@@ -89,7 +91,8 @@ load_audio_output(const char *name)
return
nullptr
;
}
static
struct
PlayerControl
dummy_player_control
(
32
,
4
);
static
struct
PlayerControl
dummy_player_control
(
*
(
MultipleOutputs
*
)
nullptr
,
32
,
4
);
Error
error
;
struct
audio_output
*
ao
=
...
...
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