Commit e6600b85 authored by Max Kellermann's avatar Max Kellermann

Merge tag 'v0.21.14'

release v0.21.14
parents 04e2d084 bc89ca92
......@@ -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) */
while (true) {
if (args.size == COMMAND_ARGV_MAX) {
r.Error(ACK_ERROR_ARG, "Too many arguments");
return CommandResult::ERROR;
}
try {
/* now parse the arguments (quoted or unquoted) */
char *a = tokenizer.NextParam();
if (a == nullptr)
break;
while (true) {
if (args.size == COMMAND_ARGV_MAX) {
r.Error(ACK_ERROR_ARG, "Too many arguments");
return CommandResult::ERROR;
}
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);
/* look up and invoke the command handler */
CommandResult ret = cmd
? cmd->handler(client, args, r)
: CommandResult::ERROR;
const struct command *cmd =
command_checked_lookup(r, client.GetPermission(),
cmd_name, args);
if (cmd == nullptr)
return CommandResult::ERROR;
return ret;
} catch (const std::exception &e) {
Response r(client, num);
PrintError(r, std::current_exception());
return CommandResult::ERROR;
return cmd->handler(client, args, r);
} catch (...) {
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;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment