Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Newer
Older
100644 707 lines (653 sloc) 26.481 kb
4d4d90b3 »
2012-09-23 o More legalese
1 // Folve - A fuse filesystem that convolves audio files on-the-fly.
2 //
e9c07dd6 »
2012-09-08 o add documentation
3 // Copyright (C) 2012 Henner Zeller <h.zeller@acm.org>
cca212e5 »
2012-09-24 o document compile error with old version of fuse.
4 //
e9c07dd6 »
2012-09-08 o add documentation
5 // This program is free software; you can redistribute it and/or modify
6 // it under the terms of the GNU General Public License as published by
7 // the Free Software Foundation; either version 3 of the License, or
8 // (at your option) any later version.
9 //
10 // This program is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 // GNU General Public License for more details.
14 //
15 // You should have received a copy of the GNU General Public License
16 // along with this program. If not, see <http://www.gnu.org/licenses/>.
17
a9d3e538 »
2012-09-15 o Found a project name: "Folve". Some renamings because of that.
18 #include "folve-filesystem.h"
19
9a8ea565 »
2012-09-12 o add infrastructure to provide Stat() output of currently open
20 #include <FLAC/metadata.h>
08bbf282 »
2012-09-21 o First shot at gapless. Works pretty well for my test-case.
21 #include <dirent.h>
e9c07dd6 »
2012-09-08 o add documentation
22 #include <errno.h>
15b305a6 »
2012-09-13 o Build infrastructure to re-use file handler objects. Files seem to
23 #include <fcntl.h>
7c7bda6f »
2012-09-10 o attempt to create a sndfile for every file, if that fails, fall
24 #include <sndfile.h>
5830c5a6 »
2012-09-17 o Switch logging to syslog()
25 #include <stdarg.h>
e9c07dd6 »
2012-09-08 o add documentation
26 #include <stdio.h>
27 #include <string.h>
28 #include <strings.h>
9a8ea565 »
2012-09-12 o add infrastructure to provide Stat() output of currently open
29 #include <sys/stat.h>
30 #include <sys/types.h>
5830c5a6 »
2012-09-17 o Switch logging to syslog()
31 #include <syslog.h>
e9c07dd6 »
2012-09-08 o add documentation
32 #include <unistd.h>
7c7bda6f »
2012-09-10 o attempt to create a sndfile for every file, if that fails, fall
33
9a8ea565 »
2012-09-12 o add infrastructure to provide Stat() output of currently open
34 #include <map>
a9d3e538 »
2012-09-15 o Found a project name: "Folve". Some renamings because of that.
35 #include <string>
6c6dedb8 »
2012-09-16 o Some readme tweaks and readability improvements.
36 #include <zita-convolver.h>
e9c07dd6 »
2012-09-08 o add documentation
37
f00e6a47 »
2012-09-22 o Don't do unprotected read on status information such as
38 #include <boost/thread/locks.hpp>
39 #include <boost/thread/mutex.hpp>
40
6e895e3e »
2012-09-09 o add conversion buffer that uses the virtual file provided
41 #include "conversion-buffer.h"
a9d3e538 »
2012-09-15 o Found a project name: "Folve". Some renamings because of that.
42 #include "file-handler-cache.h"
43 #include "file-handler.h"
2fee1d65 »
2012-09-20 o extract sound processor
44 #include "sound-processor.h"
8268c37e »
2012-09-15 o Bubble up error messages in status server.
45 #include "util.h"
6c6dedb8 »
2012-09-16 o Some readme tweaks and readability improvements.
46
fd65cee4 »
2012-09-09 o add basic zita filter.
47 #include "zita-config.h"
48
8268c37e »
2012-09-15 o Bubble up error messages in status server.
49 using folve::Appendf;
3e883b73 »
2012-09-18 o Match filters in sequence
50 using folve::StringPrintf;
7689c9a8 »
2012-09-22 o preparation to extract classes from folve-filesystem into their
51 using folve::DLogf;
b5c1e960 »
2012-09-14 o more useful output on status server.
52
e9c07dd6 »
2012-09-08 o add documentation
53 namespace {
54
5476cc29 »
2012-09-09 o provide absolute path.
55 // Very simple filter that just passes the original file through. Used for
56 // everything that is not a sound-file.
f1ef9608 »
2012-09-21 o a bit HTML polishing.
57 class PassThroughHandler : public FileHandler {
e9c07dd6 »
2012-09-08 o add documentation
58 public:
f1ef9608 »
2012-09-21 o a bit HTML polishing.
59 PassThroughHandler(int filedes, int filter_id,
ee675fea »
2012-09-17 o Provide a way to select between different filter diretories.
60 const HandlerStats &known_stats)
f1ef9608 »
2012-09-21 o a bit HTML polishing.
61 : FileHandler(filter_id), filedes_(filedes),
62 file_size_(-1), max_accessed_(0), info_stats_(known_stats) {
7689c9a8 »
2012-09-22 o preparation to extract classes from folve-filesystem into their
63 DLogf("Creating PassThrough filter for '%s'", known_stats.filename.c_str());
f1ef9608 »
2012-09-21 o a bit HTML polishing.
64 struct stat st;
65 file_size_ = (Stat(&st) == 0) ? st.st_size : -1;
30bf3100 »
2012-09-22 o Pass-through file handler is always filter '0'.
66 info_stats_.filter_id = 0; // pass through.
e9c07dd6 »
2012-09-08 o add documentation
67 }
f1ef9608 »
2012-09-21 o a bit HTML polishing.
68 ~PassThroughHandler() { close(filedes_); }
680f5909 »
2012-09-13 o report opening error.
69
e9c07dd6 »
2012-09-08 o add documentation
70 virtual int Read(char *buf, size_t size, off_t offset) {
71 const int result = pread(filedes_, buf, size, offset);
79e7608e »
2012-09-23 o Don't add a message if not necessary to the pass through filter.
72 max_accessed_ = std::max(max_accessed_, (long unsigned int) offset + result);
e9c07dd6 »
2012-09-08 o add documentation
73 return result == -1 ? -errno : result;
74 }
9a8ea565 »
2012-09-12 o add infrastructure to provide Stat() output of currently open
75 virtual int Stat(struct stat *st) {
76 return fstat(filedes_, st);
77 }
e2c7d54e »
2012-09-28 o There was a report of a 2.7.1 ICE, apparently on a generated move
78 virtual void GetHandlerStatus(HandlerStats *stats) {
8268c37e »
2012-09-15 o Bubble up error messages in status server.
79 *stats = info_stats_;
f1ef9608 »
2012-09-21 o a bit HTML polishing.
80 if (file_size_ > 0) {
81 stats->progress = 1.0 * max_accessed_ / file_size_;
82 }
223f392f »
2012-09-14 o Have a separate HandlerStats object that contains relevant
83 }
e2c7d54e »
2012-09-28 o There was a report of a 2.7.1 ICE, apparently on a generated move
84
e9c07dd6 »
2012-09-08 o add documentation
85 private:
86 const int filedes_;
f1ef9608 »
2012-09-21 o a bit HTML polishing.
87 size_t file_size_;
88 long unsigned int max_accessed_;
8268c37e »
2012-09-15 o Bubble up error messages in status server.
89 HandlerStats info_stats_;
e9c07dd6 »
2012-09-08 o add documentation
90 };
91
3e883b73 »
2012-09-18 o Match filters in sequence
92 static bool FindFirstAccessiblePath(const std::vector<std::string> &path,
93 std::string *match) {
94 for (size_t i = 0; i < path.size(); ++i) {
95 if (access(path[i].c_str(), R_OK) == 0) {
96 *match = path[i];
97 return true;
98 }
99 }
100 return false;
101 }
102
15b305a6 »
2012-09-13 o Build infrastructure to re-use file handler objects. Files seem to
103 class SndFileHandler :
104 public FileHandler,
fd4ed76e »
2012-09-09 o write to a file instead of a buffer.
105 public ConversionBuffer::SoundSource {
e9c07dd6 »
2012-09-08 o add documentation
106 public:
15b305a6 »
2012-09-13 o Build infrastructure to re-use file handler objects. Files seem to
107 // Attempt to create a SndFileHandler from the given file descriptor. This
7c7bda6f »
2012-09-10 o attempt to create a sndfile for every file, if that fails, fall
108 // returns NULL if this is not a sound-file or if there is no available
109 // convolution filter configuration available.
8268c37e »
2012-09-15 o Bubble up error messages in status server.
110 // "partial_file_info" will be set to information known so far, including
111 // error message.
08bbf282 »
2012-09-21 o First shot at gapless. Works pretty well for my test-case.
112 static FileHandler *Create(FolveFilesystem *fs,
113 int filedes, const char *fs_path,
114 const std::string &underlying_file,
ee675fea »
2012-09-17 o Provide a way to select between different filter diretories.
115 int filter_id,
8268c37e »
2012-09-15 o Bubble up error messages in status server.
116 const std::string &zita_config_dir,
117 HandlerStats *partial_file_info) {
bea11d6b »
2012-09-15 o Don't use snprintf, but use a new function Appendf() to assemble
118 SF_INFO in_info;
6e895e3e »
2012-09-09 o add conversion buffer that uses the virtual file provided
119 memset(&in_info, 0, sizeof(in_info));
7c7bda6f »
2012-09-10 o attempt to create a sndfile for every file, if that fails, fall
120 SNDFILE *snd = sf_open_fd(filedes, SFM_READ, &in_info, 0);
121 if (snd == NULL) {
7689c9a8 »
2012-09-22 o preparation to extract classes from folve-filesystem into their
122 DLogf("File %s: %s", underlying_file.c_str(), sf_strerror(NULL));
8268c37e »
2012-09-15 o Bubble up error messages in status server.
123 partial_file_info->message = sf_strerror(NULL);
7c7bda6f »
2012-09-10 o attempt to create a sndfile for every file, if that fails, fall
124 return NULL;
6e895e3e »
2012-09-09 o add conversion buffer that uses the virtual file provided
125 }
fd4ed76e »
2012-09-09 o write to a file instead of a buffer.
126
7e756eb9 »
2012-09-10 o Fix problem with re-initializing the convproc. Turns out that the c…
127 int bits = 16;
31bd9ce1 »
2012-09-10 o README update.
128 if ((in_info.format & SF_FORMAT_SUBMASK) == SF_FORMAT_PCM_24) bits = 24;
129 if ((in_info.format & SF_FORMAT_SUBMASK) == SF_FORMAT_PCM_32) bits = 32;
e2619a6c »
2012-09-11 o skipping debug message.
130
8268c37e »
2012-09-15 o Bubble up error messages in status server.
131 // Remember whatever we could got to know in the partial file info.
132 Appendf(&partial_file_info->format, "%.1fkHz, %d Bit",
133 in_info.samplerate / 1000.0, bits);
134 partial_file_info->duration_seconds = in_info.frames / in_info.samplerate;
135
3e883b73 »
2012-09-18 o Match filters in sequence
136 std::vector<std::string> path_choices;
137 // From specific to non-specific.
138 path_choices.push_back(StringPrintf("%s/filter-%d-%d-%d.conf",
139 zita_config_dir.c_str(),
140 in_info.samplerate,
141 in_info.channels, bits));
142 path_choices.push_back(StringPrintf("%s/filter-%d-%d.conf",
143 zita_config_dir.c_str(),
144 in_info.samplerate,
145 in_info.channels));
146 path_choices.push_back(StringPrintf("%s/filter-%d.conf",
147 zita_config_dir.c_str(),
148 in_info.samplerate));
0b0bd338 »
2012-09-20 Fix bug: the last couple of sound-samples were not flushed. We want
149 const int seconds = in_info.frames / in_info.samplerate;
3e883b73 »
2012-09-18 o Match filters in sequence
150 const int max_choice = path_choices.size() - 1;
ee675fea »
2012-09-17 o Provide a way to select between different filter diretories.
151 std::string config_path;
3e883b73 »
2012-09-18 o Match filters in sequence
152 const bool found_config = FindFirstAccessiblePath(path_choices,
153 &config_path);
a2aa9a4f »
2012-09-14 o let error messages make it to the console.
154 if (found_config) {
7689c9a8 »
2012-09-22 o preparation to extract classes from folve-filesystem into their
155 DLogf("File %s, %.1fkHz, %d Bit, %d:%02d: filter config %s",
156 underlying_file.c_str(), in_info.samplerate / 1000.0, bits,
157 seconds / 60, seconds % 60,
158 config_path.c_str());
a2aa9a4f »
2012-09-14 o let error messages make it to the console.
159 } else {
7689c9a8 »
2012-09-22 o preparation to extract classes from folve-filesystem into their
160 DLogf("File %s: couldn't find filter config %s...%s",
161 underlying_file.c_str(),
162 path_choices[0].c_str(), path_choices[max_choice].c_str());
3e883b73 »
2012-09-18 o Match filters in sequence
163 partial_file_info->message = "Missing ( " + path_choices[0]
164 + "<br/> ... " + path_choices[max_choice] + " )";
7c7bda6f »
2012-09-10 o attempt to create a sndfile for every file, if that fails, fall
165 sf_close(snd);
166 return NULL;
b5c1e960 »
2012-09-14 o more useful output on status server.
167 }
08bbf282 »
2012-09-21 o First shot at gapless. Works pretty well for my test-case.
168 return new SndFileHandler(fs, fs_path, filter_id,
ee675fea »
2012-09-17 o Provide a way to select between different filter diretories.
169 underlying_file, filedes, snd, in_info,
8268c37e »
2012-09-15 o Bubble up error messages in status server.
170 *partial_file_info, config_path);
e9c07dd6 »
2012-09-08 o add documentation
171 }
172
15b305a6 »
2012-09-13 o Build infrastructure to re-use file handler objects. Files seem to
173 virtual ~SndFileHandler() {
680f5909 »
2012-09-13 o report opening error.
174 Close();
6e895e3e »
2012-09-09 o add conversion buffer that uses the virtual file provided
175 delete output_buffer_;
176 }
177
e9c07dd6 »
2012-09-08 o add documentation
178 virtual int Read(char *buf, size_t size, off_t offset) {
6e895e3e »
2012-09-09 o add conversion buffer that uses the virtual file provided
179 if (error_) return -1;
47c2b036 »
2012-09-12 o better skip mode detection. Only if someone _really_ jumps to the
180 // If this is a skip suspiciously at the very end of the file as
181 // reported by stat, we don't do any encoding, just return garbage.
1118ad99 »
2012-09-15 o comment update.
182 // (otherwise we'd to convolve up to that point).
183 //
184 // While indexing, media players do this sometimes apparently.
185 // And sometimes not even to the very end but 'almost' at the end.
186 // So add some FudeOverhang
bcb77e7f »
2012-09-14 o Ran with Amarok over my files. Looks like it sometimes only 'almost'
187 static const int kFudgeOverhang = 512;
abb04719 »
2012-09-14 o remove debug message from last commit.
188 // But of course only if this is really a skip, not a regular approaching
189 // end-of-file.
bcb77e7f »
2012-09-14 o Ran with Amarok over my files. Looks like it sometimes only 'almost'
190 if (output_buffer_->FileSize() < offset
191 && (int) (offset + size + kFudgeOverhang) >= file_stat_.st_size) {
192 const int pretended_bytes = std::min((off_t)size,
193 file_stat_.st_size - offset);
194 if (pretended_bytes > 0) {
195 memset(buf, 0x00, pretended_bytes);
196 return pretended_bytes;
680f5909 »
2012-09-13 o report opening error.
197 } else {
198 return 0;
199 }
47c2b036 »
2012-09-12 o better skip mode detection. Only if someone _really_ jumps to the
200 }
6e895e3e »
2012-09-09 o add conversion buffer that uses the virtual file provided
201 // The following read might block and call WriteToSoundfile() until the
202 // buffer is filled.
203 return output_buffer_->Read(buf, size, offset);
e9c07dd6 »
2012-09-08 o add documentation
204 }
205
e2c7d54e »
2012-09-28 o There was a report of a 2.7.1 ICE, apparently on a generated move
206 virtual void GetHandlerStatus(HandlerStats *stats) {
f00e6a47 »
2012-09-22 o Don't do unprotected read on status information such as
207 boost::lock_guard<boost::mutex> l(stats_mutex_);
208 if (processor_ != NULL) {
209 base_stats_.max_output_value = processor_->max_output_value();
210 }
8268c37e »
2012-09-15 o Bubble up error messages in status server.
211 *stats = base_stats_;
2fee1d65 »
2012-09-20 o extract sound processor
212 const int frames_done = in_info_.frames - input_frames_left_;
213 if (frames_done == 0 || in_info_.frames == 0)
223f392f »
2012-09-14 o Have a separate HandlerStats object that contains relevant
214 stats->progress = 0.0;
215 else
2fee1d65 »
2012-09-20 o extract sound processor
216 stats->progress = 1.0 * frames_done / in_info_.frames;
f00e6a47 »
2012-09-22 o Don't do unprotected read on status information such as
217
d93a35b3 »
2012-09-21 o Uh, typo in gapless message.
218 if (base_stats_.max_output_value > 1.0) {
a5c63829 »
2012-09-21 o Remember the max output value in the stats
219 // TODO: the status server could inspect this value and make better
220 // rendering.
60477fec »
2012-09-18 o Detect output overdrive and give useful message what to do.
221 base_stats_.message =
ce22b6e1 »
2012-09-18 o change wording from 'overdrive' to 'clipping' so that
222 StringPrintf("Output clipping! "
60477fec »
2012-09-18 o Detect output overdrive and give useful message what to do.
223 "(max=%.3f; Multiply gain with <= %.5f<br/>in %s)",
a5c63829 »
2012-09-21 o Remember the max output value in the stats
224 base_stats_.max_output_value,
225 1.0 / base_stats_.max_output_value,
60477fec »
2012-09-18 o Detect output overdrive and give useful message what to do.
226 config_path_.c_str());
227 }
b5c1e960 »
2012-09-14 o more useful output on status server.
228 }
229
9a8ea565 »
2012-09-12 o add infrastructure to provide Stat() output of currently open
230 virtual int Stat(struct stat *st) {
ba7fde84 »
2012-09-12 o implement size estimate. If the user stat() or fstat() the file,
231 if (output_buffer_->FileSize() > start_estimating_size_) {
2fee1d65 »
2012-09-20 o extract sound processor
232 const int frames_done = in_info_.frames - input_frames_left_;
ba7fde84 »
2012-09-12 o implement size estimate. If the user stat() or fstat() the file,
233 if (frames_done > 0) {
2fee1d65 »
2012-09-20 o extract sound processor
234 const float estimated_end = 1.0 * in_info_.frames / frames_done;
ba7fde84 »
2012-09-12 o implement size estimate. If the user stat() or fstat() the file,
235 off_t new_size = estimated_end * output_buffer_->FileSize();
236 // Report a bit bigger size which is less harmful than programs
237 // reading short.
238 new_size += 16384;
453489ff »
2012-09-12 o smallish whitespace changes.
239 if (new_size > file_stat_.st_size) { // Only go forward in size.
ba7fde84 »
2012-09-12 o implement size estimate. If the user stat() or fstat() the file,
240 file_stat_.st_size = new_size;
241 }
242 }
243 }
244 *st = file_stat_;
9a8ea565 »
2012-09-12 o add infrastructure to provide Stat() output of currently open
245 return 0;
246 }
6e895e3e »
2012-09-09 o add conversion buffer that uses the virtual file provided
247
e9c07dd6 »
2012-09-08 o add documentation
248 private:
ee675fea »
2012-09-17 o Provide a way to select between different filter diretories.
249 // TODO(hzeller): trim parameter list.
08bbf282 »
2012-09-21 o First shot at gapless. Works pretty well for my test-case.
250 SndFileHandler(FolveFilesystem *fs, const char *fs_path, int filter_id,
251 const std::string &underlying_file,
252 int filedes, SNDFILE *snd_in,
8268c37e »
2012-09-15 o Bubble up error messages in status server.
253 const SF_INFO &in_info, const HandlerStats &file_info,
254 const std::string &config_path)
08bbf282 »
2012-09-21 o First shot at gapless. Works pretty well for my test-case.
255 : FileHandler(filter_id), fs_(fs),
2fee1d65 »
2012-09-20 o extract sound processor
256 filedes_(filedes), snd_in_(snd_in), in_info_(in_info),
8268c37e »
2012-09-15 o Bubble up error messages in status server.
257 config_path_(config_path),
2fee1d65 »
2012-09-20 o extract sound processor
258 base_stats_(file_info),
b5c1e960 »
2012-09-14 o more useful output on status server.
259 error_(false), output_buffer_(NULL),
2fee1d65 »
2012-09-20 o extract sound processor
260 snd_out_(NULL), processor_(NULL),
60477fec »
2012-09-18 o Detect output overdrive and give useful message what to do.
261 input_frames_left_(in_info.frames) {
ba7fde84 »
2012-09-12 o implement size estimate. If the user stat() or fstat() the file,
262
263 // Initial stat that we're going to report to clients. We'll adapt
264 // the filesize as we see it grow. Some clients continuously monitor
265 // the size of the file to check when to stop.
266 fstat(filedes_, &file_stat_);
47c2b036 »
2012-09-12 o better skip mode detection. Only if someone _really_ jumps to the
267 start_estimating_size_ = 0.4 * file_stat_.st_size;
7c7bda6f »
2012-09-10 o attempt to create a sndfile for every file, if that fails, fall
268
8e36effd »
2012-09-12 o comment update.
269 // The flac header we get is more rich than what we can create via
270 // sndfile. So if we have one, just copy it.
bea11d6b »
2012-09-15 o Don't use snprintf, but use a new function Appendf() to assemble
271 copy_flac_header_verbatim_ = LooksLikeInputIsFlac(in_info, filedes);
7e27dab7 »
2012-09-11 o initial version of copying header. Not sure yet though if
272
7c7bda6f »
2012-09-10 o attempt to create a sndfile for every file, if that fails, fall
273 // Create a conversion buffer that creates a soundfile of a particular
8e36effd »
2012-09-12 o comment update.
274 // format that we choose here. Essentially we want to generate mostly what
7c7bda6f »
2012-09-10 o attempt to create a sndfile for every file, if that fails, fall
275 // our input is.
bea11d6b »
2012-09-15 o Don't use snprintf, but use a new function Appendf() to assemble
276 SF_INFO out_info = in_info;
31bd9ce1 »
2012-09-10 o README update.
277 out_info.seekable = 0;
278 if ((in_info.format & SF_FORMAT_TYPEMASK) == SF_FORMAT_OGG) {
8e36effd »
2012-09-12 o comment update.
279 // If the input was ogg, we're re-coding this to flac, because it
280 // wouldn't let us stream the output.
7c7bda6f »
2012-09-10 o attempt to create a sndfile for every file, if that fails, fall
281 out_info.format = SF_FORMAT_FLAC;
f11d8cdf »
2012-09-12 o implement initial skip mode detection.
282 out_info.format |= SF_FORMAT_PCM_16;
7c7bda6f »
2012-09-10 o attempt to create a sndfile for every file, if that fails, fall
283 }
a85bebb2 »
2012-09-12 o Fix Flac header manually. Luckily the streamheader is a fixed
284 else if ((in_info.format & SF_FORMAT_TYPEMASK) == SF_FORMAT_WAV) {
285 out_info.format = SF_FORMAT_FLAC; // recode as flac.
286 out_info.format |= SF_FORMAT_PCM_24;
31bd9ce1 »
2012-09-10 o README update.
287 }
288 else { // original format.
289 out_info.format = in_info.format;
290 }
291
7c7bda6f »
2012-09-10 o attempt to create a sndfile for every file, if that fails, fall
292 output_buffer_ = new ConversionBuffer(this, out_info);
293 }
294
f8cb9e52 »
2012-09-11 o redacting MD5.
295 virtual void SetOutputSoundfile(ConversionBuffer *out_buffer,
7e27dab7 »
2012-09-11 o initial version of copying header. Not sure yet though if
296 SNDFILE *sndfile) {
fd4ed76e »
2012-09-09 o write to a file instead of a buffer.
297 snd_out_ = sndfile;
298 if (snd_out_ == NULL) {
299 error_ = true;
5830c5a6 »
2012-09-17 o Switch logging to syslog()
300 syslog(LOG_ERR, "Opening output: %s", sf_strerror(NULL));
8268c37e »
2012-09-15 o Bubble up error messages in status server.
301 base_stats_.message = sf_strerror(NULL);
fd4ed76e »
2012-09-09 o write to a file instead of a buffer.
302 return;
303 }
bea11d6b »
2012-09-15 o Don't use snprintf, but use a new function Appendf() to assemble
304 if (copy_flac_header_verbatim_) {
8e36effd »
2012-09-12 o comment update.
305 out_buffer->set_sndfile_writes_enabled(false);
f8cb9e52 »
2012-09-11 o redacting MD5.
306 CopyFlacHeader(out_buffer);
7e27dab7 »
2012-09-11 o initial version of copying header. Not sure yet though if
307 } else {
f8cb9e52 »
2012-09-11 o redacting MD5.
308 out_buffer->set_sndfile_writes_enabled(true);
8e36effd »
2012-09-12 o comment update.
309 GenerateHeaderFromInputFile(out_buffer);
fd4ed76e »
2012-09-09 o write to a file instead of a buffer.
310 }
311 // Now flush the header: that way if someone only reads the metadata, then
312 // our AddMoreSoundData() is never called.
8e36effd »
2012-09-12 o comment update.
313 // We need to do this even if we copied our own header: that way we make
314 // sure that the sndfile-header is flushed into the nirwana before we
315 // re-enable sndfile_writes.
fd4ed76e »
2012-09-09 o write to a file instead of a buffer.
316 sf_command(snd_out_, SFC_UPDATE_HEADER_NOW, NULL, 0);
a85bebb2 »
2012-09-12 o Fix Flac header manually. Luckily the streamheader is a fixed
317
318 // -- time for some hackery ...
319 // If we have copied the header over from the original, we need to
320 // redact the values for min/max blocksize and min/max framesize with
321 // what SNDFILE is going to use, otherwise programs will trip over this.
322 // http://flac.sourceforge.net/format.html
bea11d6b »
2012-09-15 o Don't use snprintf, but use a new function Appendf() to assemble
323 if (copy_flac_header_verbatim_) {
a85bebb2 »
2012-09-12 o Fix Flac header manually. Luckily the streamheader is a fixed
324 out_buffer->WriteCharAt((1152 & 0xFF00) >> 8, 8);
325 out_buffer->WriteCharAt((1152 & 0x00FF) , 9);
335d1854 »
2012-09-25 o Fix blocksize header; this was once an experiment that seems
326 out_buffer->WriteCharAt((1152 & 0xFF00) >> 8, 10);
327 out_buffer->WriteCharAt((1152 & 0x00FF) , 11);
328 for (int i = 12; i < 18; ++i) out_buffer->WriteCharAt(0, i); // framesize
a85bebb2 »
2012-09-12 o Fix Flac header manually. Luckily the streamheader is a fixed
329 } else {
330 // .. and if SNDFILE writes the header, it misses out in writing the
331 // number of samples to be expected. So let's fill that in.
332 // The MD5 sum starts at position strlen("fLaC") + 4 + 18 = 26
333 // The 32 bits before that are the samples (and another 4 bit before that,
334 // ignoring that for now).
2fee1d65 »
2012-09-20 o extract sound processor
335 out_buffer->WriteCharAt((in_info_.frames & 0xFF000000) >> 24, 22);
336 out_buffer->WriteCharAt((in_info_.frames & 0x00FF0000) >> 16, 23);
337 out_buffer->WriteCharAt((in_info_.frames & 0x0000FF00) >> 8, 24);
338 out_buffer->WriteCharAt((in_info_.frames & 0x000000FF), 25);
a85bebb2 »
2012-09-12 o Fix Flac header manually. Luckily the streamheader is a fixed
339 }
f8cb9e52 »
2012-09-11 o redacting MD5.
340
341 out_buffer->set_sndfile_writes_enabled(true); // ready for sound-stream.
7689c9a8 »
2012-09-22 o preparation to extract classes from folve-filesystem into their
342 DLogf("Header init done (%s).", base_stats_.filename.c_str());
550c4e01 »
2012-09-11 o allow short read in header area, but not in the stream
343 out_buffer->HeaderFinished();
fd4ed76e »
2012-09-09 o write to a file instead of a buffer.
344 }
345
9af16fe1 »
2012-09-22 o Gapless string should break when configuration changes.
346 virtual bool AcceptProcessor(SoundProcessor *new_processor) {
1113409a »
2012-09-21 o Diagnostic if gapless cannot be done, because the other file is
347 if (processor_ != NULL || !input_frames_left_) {
7689c9a8 »
2012-09-22 o preparation to extract classes from folve-filesystem into their
348 DLogf("Gapless attempt: Cannot bridge gap to alrady open file %s",
349 base_stats_.filename.c_str());
08bbf282 »
2012-09-21 o First shot at gapless. Works pretty well for my test-case.
350 return false; // We already have one.
1113409a »
2012-09-21 o Diagnostic if gapless cannot be done, because the other file is
351 }
9af16fe1 »
2012-09-22 o Gapless string should break when configuration changes.
352 if (new_processor->config_file() != config_path_) {
7689c9a8 »
2012-09-22 o preparation to extract classes from folve-filesystem into their
353 DLogf("Gapless: Configuration changed; can't join gapless");
9af16fe1 »
2012-09-22 o Gapless string should break when configuration changes.
354 return false;
355 }
ac40d857 »
2012-09-21 o Make sure we match the alphabetically next file with
356 // TODO: check that other parameters such as sampling rate and channels
7b499ea6 »
2012-09-22 o Uh, log() is actually ln() and log10f() is log() :)
357 // match (should be a rare problem as files in one dir typically match).
9af16fe1 »
2012-09-22 o Gapless string should break when configuration changes.
358 processor_ = new_processor;
08bbf282 »
2012-09-21 o First shot at gapless. Works pretty well for my test-case.
359 if (!processor_->is_input_buffer_complete()) {
7b499ea6 »
2012-09-22 o Uh, log() is actually ln() and log10f() is log() :)
360 // Fill with our beginning so that the donor can finish its processing.
08bbf282 »
2012-09-21 o First shot at gapless. Works pretty well for my test-case.
361 input_frames_left_ -= processor_->FillBuffer(snd_in_);
362 }
363 base_stats_.in_gapless = true;
364 return true;
365 }
366
ac40d857 »
2012-09-21 o Make sure we match the alphabetically next file with
367 static bool ExtractDirAndSuffix(const std::string &filename,
368 std::string *dir, std::string *suffix) {
369 const std::string::size_type slash_pos = filename.find_last_of('/');
370 if (slash_pos == std::string::npos) return false;
371 *dir = filename.substr(0, slash_pos + 1);
372 const std::string::size_type dot_pos = filename.find_last_of('.');
373 if (dot_pos != std::string::npos && dot_pos > slash_pos) {
374 *suffix = filename.substr(dot_pos);
375 }
376 return true;
08bbf282 »
2012-09-21 o First shot at gapless. Works pretty well for my test-case.
377 }
378
47c2b036 »
2012-09-12 o better skip mode detection. Only if someone _really_ jumps to the
379 virtual bool AddMoreSoundData() {
08bbf282 »
2012-09-21 o First shot at gapless. Works pretty well for my test-case.
380 if (processor_ && processor_->pending_writes() > 0) {
381 processor_->WriteProcessed(snd_out_, processor_->pending_writes());
382 return input_frames_left_;
383 }
0edb358c »
2012-09-10 o attempt to remove global references in the zita config.
384 if (!input_frames_left_)
385 return false;
2fee1d65 »
2012-09-20 o extract sound processor
386 if (!processor_) {
08bbf282 »
2012-09-21 o First shot at gapless. Works pretty well for my test-case.
387 // First time we're called and we don't have any processor yet.
2fee1d65 »
2012-09-20 o extract sound processor
388 processor_ = SoundProcessor::Create(config_path_, in_info_.samplerate,
389 in_info_.channels);
390 if (processor_ == NULL) {
391 syslog(LOG_ERR, "Oops - filter-config %s is broken. Please fix. "
23b24f0b »
2012-09-17 o Write zita config problems to syslog.
392 "Won't play this stream %s (simulating empty file)",
393 config_path_.c_str(), base_stats_.filename.c_str());
8268c37e »
2012-09-15 o Bubble up error messages in status server.
394 base_stats_.message = "Problem parsing " + config_path_;
0c8561b0 »
2012-09-13 o Handle configuration file error gracefully.
395 input_frames_left_ = 0;
a2aa9a4f »
2012-09-14 o let error messages make it to the console.
396 Close();
0c8561b0 »
2012-09-13 o Handle configuration file error gracefully.
397 return false;
398 }
0edb358c »
2012-09-10 o attempt to remove global references in the zita config.
399 }
2fee1d65 »
2012-09-20 o extract sound processor
400 const int r = processor_->FillBuffer(snd_in_);
9c0ba158 »
2012-09-14 o work around broken files - don't endlessloop :)
401 if (r == 0) {
2fee1d65 »
2012-09-20 o extract sound processor
402 syslog(LOG_ERR, "Expected %d frames left, "
5830c5a6 »
2012-09-17 o Switch logging to syslog()
403 "but got EOF; corrupt file '%s' ?",
2fee1d65 »
2012-09-20 o extract sound processor
404 input_frames_left_, base_stats_.filename.c_str());
07064527 »
2012-09-16 o Report broken input file in UI.
405 base_stats_.message = "Premature EOF in input file.";
9c0ba158 »
2012-09-14 o work around broken files - don't endlessloop :)
406 input_frames_left_ = 0;
407 Close();
408 return false;
409 }
3b8fe7ef »
2012-09-22 o Remove deadlock
410 stats_mutex_.lock();
6e895e3e »
2012-09-09 o add conversion buffer that uses the virtual file provided
411 input_frames_left_ -= r;
3b8fe7ef »
2012-09-22 o Remove deadlock
412 stats_mutex_.unlock();
08bbf282 »
2012-09-21 o First shot at gapless. Works pretty well for my test-case.
413 if (!input_frames_left_ && !processor_->is_input_buffer_complete()
414 && fs_->gapless_processing()) {
415 typedef std::set<std::string> DirSet;
ac40d857 »
2012-09-21 o Make sure we match the alphabetically next file with
416 DirSet dirset;
417 std::string fs_dir, file_suffix;
08bbf282 »
2012-09-21 o First shot at gapless. Works pretty well for my test-case.
418 FileHandler *next_file = NULL;
ac40d857 »
2012-09-21 o Make sure we match the alphabetically next file with
419 DirSet::const_iterator found;
08bbf282 »
2012-09-21 o First shot at gapless. Works pretty well for my test-case.
420 const bool passed_processor
ac40d857 »
2012-09-21 o Make sure we match the alphabetically next file with
421 = (ExtractDirAndSuffix(base_stats_.filename, &fs_dir, &file_suffix)
422 && fs_->ListDirectory(fs_dir, file_suffix, &dirset)
423 && (found = dirset.upper_bound(base_stats_.filename)) != dirset.end()
08bbf282 »
2012-09-21 o First shot at gapless. Works pretty well for my test-case.
424 && (next_file = fs_->GetOrCreateHandler(found->c_str()))
425 && next_file->AcceptProcessor(processor_));
426 if (passed_processor) {
7689c9a8 »
2012-09-22 o preparation to extract classes from folve-filesystem into their
427 DLogf("Gapless pass-on from '%s' to alphabetically next '%s'",
428 base_stats_.filename.c_str(), found->c_str());
08bbf282 »
2012-09-21 o First shot at gapless. Works pretty well for my test-case.
429 }
3b8fe7ef »
2012-09-22 o Remove deadlock
430 stats_mutex_.lock();
08bbf282 »
2012-09-21 o First shot at gapless. Works pretty well for my test-case.
431 processor_->WriteProcessed(snd_out_, r);
3b8fe7ef »
2012-09-22 o Remove deadlock
432 stats_mutex_.unlock();
08bbf282 »
2012-09-21 o First shot at gapless. Works pretty well for my test-case.
433 if (passed_processor) {
434 base_stats_.out_gapless = true;
7b499ea6 »
2012-09-22 o Uh, log() is actually ln() and log10f() is log() :)
435 SaveOutputValues();
ac40d857 »
2012-09-21 o Make sure we match the alphabetically next file with
436 processor_ = NULL; // we handed over ownership.
08bbf282 »
2012-09-21 o First shot at gapless. Works pretty well for my test-case.
437 }
438 if (next_file) fs_->Close(found->c_str(), next_file);
439 } else {
3b8fe7ef »
2012-09-22 o Remove deadlock
440 stats_mutex_.lock();
08bbf282 »
2012-09-21 o First shot at gapless. Works pretty well for my test-case.
441 processor_->WriteProcessed(snd_out_, r);
3b8fe7ef »
2012-09-22 o Remove deadlock
442 stats_mutex_.unlock();
08bbf282 »
2012-09-21 o First shot at gapless. Works pretty well for my test-case.
443 }
31bd9ce1 »
2012-09-10 o README update.
444 if (input_frames_left_ == 0) {
680f5909 »
2012-09-13 o report opening error.
445 Close();
31bd9ce1 »
2012-09-10 o README update.
446 }
0edb358c »
2012-09-10 o attempt to remove global references in the zita config.
447 return input_frames_left_;
6e895e3e »
2012-09-09 o add conversion buffer that uses the virtual file provided
448 }
fd4ed76e »
2012-09-09 o write to a file instead of a buffer.
449
08bbf282 »
2012-09-21 o First shot at gapless. Works pretty well for my test-case.
450 // TODO add as a utility function to ConversionBuffer ?
8e36effd »
2012-09-12 o comment update.
451 void CopyBytes(int fd, off_t pos, ConversionBuffer *out, size_t len) {
7e27dab7 »
2012-09-11 o initial version of copying header. Not sure yet though if
452 char buf[256];
453 while (len > 0) {
454 ssize_t r = pread(fd, buf, std::min(sizeof(buf), len), pos);
8e36effd »
2012-09-12 o comment update.
455 if (r <= 0) return;
7e27dab7 »
2012-09-11 o initial version of copying header. Not sure yet though if
456 out->Append(buf, r);
457 len -= r;
458 pos += r;
459 }
460 }
461
f8cb9e52 »
2012-09-11 o redacting MD5.
462 void CopyFlacHeader(ConversionBuffer *out_buffer) {
7689c9a8 »
2012-09-22 o preparation to extract classes from folve-filesystem into their
463 DLogf("Provide FLAC header from original file %s",
464 base_stats_.filename.c_str());
f8cb9e52 »
2012-09-11 o redacting MD5.
465 out_buffer->Append("fLaC", 4);
7e27dab7 »
2012-09-11 o initial version of copying header. Not sure yet though if
466 off_t pos = 4;
467 unsigned char header[4];
f8cb9e52 »
2012-09-11 o redacting MD5.
468 bool need_finish_padding = false;
7e27dab7 »
2012-09-11 o initial version of copying header. Not sure yet though if
469 while (pread(filedes_, header, sizeof(header), pos) == sizeof(header)) {
470 pos += sizeof(header);
471 bool is_last = header[0] & 0x80;
472 unsigned int type = header[0] & 0x7F;
473 unsigned int byte_len = (header[1] << 16) + (header[2] << 8) + header[3];
5098e077 »
2012-09-20 o improve logging.
474 const char *extra_info = "";
f8cb9e52 »
2012-09-11 o redacting MD5.
475 need_finish_padding = false;
476 if (type == FLAC__METADATA_TYPE_STREAMINFO && byte_len == 34) {
477 out_buffer->Append(&header, sizeof(header));
8e36effd »
2012-09-12 o comment update.
478 // Copy everything but the MD5 at the end - which we set to empty.
f8cb9e52 »
2012-09-11 o redacting MD5.
479 CopyBytes(filedes_, pos, out_buffer, byte_len - 16);
480 for (int i = 0; i < 16; ++i) out_buffer->Append("\0", 1);
5098e077 »
2012-09-20 o improve logging.
481 extra_info = "Streaminfo; redact MD5.";
f8cb9e52 »
2012-09-11 o redacting MD5.
482 }
483 else if (type == FLAC__METADATA_TYPE_SEEKTABLE) {
484 // The SEEKTABLE header we skip, because it is bogus after encoding.
5830c5a6 »
2012-09-17 o Switch logging to syslog()
485 // TODO append log (skip the seektable)
f8cb9e52 »
2012-09-11 o redacting MD5.
486 need_finish_padding = is_last; // if we were last, force finish block.
5098e077 »
2012-09-20 o improve logging.
487 extra_info = "Skip seektable.";
f8cb9e52 »
2012-09-11 o redacting MD5.
488 }
489 else {
490 out_buffer->Append(&header, sizeof(header));
491 CopyBytes(filedes_, pos, out_buffer, byte_len);
7e27dab7 »
2012-09-11 o initial version of copying header. Not sure yet though if
492 }
7689c9a8 »
2012-09-22 o preparation to extract classes from folve-filesystem into their
493 DLogf(" %02x %02x %02x %02x type: %d, len: %6u %s %s ",
494 header[0], header[1], header[2], header[3],
495 type, byte_len, is_last ? "(last)" : "(cont)", extra_info);
f8cb9e52 »
2012-09-11 o redacting MD5.
496 pos += byte_len;
7e27dab7 »
2012-09-11 o initial version of copying header. Not sure yet though if
497 if (is_last)
498 break;
499 }
f8cb9e52 »
2012-09-11 o redacting MD5.
500 if (need_finish_padding) { // if the last block was not is_last: pad.
7689c9a8 »
2012-09-22 o preparation to extract classes from folve-filesystem into their
501 DLogf("write padding");
7e27dab7 »
2012-09-11 o initial version of copying header. Not sure yet though if
502 memset(&header, 0, sizeof(header));
503 header[0] = 0x80 /* is last */ | FLAC__METADATA_TYPE_PADDING;
f8cb9e52 »
2012-09-11 o redacting MD5.
504 out_buffer->Append(&header, sizeof(header));
7e27dab7 »
2012-09-11 o initial version of copying header. Not sure yet though if
505 }
506 }
507
8e36effd »
2012-09-12 o comment update.
508 void GenerateHeaderFromInputFile(ConversionBuffer *out_buffer) {
7689c9a8 »
2012-09-22 o preparation to extract classes from folve-filesystem into their
509 DLogf("Generate header from original ID3-tags.");
8e36effd »
2012-09-12 o comment update.
510 out_buffer->set_sndfile_writes_enabled(true);
511 // Copy ID tags that are supported by sndfile.
512 for (int i = SF_STR_FIRST; i <= SF_STR_LAST; ++i) {
513 const char *s = sf_get_string(snd_in_, i);
514 if (s != NULL) {
515 sf_set_string(snd_out_, i, s);
516 }
517 }
518 }
519
7b499ea6 »
2012-09-22 o Uh, log() is actually ln() and log10f() is log() :)
520 void SaveOutputValues() {
a5c63829 »
2012-09-21 o Remember the max output value in the stats
521 if (processor_) {
522 base_stats_.max_output_value = processor_->max_output_value();
fb3fb9a9 »
2012-09-21 o Log clipping even if we pass on the processor.
523 processor_->ResetMaxValues();
60477fec »
2012-09-18 o Detect output overdrive and give useful message what to do.
524 }
fb3fb9a9 »
2012-09-21 o Log clipping even if we pass on the processor.
525 }
a5c63829 »
2012-09-21 o Remember the max output value in the stats
526
fb3fb9a9 »
2012-09-21 o Log clipping even if we pass on the processor.
527 void Close() {
528 if (snd_out_ == NULL) return; // done.
7b499ea6 »
2012-09-22 o Uh, log() is actually ln() and log10f() is log() :)
529 SaveOutputValues();
a5c63829 »
2012-09-21 o Remember the max output value in the stats
530 if (base_stats_.max_output_value > 1.0) {
531 syslog(LOG_ERR, "Observed output clipping in '%s': "
532 "Max=%.3f; Multiply gain with <= %.5f in %s",
533 base_stats_.filename.c_str(), base_stats_.max_output_value,
534 1.0 / base_stats_.max_output_value, config_path_.c_str());
535 }
7b499ea6 »
2012-09-22 o Uh, log() is actually ln() and log10f() is log() :)
536 delete processor_;
537 processor_ = NULL;
0b0bd338 »
2012-09-20 Fix bug: the last couple of sound-samples were not flushed. We want
538 // We can't disable buffer writes here, because outfile closing will flush
539 // the last couple of sound samples.
680f5909 »
2012-09-13 o report opening error.
540 if (snd_in_) sf_close(snd_in_);
541 if (snd_out_) sf_close(snd_out_);
542 snd_out_ = NULL;
543 close(filedes_);
544 }
545
bea11d6b »
2012-09-15 o Don't use snprintf, but use a new function Appendf() to assemble
546 bool LooksLikeInputIsFlac(const SF_INFO &sndinfo, int filedes) {
547 if ((sndinfo.format & SF_FORMAT_TYPEMASK) != SF_FORMAT_FLAC)
548 return false;
549 // However some files contain flac encoded stuff, but are not flac files
550 // by themselve. So we can't copy headers verbatim. Sanity check header.
551 char flac_magic[4];
552 if (pread(filedes, flac_magic, sizeof(flac_magic), 0) != sizeof(flac_magic))
553 return false;
554 return memcmp(flac_magic, "fLaC", sizeof(flac_magic)) == 0;
555 }
556
08bbf282 »
2012-09-21 o First shot at gapless. Works pretty well for my test-case.
557 FolveFilesystem *const fs_;
e9c07dd6 »
2012-09-08 o add documentation
558 const int filedes_;
7c7bda6f »
2012-09-10 o attempt to create a sndfile for every file, if that fails, fall
559 SNDFILE *const snd_in_;
2fee1d65 »
2012-09-20 o extract sound processor
560 const SF_INFO in_info_;
7c7bda6f »
2012-09-10 o attempt to create a sndfile for every file, if that fails, fall
561 const std::string config_path_;
562
f00e6a47 »
2012-09-22 o Don't do unprotected read on status information such as
563 boost::mutex stats_mutex_;
2fee1d65 »
2012-09-20 o extract sound processor
564 HandlerStats base_stats_; // UI information about current file.
565
566 struct stat file_stat_; // we dynamically report a changing size.
ba7fde84 »
2012-09-12 o implement size estimate. If the user stat() or fstat() the file,
567 off_t start_estimating_size_; // essentially const.
568
6e895e3e »
2012-09-09 o add conversion buffer that uses the virtual file provided
569 bool error_;
bea11d6b »
2012-09-15 o Don't use snprintf, but use a new function Appendf() to assemble
570 bool copy_flac_header_verbatim_;
6e895e3e »
2012-09-09 o add conversion buffer that uses the virtual file provided
571 ConversionBuffer *output_buffer_;
572 SNDFILE *snd_out_;
573
574 // Used in conversion.
2fee1d65 »
2012-09-20 o extract sound processor
575 SoundProcessor *processor_;
6e895e3e »
2012-09-09 o add conversion buffer that uses the virtual file provided
576 int input_frames_left_;
e9c07dd6 »
2012-09-08 o add documentation
577 };
578 } // namespace
579
8c7e5c11 »
2012-09-18 o Only if -D is given, the debug toggle is active in the UI.
580 FolveFilesystem::FolveFilesystem()
08bbf282 »
2012-09-21 o First shot at gapless. Works pretty well for my test-case.
581 : current_cfg_index_(0), debug_ui_enabled_(false), gapless_processing_(false),
3d0ba3c0 »
2012-09-22 o Allow to have two gapless clients in parallel that keep one file
582 open_file_cache_(4), total_file_openings_(0), total_file_reopen_(0) {
8c7e5c11 »
2012-09-18 o Only if -D is given, the debug toggle is active in the UI.
583 config_dirs_.push_back(""); // The first config is special: empty.
584 }
ba7fde84 »
2012-09-12 o implement size estimate. If the user stat() or fstat() the file,
585
08bbf282 »
2012-09-21 o First shot at gapless. Works pretty well for my test-case.
586 FileHandler *FolveFilesystem::CreateFromDescriptor(
587 int filedes,
588 int cfg_idx,
589 const char *fs_path,
590 const std::string &underlying_file) {
8268c37e »
2012-09-15 o Bubble up error messages in status server.
591 HandlerStats file_info;
592 file_info.filename = fs_path;
118a454a »
2012-09-22 o Show the filter used for a particular format.
593 file_info.filter_id = cfg_idx;
ee675fea »
2012-09-17 o Provide a way to select between different filter diretories.
594 if (cfg_idx != 0) {
08bbf282 »
2012-09-21 o First shot at gapless. Works pretty well for my test-case.
595 FileHandler *filter = SndFileHandler::Create(this, filedes, fs_path,
ee675fea »
2012-09-17 o Provide a way to select between different filter diretories.
596 underlying_file,
597 cfg_idx,
598 config_dirs()[cfg_idx],
599 &file_info);
600 if (filter != NULL) return filter;
601 }
8b18938f »
2012-09-09 o some comment changes. Ready to do some flac handling.
602 // Every other file-type is just passed through as is.
f1ef9608 »
2012-09-21 o a bit HTML polishing.
603 return new PassThroughHandler(filedes, cfg_idx, file_info);
ee675fea »
2012-09-17 o Provide a way to select between different filter diretories.
604 }
605
606 std::string FolveFilesystem::CacheKey(int config_idx, const char *fs_path) {
607 std::string result;
608 Appendf(&result, "%d/%s", config_idx, fs_path);
609 return result;
9a8ea565 »
2012-09-12 o add infrastructure to provide Stat() output of currently open
610 }
611
08bbf282 »
2012-09-21 o First shot at gapless. Works pretty well for my test-case.
612 FileHandler *FolveFilesystem::GetOrCreateHandler(const char *fs_path) {
ee675fea »
2012-09-17 o Provide a way to select between different filter diretories.
613 const int config_idx = current_cfg_index_;
614 const std::string cache_key = CacheKey(config_idx, fs_path);
08bbf282 »
2012-09-21 o First shot at gapless. Works pretty well for my test-case.
615 const std::string underlying_file = underlying_dir() + fs_path;
ee675fea »
2012-09-17 o Provide a way to select between different filter diretories.
616 FileHandler *handler = open_file_cache_.FindAndPin(cache_key);
15b305a6 »
2012-09-13 o Build infrastructure to re-use file handler objects. Files seem to
617 if (handler == NULL) {
08bbf282 »
2012-09-21 o First shot at gapless. Works pretty well for my test-case.
618 int filedes = open(underlying_file.c_str(), O_RDONLY);
680f5909 »
2012-09-13 o report opening error.
619 if (filedes < 0)
620 return NULL;
b5c1e960 »
2012-09-14 o more useful output on status server.
621 ++total_file_openings_;
ee675fea »
2012-09-17 o Provide a way to select between different filter diretories.
622 handler = CreateFromDescriptor(filedes, config_idx,
08bbf282 »
2012-09-21 o First shot at gapless. Works pretty well for my test-case.
623 fs_path, underlying_file);
ee675fea »
2012-09-17 o Provide a way to select between different filter diretories.
624 handler = open_file_cache_.InsertPinned(cache_key, handler);
b5c1e960 »
2012-09-14 o more useful output on status server.
625 } else {
626 ++total_file_reopen_;
15b305a6 »
2012-09-13 o Build infrastructure to re-use file handler objects. Files seem to
627 }
628 return handler;
e9c07dd6 »
2012-09-08 o add documentation
629 }
630
a9d3e538 »
2012-09-15 o Found a project name: "Folve". Some renamings because of that.
631 int FolveFilesystem::StatByFilename(const char *fs_path, struct stat *st) {
ee675fea »
2012-09-17 o Provide a way to select between different filter diretories.
632 const std::string cache_key = CacheKey(current_cfg_index_, fs_path);
633 FileHandler *handler = open_file_cache_.FindAndPin(cache_key);
15b305a6 »
2012-09-13 o Build infrastructure to re-use file handler objects. Files seem to
634 if (handler == 0)
9a8ea565 »
2012-09-12 o add infrastructure to provide Stat() output of currently open
635 return -1;
15b305a6 »
2012-09-13 o Build infrastructure to re-use file handler objects. Files seem to
636 ssize_t result = handler->Stat(st);
ee675fea »
2012-09-17 o Provide a way to select between different filter diretories.
637 open_file_cache_.Unpin(cache_key);
15b305a6 »
2012-09-13 o Build infrastructure to re-use file handler objects. Files seem to
638 return result;
9a8ea565 »
2012-09-12 o add infrastructure to provide Stat() output of currently open
639 }
640
ee675fea »
2012-09-17 o Provide a way to select between different filter diretories.
641 void FolveFilesystem::Close(const char *fs_path, const FileHandler *handler) {
642 const std::string cache_key = CacheKey(handler->filter_id(), fs_path);
643 open_file_cache_.Unpin(cache_key);
644 }
645
08bbf282 »
2012-09-21 o First shot at gapless. Works pretty well for my test-case.
646 bool FolveFilesystem::ListDirectory(const std::string &fs_dir,
ac40d857 »
2012-09-21 o Make sure we match the alphabetically next file with
647 const std::string &suffix,
08bbf282 »
2012-09-21 o First shot at gapless. Works pretty well for my test-case.
648 std::set<std::string> *files) {
649 const std::string real_dir = underlying_dir() + fs_dir;
650 DIR *dp = opendir(real_dir.c_str());
651 if (dp == NULL) return false;
652 struct dirent *dent;
653 while ((dent = readdir(dp)) != NULL) {
ac40d857 »
2012-09-21 o Make sure we match the alphabetically next file with
654 if (!folve::HasSuffix(dent->d_name, suffix))
655 continue;
08bbf282 »
2012-09-21 o First shot at gapless. Works pretty well for my test-case.
656 files->insert(fs_dir + dent->d_name);
657 }
658 closedir(dp);
659 return true;
660 }
661
ee675fea »
2012-09-17 o Provide a way to select between different filter diretories.
662 void FolveFilesystem::SwitchCurrentConfigIndex(int i) {
663 if (i < 0 || i >= (int) config_dirs_.size())
664 return;
5830c5a6 »
2012-09-17 o Switch logging to syslog()
665 if (i != current_cfg_index_) {
666 if (i == 0) {
667 syslog(LOG_INFO, "Switching to pass-through mode.");
668 } else {
669 syslog(LOG_INFO, "Switching config directory to '%s'",
670 config_dirs()[i].c_str());
671 }
672 current_cfg_index_ = i;
673 }
e9c07dd6 »
2012-09-08 o add documentation
674 }
fd65cee4 »
2012-09-09 o add basic zita filter.
675
ee675fea »
2012-09-17 o Provide a way to select between different filter diretories.
676 static bool IsDirectory(const std::string &path) {
677 if (path.empty()) return false;
678 struct stat st;
679 if (stat(path.c_str(), &st) != 0)
680 return false;
681 return (st.st_mode & S_IFMT) == S_IFDIR;
682 }
683
684 bool FolveFilesystem::CheckInitialized() {
685 if (underlying_dir().empty()) {
686 fprintf(stderr, "Don't know the underlying directory to read from.\n");
687 return false;
688 }
689 if (!IsDirectory(underlying_dir())) {
690 fprintf(stderr, "<underlying-dir>: '%s' not a directory.\n",
691 underlying_dir().c_str());
692 return false;
693 }
694
695 for (size_t i = 1; i < config_dirs_.size(); ++i) {
696 if (!IsDirectory(config_dirs_[i])) {
697 fprintf(stderr, "<config-dir>: '%s' not a directory.\n",
698 config_dirs_[i].c_str());
699 return false;
700 }
701 }
702 if (config_dirs_.size() > 1) {
703 // By default, lets set the index to the first filter the user provided.
704 SwitchCurrentConfigIndex(1);
705 }
706 return true;
fd65cee4 »
2012-09-09 o add basic zita filter.
707 }
Something went wrong with that request. Please try again.