Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
69 changes: 61 additions & 8 deletions src/StateFile.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,17 @@
#include "io/BufferedOutputStream.hxx"
#include "storage/StorageState.hxx"
#include "Partition.hxx"
#include "config/PartitionConfig.hxx"
#include "Instance.hxx"
#include "SongLoader.hxx"
#include "util/Domain.hxx"
#include "Log.hxx"
#include "util/StringCompare.hxx"

#include <exception>

#define PARTITION_STATE "partition: "

static constexpr Domain state_file_domain("state_file");

StateFile::StateFile(StateFileConfig &&_config,
Expand Down Expand Up @@ -55,14 +59,19 @@ StateFile::IsModified() const noexcept
inline void
StateFile::Write(BufferedOutputStream &os)
{
partition.mixer_memento.SaveSoftwareVolumeState(os);
audio_output_state_save(os, partition.outputs);
for (auto &current_partition : partition.instance.partitions) {
if (&current_partition != &partition.instance.partitions.front())
os.Fmt(PARTITION_STATE "{}\n",
current_partition.name);
current_partition.mixer_memento.SaveSoftwareVolumeState(os);
audio_output_state_save(os, current_partition.outputs);

playlist_state_save(os, current_partition.playlist, current_partition.pc);
}

#ifdef ENABLE_DATABASE
storage_state_save(os, partition.instance);
#endif

playlist_state_save(os, partition.playlist, partition.pc);
}

inline void
Expand Down Expand Up @@ -106,13 +115,21 @@ try {
const SongLoader song_loader(nullptr, nullptr);
#endif

Partition *current_partition = &partition;

FmtDebug(state_file_domain,
"Starting main loop to read state file (initial partition: '{}')",
current_partition->name);

const char *line;
while ((line = file.ReadLine()) != nullptr) {
success = partition.mixer_memento.LoadSoftwareVolumeState(line, partition.outputs) ||
audio_output_state_read(line, partition.outputs) ||
success = current_partition->mixer_memento.LoadSoftwareVolumeState(line,
partition.outputs) ||
audio_output_state_read(line, partition.outputs, current_partition) ||
playlist_state_restore(config, line, file, song_loader,
partition.playlist,
partition.pc);
current_partition->playlist,
current_partition->pc) ||
PartitionSwitch(line, current_partition);
#ifdef ENABLE_DATABASE
success = success || storage_state_restore(line, file, partition.instance);
#endif
Expand Down Expand Up @@ -140,3 +157,39 @@ StateFile::OnTimeout() noexcept
{
Write();
}

/**
* Attempts to switch the current partition based on a state file line.
*
* @param line The line from the state file to parse
* @param current_partition Reference to pointer that will be updated to point
* to the target partition
* @return true if the line was a partition switch command, false otherwise
*/
bool StateFile::PartitionSwitch(const char *line,
Partition *&current_partition) noexcept {
// Check if this line contains a partition switch command
line = StringAfterPrefix(line, PARTITION_STATE);
if (line == nullptr)
return false;

// Try to find existing partition
Partition *new_partition = partition.instance.FindPartition(line);
if (new_partition != nullptr) {
current_partition = new_partition;
FmtDebug(state_file_domain, "Switched to existing partition '{}'",
current_partition->name);
return true;
}

// Partition doesn't exist, create it
partition.instance.partitions.emplace_back(partition.instance, line,
PartitionConfig{});
current_partition = &partition.instance.partitions.back();
current_partition->UpdateEffectiveReplayGainMode();

FmtDebug(state_file_domain, "Created and switched to partition '{}'",
current_partition->name);

return true;
}
6 changes: 6 additions & 0 deletions src/StateFile.hxx
Original file line number Diff line number Diff line change
Expand Up @@ -63,4 +63,10 @@ private:

/* callback for #timer_event */
void OnTimeout() noexcept;

/**
* Handle partition lines in the state file
*/
bool PartitionSwitch(const char *line,
Partition *&current_partition) noexcept;
};
54 changes: 48 additions & 6 deletions src/output/State.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,35 @@
#include "Log.hxx"
#include "io/BufferedOutputStream.hxx"
#include "util/StringCompare.hxx"
#include "Partition.hxx"
#include "config/PartitionConfig.hxx"

#include <fmt/format.h>

#include <stdlib.h>

#define AUDIO_DEVICE_STATE "audio_device_state:"
#define DEFAULT_PARTITION "default"

unsigned audio_output_state_version;

/**
* Iterate the instance and save audio output state configuration.
*
* Writes the name of the audio outputs, the partitions they are assigned to,
* and the state of the output.
*
* Writes a configuration line this format:
* AUDIO_DEVICE_STATE<value>:<device>:<partition>
*
* Where:
* <value> = 0 (disabled) or 1 (enabled)
* <device> = output device name
*
* @param os The output stream
* @param outputs The outputs assigned to this partition
* @return nothing
*/
void
audio_output_state_save(BufferedOutputStream &os,
const MultipleOutputs &outputs)
Expand All @@ -34,8 +54,25 @@ audio_output_state_save(BufferedOutputStream &os,
}
}

/**
* Parse and apply audio output state configuration.
*
* Reads a configuration line in one of these formats:
* AUDIO_DEVICE_STATE<value>:<device>
* AUDIO_DEVICE_STATE<value>:<device>:<partition>
*
* Where:
* <value> = 0 (disabled) or 1 (enabled)
* <device> = output device name
* <partition> = optional partition name
*
* @param line The configuration line to parse
* @param outputs The collection of audio outputs to modify
* @param current_partition The partition to which the outputs belong
* @return true if the line was valid and processed, false on parse error
*/
bool
audio_output_state_read(const char *line, MultipleOutputs &outputs)
audio_output_state_read(const char *line, MultipleOutputs &outputs, Partition *current_partition)
{
long value;
char *endptr;
Expand All @@ -49,10 +86,6 @@ audio_output_state_read(const char *line, MultipleOutputs &outputs)
if (*endptr != ':' || (value != 0 && value != 1))
return false;

if (value != 0)
/* state is "enabled": no-op */
return true;

name = endptr + 1;
auto *ao = outputs.FindByName(name);
if (ao == nullptr) {
Expand All @@ -61,7 +94,16 @@ audio_output_state_read(const char *line, MultipleOutputs &outputs)
return true;
}

ao->LockSetEnabled(false);
if (current_partition->name != DEFAULT_PARTITION) {
// Move the output to this partition
FmtDebug(output_domain,
"Moving device {:?} from default to partition {:?}",
name, current_partition->name);
current_partition->outputs.AddMoveFrom(std::move(*ao), value != 0);
return true;
}

ao->LockSetEnabled(value != 0);
return true;
}

Expand Down
3 changes: 2 additions & 1 deletion src/output/State.hxx
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,10 @@

class MultipleOutputs;
class BufferedOutputStream;
struct Partition;

bool
audio_output_state_read(const char *line, MultipleOutputs &outputs);
audio_output_state_read(const char *line, MultipleOutputs &outputs, Partition *partition);

void
audio_output_state_save(BufferedOutputStream &os,
Expand Down
1 change: 1 addition & 0 deletions src/queue/PlaylistState.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,7 @@ playlist_state_restore(const StateFileConfig &config,
} else if (StringStartsWith(line,
PLAYLIST_STATE_FILE_PLAYLIST_BEGIN)) {
playlist_state_load(file, song_loader, playlist);
break;
}
}

Expand Down
Loading
Loading