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