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
c94b4466
Commit
c94b4466
authored
Aug 12, 2014
by
Max Kellermann
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
MusicChunk: rename struct to MusicChunk
parent
61f9e79e
Show whitespace changes
Inline
Side-by-side
Showing
16 changed files
with
74 additions
and
73 deletions
+74
-73
MusicBuffer.cxx
src/MusicBuffer.cxx
+2
-2
MusicBuffer.hxx
src/MusicBuffer.hxx
+6
-6
MusicChunk.cxx
src/MusicChunk.cxx
+4
-4
MusicChunk.hxx
src/MusicChunk.hxx
+7
-7
MusicPipe.cxx
src/MusicPipe.cxx
+7
-7
MusicPipe.hxx
src/MusicPipe.hxx
+9
-9
PlayerThread.cxx
src/PlayerThread.cxx
+6
-6
PlayerThread.hxx
src/PlayerThread.hxx
+2
-2
DecoderAPI.cxx
src/decoder/DecoderAPI.cxx
+2
-2
DecoderControl.hxx
src/decoder/DecoderControl.hxx
+1
-1
DecoderInternal.cxx
src/decoder/DecoderInternal.cxx
+1
-1
DecoderInternal.hxx
src/decoder/DecoderInternal.hxx
+3
-2
Internal.hxx
src/output/Internal.hxx
+5
-5
MultipleOutputs.cxx
src/output/MultipleOutputs.cxx
+6
-6
MultipleOutputs.hxx
src/output/MultipleOutputs.hxx
+8
-8
OutputThread.cxx
src/output/OutputThread.cxx
+5
-5
No files found.
src/MusicBuffer.cxx
View file @
c94b4466
...
...
@@ -30,7 +30,7 @@ MusicBuffer::MusicBuffer(unsigned num_chunks)
FatalError
(
"Failed to allocate buffer"
);
}
music_c
hunk
*
MusicC
hunk
*
MusicBuffer
::
Allocate
()
{
const
ScopeLock
protect
(
mutex
);
...
...
@@ -38,7 +38,7 @@ MusicBuffer::Allocate()
}
void
MusicBuffer
::
Return
(
music_c
hunk
*
chunk
)
MusicBuffer
::
Return
(
MusicC
hunk
*
chunk
)
{
assert
(
chunk
!=
nullptr
);
...
...
src/MusicBuffer.hxx
View file @
c94b4466
...
...
@@ -23,22 +23,22 @@
#include "util/SliceBuffer.hxx"
#include "thread/Mutex.hxx"
struct
music_c
hunk
;
struct
MusicC
hunk
;
/**
* An allocator for #
music_c
hunk objects.
* An allocator for #
MusicC
hunk objects.
*/
class
MusicBuffer
{
/** a mutex which protects #buffer */
Mutex
mutex
;
SliceBuffer
<
music_c
hunk
>
buffer
;
SliceBuffer
<
MusicC
hunk
>
buffer
;
public
:
/**
* Creates a new #MusicBuffer object.
*
* @param num_chunks the number of #
music_c
hunk reserved in
* @param num_chunks the number of #
MusicC
hunk reserved in
* this buffer
*/
MusicBuffer
(
unsigned
num_chunks
);
...
...
@@ -71,13 +71,13 @@ public:
* @return an empty chunk or nullptr if there are no chunks
* available
*/
music_c
hunk
*
Allocate
();
MusicC
hunk
*
Allocate
();
/**
* Returns a chunk to the buffer. It can be reused by
* Allocate() then.
*/
void
Return
(
music_c
hunk
*
chunk
);
void
Return
(
MusicC
hunk
*
chunk
);
};
#endif
src/MusicChunk.cxx
View file @
c94b4466
...
...
@@ -24,14 +24,14 @@
#include <assert.h>
music_chunk
::~
music_c
hunk
()
MusicChunk
::~
MusicC
hunk
()
{
delete
tag
;
}
#ifndef NDEBUG
bool
music_c
hunk
::
CheckFormat
(
const
AudioFormat
other_format
)
const
MusicC
hunk
::
CheckFormat
(
const
AudioFormat
other_format
)
const
{
assert
(
other_format
.
IsValid
());
...
...
@@ -40,7 +40,7 @@ music_chunk::CheckFormat(const AudioFormat other_format) const
#endif
WritableBuffer
<
void
>
music_c
hunk
::
Write
(
const
AudioFormat
af
,
MusicC
hunk
::
Write
(
const
AudioFormat
af
,
float
data_time
,
uint16_t
_bit_rate
)
{
assert
(
CheckFormat
(
af
));
...
...
@@ -67,7 +67,7 @@ music_chunk::Write(const AudioFormat af,
}
bool
music_c
hunk
::
Expand
(
const
AudioFormat
af
,
size_t
_length
)
MusicC
hunk
::
Expand
(
const
AudioFormat
af
,
size_t
_length
)
{
const
size_t
frame_size
=
af
.
GetFrameSize
();
...
...
src/MusicChunk.hxx
View file @
c94b4466
...
...
@@ -39,15 +39,15 @@ struct Tag;
* A chunk of music data. Its format is defined by the
* MusicPipe::Push() caller.
*/
struct
music_c
hunk
{
struct
MusicC
hunk
{
/** the next chunk in a linked list */
struct
music_c
hunk
*
next
;
MusicC
hunk
*
next
;
/**
* An optional chunk which should be mixed into this chunk.
* This is used for cross-fading.
*/
struct
music_c
hunk
*
other
;
MusicC
hunk
*
other
;
/**
* The current mix ratio for cross-fading: 1.0 means play 100%
...
...
@@ -92,13 +92,13 @@ struct music_chunk {
AudioFormat
audio_format
;
#endif
music_c
hunk
()
MusicC
hunk
()
:
other
(
nullptr
),
length
(
0
),
tag
(
nullptr
),
replay_gain_serial
(
0
)
{}
~
music_c
hunk
();
~
MusicC
hunk
();
bool
IsEmpty
()
const
{
return
length
==
0
&&
tag
==
nullptr
;
...
...
@@ -118,7 +118,7 @@ struct music_chunk {
* where you may write into. After you are finished, call
* Expand().
*
* @param chunk the
music_c
hunk object
* @param chunk the
MusicC
hunk object
* @param audio_format the audio format for the appended data;
* must stay the same for the life cycle of this chunk
* @param data_time the time within the song
...
...
@@ -134,7 +134,7 @@ struct music_chunk {
* Increases the length of the chunk after the caller has written to
* the buffer returned by Write().
*
* @param chunk the
music_c
hunk object
* @param chunk the
MusicC
hunk object
* @param audio_format the audio format for the appended data; must
* stay the same for the life cycle of this chunk
* @param length the number of bytes which were appended
...
...
src/MusicPipe.cxx
View file @
c94b4466
...
...
@@ -25,11 +25,11 @@
#ifndef NDEBUG
bool
MusicPipe
::
Contains
(
const
music_c
hunk
*
chunk
)
const
MusicPipe
::
Contains
(
const
MusicC
hunk
*
chunk
)
const
{
const
ScopeLock
protect
(
mutex
);
for
(
const
struct
music_c
hunk
*
i
=
head
;
i
!=
nullptr
;
i
=
i
->
next
)
for
(
const
MusicC
hunk
*
i
=
head
;
i
!=
nullptr
;
i
=
i
->
next
)
if
(
i
==
chunk
)
return
true
;
...
...
@@ -38,12 +38,12 @@ MusicPipe::Contains(const music_chunk *chunk) const
#endif
music_c
hunk
*
MusicC
hunk
*
MusicPipe
::
Shift
()
{
const
ScopeLock
protect
(
mutex
);
music_c
hunk
*
chunk
=
head
;
MusicC
hunk
*
chunk
=
head
;
if
(
chunk
!=
nullptr
)
{
assert
(
!
chunk
->
IsEmpty
());
...
...
@@ -62,7 +62,7 @@ MusicPipe::Shift()
#ifndef NDEBUG
/* poison the "next" reference */
chunk
->
next
=
(
music_c
hunk
*
)(
void
*
)
0x01010101
;
chunk
->
next
=
(
MusicC
hunk
*
)(
void
*
)
0x01010101
;
if
(
size
==
0
)
audio_format
.
Clear
();
...
...
@@ -75,14 +75,14 @@ MusicPipe::Shift()
void
MusicPipe
::
Clear
(
MusicBuffer
&
buffer
)
{
music_c
hunk
*
chunk
;
MusicC
hunk
*
chunk
;
while
((
chunk
=
Shift
())
!=
nullptr
)
buffer
.
Return
(
chunk
);
}
void
MusicPipe
::
Push
(
music_c
hunk
*
chunk
)
MusicPipe
::
Push
(
MusicC
hunk
*
chunk
)
{
assert
(
!
chunk
->
IsEmpty
());
assert
(
chunk
->
length
==
0
||
chunk
->
audio_format
.
IsValid
());
...
...
src/MusicPipe.hxx
View file @
c94b4466
...
...
@@ -29,19 +29,19 @@
#include <assert.h>
struct
music_c
hunk
;
struct
MusicC
hunk
;
class
MusicBuffer
;
/**
* A queue of #
music_c
hunk objects. One party appends chunks at the
* A queue of #
MusicC
hunk objects. One party appends chunks at the
* tail, and the other consumes them from the head.
*/
class
MusicPipe
{
/** the first chunk */
music_c
hunk
*
head
;
MusicC
hunk
*
head
;
/** a pointer to the tail of the chunk */
music_c
hunk
**
tail_r
;
MusicC
hunk
**
tail_r
;
/** the current number of chunks */
unsigned
size
;
...
...
@@ -87,22 +87,22 @@ public:
* Checks if the specified chunk is enqueued in the music pipe.
*/
gcc_pure
bool
Contains
(
const
music_c
hunk
*
chunk
)
const
;
bool
Contains
(
const
MusicC
hunk
*
chunk
)
const
;
#endif
/**
* Returns the first #
music_c
hunk from the pipe. Returns
* Returns the first #
MusicC
hunk from the pipe. Returns
* nullptr if the pipe is empty.
*/
gcc_pure
const
music_c
hunk
*
Peek
()
const
{
const
MusicC
hunk
*
Peek
()
const
{
return
head
;
}
/**
* Removes the first chunk from the head, and returns it.
*/
music_c
hunk
*
Shift
();
MusicC
hunk
*
Shift
();
/**
* Clears the whole pipe and returns the chunks to the buffer.
...
...
@@ -114,7 +114,7 @@ public:
/**
* Pushes a chunk to the tail of the pipe.
*/
void
Push
(
music_c
hunk
*
chunk
);
void
Push
(
MusicC
hunk
*
chunk
);
/**
* Returns the number of chunks currently in this pipe.
...
...
src/PlayerThread.cxx
View file @
c94b4466
...
...
@@ -484,7 +484,7 @@ Player::SendSilence()
assert
(
output_open
);
assert
(
play_audio_format
.
IsDefined
());
struct
music_c
hunk
*
chunk
=
buffer
.
Allocate
();
MusicC
hunk
*
chunk
=
buffer
.
Allocate
();
if
(
chunk
==
nullptr
)
{
LogError
(
player_domain
,
"Failed to allocate silence buffer"
);
return
false
;
...
...
@@ -704,7 +704,7 @@ update_song_tag(PlayerControl &pc, DetachedSong &song, const Tag &new_tag)
}
/**
* Plays a #
music_c
hunk object (after applying software volume). If
* Plays a #
MusicC
hunk object (after applying software volume). If
* it contains a (stream) tag, copy it to the current song, so MPD's
* playlist reflects the new stream tag.
*
...
...
@@ -712,7 +712,7 @@ update_song_tag(PlayerControl &pc, DetachedSong &song, const Tag &new_tag)
*/
static
bool
play_chunk
(
PlayerControl
&
pc
,
DetachedSong
&
song
,
struct
music_c
hunk
*
chunk
,
DetachedSong
&
song
,
MusicC
hunk
*
chunk
,
MusicBuffer
&
buffer
,
const
AudioFormat
format
,
Error
&
error
)
...
...
@@ -750,11 +750,11 @@ Player::PlayNextChunk()
return
true
;
unsigned
cross_fade_position
;
struct
music_c
hunk
*
chunk
=
nullptr
;
MusicC
hunk
*
chunk
=
nullptr
;
if
(
xfade_state
==
CrossFadeState
::
ENABLED
&&
IsDecoderAtNextSong
()
&&
(
cross_fade_position
=
pipe
->
GetSize
())
<=
cross_fade_chunks
)
{
/* perform cross fade */
music_c
hunk
*
other_chunk
=
dc
.
pipe
->
Shift
();
MusicC
hunk
*
other_chunk
=
dc
.
pipe
->
Shift
();
if
(
!
cross_fading
)
{
/* beginning of the cross fade - adjust
...
...
@@ -786,7 +786,7 @@ Player::PlayNextChunk()
}
if
(
other_chunk
->
IsEmpty
())
{
/* the "other" chunk was a
music_c
hunk
/* the "other" chunk was a
MusicC
hunk
which had only a tag, but no music
data - we cannot cross-fade that;
but since this happens only at the
...
...
src/PlayerThread.hxx
View file @
c94b4466
...
...
@@ -21,7 +21,7 @@
*
* The player thread controls the playback. It acts as a bridge
* between the decoder thread and the output thread(s): it receives
* #
music_c
hunk objects from the decoder, optionally mixes them
* #
MusicC
hunk objects from the decoder, optionally mixes them
* (cross-fading), applies software volume, and sends them to the
* audio outputs via audio_output_all_play().
*
...
...
@@ -31,7 +31,7 @@
*
* The player thread itself does not do any I/O. It synchronizes with
* other threads via #GMutex and #GCond objects, and passes
* #
music_c
hunk instances around in #MusicPipe objects.
* #
MusicC
hunk instances around in #MusicPipe objects.
*/
#ifndef MPD_PLAYER_THREAD_HXX
...
...
src/decoder/DecoderAPI.cxx
View file @
c94b4466
...
...
@@ -387,7 +387,7 @@ decoder_timestamp(Decoder &decoder, double t)
static
DecoderCommand
do_send_tag
(
Decoder
&
decoder
,
const
Tag
&
tag
)
{
struct
music_c
hunk
*
chunk
;
MusicC
hunk
*
chunk
;
if
(
decoder
.
chunk
!=
nullptr
)
{
/* there is a partial chunk - flush it, we want the
...
...
@@ -487,7 +487,7 @@ decoder_data(Decoder &decoder,
}
while
(
length
>
0
)
{
struct
music_c
hunk
*
chunk
;
MusicC
hunk
*
chunk
;
bool
full
;
chunk
=
decoder
.
GetChunk
();
...
...
src/decoder/DecoderControl.hxx
View file @
c94b4466
...
...
@@ -144,7 +144,7 @@ struct DecoderControl {
float
total_time
;
/** the #
music_c
hunk allocator */
/** the #
MusicC
hunk allocator */
MusicBuffer
*
buffer
;
/**
...
...
src/decoder/DecoderInternal.cxx
View file @
c94b4466
...
...
@@ -56,7 +56,7 @@ need_chunks(DecoderControl &dc)
return
dc
.
command
;
}
struct
music_c
hunk
*
MusicC
hunk
*
Decoder
::
GetChunk
()
{
DecoderCommand
cmd
;
...
...
src/decoder/DecoderInternal.hxx
View file @
c94b4466
...
...
@@ -24,6 +24,7 @@
#include "util/Error.hxx"
class
PcmConvert
;
struct
MusicChunk
;
struct
DecoderControl
;
struct
Tag
;
...
...
@@ -76,7 +77,7 @@ struct Decoder {
Tag
*
decoder_tag
;
/** the chunk currently being written to */
struct
music_c
hunk
*
chunk
;
MusicC
hunk
*
chunk
;
ReplayGainInfo
replay_gain_info
;
...
...
@@ -112,7 +113,7 @@ struct Decoder {
*
* @return the chunk, or NULL if we have received a decoder command
*/
music_c
hunk
*
GetChunk
();
MusicC
hunk
*
GetChunk
();
/**
* Flushes the current chunk.
...
...
src/output/Internal.hxx
View file @
c94b4466
...
...
@@ -35,7 +35,7 @@ class MusicPipe;
class
EventLoop
;
class
Mixer
;
class
MixerListener
;
struct
music_c
hunk
;
struct
MusicC
hunk
;
struct
config_param
;
struct
PlayerControl
;
struct
AudioOutputPlugin
;
...
...
@@ -257,12 +257,12 @@ struct AudioOutput {
PlayerControl
*
player_control
;
/**
* The #
music_c
hunk which is currently being played. All
* The #
MusicC
hunk which is currently being played. All
* chunks before this one may be returned to the
* #music_buffer, because they are not going to be used by
* this output anymore.
*/
const
music_c
hunk
*
current_chunk
;
const
MusicC
hunk
*
current_chunk
;
/**
* Has the output finished playing #current_chunk?
...
...
@@ -395,9 +395,9 @@ private:
bool
WaitForDelay
();
gcc_pure
const
music_c
hunk
*
GetNextChunk
()
const
;
const
MusicC
hunk
*
GetNextChunk
()
const
;
bool
PlayChunk
(
const
music_c
hunk
*
chunk
);
bool
PlayChunk
(
const
MusicC
hunk
*
chunk
);
/**
* Plays all remaining chunks, until the tail of the pipe has
...
...
src/output/MultipleOutputs.cxx
View file @
c94b4466
...
...
@@ -187,7 +187,7 @@ MultipleOutputs::SetReplayGainMode(ReplayGainMode mode)
}
bool
MultipleOutputs
::
Play
(
music_c
hunk
*
chunk
,
Error
&
error
)
MultipleOutputs
::
Play
(
MusicC
hunk
*
chunk
,
Error
&
error
)
{
assert
(
buffer
!=
nullptr
);
assert
(
pipe
!=
nullptr
);
...
...
@@ -265,7 +265,7 @@ gcc_pure
static
bool
chunk_is_consumed_in
(
const
AudioOutput
*
ao
,
gcc_unused
const
MusicPipe
*
pipe
,
const
struct
music_c
hunk
*
chunk
)
const
MusicC
hunk
*
chunk
)
{
if
(
!
ao
->
open
)
return
true
;
...
...
@@ -285,7 +285,7 @@ chunk_is_consumed_in(const AudioOutput *ao,
}
bool
MultipleOutputs
::
IsChunkConsumed
(
const
music_c
hunk
*
chunk
)
const
MultipleOutputs
::
IsChunkConsumed
(
const
MusicC
hunk
*
chunk
)
const
{
for
(
auto
ao
:
outputs
)
{
const
ScopeLock
protect
(
ao
->
mutex
);
...
...
@@ -297,7 +297,7 @@ MultipleOutputs::IsChunkConsumed(const music_chunk *chunk) const
}
inline
void
MultipleOutputs
::
ClearTailChunk
(
gcc_unused
const
struct
music_c
hunk
*
chunk
,
MultipleOutputs
::
ClearTailChunk
(
gcc_unused
const
MusicC
hunk
*
chunk
,
bool
*
locked
)
{
assert
(
chunk
->
next
==
nullptr
);
...
...
@@ -325,9 +325,9 @@ MultipleOutputs::ClearTailChunk(gcc_unused const struct music_chunk *chunk,
unsigned
MultipleOutputs
::
Check
()
{
const
struct
music_c
hunk
*
chunk
;
const
MusicC
hunk
*
chunk
;
bool
is_tail
;
struct
music_c
hunk
*
shifted
;
MusicC
hunk
*
shifted
;
bool
locked
[
outputs
.
size
()];
assert
(
buffer
!=
nullptr
);
...
...
src/output/MultipleOutputs.hxx
View file @
c94b4466
...
...
@@ -39,7 +39,7 @@ class MusicBuffer;
class
MusicPipe
;
class
EventLoop
;
class
MixerListener
;
struct
music_c
hunk
;
struct
MusicC
hunk
;
struct
PlayerControl
;
struct
AudioOutput
;
class
Error
;
...
...
@@ -119,7 +119,7 @@ public:
* Opens all audio outputs which are not disabled.
*
* @param audio_format the preferred audio format
* @param buffer the #music_buffer where consumed #
music_c
hunk objects
* @param buffer the #music_buffer where consumed #
MusicC
hunk objects
* should be returned
* @return true on success, false on failure
*/
...
...
@@ -140,14 +140,14 @@ public:
void
SetReplayGainMode
(
ReplayGainMode
mode
);
/**
* Enqueue a #
music_c
hunk object for playing, i.e. pushes it to a
* Enqueue a #
MusicC
hunk object for playing, i.e. pushes it to a
* #MusicPipe.
*
* @param chunk the #
music_c
hunk object to be played
* @param chunk the #
MusicC
hunk object to be played
* @return true on success, false if no audio output was able to play
* (all closed then)
*/
bool
Play
(
music_c
hunk
*
chunk
,
Error
&
error
);
bool
Play
(
MusicC
hunk
*
chunk
,
Error
&
error
);
/**
* Checks if the output devices have drained their music pipe, and
...
...
@@ -160,7 +160,7 @@ public:
/**
* Checks if the size of the #MusicPipe is below the #threshold. If
* not, it attempts to synchronize with all output threads, and waits
* until another #
music_c
hunk is finished.
* until another #
MusicC
hunk is finished.
*
* @param threshold the maximum number of chunks in the pipe
* @return true if there are less than #threshold chunks in the pipe
...
...
@@ -262,14 +262,14 @@ private:
/**
* Has this chunk been consumed by all audio outputs?
*/
bool
IsChunkConsumed
(
const
music_c
hunk
*
chunk
)
const
;
bool
IsChunkConsumed
(
const
MusicC
hunk
*
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_c
hunk
*
chunk
,
bool
*
locked
);
void
ClearTailChunk
(
const
MusicC
hunk
*
chunk
,
bool
*
locked
);
};
#endif
src/output/OutputThread.cxx
View file @
c94b4466
...
...
@@ -307,7 +307,7 @@ AudioOutput::WaitForDelay()
}
static
const
void
*
ao_chunk_data
(
AudioOutput
*
ao
,
const
struct
music_c
hunk
*
chunk
,
ao_chunk_data
(
AudioOutput
*
ao
,
const
MusicC
hunk
*
chunk
,
Filter
*
replay_gain_filter
,
unsigned
*
replay_gain_serial_p
,
size_t
*
length_r
)
...
...
@@ -347,7 +347,7 @@ ao_chunk_data(AudioOutput *ao, const struct music_chunk *chunk,
}
static
const
void
*
ao_filter_chunk
(
AudioOutput
*
ao
,
const
struct
music_c
hunk
*
chunk
,
ao_filter_chunk
(
AudioOutput
*
ao
,
const
MusicC
hunk
*
chunk
,
size_t
*
length_r
)
{
size_t
length
;
...
...
@@ -417,7 +417,7 @@ ao_filter_chunk(AudioOutput *ao, const struct music_chunk *chunk,
}
inline
bool
AudioOutput
::
PlayChunk
(
const
music_c
hunk
*
chunk
)
AudioOutput
::
PlayChunk
(
const
MusicC
hunk
*
chunk
)
{
assert
(
filter
!=
nullptr
);
...
...
@@ -478,7 +478,7 @@ AudioOutput::PlayChunk(const music_chunk *chunk)
return
true
;
}
inline
const
music_c
hunk
*
inline
const
MusicC
hunk
*
AudioOutput
::
GetNextChunk
()
const
{
return
current_chunk
!=
nullptr
...
...
@@ -493,7 +493,7 @@ AudioOutput::Play()
{
assert
(
pipe
!=
nullptr
);
const
music_c
hunk
*
chunk
=
GetNextChunk
();
const
MusicC
hunk
*
chunk
=
GetNextChunk
();
if
(
chunk
==
nullptr
)
/* no chunk available */
return
false
;
...
...
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