release v0.21.14
-----BEGIN PGP SIGNATURE----- iQJEBAABCgAuFiEEA5IzWngIOJSkMBxDI26KWMbbRRIFAl1dBToQHG1heEBtdXNp Y3BkLm9yZwAKCRAjbopYxttFEhlID/4gcrbaegKpTkftGPLBGlSUc4W0qwSHbdiJ SuzK7sraSGUlLHHiBfqvFfE8tyEaLfsJarqiOdGgfiaW5QyapdwuU5s76nAN6jjY onEo2QK6vSbv4J/B1+Jv+NGCgVM3ZkPy91GhfTz8MEtJMaBmztFhnpxK60r9jYPG 5EBsnVa6HhI7gniQAtOwE8SXYnn92Q4j72S8OuAbJ7Vwh0oqdIyXECqzcAE97Fk2 TNX/YSLST3I7Chv2OBDb3vOegh9nFUyr0qeSYdi8vk2BBYcgX2xYOOBQCC3ta/nr NNeuSJOLgcF1XrFqVRRhDKZ8Y2inD6qVAXOH5WtChT1n3uXqYW7vdq0fW+/w3W/E vouzgt5KvU5Me4Mk2M2dMjEWW+7Y8EUjvrwDnbDIkyP+Yi+BLTmTnyBgAW3cvRO9 UoCcWTBOEgyX2wAFl3r+NMPEneuMLbMCZUMju4/zveiRJdFExA0LC8wk0/iWqQbW +WD8y6RTo7Z1jsP1vnBimgAkzkLAOuMlKLYNI15ETrxwKWgOjN6a7Q61GVdsaiqG fKQeO0kZWWFcJ7HECgp3tpwWEi1+7/uqt0TwQgOKDdZHYL0Wb6Ur09KJS4b+eKIl UzYwCrPgUx1pcYR+rTbMxCNpWn2kA+vp2UaPBN60c/J98d+6C/2nKtIvXfr5MrUA CEb1epw/ew== =mEFF -----END PGP SIGNATURE----- Merge tag 'v0.21.14' release v0.21.14
This commit is contained in:
commit
e6600b8562
7
NEWS
7
NEWS
@ -29,11 +29,16 @@ ver 0.22 (not yet released)
|
||||
* switch to C++17
|
||||
- GCC 7 or clang 4 (or newer) recommended
|
||||
|
||||
ver 0.21.14 (not yet released)
|
||||
ver 0.21.14 (2019/08/21)
|
||||
* decoder
|
||||
- sidplay: show track durations in database
|
||||
- sidplay: convert tag values from Windows-1252 charset
|
||||
- sidplay: strip text from "Date" tag
|
||||
* player
|
||||
- fix crash after song change
|
||||
- fix seek position after restarting the decoder
|
||||
* protocol
|
||||
- include command name in error responses
|
||||
|
||||
ver 0.21.13 (2019/08/06)
|
||||
* input
|
||||
|
@ -839,7 +839,8 @@ The music database
|
||||
albumart foo/bar.ogg 0
|
||||
size: 1024768
|
||||
binary: 8192
|
||||
<8192 bytes>OK
|
||||
<8192 bytes>
|
||||
OK
|
||||
|
||||
:command:`count {FILTER} [group {GROUPTYPE}]`
|
||||
Count the number of songs and their total playtime in
|
||||
|
@ -212,9 +212,10 @@ static constexpr struct command commands[] = {
|
||||
|
||||
static constexpr unsigned num_commands = std::size(commands);
|
||||
|
||||
gcc_pure
|
||||
static bool
|
||||
command_available(gcc_unused const Partition &partition,
|
||||
gcc_unused const struct command *cmd)
|
||||
gcc_unused const struct command *cmd) noexcept
|
||||
{
|
||||
#ifdef ENABLE_SQLITE
|
||||
if (StringIsEqual(cmd->cmd, "sticker"))
|
||||
@ -241,7 +242,7 @@ command_available(gcc_unused const Partition &partition,
|
||||
|
||||
static CommandResult
|
||||
PrintAvailableCommands(Response &r, const Partition &partition,
|
||||
unsigned permission)
|
||||
unsigned permission) noexcept
|
||||
{
|
||||
for (unsigned i = 0; i < num_commands; ++i) {
|
||||
const struct command *cmd = &commands[i];
|
||||
@ -255,7 +256,7 @@ PrintAvailableCommands(Response &r, const Partition &partition,
|
||||
}
|
||||
|
||||
static CommandResult
|
||||
PrintUnavailableCommands(Response &r, unsigned permission)
|
||||
PrintUnavailableCommands(Response &r, unsigned permission) noexcept
|
||||
{
|
||||
for (unsigned i = 0; i < num_commands; ++i) {
|
||||
const struct command *cmd = &commands[i];
|
||||
@ -282,7 +283,7 @@ handle_not_commands(Client &client, gcc_unused Request request, Response &r)
|
||||
}
|
||||
|
||||
void
|
||||
command_init()
|
||||
command_init() noexcept
|
||||
{
|
||||
#ifndef NDEBUG
|
||||
/* ensure that the command list is sorted */
|
||||
@ -291,8 +292,9 @@ command_init()
|
||||
#endif
|
||||
}
|
||||
|
||||
gcc_pure
|
||||
static const struct command *
|
||||
command_lookup(const char *name)
|
||||
command_lookup(const char *name) noexcept
|
||||
{
|
||||
unsigned a = 0, b = num_commands, i;
|
||||
|
||||
@ -314,7 +316,7 @@ command_lookup(const char *name)
|
||||
|
||||
static bool
|
||||
command_check_request(const struct command *cmd, Response &r,
|
||||
unsigned permission, Request args)
|
||||
unsigned permission, Request args) noexcept
|
||||
{
|
||||
if (cmd->permission != (permission & cmd->permission)) {
|
||||
r.FormatError(ACK_ERROR_PERMISSION,
|
||||
@ -348,7 +350,7 @@ command_check_request(const struct command *cmd, Response &r,
|
||||
|
||||
static const struct command *
|
||||
command_checked_lookup(Response &r, unsigned permission,
|
||||
const char *cmd_name, Request args)
|
||||
const char *cmd_name, Request args) noexcept
|
||||
{
|
||||
const struct command *cmd = command_lookup(cmd_name);
|
||||
if (cmd == nullptr) {
|
||||
@ -366,8 +368,8 @@ command_checked_lookup(Response &r, unsigned permission,
|
||||
}
|
||||
|
||||
CommandResult
|
||||
command_process(Client &client, unsigned num, char *line)
|
||||
try {
|
||||
command_process(Client &client, unsigned num, char *line) noexcept
|
||||
{
|
||||
Response r(client, num);
|
||||
|
||||
/* get the command name (first word on the line) */
|
||||
@ -395,34 +397,33 @@ try {
|
||||
char *argv[COMMAND_ARGV_MAX];
|
||||
Request args(argv, 0);
|
||||
|
||||
/* now parse the arguments (quoted or unquoted) */
|
||||
try {
|
||||
/* now parse the arguments (quoted or unquoted) */
|
||||
|
||||
while (true) {
|
||||
if (args.size == COMMAND_ARGV_MAX) {
|
||||
r.Error(ACK_ERROR_ARG, "Too many arguments");
|
||||
return CommandResult::ERROR;
|
||||
while (true) {
|
||||
if (args.size == COMMAND_ARGV_MAX) {
|
||||
r.Error(ACK_ERROR_ARG, "Too many arguments");
|
||||
return CommandResult::ERROR;
|
||||
}
|
||||
|
||||
char *a = tokenizer.NextParam();
|
||||
if (a == nullptr)
|
||||
break;
|
||||
|
||||
argv[args.size++] = a;
|
||||
}
|
||||
|
||||
char *a = tokenizer.NextParam();
|
||||
if (a == nullptr)
|
||||
break;
|
||||
/* look up and invoke the command handler */
|
||||
|
||||
argv[args.size++] = a;
|
||||
const struct command *cmd =
|
||||
command_checked_lookup(r, client.GetPermission(),
|
||||
cmd_name, args);
|
||||
if (cmd == nullptr)
|
||||
return CommandResult::ERROR;
|
||||
|
||||
return cmd->handler(client, args, r);
|
||||
} catch (...) {
|
||||
PrintError(r, std::current_exception());
|
||||
return CommandResult::ERROR;
|
||||
}
|
||||
|
||||
/* look up and invoke the command handler */
|
||||
|
||||
const struct command *cmd =
|
||||
command_checked_lookup(r, client.GetPermission(),
|
||||
cmd_name, args);
|
||||
|
||||
CommandResult ret = cmd
|
||||
? cmd->handler(client, args, r)
|
||||
: CommandResult::ERROR;
|
||||
|
||||
return ret;
|
||||
} catch (const std::exception &e) {
|
||||
Response r(client, num);
|
||||
PrintError(r, std::current_exception());
|
||||
return CommandResult::ERROR;
|
||||
}
|
||||
|
@ -25,12 +25,9 @@
|
||||
class Client;
|
||||
|
||||
void
|
||||
command_init();
|
||||
|
||||
void
|
||||
command_finish();
|
||||
command_init() noexcept;
|
||||
|
||||
CommandResult
|
||||
command_process(Client &client, unsigned num, char *line);
|
||||
command_process(Client &client, unsigned num, char *line) noexcept;
|
||||
|
||||
#endif
|
||||
|
@ -138,6 +138,18 @@ DecoderControl::Seek(std::unique_lock<Mutex> &lock, SongTime t)
|
||||
seek_error = false;
|
||||
SynchronousCommandLocked(lock, DecoderCommand::SEEK);
|
||||
|
||||
while (state == DecoderState::START)
|
||||
/* If the decoder falls back to DecoderState::START,
|
||||
this means that our SEEK command arrived too late,
|
||||
and the decoder had meanwhile finished decoding and
|
||||
went idle. Our SEEK command is finished, but that
|
||||
means only that the decoder thread has launched the
|
||||
decoder. To work around illegal states, we wait
|
||||
until the decoder plugin has become ready. This is
|
||||
a kludge, built on top of the "late seek" kludge.
|
||||
Not exactly elegant, sorry. */
|
||||
WaitForDecoder(lock);
|
||||
|
||||
if (seek_error)
|
||||
throw std::runtime_error("Decoder failed to seek");
|
||||
}
|
||||
|
@ -418,6 +418,11 @@ static void
|
||||
decoder_run_song(DecoderControl &dc,
|
||||
const DetachedSong &song, const char *uri, Path path_fs)
|
||||
{
|
||||
if (dc.command == DecoderCommand::SEEK)
|
||||
/* if the SEEK command arrived too late, start the
|
||||
decoder at the seek position */
|
||||
dc.start_time = dc.seek_time;
|
||||
|
||||
DecoderBridge bridge(dc, dc.start_time.IsPositive(),
|
||||
/* pass the song tag only if it's
|
||||
authoritative, i.e. if it's a local
|
||||
|
@ -158,6 +158,7 @@ AudioOutputControl::InternalOpen(const AudioFormat in_audio_format,
|
||||
} catch (...) {
|
||||
LogError(std::current_exception());
|
||||
Failure(std::current_exception());
|
||||
return;
|
||||
}
|
||||
|
||||
if (f != in_audio_format || f != output->out_audio_format)
|
||||
@ -457,7 +458,7 @@ AudioOutputControl::Task() noexcept
|
||||
case Command::RELEASE:
|
||||
if (!open) {
|
||||
/* the output has failed after
|
||||
the PAUSE command was submitted; bail
|
||||
the RELEASE command was submitted; bail
|
||||
out */
|
||||
CommandFinished();
|
||||
break;
|
||||
|
Loading…
Reference in New Issue
Block a user