Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

o Wire up processor-pool to folve-filesystem.

  • Loading branch information...
commit 950f19237b0f05a239cabd4d9f1931aa2224f5af 1 parent fa02b45
@hzeller authored
View
2  Makefile
@@ -14,7 +14,7 @@ LDFLAGS+=-lFLAC -lvorbisenc -lvorbis -logg -lstdc++ -lm -lrt -ldl
LD_STATIC=-static
endif
-OBJECTS = folve-main.o folve-filesystem.o conversion-buffer.o \
+OBJECTS = folve-main.o folve-filesystem.o conversion-buffer.o processor-pool.o \
sound-processor.o file-handler-cache.o status-server.o util.o \
zita-audiofile.o zita-config.o zita-fconfig.o zita-sstring.o
View
116 folve-filesystem.cc
@@ -86,17 +86,6 @@ class PassThroughHandler : public FileHandler {
HandlerStats info_stats_;
};
-static bool FindFirstAccessiblePath(const std::vector<std::string> &path,
- std::string *match) {
- for (size_t i = 0; i < path.size(); ++i) {
- if (access(path[i].c_str(), R_OK) == 0) {
- *match = path[i];
- return true;
- }
- }
- return false;
-}
-
class SndFileHandler :
public FileHandler,
public ConversionBuffer::SoundSource {
@@ -130,41 +119,26 @@ class SndFileHandler :
in_info.samplerate / 1000.0, bits);
partial_file_info->duration_seconds = in_info.frames / in_info.samplerate;
- std::vector<std::string> path_choices;
- // From specific to non-specific.
- path_choices.push_back(StringPrintf("%s/filter-%d-%d-%d.conf",
- zita_config_dir.c_str(),
- in_info.samplerate,
- in_info.channels, bits));
- path_choices.push_back(StringPrintf("%s/filter-%d-%d.conf",
- zita_config_dir.c_str(),
- in_info.samplerate,
- in_info.channels));
- path_choices.push_back(StringPrintf("%s/filter-%d.conf",
- zita_config_dir.c_str(),
- in_info.samplerate));
- const int seconds = in_info.frames / in_info.samplerate;
- const int max_choice = path_choices.size() - 1;
- std::string config_path;
- const bool found_config = FindFirstAccessiblePath(path_choices,
- &config_path);
- if (found_config) {
- DLogf("File %s, %.1fkHz, %d Bit, %d:%02d: filter config %s",
- underlying_file.c_str(), in_info.samplerate / 1000.0, bits,
- seconds / 60, seconds % 60,
- config_path.c_str());
- } else {
- DLogf("File %s: couldn't find filter config %s...%s",
- underlying_file.c_str(),
- path_choices[0].c_str(), path_choices[max_choice].c_str());
+ SoundProcessor *processor = fs->processor_pool()
+ ->GetOrCreate(zita_config_dir, in_info.samplerate, in_info.channels, bits);
+ if (processor == NULL) {
+ // TODO: depending on why this happened, provide one of these messages.
+ /*
+ partial_file_info_.message = "Problem parsing " + config_path_;
partial_file_info->message = "Missing ( " + path_choices[0]
+ "<br/> ... " + path_choices[max_choice] + " )";
+ */
sf_close(snd);
return NULL;
}
+ const int seconds = in_info.frames / in_info.samplerate;
+ DLogf("File %s, %.1fkHz, %d Bit, %d:%02d: filter config %s",
+ underlying_file.c_str(), in_info.samplerate / 1000.0, bits,
+ seconds / 60, seconds % 60,
+ processor->config_file().c_str());
return new SndFileHandler(fs, fs_path, filter_subdir,
underlying_file, filedes, snd, in_info,
- *partial_file_info, config_path);
+ *partial_file_info, processor);
}
virtual ~SndFileHandler() {
@@ -220,7 +194,9 @@ class SndFileHandler :
"(max=%.3f; Multiply gain with <= %.5f<br/>in %s)",
base_stats_.max_output_value,
1.0 / base_stats_.max_output_value,
- config_path_.c_str());
+ processor_ != NULL
+ ? processor_->config_file().c_str()
+ : "filter");
}
}
@@ -249,13 +225,12 @@ class SndFileHandler :
const std::string &underlying_file,
int filedes, SNDFILE *snd_in,
const SF_INFO &in_info, const HandlerStats &file_info,
- const std::string &config_path)
+ SoundProcessor *processor)
: FileHandler(filter_dir), fs_(fs),
filedes_(filedes), snd_in_(snd_in), in_info_(in_info),
- config_path_(config_path),
base_stats_(file_info),
error_(false), output_buffer_(NULL),
- snd_out_(NULL), processor_(NULL),
+ snd_out_(NULL), processor_(processor),
input_frames_left_(in_info.frames) {
// Initial stat that we're going to report to clients. We'll adapt
@@ -341,19 +316,24 @@ class SndFileHandler :
out_buffer->HeaderFinished();
}
- virtual bool AcceptProcessor(SoundProcessor *new_processor) {
- if (processor_ != NULL || !input_frames_left_) {
- DLogf("Gapless attempt: Cannot bridge gap to alrady open file %s",
+ bool HasStarted() { return in_info_.frames != input_frames_left_; }
+ virtual bool AcceptProcessor(SoundProcessor *passover_processor) {
+ if (HasStarted()) {
+ DLogf("Gapless attempt: Cannot bridge gap to already open file %s",
base_stats_.filename.c_str());
- return false; // We already have one.
+ return false;
}
- if (new_processor->config_file() != config_path_) {
- DLogf("Gapless: Configuration changed; can't join gapless");
+ assert(processor_);
+ if (passover_processor->config_file() != processor_->config_file()
+ || (passover_processor->config_file_timestamp()
+ != processor_->config_file_timestamp())) {
+ DLogf("Gapless: Configuration changed; can't use %p to join gapless.",
+ passover_processor);
return false;
}
- // TODO: check that other parameters such as sampling rate and channels
- // match (should be a rare problem as files in one dir typically match).
- processor_ = new_processor;
+ // Ok, so don't use the processor we already have, but use the other one.
+ fs_->processor_pool()->Return(processor_);
+ processor_ = passover_processor;
if (!processor_->is_input_buffer_complete()) {
// Fill with our beginning so that the donor can finish its processing.
input_frames_left_ -= processor_->FillBuffer(snd_in_);
@@ -375,25 +355,11 @@ class SndFileHandler :
}
virtual bool AddMoreSoundData() {
- if (processor_ && processor_->pending_writes() > 0) {
- processor_->WriteProcessed(snd_out_, processor_->pending_writes());
- return input_frames_left_;
- }
if (!input_frames_left_)
return false;
- if (!processor_) {
- // First time we're called and we don't have any processor yet.
- processor_ = SoundProcessor::Create(config_path_, in_info_.samplerate,
- in_info_.channels);
- if (processor_ == NULL) {
- syslog(LOG_ERR, "Oops - filter-config %s is broken. Please fix. "
- "Won't play this stream %s (simulating empty file)",
- config_path_.c_str(), base_stats_.filename.c_str());
- base_stats_.message = "Problem parsing " + config_path_;
- input_frames_left_ = 0;
- Close();
- return false;
- }
+ if (processor_->pending_writes() > 0) {
+ processor_->WriteProcessed(snd_out_, processor_->pending_writes());
+ return input_frames_left_;
}
const int r = processor_->FillBuffer(snd_in_);
if (r == 0) {
@@ -422,7 +388,8 @@ class SndFileHandler :
&& (next_file = fs_->GetOrCreateHandler(found->c_str()))
&& next_file->AcceptProcessor(processor_));
if (passed_processor) {
- DLogf("Gapless pass-on from '%s' to alphabetically next '%s'",
+ DLogf("Processor %p: Gapless pass-on from "
+ "'%s' to alphabetically next '%s'", processor_,
base_stats_.filename.c_str(), found->c_str());
}
stats_mutex_.Lock();
@@ -529,9 +496,10 @@ class SndFileHandler :
syslog(LOG_ERR, "Observed output clipping in '%s': "
"Max=%.3f; Multiply gain with <= %.5f in %s",
base_stats_.filename.c_str(), base_stats_.max_output_value,
- 1.0 / base_stats_.max_output_value, config_path_.c_str());
+ 1.0 / base_stats_.max_output_value,
+ processor_ != NULL ? processor_->config_file().c_str() : "filter");
}
- delete processor_;
+ fs_->processor_pool()->Return(processor_);
processor_ = NULL;
// We can't disable buffer writes here, because outfile closing will flush
// the last couple of sound samples.
@@ -556,7 +524,6 @@ class SndFileHandler :
const int filedes_;
SNDFILE *const snd_in_;
const SF_INFO in_info_;
- const std::string config_path_;
folve::Mutex stats_mutex_;
HandlerStats base_stats_; // UI information about current file.
@@ -577,7 +544,8 @@ class SndFileHandler :
FolveFilesystem::FolveFilesystem()
: debug_ui_enabled_(false), gapless_processing_(false),
- open_file_cache_(4), total_file_openings_(0), total_file_reopen_(0) {
+ open_file_cache_(4), processor_pool_(3),
+ total_file_openings_(0), total_file_reopen_(0) {
}
FileHandler *FolveFilesystem::CreateFromDescriptor(
View
3  folve-filesystem.h
@@ -85,6 +85,7 @@ class FolveFilesystem {
std::set<std::string> *files);
FileHandlerCache *handler_cache() { return &open_file_cache_; }
+ ProcessorPool *processor_pool() { return &processor_pool_; }
void set_debug_ui_enabled(bool b) { debug_ui_enabled_ = b; }
bool is_debug_ui_enabled() const { return debug_ui_enabled_; }
@@ -117,10 +118,12 @@ class FolveFilesystem {
std::string underlying_dir_;
std::string base_config_dir_;
+
std::string current_config_subdir_;
bool debug_ui_enabled_;
bool gapless_processing_;
FileHandlerCache open_file_cache_;
+ ProcessorPool processor_pool_;
int total_file_openings_;
int total_file_reopen_;
};
View
36 processor-pool.cc
@@ -27,8 +27,10 @@
#include "util.h"
using folve::StringPrintf;
+using folve::DLogf;
-ProcessorPool::ProcessorPool(int max_available) : max_per_config_(max_available) {
+ProcessorPool::ProcessorPool(int max_available)
+ : max_per_config_(max_available) {
}
static bool FindFirstAccessiblePath(const std::vector<std::string> &path,
@@ -59,34 +61,50 @@ SoundProcessor *ProcessorPool::GetOrCreate(const std::string &base_dir,
std::string config_path;
if (!FindFirstAccessiblePath(path_choices, &config_path))
return NULL;
- SoundProcessor *result = CheckOutOfPool(config_path);
+ SoundProcessor *result;
+ while ((result = CheckOutOfPool(config_path)) != NULL) {
+ if (result->ConfigStillUpToDate())
+ break;
+ DLogf("Processor %p: outdated; Good riddance after config file change %s",
+ result, config_path.c_str());
+ delete result;
+ }
if (result != NULL) {
- fprintf(stderr, "From Pool: %s\n", config_path.c_str());
+ DLogf("Processor %p: Got from pool [%s]", result, config_path.c_str());
return result;
}
- fprintf(stderr, "Creating new for %s\n", config_path.c_str());
result = SoundProcessor::Create(config_path, sampling_rate, channels);
if (result == NULL) {
syslog(LOG_ERR, "filter-config %s is broken.", config_path.c_str());
}
-
+ DLogf("Processor %p: Newly created [%s]", result, config_path.c_str());
return result;
}
void ProcessorPool::Return(SoundProcessor *processor) {
+ if (processor == NULL) return;
+ if (!processor->ConfigStillUpToDate()) {
+ DLogf("Processor %p: outdated. Not returning back in pool [%s]", processor,
+ processor->config_file().c_str());
+ delete processor;
+ return;
+ }
folve::MutexLock l(&pool_mutex_);
- PoolMap::iterator ins_pos = pool_.insert(make_pair(processor->config_file(),
- (ProcessorList*) NULL)).first;
+ PoolMap::iterator ins_pos =
+ pool_.insert(make_pair(processor->config_file(),
+ (ProcessorList*) NULL)).first;
if (ins_pos->second == NULL) {
ins_pos->second = new ProcessorList();
}
if (ins_pos->second->size() < max_per_config_) {
processor->Reset();
ins_pos->second->push_back(processor);
- fprintf(stderr, "Returned %s (%zd)\n", processor->config_file().c_str(),
- ins_pos->second->size());
+ DLogf("Processor %p: Returned to pool (count=%zd) [%s]\n",
+ processor, ins_pos->second->size(), processor->config_file().c_str());
} else {
+ DLogf("Processor %p: Getting rid of it; enough processors in pool.",
+ processor);
delete processor;
}
}
View
10 processor-pool.h
@@ -23,9 +23,15 @@
#include "util.h"
class SoundProcessor;
+
+// An object pool for SoundProcessors. They are expensive to create, in
+// particular on slow machines, but they only change if the configuration
+// file is touched. Good candidates for re-use.
class ProcessorPool {
public:
- ProcessorPool(int max_available);
+ // Create a processor pool. Stores at most "max_per_config" processors in
+ // pool per configuration file.
+ ProcessorPool(int max_per_config);
// Get a new SoundProcesor from this pool with the given configuration.
SoundProcessor *GetOrCreate(const std::string &base_dir,
@@ -35,7 +41,6 @@ class ProcessorPool {
void Return(SoundProcessor *processor);
private:
- typedef std::map<std::string, time_t> LastModifiedMap;
typedef std::deque<SoundProcessor*> ProcessorList;
typedef std::map<std::string, ProcessorList*> PoolMap;
@@ -44,7 +49,6 @@ class ProcessorPool {
const size_t max_per_config_;
folve::Mutex pool_mutex_;
PoolMap pool_;
- LastModifiedMap config_changed_;
};
#endif // FOLVE_PROCESSOR_POOL_
View
19 sound-processor.cc
@@ -19,6 +19,9 @@
#include <assert.h>
#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
#include "util.h"
@@ -46,15 +49,19 @@ SoundProcessor *SoundProcessor::Create(const std::string &config_file,
return new SoundProcessor(zita, config_file);
}
+static time_t GetModificationTime(const std::string &filename) {
+ struct stat st;
+ stat(filename.c_str(), &st);
+ return st.st_mtime;
+}
+
SoundProcessor::SoundProcessor(const ZitaConfig &config, const std::string &cfg)
: zita_config_(config), config_file_(cfg),
+ config_file_timestamp_(GetModificationTime(cfg)),
buffer_(new float[config.fragm * config.ninp]),
channels_(config.ninp),
input_pos_(0), output_pos_(0),
max_out_value_observed_(0.0) {
- struct stat st;
- stat(cfg.c_str(), &st);
- config_file_timestamp_ = st.st_mtime;
Reset();
}
@@ -117,6 +124,12 @@ void SoundProcessor::Process() {
output_pos_ = 0;
}
+bool SoundProcessor::ConfigStillUpToDate() const {
+ // TODO(hzeller): this should as well check if any *.wav file mentioned in
+ // config is still the same timestamp.
+ return config_file_timestamp_ == GetModificationTime(config_file_);
+}
+
void SoundProcessor::ResetMaxValues() {
max_out_value_observed_ = 0.0;
}
View
8 sound-processor.h
@@ -19,9 +19,8 @@
#ifndef FOLVE_SOUND_PROCESSOR_H
#define FOLVE_SOUND_PROCESSOR_H
-#include <sndfile.h>
-
#include <string>
+#include <sndfile.h>
#include "zita-config.h"
@@ -63,13 +62,16 @@ class SoundProcessor {
const std::string &config_file() const { return config_file_; }
const time_t config_file_timestamp() const { return config_file_timestamp_; }
+ // Verifies if configuration is still up-to-date.
+ bool ConfigStillUpToDate() const;
+
private:
SoundProcessor(const ZitaConfig &config, const std::string &cfg_file);
void Process();
const ZitaConfig zita_config_;
const std::string config_file_;
- time_t config_file_timestamp_;
+ const time_t config_file_timestamp_;
float *const buffer_;
const int channels_;
Please sign in to comment.
Something went wrong with that request. Please try again.