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 = \
...
@@ -961,7 +961,7 @@ OUTPUT_API_SRC = \
src/output/OutputAPI.hxx
\
src/output/OutputAPI.hxx
\
src/output/OutputInternal.hxx
\
src/output/OutputInternal.hxx
\
src/output/OutputList.cxx src/output/OutputList.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/OutputThread.cxx src/output/OutputThread.hxx
\
src/output/OutputError.cxx src/output/OutputError.hxx
\
src/output/OutputError.cxx src/output/OutputError.hxx
\
src/output/OutputControl.cxx src/output/OutputControl.hxx
\
src/output/OutputControl.cxx src/output/OutputControl.hxx
\
...
@@ -985,7 +985,7 @@ MIXER_API_SRC = \
...
@@ -985,7 +985,7 @@ MIXER_API_SRC = \
src/mixer/MixerList.hxx
\
src/mixer/MixerList.hxx
\
src/mixer/MixerControl.cxx src/mixer/MixerControl.hxx
\
src/mixer/MixerControl.cxx src/mixer/MixerControl.hxx
\
src/mixer/MixerType.cxx src/mixer/MixerType.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
src/mixer/MixerInternal.hxx
libmixer_plugins_a_SOURCES
=
\
libmixer_plugins_a_SOURCES
=
\
...
...
src/Main.cxx
View file @
f5a923b9
...
@@ -37,7 +37,6 @@
...
@@ -37,7 +37,6 @@
#include "command/AllCommands.hxx"
#include "command/AllCommands.hxx"
#include "Partition.hxx"
#include "Partition.hxx"
#include "mixer/Volume.hxx"
#include "mixer/Volume.hxx"
#include "output/OutputAll.hxx"
#include "tag/TagConfig.hxx"
#include "tag/TagConfig.hxx"
#include "ReplayGainConfig.hxx"
#include "ReplayGainConfig.hxx"
#include "Idle.hxx"
#include "Idle.hxx"
...
@@ -458,7 +457,7 @@ int mpd_main(int argc, char *argv[])
...
@@ -458,7 +457,7 @@ int mpd_main(int argc, char *argv[])
initialize_decoder_and_player
();
initialize_decoder_and_player
();
volume_init
();
volume_init
();
initAudioConfig
();
initAudioConfig
();
audio_output_all_init
(
instance
->
partition
->
pc
);
instance
->
partition
->
outputs
.
Configure
(
instance
->
partition
->
pc
);
client_manager_init
();
client_manager_init
();
replay_gain_global_init
();
replay_gain_global_init
();
...
@@ -500,7 +499,7 @@ int mpd_main(int argc, char *argv[])
...
@@ -500,7 +499,7 @@ int mpd_main(int argc, char *argv[])
return
EXIT_FAILURE
;
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
))
{
if
(
config_get_bool
(
CONF_AUTO_UPDATE
,
false
))
{
#ifdef ENABLE_INOTIFY
#ifdef ENABLE_INOTIFY
...
@@ -567,7 +566,6 @@ int mpd_main(int argc, char *argv[])
...
@@ -567,7 +566,6 @@ int mpd_main(int argc, char *argv[])
playlist_list_global_finish
();
playlist_list_global_finish
();
input_stream_global_finish
();
input_stream_global_finish
();
audio_output_all_finish
();
mapper_finish
();
mapper_finish
();
delete
instance
->
partition
;
delete
instance
->
partition
;
command_finish
();
command_finish
();
...
...
src/Partition.cxx
View file @
f5a923b9
...
@@ -20,6 +20,7 @@
...
@@ -20,6 +20,7 @@
#include "config.h"
#include "config.h"
#include "Partition.hxx"
#include "Partition.hxx"
#include "DetachedSong.hxx"
#include "DetachedSong.hxx"
#include "output/MultipleOutputs.hxx"
void
void
Partition
::
DatabaseModified
()
Partition
::
DatabaseModified
()
...
...
src/Partition.hxx
View file @
f5a923b9
...
@@ -21,9 +21,11 @@
...
@@ -21,9 +21,11 @@
#define MPD_PARTITION_HXX
#define MPD_PARTITION_HXX
#include "Playlist.hxx"
#include "Playlist.hxx"
#include "output/MultipleOutputs.hxx"
#include "PlayerControl.hxx"
#include "PlayerControl.hxx"
struct
Instance
;
struct
Instance
;
class
MultipleOutputs
;
/**
/**
* A partition of the Music Player Daemon. It is a separate unit with
* A partition of the Music Player Daemon. It is a separate unit with
...
@@ -34,6 +36,8 @@ struct Partition {
...
@@ -34,6 +36,8 @@ struct Partition {
struct
playlist
playlist
;
struct
playlist
playlist
;
MultipleOutputs
outputs
;
PlayerControl
pc
;
PlayerControl
pc
;
Partition
(
Instance
&
_instance
,
Partition
(
Instance
&
_instance
,
...
@@ -41,8 +45,7 @@ struct Partition {
...
@@ -41,8 +45,7 @@ struct Partition {
unsigned
buffer_chunks
,
unsigned
buffer_chunks
,
unsigned
buffered_before_play
)
unsigned
buffered_before_play
)
:
instance
(
_instance
),
playlist
(
max_length
),
:
instance
(
_instance
),
playlist
(
max_length
),
pc
(
buffer_chunks
,
buffered_before_play
)
{
pc
(
outputs
,
buffer_chunks
,
buffered_before_play
)
{}
}
void
ClearQueue
()
{
void
ClearQueue
()
{
playlist
.
Clear
(
pc
);
playlist
.
Clear
(
pc
);
...
...
src/PlayerControl.cxx
View file @
f5a923b9
...
@@ -26,9 +26,11 @@
...
@@ -26,9 +26,11 @@
#include <assert.h>
#include <assert.h>
PlayerControl
::
PlayerControl
(
unsigned
_buffer_chunks
,
PlayerControl
::
PlayerControl
(
MultipleOutputs
&
_outputs
,
unsigned
_buffer_chunks
,
unsigned
_buffered_before_play
)
unsigned
_buffered_before_play
)
:
buffer_chunks
(
_buffer_chunks
),
:
outputs
(
_outputs
),
buffer_chunks
(
_buffer_chunks
),
buffered_before_play
(
_buffered_before_play
),
buffered_before_play
(
_buffered_before_play
),
command
(
PlayerCommand
::
NONE
),
command
(
PlayerCommand
::
NONE
),
state
(
PlayerState
::
STOP
),
state
(
PlayerState
::
STOP
),
...
...
src/PlayerControl.hxx
View file @
f5a923b9
...
@@ -29,6 +29,7 @@
...
@@ -29,6 +29,7 @@
#include <stdint.h>
#include <stdint.h>
class
MultipleOutputs
;
class
DetachedSong
;
class
DetachedSong
;
enum
class
PlayerState
:
uint8_t
{
enum
class
PlayerState
:
uint8_t
{
...
@@ -91,6 +92,8 @@ struct player_status {
...
@@ -91,6 +92,8 @@ struct player_status {
};
};
struct
PlayerControl
{
struct
PlayerControl
{
MultipleOutputs
&
outputs
;
unsigned
buffer_chunks
;
unsigned
buffer_chunks
;
unsigned
int
buffered_before_play
;
unsigned
int
buffered_before_play
;
...
@@ -170,7 +173,8 @@ struct PlayerControl {
...
@@ -170,7 +173,8 @@ struct PlayerControl {
*/
*/
bool
border_pause
;
bool
border_pause
;
PlayerControl
(
unsigned
buffer_chunks
,
PlayerControl
(
MultipleOutputs
&
_outputs
,
unsigned
buffer_chunks
,
unsigned
buffered_before_play
);
unsigned
buffered_before_play
);
~
PlayerControl
();
~
PlayerControl
();
...
...
src/PlayerThread.cxx
View file @
f5a923b9
...
@@ -28,7 +28,7 @@
...
@@ -28,7 +28,7 @@
#include "system/FatalError.hxx"
#include "system/FatalError.hxx"
#include "CrossFade.hxx"
#include "CrossFade.hxx"
#include "PlayerControl.hxx"
#include "PlayerControl.hxx"
#include "output/
OutputAll
.hxx"
#include "output/
MultipleOutputs
.hxx"
#include "tag/Tag.hxx"
#include "tag/Tag.hxx"
#include "Idle.hxx"
#include "Idle.hxx"
#include "GlobalEvents.hxx"
#include "GlobalEvents.hxx"
...
@@ -125,7 +125,7 @@ class Player {
...
@@ -125,7 +125,7 @@ class Player {
/**
/**
* The time stamp of the chunk most recently sent to the
* The time stamp of the chunk most recently sent to the
* output thread. This attribute is only used if
* 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
* value; the output thread can estimate the elapsed time more
* precisely.
* precisely.
*/
*/
...
@@ -228,8 +228,8 @@ private:
...
@@ -228,8 +228,8 @@ private:
bool
WaitForDecoder
();
bool
WaitForDecoder
();
/**
/**
* Wrapper for
audio_output_all_open(). Upon failure, it pauses the
* Wrapper for
MultipleOutputs::Open(). Upon failure, it
* player.
* p
auses the p
layer.
*
*
* @return true on success
* @return true on success
*/
*/
...
@@ -393,7 +393,7 @@ Player::OpenOutput()
...
@@ -393,7 +393,7 @@ Player::OpenOutput()
pc
.
state
==
PlayerState
::
PAUSE
);
pc
.
state
==
PlayerState
::
PAUSE
);
Error
error
;
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
;
output_open
=
true
;
paused
=
false
;
paused
=
false
;
...
@@ -444,7 +444,7 @@ Player::CheckDecoderStartup()
...
@@ -444,7 +444,7 @@ Player::CheckDecoderStartup()
pc
.
Unlock
();
pc
.
Unlock
();
if
(
output_open
&&
if
(
output_open
&&
!
audio_output_all_w
ait
(
pc
,
1
))
!
pc
.
outputs
.
W
ait
(
pc
,
1
))
/* the output devices havn't finished playing
/* the output devices havn't finished playing
all chunks yet - wait for that */
all chunks yet - wait for that */
return
true
;
return
true
;
...
@@ -504,7 +504,7 @@ Player::SendSilence()
...
@@ -504,7 +504,7 @@ Player::SendSilence()
memset
(
chunk
->
data
,
0
,
chunk
->
length
);
memset
(
chunk
->
data
,
0
,
chunk
->
length
);
Error
error
;
Error
error
;
if
(
!
audio_output_all_p
lay
(
chunk
,
error
))
{
if
(
!
pc
.
outputs
.
P
lay
(
chunk
,
error
))
{
LogError
(
error
);
LogError
(
error
);
buffer
.
Return
(
chunk
);
buffer
.
Return
(
chunk
);
return
false
;
return
false
;
...
@@ -582,7 +582,7 @@ Player::SeekDecoder()
...
@@ -582,7 +582,7 @@ Player::SeekDecoder()
/* re-fill the buffer after seeking */
/* re-fill the buffer after seeking */
buffering
=
true
;
buffering
=
true
;
audio_output_all_c
ancel
();
pc
.
outputs
.
C
ancel
();
return
true
;
return
true
;
}
}
...
@@ -599,7 +599,7 @@ Player::ProcessCommand()
...
@@ -599,7 +599,7 @@ Player::ProcessCommand()
case
PlayerCommand
:
:
UPDATE_AUDIO
:
case
PlayerCommand
:
:
UPDATE_AUDIO
:
pc
.
Unlock
();
pc
.
Unlock
();
audio_output_all_enable_d
isable
();
pc
.
outputs
.
EnableD
isable
();
pc
.
Lock
();
pc
.
Lock
();
pc
.
CommandFinished
();
pc
.
CommandFinished
();
break
;
break
;
...
@@ -618,7 +618,7 @@ Player::ProcessCommand()
...
@@ -618,7 +618,7 @@ Player::ProcessCommand()
paused
=
!
paused
;
paused
=
!
paused
;
if
(
paused
)
{
if
(
paused
)
{
audio_output_all_p
ause
();
pc
.
outputs
.
P
ause
();
pc
.
Lock
();
pc
.
Lock
();
pc
.
state
=
PlayerState
::
PAUSE
;
pc
.
state
=
PlayerState
::
PAUSE
;
...
@@ -669,11 +669,11 @@ Player::ProcessCommand()
...
@@ -669,11 +669,11 @@ Player::ProcessCommand()
case
PlayerCommand
:
:
REFRESH
:
case
PlayerCommand
:
:
REFRESH
:
if
(
output_open
&&
!
paused
)
{
if
(
output_open
&&
!
paused
)
{
pc
.
Unlock
();
pc
.
Unlock
();
audio_output_all_c
heck
();
pc
.
outputs
.
C
heck
();
pc
.
Lock
();
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
)
if
(
pc
.
elapsed_time
<
0.0
)
pc
.
elapsed_time
=
elapsed_time
;
pc
.
elapsed_time
=
elapsed_time
;
...
@@ -733,7 +733,7 @@ play_chunk(PlayerControl &pc,
...
@@ -733,7 +733,7 @@ play_chunk(PlayerControl &pc,
/* send the chunk to the audio outputs */
/* send the chunk to the audio outputs */
if
(
!
audio_output_all_p
lay
(
chunk
,
error
))
if
(
!
pc
.
outputs
.
P
lay
(
chunk
,
error
))
return
false
;
return
false
;
pc
.
total_play_time
+=
(
double
)
chunk
->
length
/
pc
.
total_play_time
+=
(
double
)
chunk
->
length
/
...
@@ -744,7 +744,7 @@ play_chunk(PlayerControl &pc,
...
@@ -744,7 +744,7 @@ play_chunk(PlayerControl &pc,
inline
bool
inline
bool
Player
::
PlayNextChunk
()
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
/* the output pipe is still large enough, don't send
another chunk */
another chunk */
return
true
;
return
true
;
...
@@ -883,7 +883,7 @@ Player::SongBorder()
...
@@ -883,7 +883,7 @@ Player::SongBorder()
ReplacePipe
(
dc
.
pipe
);
ReplacePipe
(
dc
.
pipe
);
audio_output_all_song_b
order
();
pc
.
outputs
.
SongB
order
();
if
(
!
WaitForDecoder
())
if
(
!
WaitForDecoder
())
return
false
;
return
false
;
...
@@ -933,7 +933,7 @@ Player::Run()
...
@@ -933,7 +933,7 @@ Player::Run()
pc
.
command
==
PlayerCommand
::
EXIT
||
pc
.
command
==
PlayerCommand
::
EXIT
||
pc
.
command
==
PlayerCommand
::
CLOSE_AUDIO
)
{
pc
.
command
==
PlayerCommand
::
CLOSE_AUDIO
)
{
pc
.
Unlock
();
pc
.
Unlock
();
audio_output_all_c
ancel
();
pc
.
outputs
.
C
ancel
();
break
;
break
;
}
}
...
@@ -949,7 +949,7 @@ Player::Run()
...
@@ -949,7 +949,7 @@ Player::Run()
/* not enough decoded buffer space yet */
/* not enough decoded buffer space yet */
if
(
!
paused
&&
output_open
&&
if
(
!
paused
&&
output_open
&&
audio_output_all_c
heck
()
<
4
&&
pc
.
outputs
.
C
heck
()
<
4
&&
!
SendSilence
())
!
SendSilence
())
break
;
break
;
...
@@ -1029,7 +1029,7 @@ Player::Run()
...
@@ -1029,7 +1029,7 @@ Player::Run()
to the audio output */
to the audio output */
PlayNextChunk
();
PlayNextChunk
();
}
else
if
(
audio_output_all_c
heck
()
>
0
)
{
}
else
if
(
pc
.
outputs
.
C
heck
()
>
0
)
{
/* not enough data from decoder, but the
/* not enough data from decoder, but the
output thread is still busy, so it's
output thread is still busy, so it's
okay */
okay */
...
@@ -1054,7 +1054,7 @@ Player::Run()
...
@@ -1054,7 +1054,7 @@ Player::Run()
if
(
pipe
->
IsEmpty
())
{
if
(
pipe
->
IsEmpty
())
{
/* wait for the hardware to finish
/* wait for the hardware to finish
playback */
playback */
audio_output_all_d
rain
();
pc
.
outputs
.
D
rain
();
break
;
break
;
}
}
}
else
if
(
output_open
)
{
}
else
if
(
output_open
)
{
...
@@ -1130,7 +1130,7 @@ player_task(void *arg)
...
@@ -1130,7 +1130,7 @@ player_task(void *arg)
case
PlayerCommand
:
:
STOP
:
case
PlayerCommand
:
:
STOP
:
pc
.
Unlock
();
pc
.
Unlock
();
audio_output_all_c
ancel
();
pc
.
outputs
.
C
ancel
();
pc
.
Lock
();
pc
.
Lock
();
/* fall through */
/* fall through */
...
@@ -1145,7 +1145,7 @@ player_task(void *arg)
...
@@ -1145,7 +1145,7 @@ player_task(void *arg)
case
PlayerCommand
:
:
CLOSE_AUDIO
:
case
PlayerCommand
:
:
CLOSE_AUDIO
:
pc
.
Unlock
();
pc
.
Unlock
();
audio_output_all_r
elease
();
pc
.
outputs
.
R
elease
();
pc
.
Lock
();
pc
.
Lock
();
pc
.
CommandFinished
();
pc
.
CommandFinished
();
...
@@ -1156,7 +1156,7 @@ player_task(void *arg)
...
@@ -1156,7 +1156,7 @@ player_task(void *arg)
case
PlayerCommand
:
:
UPDATE_AUDIO
:
case
PlayerCommand
:
:
UPDATE_AUDIO
:
pc
.
Unlock
();
pc
.
Unlock
();
audio_output_all_enable_d
isable
();
pc
.
outputs
.
EnableD
isable
();
pc
.
Lock
();
pc
.
Lock
();
pc
.
CommandFinished
();
pc
.
CommandFinished
();
break
;
break
;
...
@@ -1166,7 +1166,7 @@ player_task(void *arg)
...
@@ -1166,7 +1166,7 @@ player_task(void *arg)
dc
.
Quit
();
dc
.
Quit
();
audio_output_all_c
lose
();
pc
.
outputs
.
C
lose
();
player_command_finished
(
pc
);
player_command_finished
(
pc
);
return
;
return
;
...
...
src/StateFile.cxx
View file @
f5a923b9
...
@@ -75,7 +75,7 @@ StateFile::Write()
...
@@ -75,7 +75,7 @@ StateFile::Write()
}
}
save_sw_volume_state
(
fp
);
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
);
playlist_state_save
(
fp
,
partition
.
playlist
,
partition
.
pc
);
fclose
(
fp
);
fclose
(
fp
);
...
@@ -99,8 +99,8 @@ StateFile::Read()
...
@@ -99,8 +99,8 @@ StateFile::Read()
const
char
*
line
;
const
char
*
line
;
while
((
line
=
file
.
ReadLine
())
!=
NULL
)
{
while
((
line
=
file
.
ReadLine
())
!=
NULL
)
{
success
=
read_sw_volume_state
(
line
)
||
success
=
read_sw_volume_state
(
line
,
partition
.
outputs
)
||
audio_output_state_read
(
line
)
||
audio_output_state_read
(
line
,
partition
.
outputs
)
||
playlist_state_restore
(
line
,
file
,
partition
.
playlist
,
playlist_state_restore
(
line
,
file
,
partition
.
playlist
,
partition
.
pc
);
partition
.
pc
);
if
(
!
success
)
if
(
!
success
)
...
...
src/command/OtherCommands.cxx
View file @
f5a923b9
...
@@ -45,6 +45,7 @@
...
@@ -45,6 +45,7 @@
#include "db/PlaylistVector.hxx"
#include "db/PlaylistVector.hxx"
#include "client/ClientFile.hxx"
#include "client/ClientFile.hxx"
#include "client/Client.hxx"
#include "client/Client.hxx"
#include "Partition.hxx"
#include "Idle.hxx"
#include "Idle.hxx"
#include <assert.h>
#include <assert.h>
...
@@ -254,7 +255,7 @@ handle_setvol(Client &client, gcc_unused int argc, char *argv[])
...
@@ -254,7 +255,7 @@ handle_setvol(Client &client, gcc_unused int argc, char *argv[])
return
CommandResult
::
ERROR
;
return
CommandResult
::
ERROR
;
}
}
success
=
volume_level_change
(
level
);
success
=
volume_level_change
(
client
.
partition
.
outputs
,
level
);
if
(
!
success
)
{
if
(
!
success
)
{
command_error
(
client
,
ACK_ERROR_SYSTEM
,
command_error
(
client
,
ACK_ERROR_SYSTEM
,
"problems setting volume"
);
"problems setting volume"
);
...
@@ -276,7 +277,7 @@ handle_volume(Client &client, gcc_unused int argc, char *argv[])
...
@@ -276,7 +277,7 @@ handle_volume(Client &client, gcc_unused int argc, char *argv[])
return
CommandResult
::
ERROR
;
return
CommandResult
::
ERROR
;
}
}
const
int
old_volume
=
volume_level_get
();
const
int
old_volume
=
volume_level_get
(
client
.
partition
.
outputs
);
if
(
old_volume
<
0
)
{
if
(
old_volume
<
0
)
{
command_error
(
client
,
ACK_ERROR_SYSTEM
,
"No mixer"
);
command_error
(
client
,
ACK_ERROR_SYSTEM
,
"No mixer"
);
return
CommandResult
::
ERROR
;
return
CommandResult
::
ERROR
;
...
@@ -288,7 +289,8 @@ handle_volume(Client &client, gcc_unused int argc, char *argv[])
...
@@ -288,7 +289,8 @@ handle_volume(Client &client, gcc_unused int argc, char *argv[])
else
if
(
new_volume
>
100
)
else
if
(
new_volume
>
100
)
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
,
command_error
(
client
,
ACK_ERROR_SYSTEM
,
"problems setting volume"
);
"problems setting volume"
);
return
CommandResult
::
ERROR
;
return
CommandResult
::
ERROR
;
...
...
src/command/OutputCommands.cxx
View file @
f5a923b9
...
@@ -23,18 +23,17 @@
...
@@ -23,18 +23,17 @@
#include "output/OutputCommand.hxx"
#include "output/OutputCommand.hxx"
#include "protocol/Result.hxx"
#include "protocol/Result.hxx"
#include "protocol/ArgParser.hxx"
#include "protocol/ArgParser.hxx"
#include "client/Client.hxx"
#include "Partition.hxx"
CommandResult
CommandResult
handle_enableoutput
(
Client
&
client
,
gcc_unused
int
argc
,
char
*
argv
[])
handle_enableoutput
(
Client
&
client
,
gcc_unused
int
argc
,
char
*
argv
[])
{
{
unsigned
device
;
unsigned
device
;
bool
ret
;
if
(
!
check_unsigned
(
client
,
&
device
,
argv
[
1
]))
if
(
!
check_unsigned
(
client
,
&
device
,
argv
[
1
]))
return
CommandResult
::
ERROR
;
return
CommandResult
::
ERROR
;
ret
=
audio_output_enable_index
(
device
);
if
(
!
audio_output_enable_index
(
client
.
partition
.
outputs
,
device
))
{
if
(
!
ret
)
{
command_error
(
client
,
ACK_ERROR_NO_EXIST
,
command_error
(
client
,
ACK_ERROR_NO_EXIST
,
"No such audio output"
);
"No such audio output"
);
return
CommandResult
::
ERROR
;
return
CommandResult
::
ERROR
;
...
@@ -47,13 +46,10 @@ CommandResult
...
@@ -47,13 +46,10 @@ CommandResult
handle_disableoutput
(
Client
&
client
,
gcc_unused
int
argc
,
char
*
argv
[])
handle_disableoutput
(
Client
&
client
,
gcc_unused
int
argc
,
char
*
argv
[])
{
{
unsigned
device
;
unsigned
device
;
bool
ret
;
if
(
!
check_unsigned
(
client
,
&
device
,
argv
[
1
]))
if
(
!
check_unsigned
(
client
,
&
device
,
argv
[
1
]))
return
CommandResult
::
ERROR
;
return
CommandResult
::
ERROR
;
ret
=
audio_output_disable_index
(
device
);
if
(
!
audio_output_disable_index
(
client
.
partition
.
outputs
,
device
))
{
if
(
!
ret
)
{
command_error
(
client
,
ACK_ERROR_NO_EXIST
,
command_error
(
client
,
ACK_ERROR_NO_EXIST
,
"No such audio output"
);
"No such audio output"
);
return
CommandResult
::
ERROR
;
return
CommandResult
::
ERROR
;
...
@@ -69,7 +65,7 @@ handle_toggleoutput(Client &client, gcc_unused int argc, char *argv[])
...
@@ -69,7 +65,7 @@ handle_toggleoutput(Client &client, gcc_unused int argc, char *argv[])
if
(
!
check_unsigned
(
client
,
&
device
,
argv
[
1
]))
if
(
!
check_unsigned
(
client
,
&
device
,
argv
[
1
]))
return
CommandResult
::
ERROR
;
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
,
command_error
(
client
,
ACK_ERROR_NO_EXIST
,
"No such audio output"
);
"No such audio output"
);
return
CommandResult
::
ERROR
;
return
CommandResult
::
ERROR
;
...
@@ -82,7 +78,7 @@ CommandResult
...
@@ -82,7 +78,7 @@ CommandResult
handle_devices
(
Client
&
client
,
handle_devices
(
Client
&
client
,
gcc_unused
int
argc
,
gcc_unused
char
*
argv
[])
gcc_unused
int
argc
,
gcc_unused
char
*
argv
[])
{
{
printAudioDevices
(
client
);
printAudioDevices
(
client
,
client
.
partition
.
outputs
);
return
CommandResult
::
OK
;
return
CommandResult
::
OK
;
}
}
src/command/PlayerCommands.cxx
View file @
f5a923b9
...
@@ -25,7 +25,6 @@
...
@@ -25,7 +25,6 @@
#include "db/update/UpdateGlue.hxx"
#include "db/update/UpdateGlue.hxx"
#include "client/Client.hxx"
#include "client/Client.hxx"
#include "mixer/Volume.hxx"
#include "mixer/Volume.hxx"
#include "output/OutputAll.hxx"
#include "Partition.hxx"
#include "Partition.hxx"
#include "protocol/Result.hxx"
#include "protocol/Result.hxx"
#include "protocol/ArgParser.hxx"
#include "protocol/ArgParser.hxx"
...
@@ -140,7 +139,7 @@ handle_status(Client &client,
...
@@ -140,7 +139,7 @@ handle_status(Client &client,
COMMAND_STATUS_PLAYLIST_LENGTH
": %i
\n
"
COMMAND_STATUS_PLAYLIST_LENGTH
": %i
\n
"
COMMAND_STATUS_MIXRAMPDB
": %f
\n
"
COMMAND_STATUS_MIXRAMPDB
": %f
\n
"
COMMAND_STATUS_STATE
": %s
\n
"
,
COMMAND_STATUS_STATE
": %s
\n
"
,
volume_level_get
(),
volume_level_get
(
client
.
partition
.
outputs
),
playlist
.
GetRepeat
(),
playlist
.
GetRepeat
(),
playlist
.
GetRandom
(),
playlist
.
GetRandom
(),
playlist
.
GetSingle
(),
playlist
.
GetSingle
(),
...
@@ -277,7 +276,7 @@ handle_random(Client &client, gcc_unused int argc, char *argv[])
...
@@ -277,7 +276,7 @@ handle_random(Client &client, gcc_unused int argc, char *argv[])
return
CommandResult
::
ERROR
;
return
CommandResult
::
ERROR
;
client
.
partition
.
SetRandom
(
status
);
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
;
return
CommandResult
::
OK
;
}
}
...
@@ -379,8 +378,7 @@ handle_replay_gain_mode(Client &client,
...
@@ -379,8 +378,7 @@ handle_replay_gain_mode(Client &client,
return
CommandResult
::
ERROR
;
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
;
return
CommandResult
::
OK
;
}
}
...
...
src/mixer/MixerAll.cxx
View file @
f5a923b9
...
@@ -18,11 +18,10 @@
...
@@ -18,11 +18,10 @@
*/
*/
#include "config.h"
#include "config.h"
#include "
MixerAll
.hxx"
#include "
output/MultipleOutputs
.hxx"
#include "MixerControl.hxx"
#include "MixerControl.hxx"
#include "MixerInternal.hxx"
#include "MixerInternal.hxx"
#include "MixerList.hxx"
#include "MixerList.hxx"
#include "output/OutputAll.hxx"
#include "output/OutputInternal.hxx"
#include "output/OutputInternal.hxx"
#include "pcm/Volume.hxx"
#include "pcm/Volume.hxx"
#include "util/Error.hxx"
#include "util/Error.hxx"
...
@@ -34,39 +33,33 @@
...
@@ -34,39 +33,33 @@
static
constexpr
Domain
mixer_domain
(
"mixer"
);
static
constexpr
Domain
mixer_domain
(
"mixer"
);
static
int
static
int
output_mixer_get_volume
(
unsigned
i
)
output_mixer_get_volume
(
const
audio_output
&
ao
)
{
{
struct
audio_output
*
output
;
if
(
!
ao
.
enabled
)
int
volume
;
assert
(
i
<
audio_output_count
());
output
=
audio_output_get
(
i
);
if
(
!
output
->
enabled
)
return
-
1
;
return
-
1
;
Mixer
*
mixer
=
output
->
mixer
;
Mixer
*
mixer
=
ao
.
mixer
;
if
(
mixer
==
nullptr
)
if
(
mixer
==
nullptr
)
return
-
1
;
return
-
1
;
Error
error
;
Error
error
;
volume
=
mixer_get_volume
(
mixer
,
error
);
int
volume
=
mixer_get_volume
(
mixer
,
error
);
if
(
volume
<
0
&&
error
.
IsDefined
())
if
(
volume
<
0
&&
error
.
IsDefined
())
FormatError
(
error
,
FormatError
(
error
,
"Failed to read mixer for '%s'"
,
"Failed to read mixer for '%s'"
,
output
->
name
);
ao
.
name
);
return
volume
;
return
volume
;
}
}
int
int
mixer_all_get_volume
(
void
)
MultipleOutputs
::
GetVolume
()
const
{
{
unsigned
count
=
audio_output_count
(),
ok
=
0
;
unsigned
ok
=
0
;
int
volume
,
total
=
0
;
int
total
=
0
;
for
(
unsigned
i
=
0
;
i
<
count
;
i
++
)
{
for
(
auto
ao
:
outputs
)
{
volume
=
output_mixer_get_volume
(
i
);
int
volume
=
output_mixer_get_volume
(
*
ao
);
if
(
volume
>=
0
)
{
if
(
volume
>=
0
)
{
total
+=
volume
;
total
+=
volume
;
++
ok
;
++
ok
;
...
@@ -80,59 +73,47 @@ mixer_all_get_volume(void)
...
@@ -80,59 +73,47 @@ mixer_all_get_volume(void)
}
}
static
bool
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
);
assert
(
volume
<=
100
);
output
=
audio_output_get
(
i
);
if
(
!
ao
.
enabled
)
if
(
!
output
->
enabled
)
return
false
;
return
false
;
Mixer
*
mixer
=
output
->
mixer
;
Mixer
*
mixer
=
ao
.
mixer
;
if
(
mixer
==
nullptr
)
if
(
mixer
==
nullptr
)
return
false
;
return
false
;
Error
error
;
Error
error
;
success
=
mixer_set_volume
(
mixer
,
volume
,
error
);
bool
success
=
mixer_set_volume
(
mixer
,
volume
,
error
);
if
(
!
success
&&
error
.
IsDefined
())
if
(
!
success
&&
error
.
IsDefined
())
FormatError
(
error
,
FormatError
(
error
,
"Failed to set mixer for '%s'"
,
"Failed to set mixer for '%s'"
,
output
->
name
);
ao
.
name
);
return
success
;
return
success
;
}
}
bool
bool
mixer_all_set_v
olume
(
unsigned
volume
)
MultipleOutputs
::
SetV
olume
(
unsigned
volume
)
{
{
bool
success
=
false
;
unsigned
count
=
audio_output_count
();
assert
(
volume
<=
100
);
assert
(
volume
<=
100
);
for
(
unsigned
i
=
0
;
i
<
count
;
i
++
)
bool
success
=
false
;
success
=
output_mixer_set_volume
(
i
,
volume
)
for
(
auto
ao
:
outputs
)
success
=
output_mixer_set_volume
(
*
ao
,
volume
)
||
success
;
||
success
;
return
success
;
return
success
;
}
}
static
int
static
int
output_mixer_get_software_volume
(
unsigned
i
)
output_mixer_get_software_volume
(
const
audio_output
&
ao
)
{
{
struct
audio_output
*
output
;
if
(
!
ao
.
enabled
)
assert
(
i
<
audio_output_count
());
output
=
audio_output_get
(
i
);
if
(
!
output
->
enabled
)
return
-
1
;
return
-
1
;
Mixer
*
mixer
=
output
->
mixer
;
Mixer
*
mixer
=
ao
.
mixer
;
if
(
mixer
==
nullptr
||
!
mixer
->
IsPlugin
(
software_mixer_plugin
))
if
(
mixer
==
nullptr
||
!
mixer
->
IsPlugin
(
software_mixer_plugin
))
return
-
1
;
return
-
1
;
...
@@ -140,13 +121,13 @@ output_mixer_get_software_volume(unsigned i)
...
@@ -140,13 +121,13 @@ output_mixer_get_software_volume(unsigned i)
}
}
int
int
mixer_all_get_software_volume
(
void
)
MultipleOutputs
::
GetSoftwareVolume
()
const
{
{
unsigned
count
=
audio_output_count
(),
ok
=
0
;
unsigned
ok
=
0
;
int
volume
,
total
=
0
;
int
total
=
0
;
for
(
unsigned
i
=
0
;
i
<
count
;
i
++
)
{
for
(
auto
ao
:
outputs
)
{
volume
=
output_mixer_get_software_volume
(
i
);
int
volume
=
output_mixer_get_software_volume
(
*
ao
);
if
(
volume
>=
0
)
{
if
(
volume
>=
0
)
{
total
+=
volume
;
total
+=
volume
;
++
ok
;
++
ok
;
...
@@ -160,16 +141,15 @@ mixer_all_get_software_volume(void)
...
@@ -160,16 +141,15 @@ mixer_all_get_software_volume(void)
}
}
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
);
assert
(
volume
<=
PCM_VOLUME_1
);
for
(
unsigned
i
=
0
;
i
<
count
;
i
++
)
{
for
(
auto
ao
:
outputs
)
{
struct
audio_output
*
output
=
audio_output_get
(
i
);
const
auto
mixer
=
ao
->
mixer
;
if
(
output
->
mixer
!=
nullptr
&&
output
->
mixer
->
plugin
==
&
software_mixer_plugin
)
if
(
mixer
!=
nullptr
&&
mixer_set_volume
(
output
->
mixer
,
volume
,
IgnoreError
());
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 @@
...
@@ -19,7 +19,7 @@
#include "config.h"
#include "config.h"
#include "Volume.hxx"
#include "Volume.hxx"
#include "
MixerAll
.hxx"
#include "
output/MultipleOutputs
.hxx"
#include "Idle.hxx"
#include "Idle.hxx"
#include "GlobalEvents.hxx"
#include "GlobalEvents.hxx"
#include "util/StringUtil.hxx"
#include "util/StringUtil.hxx"
...
@@ -59,36 +59,40 @@ void volume_init(void)
...
@@ -59,36 +59,40 @@ void volume_init(void)
GlobalEvents
::
Register
(
GlobalEvents
::
MIXER
,
mixer_event_callback
);
GlobalEvents
::
Register
(
GlobalEvents
::
MIXER
,
mixer_event_callback
);
}
}
int
volume_level_get
(
void
)
int
volume_level_get
(
const
MultipleOutputs
&
outputs
)
{
{
if
(
last_hardware_volume
>=
0
&&
if
(
last_hardware_volume
>=
0
&&
!
hardware_volume_clock
.
CheckUpdate
(
1000
))
!
hardware_volume_clock
.
CheckUpdate
(
1000
))
/* throttle access to hardware mixers */
/* throttle access to hardware mixers */
return
last_hardware_volume
;
return
last_hardware_volume
;
last_hardware_volume
=
mixer_all_get_v
olume
();
last_hardware_volume
=
outputs
.
GetV
olume
();
return
last_hardware_volume
;
return
last_hardware_volume
;
}
}
static
bool
software_volume_change
(
unsigned
volume
)
static
bool
software_volume_change
(
MultipleOutputs
&
outputs
,
unsigned
volume
)
{
{
assert
(
volume
<=
100
);
assert
(
volume
<=
100
);
volume_software_set
=
volume
;
volume_software_set
=
volume
;
mixer_all_set_software_v
olume
(
volume
);
outputs
.
SetSoftwareV
olume
(
volume
);
return
true
;
return
true
;
}
}
static
bool
hardware_volume_change
(
unsigned
volume
)
static
bool
hardware_volume_change
(
MultipleOutputs
&
outputs
,
unsigned
volume
)
{
{
/* reset the cache */
/* reset the cache */
last_hardware_volume
=
-
1
;
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
);
assert
(
volume
<=
100
);
...
@@ -96,11 +100,11 @@ bool volume_level_change(unsigned volume)
...
@@ -96,11 +100,11 @@ bool volume_level_change(unsigned volume)
idle_add
(
IDLE_MIXER
);
idle_add
(
IDLE_MIXER
);
return
hardware_volume_change
(
volume
);
return
hardware_volume_change
(
outputs
,
volume
);
}
}
bool
bool
read_sw_volume_state
(
const
char
*
line
)
read_sw_volume_state
(
const
char
*
line
,
MultipleOutputs
&
outputs
)
{
{
char
*
end
=
nullptr
;
char
*
end
=
nullptr
;
long
int
sv
;
long
int
sv
;
...
@@ -111,7 +115,7 @@ read_sw_volume_state(const char *line)
...
@@ -111,7 +115,7 @@ read_sw_volume_state(const char *line)
line
+=
sizeof
(
SW_VOLUME_STATE
)
-
1
;
line
+=
sizeof
(
SW_VOLUME_STATE
)
-
1
;
sv
=
strtol
(
line
,
&
end
,
10
);
sv
=
strtol
(
line
,
&
end
,
10
);
if
(
*
end
==
0
&&
sv
>=
0
&&
sv
<=
100
)
if
(
*
end
==
0
&&
sv
>=
0
&&
sv
<=
100
)
software_volume_change
(
sv
);
software_volume_change
(
outputs
,
sv
);
else
else
FormatWarning
(
volume_domain
,
FormatWarning
(
volume_domain
,
"Can't parse software volume: %s"
,
line
);
"Can't parse software volume: %s"
,
line
);
...
...
src/mixer/Volume.hxx
View file @
f5a923b9
...
@@ -24,15 +24,19 @@
...
@@ -24,15 +24,19 @@
#include <stdio.h>
#include <stdio.h>
class
MultipleOutputs
;
void
volume_init
(
void
);
void
volume_init
(
void
);
gcc_pure
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
bool
read_sw_volume_state
(
const
char
*
line
);
read_sw_volume_state
(
const
char
*
line
,
MultipleOutputs
&
outputs
);
void
save_sw_volume_state
(
FILE
*
fp
);
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 @@
...
@@ -26,58 +26,92 @@
#ifndef OUTPUT_ALL_H
#ifndef OUTPUT_ALL_H
#define OUTPUT_ALL_H
#define OUTPUT_ALL_H
#include "AudioFormat.hxx"
#include "ReplayGainInfo.hxx"
#include "ReplayGainInfo.hxx"
#include "Compiler.h"
#include "Compiler.h"
#include <vector>
#include <assert.h>
struct
AudioFormat
;
struct
AudioFormat
;
class
MusicBuffer
;
class
MusicBuffer
;
class
MusicPipe
;
struct
music_chunk
;
struct
music_chunk
;
struct
PlayerControl
;
struct
PlayerControl
;
struct
audio_output
;
class
Error
;
class
Error
;
/**
class
MultipleOutputs
{
* Global initialization: load audio outputs from the configuration
std
::
vector
<
audio_output
*>
outputs
;
* file and initialize them.
AudioFormat
input_audio_format
;
/**
* The #MusicBuffer object where consumed chunks are returned.
*/
*/
void
MusicBuffer
*
buffer
;
audio_output_all_init
(
PlayerControl
&
pc
);
/**
/**
* 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
MusicPipe
*
pipe
;
audio_output_all_finish
(
void
);
/**
* 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
* Returns the total number of audio output devices, including
* who
are disabled right now.
* those which
are disabled right now.
*/
*/
gcc_const
gcc_pure
unsigned
int
audio_output_count
(
void
);
unsigned
Size
()
const
{
return
outputs
.
size
();
}
/**
/**
* Returns the "i"th audio output device.
* Returns the "i"th audio output device.
*/
*/
gcc_const
const
audio_output
&
Get
(
unsigned
i
)
const
{
struct
audio_output
*
assert
(
i
<
Size
());
audio_output_get
(
unsigned
i
);
/**
return
*
outputs
[
i
];
* Returns the audio output device with the specified name. Returns
}
* NULL if the name does not exist.
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
gcc_pure
struct
audio_output
*
audio_output
*
FindByName
(
const
char
*
name
)
const
;
audio_output_find
(
const
char
*
name
);
/**
/**
* Checks the "enabled" flag of all audio outputs, and if one has
* Checks the "enabled" flag of all audio outputs, and if one has
* changed, commit the change.
* changed, commit the change.
*/
*/
void
void
EnableDisable
();
audio_output_all_enable_disable
(
void
);
/**
/**
* Opens all audio outputs which are not disabled.
* Opens all audio outputs which are not disabled.
*
*
* @param audio_format the preferred audio format
* @param audio_format the preferred audio format
...
@@ -85,28 +119,23 @@ audio_output_all_enable_disable(void);
...
@@ -85,28 +119,23 @@ audio_output_all_enable_disable(void);
* should be returned
* should be returned
* @return true on success, false on failure
* @return true on success, false on failure
*/
*/
bool
bool
Open
(
const
AudioFormat
audio_format
,
MusicBuffer
&
_buffer
,
audio_output_all_open
(
AudioFormat
audio_format
,
MusicBuffer
&
buffer
,
Error
&
error
);
Error
&
error
);
/**
/**
* Closes all audio outputs.
* Closes all audio outputs.
*/
*/
void
void
Close
();
audio_output_all_close
(
void
);
/**
/**
* Closes all audio outputs. Outputs with the "always_on" flag are
* Closes all audio outputs. Outputs with the "always_on"
*
put into pause mode.
* flag are
put into pause mode.
*/
*/
void
void
Release
();
audio_output_all_release
(
void
);
void
void
SetReplayGainMode
(
ReplayGainMode
mode
);
audio_output_all_set_replay_gain_mode
(
ReplayGainMode
mode
);
/**
/**
* Enqueue a #music_chunk object for playing, i.e. pushes it to a
* Enqueue a #music_chunk object for playing, i.e. pushes it to a
* #MusicPipe.
* #MusicPipe.
*
*
...
@@ -114,19 +143,17 @@ audio_output_all_set_replay_gain_mode(ReplayGainMode mode);
...
@@ -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
* @return true on success, false if no audio output was able to play
* (all closed then)
* (all closed then)
*/
*/
bool
bool
Play
(
music_chunk
*
chunk
,
Error
&
error
);
audio_output_all_play
(
music_chunk
*
chunk
,
Error
&
error
);
/**
/**
* Checks if the output devices have drained their music pipe, and
* Checks if the output devices have drained their music pipe, and
* returns the consumed music chunks to the #music_buffer.
* returns the consumed music chunks to the #music_buffer.
*
*
* @return the number of chunks to play left in the #MusicPipe
* @return the number of chunks to play left in the #MusicPipe
*/
*/
unsigned
unsigned
Check
();
audio_output_all_check
(
void
);
/**
/**
* Checks if the size of the #MusicPipe is below the #threshold. If
* Checks if the size of the #MusicPipe is below the #threshold. If
* not, it attempts to synchronize with all output threads, and waits
* not, it attempts to synchronize with all output threads, and waits
* until another #music_chunk is finished.
* until another #music_chunk is finished.
...
@@ -134,41 +161,111 @@ audio_output_all_check(void);
...
@@ -134,41 +161,111 @@ audio_output_all_check(void);
* @param threshold the maximum number of chunks in the pipe
* @param threshold the maximum number of chunks in the pipe
* @return true if there are less than #threshold chunks in the pipe
* @return true if there are less than #threshold chunks in the pipe
*/
*/
bool
bool
Wait
(
PlayerControl
&
pc
,
unsigned
threshold
);
audio_output_all_wait
(
PlayerControl
&
pc
,
unsigned
threshold
);
/**
/**
* Puts all audio outputs into pause mode. Most implementations will
* Puts all audio outputs into pause mode. Most implementations will
* simply close it then.
* simply close it then.
*/
*/
void
void
Pause
();
audio_output_all_pause
(
void
);
/**
/**
* Drain all audio outputs.
* Drain all audio outputs.
*/
*/
void
void
Drain
();
audio_output_all_drain
(
void
);
/**
/**
* Try to cancel data which may still be in the device's buffers.
* Try to cancel data which may still be in the device's buffers.
*/
*/
void
void
Cancel
();
audio_output_all_cancel
(
void
);
/**
/**
* Indicate that a new song will begin now.
* Indicate that a new song will begin now.
*/
*/
void
void
SongBorder
();
audio_output_all_song_border
(
void
);
/**
/**
* Returns the "elapsed_time" stamp of the most recently finished
* Returns the "elapsed_time" stamp of the most recently finished
* chunk. A negative value is returned when no chunk has been
* chunk. A negative value is returned when no chunk has been
* finished yet.
* finished yet.
*/
*/
gcc_pure
gcc_pure
float
float
GetElapsedTime
()
const
{
audio_output_all_get_elapsed_time
(
void
);
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
#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 @@
...
@@ -26,7 +26,7 @@
#include "config.h"
#include "config.h"
#include "OutputCommand.hxx"
#include "OutputCommand.hxx"
#include "
OutputAll
.hxx"
#include "
MultipleOutputs
.hxx"
#include "OutputInternal.hxx"
#include "OutputInternal.hxx"
#include "PlayerControl.hxx"
#include "PlayerControl.hxx"
#include "mixer/MixerControl.hxx"
#include "mixer/MixerControl.hxx"
...
@@ -35,21 +35,19 @@
...
@@ -35,21 +35,19 @@
extern
unsigned
audio_output_state_version
;
extern
unsigned
audio_output_state_version
;
bool
bool
audio_output_enable_index
(
unsigned
idx
)
audio_output_enable_index
(
MultipleOutputs
&
outputs
,
unsigned
idx
)
{
{
struct
audio_output
*
ao
;
if
(
idx
>=
outputs
.
Size
())
if
(
idx
>=
audio_output_count
())
return
false
;
return
false
;
a
o
=
audio_output_g
et
(
idx
);
a
udio_output
&
ao
=
outputs
.
G
et
(
idx
);
if
(
ao
->
enabled
)
if
(
ao
.
enabled
)
return
true
;
return
true
;
ao
->
enabled
=
true
;
ao
.
enabled
=
true
;
idle_add
(
IDLE_OUTPUT
);
idle_add
(
IDLE_OUTPUT
);
ao
->
player_control
->
UpdateAudio
();
ao
.
player_control
->
UpdateAudio
();
++
audio_output_state_version
;
++
audio_output_state_version
;
...
@@ -57,27 +55,25 @@ audio_output_enable_index(unsigned idx)
...
@@ -57,27 +55,25 @@ audio_output_enable_index(unsigned idx)
}
}
bool
bool
audio_output_disable_index
(
unsigned
idx
)
audio_output_disable_index
(
MultipleOutputs
&
outputs
,
unsigned
idx
)
{
{
struct
audio_output
*
ao
;
if
(
idx
>=
outputs
.
Size
())
if
(
idx
>=
audio_output_count
())
return
false
;
return
false
;
a
o
=
audio_output_g
et
(
idx
);
a
udio_output
&
ao
=
outputs
.
G
et
(
idx
);
if
(
!
ao
->
enabled
)
if
(
!
ao
.
enabled
)
return
true
;
return
true
;
ao
->
enabled
=
false
;
ao
.
enabled
=
false
;
idle_add
(
IDLE_OUTPUT
);
idle_add
(
IDLE_OUTPUT
);
Mixer
*
mixer
=
ao
->
mixer
;
Mixer
*
mixer
=
ao
.
mixer
;
if
(
mixer
!=
nullptr
)
{
if
(
mixer
!=
nullptr
)
{
mixer_close
(
mixer
);
mixer_close
(
mixer
);
idle_add
(
IDLE_MIXER
);
idle_add
(
IDLE_MIXER
);
}
}
ao
->
player_control
->
UpdateAudio
();
ao
.
player_control
->
UpdateAudio
();
++
audio_output_state_version
;
++
audio_output_state_version
;
...
@@ -85,26 +81,24 @@ audio_output_disable_index(unsigned idx)
...
@@ -85,26 +81,24 @@ audio_output_disable_index(unsigned idx)
}
}
bool
bool
audio_output_toggle_index
(
unsigned
idx
)
audio_output_toggle_index
(
MultipleOutputs
&
outputs
,
unsigned
idx
)
{
{
struct
audio_output
*
ao
;
if
(
idx
>=
outputs
.
Size
())
if
(
idx
>=
audio_output_count
())
return
false
;
return
false
;
a
o
=
audio_output_g
et
(
idx
);
a
udio_output
&
ao
=
outputs
.
G
et
(
idx
);
const
bool
enabled
=
ao
->
enabled
=
!
ao
->
enabled
;
const
bool
enabled
=
ao
.
enabled
=
!
ao
.
enabled
;
idle_add
(
IDLE_OUTPUT
);
idle_add
(
IDLE_OUTPUT
);
if
(
!
enabled
)
{
if
(
!
enabled
)
{
Mixer
*
mixer
=
ao
->
mixer
;
Mixer
*
mixer
=
ao
.
mixer
;
if
(
mixer
!=
nullptr
)
{
if
(
mixer
!=
nullptr
)
{
mixer_close
(
mixer
);
mixer_close
(
mixer
);
idle_add
(
IDLE_MIXER
);
idle_add
(
IDLE_MIXER
);
}
}
}
}
ao
->
player_control
->
UpdateAudio
();
ao
.
player_control
->
UpdateAudio
();
++
audio_output_state_version
;
++
audio_output_state_version
;
...
...
src/output/OutputCommand.hxx
View file @
f5a923b9
...
@@ -27,25 +27,27 @@
...
@@ -27,25 +27,27 @@
#ifndef MPD_OUTPUT_COMMAND_HXX
#ifndef MPD_OUTPUT_COMMAND_HXX
#define MPD_OUTPUT_COMMAND_HXX
#define MPD_OUTPUT_COMMAND_HXX
class
MultipleOutputs
;
/**
/**
* Enables an audio output. Returns false if the specified output
* Enables an audio output. Returns false if the specified output
* does not exist.
* does not exist.
*/
*/
bool
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
* Disables an audio output. Returns false if the specified output
* does not exist.
* does not exist.
*/
*/
bool
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
* Toggles an audio output. Returns false if the specified output
* does not exist.
* does not exist.
*/
*/
bool
bool
audio_output_toggle_index
(
unsigned
idx
);
audio_output_toggle_index
(
MultipleOutputs
&
outputs
,
unsigned
idx
);
#endif
#endif
src/output/OutputPrint.cxx
View file @
f5a923b9
...
@@ -24,22 +24,20 @@
...
@@ -24,22 +24,20 @@
#include "config.h"
#include "config.h"
#include "OutputPrint.hxx"
#include "OutputPrint.hxx"
#include "
OutputAll
.hxx"
#include "
MultipleOutputs
.hxx"
#include "OutputInternal.hxx"
#include "OutputInternal.hxx"
#include "client/Client.hxx"
#include "client/Client.hxx"
void
void
printAudioDevices
(
Client
&
client
)
printAudioDevices
(
Client
&
client
,
const
MultipleOutputs
&
outputs
)
{
{
const
unsigned
n
=
audio_output_count
();
for
(
unsigned
i
=
0
,
n
=
outputs
.
Size
();
i
!=
n
;
++
i
)
{
const
audio_output
&
ao
=
outputs
.
Get
(
i
);
for
(
unsigned
i
=
0
;
i
<
n
;
++
i
)
{
const
struct
audio_output
*
ao
=
audio_output_get
(
i
);
client_printf
(
client
,
client_printf
(
client
,
"outputid: %i
\n
"
"outputid: %i
\n
"
"outputname: %s
\n
"
"outputname: %s
\n
"
"outputenabled: %i
\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
* Copyright (C) 2003-2014 The Music Player Daemon Project
* http://www.musicpd.org
* http://www.musicpd.org
...
@@ -27,8 +26,9 @@
...
@@ -27,8 +26,9 @@
#define MPD_OUTPUT_PRINT_HXX
#define MPD_OUTPUT_PRINT_HXX
class
Client
;
class
Client
;
class
MultipleOutputs
;
void
void
printAudioDevices
(
Client
&
client
);
printAudioDevices
(
Client
&
client
,
const
MultipleOutputs
&
outputs
);
#endif
#endif
src/output/OutputState.cxx
View file @
f5a923b9
...
@@ -24,7 +24,7 @@
...
@@ -24,7 +24,7 @@
#include "config.h"
#include "config.h"
#include "OutputState.hxx"
#include "OutputState.hxx"
#include "
OutputAll
.hxx"
#include "
MultipleOutputs
.hxx"
#include "OutputInternal.hxx"
#include "OutputInternal.hxx"
#include "OutputError.hxx"
#include "OutputError.hxx"
#include "Log.hxx"
#include "Log.hxx"
...
@@ -38,27 +38,22 @@
...
@@ -38,27 +38,22 @@
unsigned
audio_output_state_version
;
unsigned
audio_output_state_version
;
void
void
audio_output_state_save
(
FILE
*
fp
)
audio_output_state_save
(
FILE
*
fp
,
const
MultipleOutputs
&
outputs
)
{
{
unsigned
n
=
audio_output_count
();
for
(
unsigned
i
=
0
,
n
=
outputs
.
Size
();
i
!=
n
;
++
i
)
{
const
audio_output
&
ao
=
outputs
.
Get
(
i
);
assert
(
n
>
0
);
for
(
unsigned
i
=
0
;
i
<
n
;
++
i
)
{
const
struct
audio_output
*
ao
=
audio_output_get
(
i
);
fprintf
(
fp
,
AUDIO_DEVICE_STATE
"%d:%s
\n
"
,
fprintf
(
fp
,
AUDIO_DEVICE_STATE
"%d:%s
\n
"
,
ao
->
enabled
,
ao
->
name
);
ao
.
enabled
,
ao
.
name
);
}
}
}
}
bool
bool
audio_output_state_read
(
const
char
*
line
)
audio_output_state_read
(
const
char
*
line
,
MultipleOutputs
&
outputs
)
{
{
long
value
;
long
value
;
char
*
endptr
;
char
*
endptr
;
const
char
*
name
;
const
char
*
name
;
struct
audio_output
*
ao
;
if
(
!
StringStartsWith
(
line
,
AUDIO_DEVICE_STATE
))
if
(
!
StringStartsWith
(
line
,
AUDIO_DEVICE_STATE
))
return
false
;
return
false
;
...
@@ -74,7 +69,7 @@ audio_output_state_read(const char *line)
...
@@ -74,7 +69,7 @@ audio_output_state_read(const char *line)
return
true
;
return
true
;
name
=
endptr
+
1
;
name
=
endptr
+
1
;
a
o
=
audio_output_find
(
name
);
a
udio_output
*
ao
=
outputs
.
FindByName
(
name
);
if
(
ao
==
NULL
)
{
if
(
ao
==
NULL
)
{
FormatDebug
(
output_domain
,
FormatDebug
(
output_domain
,
"Ignoring device state for '%s'"
,
name
);
"Ignoring device state for '%s'"
,
name
);
...
...
src/output/OutputState.hxx
View file @
f5a923b9
...
@@ -27,11 +27,13 @@
...
@@ -27,11 +27,13 @@
#include <stdio.h>
#include <stdio.h>
class
MultipleOutputs
;
bool
bool
audio_output_state_read
(
const
char
*
line
);
audio_output_state_read
(
const
char
*
line
,
MultipleOutputs
&
outputs
);
void
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
* 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)
...
@@ -74,8 +74,10 @@ find_named_config_block(ConfigOption option, const char *name)
return
NULL
;
return
NULL
;
}
}
PlayerControl
::
PlayerControl
(
gcc_unused
unsigned
_buffer_chunks
,
PlayerControl
::
PlayerControl
(
gcc_unused
MultipleOutputs
&
_outputs
,
gcc_unused
unsigned
_buffered_before_play
)
{}
gcc_unused
unsigned
_buffer_chunks
,
gcc_unused
unsigned
_buffered_before_play
)
:
outputs
(
_outputs
)
{}
PlayerControl
::~
PlayerControl
()
{}
PlayerControl
::~
PlayerControl
()
{}
static
struct
audio_output
*
static
struct
audio_output
*
...
@@ -89,7 +91,8 @@ load_audio_output(const char *name)
...
@@ -89,7 +91,8 @@ load_audio_output(const char *name)
return
nullptr
;
return
nullptr
;
}
}
static
struct
PlayerControl
dummy_player_control
(
32
,
4
);
static
struct
PlayerControl
dummy_player_control
(
*
(
MultipleOutputs
*
)
nullptr
,
32
,
4
);
Error
error
;
Error
error
;
struct
audio_output
*
ao
=
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