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
8a407bfb
Commit
8a407bfb
authored
8 years ago
by
Max Kellermann
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
output/Thread: move code to new class AudioOutputSource
parent
d30a590d
Hide whitespace changes
Inline
Side-by-side
Showing
8 changed files
with
389 additions
and
200 deletions
+389
-200
Makefile.am
Makefile.am
+1
-0
Init.cxx
src/output/Init.cxx
+0
-4
Internal.cxx
src/output/Internal.cxx
+1
-1
Internal.hxx
src/output/Internal.hxx
+5
-42
OutputControl.cxx
src/output/OutputControl.cxx
+1
-1
OutputThread.cxx
src/output/OutputThread.cxx
+34
-152
Source.cxx
src/output/Source.cxx
+187
-0
Source.hxx
src/output/Source.hxx
+160
-0
No files found.
Makefile.am
View file @
8a407bfb
...
...
@@ -1340,6 +1340,7 @@ OUTPUT_API_SRC = \
src/output/Registry.cxx src/output/Registry.hxx
\
src/output/MultipleOutputs.cxx src/output/MultipleOutputs.hxx
\
src/output/SharedPipeConsumer.cxx src/output/SharedPipeConsumer.hxx
\
src/output/Source.cxx src/output/Source.hxx
\
src/output/OutputThread.cxx
\
src/output/Domain.cxx src/output/Domain.hxx
\
src/output/OutputControl.cxx
\
...
...
This diff is collapsed.
Click to expand it.
src/output/Init.cxx
View file @
8a407bfb
...
...
@@ -222,13 +222,9 @@ audio_output_setup(EventLoop &event_loop,
NewReplayGainFilter
(
replay_gain_config
);
assert
(
ao
.
prepared_replay_gain_filter
!=
nullptr
);
ao
.
replay_gain_serial
=
0
;
ao
.
prepared_other_replay_gain_filter
=
NewReplayGainFilter
(
replay_gain_config
);
assert
(
ao
.
prepared_other_replay_gain_filter
!=
nullptr
);
ao
.
other_replay_gain_serial
=
0
;
}
else
{
ao
.
prepared_replay_gain_filter
=
nullptr
;
ao
.
prepared_other_replay_gain_filter
=
nullptr
;
...
...
This diff is collapsed.
Click to expand it.
src/output/Internal.cxx
View file @
8a407bfb
...
...
@@ -26,5 +26,5 @@ AudioOutput::IsChunkConsumed(const MusicChunk &chunk) const
if
(
!
open
)
return
true
;
return
pipe
.
Is
Consumed
(
chunk
);
return
source
.
IsChunk
Consumed
(
chunk
);
}
This diff is collapsed.
Click to expand it.
src/output/Internal.hxx
View file @
8a407bfb
...
...
@@ -20,11 +20,9 @@
#ifndef MPD_OUTPUT_INTERNAL_HXX
#define MPD_OUTPUT_INTERNAL_HXX
#include "Source.hxx"
#include "SharedPipeConsumer.hxx"
#include "AudioFormat.hxx"
#include "pcm/PcmBuffer.hxx"
#include "pcm/PcmDither.hxx"
#include "ReplayGainMode.hxx"
#include "filter/Observer.hxx"
#include "thread/Mutex.hxx"
#include "thread/Cond.hxx"
...
...
@@ -149,8 +147,6 @@ struct AudioOutput {
*/
bool
woken_for_play
=
false
;
ReplayGainMode
replay_gain_mode
=
ReplayGainMode
::
OFF
;
/**
* If not nullptr, the device has failed, and this timer is used
* to estimate how long it should stay disabled (unless
...
...
@@ -164,14 +160,6 @@ struct AudioOutput {
AudioFormat
config_audio_format
;
/**
* The audio_format in which audio data is received from the
* player thread (which in turn receives it from the decoder).
*
* Only accessible from within the OutputThread.
*/
AudioFormat
in_audio_format
;
/**
* The #AudioFormat which is emitted by the #Filter, with
* #config_audio_format already applied. This is used to
* decide whether this object needs to be closed and reopened
...
...
@@ -188,21 +176,10 @@ struct AudioOutput {
AudioFormat
out_audio_format
;
/**
* The buffer used to allocate the cross-fading result.
*/
PcmBuffer
cross_fade_buffer
;
/**
* The dithering state for cross-fading two streams.
*/
PcmDither
cross_fade_dither
;
/**
* The filter object of this audio output. This is an
* instance of chain_filter_plugin.
*/
PreparedFilter
*
prepared_filter
=
nullptr
;
Filter
*
filter_instance
=
nullptr
;
/**
* The #VolumeFilter instance of this audio output. It is
...
...
@@ -215,13 +192,6 @@ struct AudioOutput {
* output.
*/
PreparedFilter
*
prepared_replay_gain_filter
=
nullptr
;
Filter
*
replay_gain_filter_instance
=
nullptr
;
/**
* The serial number of the last replay gain info. 0 means no
* replay gain info was available.
*/
unsigned
replay_gain_serial
;
/**
* The replay_gain_filter_plugin instance of this audio
...
...
@@ -229,13 +199,6 @@ struct AudioOutput {
* cross-fading.
*/
PreparedFilter
*
prepared_other_replay_gain_filter
=
nullptr
;
Filter
*
other_replay_gain_filter_instance
=
nullptr
;
/**
* The serial number of the last replay gain info by the
* "other" chunk during cross-fading.
*/
unsigned
other_replay_gain_serial
;
/**
* The convert_filter_plugin instance of this audio output.
...
...
@@ -289,9 +252,9 @@ struct AudioOutput {
AudioOutputClient
*
client
;
/**
*
A reference to the #MusicPipe and the current position
.
*
Source of audio data
.
*/
SharedPipeConsumer
pip
e
;
AudioOutputSource
sourc
e
;
/**
* Throws #std::runtime_error on error.
...
...
@@ -394,7 +357,7 @@ public:
void
LockRelease
();
void
SetReplayGainMode
(
ReplayGainMode
_mode
)
{
replay_gain_mode
=
_mode
;
source
.
SetReplayGainMode
(
_mode
)
;
}
/**
...
...
@@ -442,7 +405,7 @@ public:
}
void
ClearTailChunk
(
const
MusicChunk
&
chunk
)
{
pipe
.
ClearTail
(
chunk
);
source
.
ClearTailChunk
(
chunk
);
}
private
:
...
...
This diff is collapsed.
Click to expand it.
src/output/OutputControl.cxx
View file @
8a407bfb
...
...
@@ -113,7 +113,7 @@ AudioOutput::Open(const AudioFormat audio_format, const MusicPipe &mp)
fail_timer
.
Reset
();
if
(
open
&&
audio_format
==
request
.
audio_format
)
{
assert
(
&
pipe
.
GetPipe
()
==
&
mp
||
(
always_on
&&
pause
));
assert
(
request
.
pipe
==
&
mp
||
(
always_on
&&
pause
));
if
(
!
pause
)
/* already open, already the right parameters
...
...
This diff is collapsed.
Click to expand it.
src/output/OutputThread.cxx
View file @
8a407bfb
...
...
@@ -91,27 +91,18 @@ AudioOutput::Disable()
inline
AudioFormat
AudioOutput
::
OpenFilter
(
AudioFormat
&
format
)
try
{
{
assert
(
format
.
IsValid
());
/* the replay_gain filter cannot fail here */
if
(
prepared_replay_gain_filter
!=
nullptr
)
replay_gain_filter_instance
=
prepared_replay_gain_filter
->
Open
(
format
);
if
(
prepared_other_replay_gain_filter
!=
nullptr
)
other_replay_gain_filter_instance
=
prepared_other_replay_gain_filter
->
Open
(
format
);
filter_instance
=
prepared_filter
->
Open
(
format
);
const
auto
result
=
source
.
Open
(
format
,
*
request
.
pipe
,
prepared_replay_gain_filter
,
prepared_other_replay_gain_filter
,
prepared_filter
);
if
(
mixer
!=
nullptr
&&
mixer
->
IsPlugin
(
software_mixer_plugin
))
software_mixer_set_filter
(
*
mixer
,
volume_filter
.
Get
());
return
filter_instance
->
GetOutAudioFormat
();
}
catch
(...)
{
CloseFilter
();
throw
;
return
result
;
}
void
...
...
@@ -120,14 +111,7 @@ AudioOutput::CloseFilter()
if
(
mixer
!=
nullptr
&&
mixer
->
IsPlugin
(
software_mixer_plugin
))
software_mixer_set_filter
(
*
mixer
,
nullptr
);
delete
replay_gain_filter_instance
;
replay_gain_filter_instance
=
nullptr
;
delete
other_replay_gain_filter_instance
;
other_replay_gain_filter_instance
=
nullptr
;
delete
filter_instance
;
filter_instance
=
nullptr
;
source
.
Close
();
}
inline
void
...
...
@@ -144,41 +128,29 @@ AudioOutput::Open()
return
;
}
if
(
!
open
||
request
.
pipe
!=
&
pipe
.
GetPipe
())
pipe
.
Init
(
*
request
.
pipe
);
AudioFormat
f
;
/* (re)open the filter */
if
(
filter_instance
!=
nullptr
&&
request
.
audio_format
!=
in_audio_format
)
/* the filter must be reopened on all input format
changes */
CloseFilter
();
if
(
filter_instance
==
nullptr
)
{
/* open the filter */
AudioFormat
f
;
try
{
f
=
OpenFilter
(
request
.
audio_format
)
.
WithMask
(
config_audio_format
);
}
catch
(
const
std
::
runtime_error
&
e
)
{
FormatError
(
e
,
"Failed to open filter for
\"
%s
\"
[%s]"
,
name
,
plugin
.
name
);
fail_timer
.
Update
();
return
;
}
if
(
open
&&
f
!=
filter_audio_format
)
{
/* if the filter's output format changes, the
outpuit must be reopened as well */
CloseOutput
(
true
);
open
=
false
;
}
try
{
f
=
source
.
Open
(
request
.
audio_format
,
*
request
.
pipe
,
prepared_replay_gain_filter
,
prepared_other_replay_gain_filter
,
prepared_filter
)
.
WithMask
(
config_audio_format
);
}
catch
(
const
std
::
runtime_error
&
e
)
{
FormatError
(
e
,
"Failed to open filter for
\"
%s
\"
[%s]"
,
name
,
plugin
.
name
);
fail_timer
.
Update
();
return
;
}
filter_audio_format
=
f
;
if
(
open
&&
f
!=
filter_audio_format
)
{
/* if the filter's output format changes, the output
must be reopened as well */
CloseOutput
(
true
);
open
=
false
;
}
in_audio_format
=
request
.
audio_format
;
filter_audio_format
=
f
;
if
(
!
open
)
{
if
(
OpenOutputAndConvert
(
filter_audio_format
))
{
...
...
@@ -233,9 +205,9 @@ AudioOutput::OpenOutputAndConvert(AudioFormat desired_audio_format)
plugin
.
name
,
name
,
audio_format_to_string
(
out_audio_format
,
&
af_string
));
if
(
in_audio_format
!=
out_audio_format
)
if
(
source
.
GetInputAudioFormat
()
!=
out_audio_format
)
FormatDebug
(
output_domain
,
"converting from %s"
,
audio_format_to_string
(
in_audio_format
,
audio_format_to_string
(
source
.
GetInputAudioFormat
()
,
&
af_string
));
return
true
;
...
...
@@ -246,8 +218,6 @@ AudioOutput::Close(bool drain)
{
assert
(
open
);
pipe
.
Cancel
();
open
=
false
;
const
ScopeUnlock
unlock
(
mutex
);
...
...
@@ -291,94 +261,9 @@ AudioOutput::WaitForDelay()
}
}
static
ConstBuffer
<
void
>
ao_chunk_data
(
AudioOutput
&
ao
,
const
MusicChunk
&
chunk
,
Filter
*
replay_gain_filter
,
unsigned
*
replay_gain_serial_p
)
{
assert
(
!
chunk
.
IsEmpty
());
assert
(
chunk
.
CheckFormat
(
ao
.
in_audio_format
));
ConstBuffer
<
void
>
data
(
chunk
.
data
,
chunk
.
length
);
assert
(
data
.
size
%
ao
.
in_audio_format
.
GetFrameSize
()
==
0
);
if
(
!
data
.
IsEmpty
()
&&
replay_gain_filter
!=
nullptr
)
{
replay_gain_filter_set_mode
(
*
replay_gain_filter
,
ao
.
replay_gain_mode
);
if
(
chunk
.
replay_gain_serial
!=
*
replay_gain_serial_p
)
{
replay_gain_filter_set_info
(
*
replay_gain_filter
,
chunk
.
replay_gain_serial
!=
0
?
&
chunk
.
replay_gain_info
:
nullptr
);
*
replay_gain_serial_p
=
chunk
.
replay_gain_serial
;
}
data
=
replay_gain_filter
->
FilterPCM
(
data
);
}
return
data
;
}
static
ConstBuffer
<
void
>
ao_filter_chunk
(
AudioOutput
&
ao
,
const
MusicChunk
&
chunk
)
{
ConstBuffer
<
void
>
data
=
ao_chunk_data
(
ao
,
chunk
,
ao
.
replay_gain_filter_instance
,
&
ao
.
replay_gain_serial
);
if
(
data
.
IsEmpty
())
return
data
;
/* cross-fade */
if
(
chunk
.
other
!=
nullptr
)
{
ConstBuffer
<
void
>
other_data
=
ao_chunk_data
(
ao
,
*
chunk
.
other
,
ao
.
other_replay_gain_filter_instance
,
&
ao
.
other_replay_gain_serial
);
if
(
other_data
.
IsEmpty
())
return
data
;
/* if the "other" chunk is longer, then that trailer
is used as-is, without mixing; it is part of the
"next" song being faded in, and if there's a rest,
it means cross-fading ends here */
if
(
data
.
size
>
other_data
.
size
)
data
.
size
=
other_data
.
size
;
float
mix_ratio
=
chunk
.
mix_ratio
;
if
(
mix_ratio
>=
0
)
/* reverse the mix ratio (because the
arguments to pcm_mix() are reversed), but
only if the mix ratio is non-negative; a
negative mix ratio is a MixRamp special
case */
mix_ratio
=
1.0
-
mix_ratio
;
void
*
dest
=
ao
.
cross_fade_buffer
.
Get
(
other_data
.
size
);
memcpy
(
dest
,
other_data
.
data
,
other_data
.
size
);
if
(
!
pcm_mix
(
ao
.
cross_fade_dither
,
dest
,
data
.
data
,
data
.
size
,
ao
.
in_audio_format
.
format
,
mix_ratio
))
throw
FormatRuntimeError
(
"Cannot cross-fade format %s"
,
sample_format_to_string
(
ao
.
in_audio_format
.
format
));
data
.
data
=
dest
;
data
.
size
=
other_data
.
size
;
}
/* apply filter chain */
return
ao
.
filter_instance
->
FilterPCM
(
data
);
}
inline
bool
AudioOutput
::
PlayChunk
(
const
MusicChunk
&
chunk
)
{
assert
(
filter_instance
!=
nullptr
);
if
(
tags
&&
gcc_unlikely
(
chunk
.
tag
!=
nullptr
))
{
const
ScopeUnlock
unlock
(
mutex
);
try
{
...
...
@@ -392,7 +277,7 @@ AudioOutput::PlayChunk(const MusicChunk &chunk)
ConstBuffer
<
uint8_t
>
data
;
try
{
data
=
data
.
FromVoid
(
ao_filter_chunk
(
*
this
,
chunk
));
data
=
data
.
FromVoid
(
source
.
FilterChunk
(
chunk
));
}
catch
(
const
std
::
runtime_error
&
e
)
{
FormatError
(
e
,
"Failed to filter for output
\"
%s
\"
[%s]"
,
name
,
plugin
.
name
);
...
...
@@ -443,7 +328,7 @@ AudioOutput::PlayChunk(const MusicChunk &chunk)
inline
bool
AudioOutput
::
Play
()
{
const
MusicChunk
*
chunk
=
pip
e
.
Get
();
const
MusicChunk
*
chunk
=
sourc
e
.
Get
();
if
(
chunk
==
nullptr
)
/* no chunk available */
return
false
;
...
...
@@ -474,8 +359,8 @@ AudioOutput::Play()
if
(
!
PlayChunk
(
*
chunk
))
break
;
pip
e
.
Consume
(
*
chunk
);
chunk
=
pip
e
.
Get
();
sourc
e
.
Consume
(
*
chunk
);
chunk
=
sourc
e
.
Get
();
}
while
(
chunk
!=
nullptr
);
const
ScopeUnlock
unlock
(
mutex
);
...
...
@@ -580,9 +465,6 @@ AudioOutput::Task()
case
Command
:
:
DRAIN
:
if
(
open
)
{
assert
(
pipe
.
IsInitial
());
assert
(
pipe
.
GetPipe
().
Peek
()
==
nullptr
);
const
ScopeUnlock
unlock
(
mutex
);
ao_plugin_drain
(
this
);
}
...
...
@@ -591,7 +473,7 @@ AudioOutput::Task()
continue
;
case
Command
:
:
CANCEL
:
pip
e
.
Cancel
();
sourc
e
.
Cancel
();
if
(
open
)
{
const
ScopeUnlock
unlock
(
mutex
);
...
...
@@ -603,7 +485,7 @@ AudioOutput::Task()
case
Command
:
:
KILL
:
Disable
();
pip
e
.
Cancel
();
sourc
e
.
Cancel
();
CommandFinished
();
return
;
}
...
...
This diff is collapsed.
Click to expand it.
src/output/Source.cxx
0 → 100644
View file @
8a407bfb
/*
* Copyright 2003-2016 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 "Source.hxx"
#include "MusicChunk.hxx"
#include "filter/FilterInternal.hxx"
#include "filter/plugins/ReplayGainFilterPlugin.hxx"
#include "pcm/PcmMix.hxx"
#include "util/ConstBuffer.hxx"
#include "util/RuntimeError.hxx"
#include <string.h>
AudioFormat
AudioOutputSource
::
Open
(
AudioFormat
audio_format
,
const
MusicPipe
&
_pipe
,
PreparedFilter
*
prepared_replay_gain_filter
,
PreparedFilter
*
prepared_other_replay_gain_filter
,
PreparedFilter
*
prepared_filter
)
{
assert
(
audio_format
.
IsValid
());
if
(
!
IsOpen
()
||
&
_pipe
!=
&
pipe
.
GetPipe
())
pipe
.
Init
(
_pipe
);
/* (re)open the filter */
if
(
filter_instance
!=
nullptr
&&
audio_format
!=
in_audio_format
)
/* the filter must be reopened on all input format
changes */
CloseFilter
();
if
(
filter_instance
==
nullptr
)
/* open the filter */
OpenFilter
(
audio_format
,
prepared_replay_gain_filter
,
prepared_other_replay_gain_filter
,
prepared_filter
);
in_audio_format
=
audio_format
;
return
filter_instance
->
GetOutAudioFormat
();
}
void
AudioOutputSource
::
Close
()
{
assert
(
in_audio_format
.
IsValid
());
in_audio_format
.
Clear
();
pipe
.
Cancel
();
CloseFilter
();
}
void
AudioOutputSource
::
OpenFilter
(
AudioFormat
audio_format
,
PreparedFilter
*
prepared_replay_gain_filter
,
PreparedFilter
*
prepared_other_replay_gain_filter
,
PreparedFilter
*
prepared_filter
)
try
{
assert
(
audio_format
.
IsValid
());
/* the replay_gain filter cannot fail here */
if
(
prepared_replay_gain_filter
!=
nullptr
)
replay_gain_filter_instance
=
prepared_replay_gain_filter
->
Open
(
audio_format
);
if
(
prepared_other_replay_gain_filter
!=
nullptr
)
other_replay_gain_filter_instance
=
prepared_other_replay_gain_filter
->
Open
(
audio_format
);
filter_instance
=
prepared_filter
->
Open
(
audio_format
);
}
catch
(...)
{
CloseFilter
();
throw
;
}
void
AudioOutputSource
::
CloseFilter
()
{
delete
replay_gain_filter_instance
;
replay_gain_filter_instance
=
nullptr
;
delete
other_replay_gain_filter_instance
;
other_replay_gain_filter_instance
=
nullptr
;
delete
filter_instance
;
filter_instance
=
nullptr
;
}
ConstBuffer
<
void
>
AudioOutputSource
::
GetChunkData
(
const
MusicChunk
&
chunk
,
Filter
*
replay_gain_filter
,
unsigned
*
replay_gain_serial_p
)
{
assert
(
!
chunk
.
IsEmpty
());
assert
(
chunk
.
CheckFormat
(
in_audio_format
));
ConstBuffer
<
void
>
data
(
chunk
.
data
,
chunk
.
length
);
assert
(
data
.
size
%
in_audio_format
.
GetFrameSize
()
==
0
);
if
(
!
data
.
IsEmpty
()
&&
replay_gain_filter
!=
nullptr
)
{
replay_gain_filter_set_mode
(
*
replay_gain_filter
,
replay_gain_mode
);
if
(
chunk
.
replay_gain_serial
!=
*
replay_gain_serial_p
)
{
replay_gain_filter_set_info
(
*
replay_gain_filter
,
chunk
.
replay_gain_serial
!=
0
?
&
chunk
.
replay_gain_info
:
nullptr
);
*
replay_gain_serial_p
=
chunk
.
replay_gain_serial
;
}
data
=
replay_gain_filter
->
FilterPCM
(
data
);
}
return
data
;
}
ConstBuffer
<
void
>
AudioOutputSource
::
FilterChunk
(
const
MusicChunk
&
chunk
)
{
auto
data
=
GetChunkData
(
chunk
,
replay_gain_filter_instance
,
&
replay_gain_serial
);
if
(
data
.
IsEmpty
())
return
data
;
/* cross-fade */
if
(
chunk
.
other
!=
nullptr
)
{
auto
other_data
=
GetChunkData
(
*
chunk
.
other
,
other_replay_gain_filter_instance
,
&
other_replay_gain_serial
);
if
(
other_data
.
IsEmpty
())
return
data
;
/* if the "other" chunk is longer, then that trailer
is used as-is, without mixing; it is part of the
"next" song being faded in, and if there's a rest,
it means cross-fading ends here */
if
(
data
.
size
>
other_data
.
size
)
data
.
size
=
other_data
.
size
;
float
mix_ratio
=
chunk
.
mix_ratio
;
if
(
mix_ratio
>=
0
)
/* reverse the mix ratio (because the
arguments to pcm_mix() are reversed), but
only if the mix ratio is non-negative; a
negative mix ratio is a MixRamp special
case */
mix_ratio
=
1.0
-
mix_ratio
;
void
*
dest
=
cross_fade_buffer
.
Get
(
other_data
.
size
);
memcpy
(
dest
,
other_data
.
data
,
other_data
.
size
);
if
(
!
pcm_mix
(
cross_fade_dither
,
dest
,
data
.
data
,
data
.
size
,
in_audio_format
.
format
,
mix_ratio
))
throw
FormatRuntimeError
(
"Cannot cross-fade format %s"
,
sample_format_to_string
(
in_audio_format
.
format
));
data
.
data
=
dest
;
data
.
size
=
other_data
.
size
;
}
/* apply filter chain */
return
filter_instance
->
FilterPCM
(
data
);
}
This diff is collapsed.
Click to expand it.
src/output/Source.hxx
0 → 100644
View file @
8a407bfb
/*
* Copyright 2003-2016 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.
*/
#ifndef AUDIO_OUTPUT_SOURCE_HXX
#define AUDIO_OUTPUT_SOURCE_HXX
#include "check.h"
#include "Compiler.h"
#include "SharedPipeConsumer.hxx"
#include "AudioFormat.hxx"
#include "ReplayGainMode.hxx"
#include "pcm/PcmBuffer.hxx"
#include "pcm/PcmDither.hxx"
#include <assert.h>
template
<
typename
T
>
struct
ConstBuffer
;
struct
MusicChunk
;
class
Filter
;
class
PreparedFilter
;
/**
* Source of audio data to be played by an #AudioOutput. It receives
* #MusicChunk instances from a #MusicPipe (via #SharedPipeConsumer).
* It applies configured filters, ReplayGain and returns plain PCM
* data.
*/
class
AudioOutputSource
{
/**
* The audio_format in which audio data is received from the
* player thread (which in turn receives it from the decoder).
*/
AudioFormat
in_audio_format
=
AudioFormat
::
Undefined
();
ReplayGainMode
replay_gain_mode
=
ReplayGainMode
::
OFF
;
/**
* A reference to the #MusicPipe and the current position.
*/
SharedPipeConsumer
pipe
;
/**
* The serial number of the last replay gain info. 0 means no
* replay gain info was available.
*/
unsigned
replay_gain_serial
=
0
;
/**
* The serial number of the last replay gain info by the
* "other" chunk during cross-fading.
*/
unsigned
other_replay_gain_serial
=
0
;
/**
* The replay_gain_filter_plugin instance of this audio
* output.
*/
Filter
*
replay_gain_filter_instance
=
nullptr
;
/**
* The replay_gain_filter_plugin instance of this audio
* output, to be applied to the second chunk during
* cross-fading.
*/
Filter
*
other_replay_gain_filter_instance
=
nullptr
;
/**
* The buffer used to allocate the cross-fading result.
*/
PcmBuffer
cross_fade_buffer
;
/**
* The dithering state for cross-fading two streams.
*/
PcmDither
cross_fade_dither
;
/**
* The filter object of this audio output. This is an
* instance of chain_filter_plugin.
*/
Filter
*
filter_instance
=
nullptr
;
public
:
void
SetReplayGainMode
(
ReplayGainMode
_mode
)
{
replay_gain_mode
=
_mode
;
}
bool
IsOpen
()
const
{
return
in_audio_format
.
IsDefined
();
}
const
AudioFormat
&
GetInputAudioFormat
()
const
{
return
in_audio_format
;
}
AudioFormat
Open
(
AudioFormat
audio_format
,
const
MusicPipe
&
_pipe
,
PreparedFilter
*
prepared_replay_gain_filter
,
PreparedFilter
*
prepared_other_replay_gain_filter
,
PreparedFilter
*
prepared_filter
);
void
Close
();
void
Cancel
()
{
pipe
.
Cancel
();
}
const
MusicChunk
*
Get
()
{
assert
(
IsOpen
());
return
pipe
.
Get
();
}
void
Consume
(
const
MusicChunk
&
chunk
)
{
assert
(
IsOpen
());
pipe
.
Consume
(
chunk
);
}
bool
IsChunkConsumed
(
const
MusicChunk
&
chunk
)
const
{
assert
(
IsOpen
());
return
pipe
.
IsConsumed
(
chunk
);
}
void
ClearTailChunk
(
const
MusicChunk
&
chunk
)
{
pipe
.
ClearTail
(
chunk
);
}
ConstBuffer
<
void
>
FilterChunk
(
const
MusicChunk
&
chunk
);
private
:
void
OpenFilter
(
AudioFormat
audio_format
,
PreparedFilter
*
prepared_replay_gain_filter
,
PreparedFilter
*
prepared_other_replay_gain_filter
,
PreparedFilter
*
prepared_filter
);
void
CloseFilter
();
ConstBuffer
<
void
>
GetChunkData
(
const
MusicChunk
&
chunk
,
Filter
*
replay_gain_filter
,
unsigned
*
replay_gain_serial_p
);
};
#endif
This diff is collapsed.
Click to expand it.
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