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
Dec 20, 2016
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
\
...
...
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
;
...
...
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
);
}
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
:
...
...
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
...
...
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
;
}
...
...
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
);
}
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
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