Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Newer
Older
100644 187 lines (166 sloc) 6.859 kB
4d4d90b @hzeller o More legalese
authored
1 // Folve - A fuse filesystem that convolves audio files on-the-fly.
2 //
6e895e3 @hzeller o add conversion buffer that uses the virtual file provided
authored
3 // Copyright (C) 2012 Henner Zeller <h.zeller@acm.org>
b4eec5c @hzeller o libboost caused too many troubles in embedded systems with weak
authored
4 //
6e895e3 @hzeller o add conversion buffer that uses the virtual file provided
authored
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
a9d3e53 @hzeller o Found a project name: "Folve". Some renamings because of that.
authored
18 #include "conversion-buffer.h"
19
fd4ed76 @hzeller o write to a file instead of a buffer.
authored
20 #include <errno.h>
21 #include <fcntl.h>
6e895e3 @hzeller o add conversion buffer that uses the virtual file provided
authored
22 #include <stdio.h>
b4eec5c @hzeller o libboost caused too many troubles in embedded systems with weak
authored
23 #include <stdlib.h>
6e895e3 @hzeller o add conversion buffer that uses the virtual file provided
authored
24 #include <string.h>
fd4ed76 @hzeller o write to a file instead of a buffer.
authored
25 #include <sys/stat.h>
26 #include <sys/types.h>
27 #include <unistd.h>
6e895e3 @hzeller o add conversion buffer that uses the virtual file provided
authored
28
abb0c42 @hzeller o don't use obsolete tempnam()
authored
29 // Annoyingly, mkstemp() does not do TMPDIR trickery and tempnam() is obsolete.
30 static char *TempNameAllocated(const char *pattern) {
31 const char *tmp_path = getenv("TMPDIR");
32 if (tmp_path == NULL || strlen(tmp_path) == 0) tmp_path = getenv("TMP");
33 if (tmp_path == NULL || strlen(tmp_path) == 0) tmp_path = "/tmp";
34 char *result = (char*) malloc(strlen(tmp_path) + 1 + strlen(pattern) + 1);
35 strcpy(result, tmp_path);
36 strcat(result, "/");
37 strcat(result, pattern);
38 return result;
39 }
40
fd4ed76 @hzeller o write to a file instead of a buffer.
authored
41 ConversionBuffer::ConversionBuffer(SoundSource *source, const SF_INFO &info)
b698853 @hzeller o README updates.
authored
42 : source_(source), out_filedes_(-1), snd_writing_enabled_(true),
fea3d55 @hzeller o Initial implementation of pre-buffering thread.
authored
43 total_written_(0), max_accessed_(0), header_end_(0), file_complete_(false) {
abb0c42 @hzeller o don't use obsolete tempnam()
authored
44 char *filename = TempNameAllocated("folve-XXXXXX");
45 out_filedes_ = mkstemp(filename);
b698853 @hzeller o README updates.
authored
46 if (out_filedes_ < 0) {
0c8561b @hzeller o Handle configuration file error gracefully.
authored
47 perror("Problem opening buffer file");
48 }
fd4ed76 @hzeller o write to a file instead of a buffer.
authored
49 unlink(filename);
abb0c42 @hzeller o don't use obsolete tempnam()
authored
50 free(filename);
fd4ed76 @hzeller o write to a file instead of a buffer.
authored
51
52 // After file-open: SetOutputSoundfile() already might attempt to write data.
7e27dab @hzeller o initial version of copying header. Not sure yet though if
authored
53 source_->SetOutputSoundfile(this, CreateOutputSoundfile(info));
6e895e3 @hzeller o add conversion buffer that uses the virtual file provided
authored
54 }
55
56 ConversionBuffer::~ConversionBuffer() {
b698853 @hzeller o README updates.
authored
57 close(out_filedes_);
6e895e3 @hzeller o add conversion buffer that uses the virtual file provided
authored
58 }
59
60 sf_count_t ConversionBuffer::SndTell(void *userdata) {
fea3d55 @hzeller o Initial implementation of pre-buffering thread.
authored
61 // This will be called within writing, when our mutex is locked. So only
62 // call the version that assumed locked by mutex.
7fa80f5 @hzeller o Don't lock read access to FileSize(). Otherwise we're potentially
authored
63 return reinterpret_cast<ConversionBuffer*>(userdata)->FileSize();
6e895e3 @hzeller o add conversion buffer that uses the virtual file provided
authored
64 }
65 sf_count_t ConversionBuffer::SndWrite(const void *ptr, sf_count_t count,
66 void *userdata) {
7e27dab @hzeller o initial version of copying header. Not sure yet though if
authored
67 return reinterpret_cast<ConversionBuffer*>(userdata)->SndAppend(ptr, count);
6e895e3 @hzeller o add conversion buffer that uses the virtual file provided
authored
68 }
69
70 // These callbacks we don't care about.
f8cb9e5 @hzeller o redacting MD5.
authored
71 static sf_count_t DummySeek(sf_count_t offset, int whence, void *userdata) {
0b0bd33 @hzeller Fix bug: the last couple of sound-samples were not flushed. We want
authored
72 // This seems to be called while closing, probably to modify the
73 // header. But we already have a custom header, so we need to avoid writing
74 // to that position. So as soon as we see this, disable writing,
a1a11d4 @hzeller o update comments
authored
75 // because it would mess up the file.
0b0bd33 @hzeller Fix bug: the last couple of sound-samples were not flushed. We want
authored
76 if (offset > 0) {
77 reinterpret_cast<ConversionBuffer*>(userdata)
78 ->set_sndfile_writes_enabled(false);
79 //fprintf(stderr, "Skipping seek to %lld\n", (long long int) offset);
f8cb9e5 @hzeller o redacting MD5.
authored
80 }
6e895e3 @hzeller o add conversion buffer that uses the virtual file provided
authored
81 return 0;
82 }
83 static sf_count_t DummyRead(void *ptr, sf_count_t count, void *user_data) {
84 fprintf(stderr, "DummyRead called\n");
85 return 0;
86 }
87
fd4ed76 @hzeller o write to a file instead of a buffer.
authored
88 SNDFILE *ConversionBuffer::CreateOutputSoundfile(const SF_INFO &out_info) {
89 SF_INFO info_copy = out_info;
6e895e3 @hzeller o add conversion buffer that uses the virtual file provided
authored
90 SF_VIRTUAL_IO virtual_io;
91 memset(&virtual_io, 0, sizeof(virtual_io));
92 virtual_io.get_filelen = &ConversionBuffer::SndTell;
93 virtual_io.write = &ConversionBuffer::SndWrite;
94 virtual_io.tell = &ConversionBuffer::SndTell;
95 virtual_io.read = &DummyRead;
96 virtual_io.seek = &DummySeek;
fd4ed76 @hzeller o write to a file instead of a buffer.
authored
97 return sf_open_virtual(&virtual_io, SFM_WRITE, &info_copy, this);
6e895e3 @hzeller o add conversion buffer that uses the virtual file provided
authored
98 }
99
fd4ed76 @hzeller o write to a file instead of a buffer.
authored
100 ssize_t ConversionBuffer::Append(const void *data, size_t count) {
b698853 @hzeller o README updates.
authored
101 if (out_filedes_ < 0) return -1;
0115879 @hzeller o less noisy logging.
authored
102 //fprintf(stderr, "Extend horizon by %ld bytes.\n", count);
fd4ed76 @hzeller o write to a file instead of a buffer.
authored
103 int remaining = count;
104 const char *buf = (const char*)data;
105 while (remaining > 0) {
b698853 @hzeller o README updates.
authored
106 int w = write(out_filedes_, data, count);
fd4ed76 @hzeller o write to a file instead of a buffer.
authored
107 if (w < 0) return -errno;
108 remaining -= w;
109 buf += w;
6e895e3 @hzeller o add conversion buffer that uses the virtual file provided
authored
110 }
fd4ed76 @hzeller o write to a file instead of a buffer.
authored
111 total_written_ += count;
6e895e3 @hzeller o add conversion buffer that uses the virtual file provided
authored
112 return count;
113 }
114
a85bebb @hzeller o Fix Flac header manually. Luckily the streamheader is a fixed
authored
115 void ConversionBuffer::WriteCharAt(unsigned char c, off_t offset) {
b698853 @hzeller o README updates.
authored
116 if (out_filedes_ < 0) return;
117 if (pwrite(out_filedes_, &c, 1, offset) != 1) fprintf(stderr, "Oops.");
a85bebb @hzeller o Fix Flac header manually. Luckily the streamheader is a fixed
authored
118 }
119
7e27dab @hzeller o initial version of copying header. Not sure yet though if
authored
120 ssize_t ConversionBuffer::SndAppend(const void *data, size_t count) {
f8cb9e5 @hzeller o redacting MD5.
authored
121 if (!snd_writing_enabled_) return count;
7e27dab @hzeller o initial version of copying header. Not sure yet though if
authored
122 return Append(data, count);
123 }
124
ba7fde8 @hzeller o implement size estimate. If the user stat() or fstat() the file,
authored
125 void ConversionBuffer::HeaderFinished() { header_end_ = FileSize(); }
550c4e0 @hzeller o allow short read in header area, but not in the stream
authored
126
7fa80f5 @hzeller o Don't lock read access to FileSize(). Otherwise we're potentially
authored
127 // The following are rather informal; because we access them in
128 // StatusServer:
129 // FileCacheHandler::GetHandlerStatus() -> ConversionBuffer::FileSize()
130 // PreBufferThread:
131 // ConversionBuffer::FillUntil() -> FileHandlerCache::FindAndPin
132 // We don't lock these values here.
fea3d55 @hzeller o Initial implementation of pre-buffering thread.
authored
133 off_t ConversionBuffer::FileSize() const {
134 return total_written_;
135 }
136
137 off_t ConversionBuffer::MaxAccessed() const {
138 return max_accessed_;
139 }
140
141 bool ConversionBuffer::IsFileComplete() const {
6d7bc9a @hzeller o Don't call conversion buffer FileSize() etc. within status mutex
authored
142 folve::MutexLock l(&mutex_);
fea3d55 @hzeller o Initial implementation of pre-buffering thread.
authored
143 return file_complete_;
144 }
145
146 void ConversionBuffer::FillUntil(off_t requested_min_written) {
147 // As soon as someone tries to read beyond of what we already have, we call
148 // the callback that fills more of it.
149 // We are shared between potentially several open files. Serialize threads.
150 folve::MutexLock l(&mutex_);
151 while (!file_complete_ && total_written_ < requested_min_written) {
152 if (!source_->AddMoreSoundData()) {
153 file_complete_ = true;
154 break;
155 }
156 }
157 }
158
6e895e3 @hzeller o add conversion buffer that uses the virtual file provided
authored
159 ssize_t ConversionBuffer::Read(char *buf, size_t size, off_t offset) {
550c4e0 @hzeller o allow short read in header area, but not in the stream
authored
160 // As long as we're reading only within the header area, allow 'short' reads,
161 // i.e. reads that return less bytes than requested (but up to the headers'
162 // size). That means:
163 // required_min_written = offset + 1; // at least one byte.
164 // That way, we don't need to start the convolver if someone only reads
a1a11d4 @hzeller o update comments
authored
165 // the header: we stop at the header boundary.
550c4e0 @hzeller o allow short read in header area, but not in the stream
authored
166 //
167 // After beginning to read the sound stream, some programs (e.g. kaffeine)
168 // behave finicky if they don't get the full number of bytes they
169 // requested in a read() call (this is a bug in these programs, but we've
170 // to work around it). So that means in that case we make sure that we have
171 // at least the number of bytes available that are requested:
172 // required_min_written = offset + size; // all requested bytes.
173 const off_t required_min_written = offset + (offset >= header_end_ ? size : 1);
174
fea3d55 @hzeller o Initial implementation of pre-buffering thread.
authored
175 FillUntil(required_min_written);
a31e4b5 @hzeller o whitespace.
authored
176
fea3d55 @hzeller o Initial implementation of pre-buffering thread.
authored
177 const ssize_t read_result = pread(out_filedes_, buf, size, offset);
178 if (read_result >= 0) {
179 const off_t new_max_accessed = offset + read_result;
180 if (new_max_accessed > max_accessed_) {
181 folve::MutexLock l(&mutex_);
182 max_accessed_ = new_max_accessed;
183 }
184 }
185 return read_result;
6e895e3 @hzeller o add conversion buffer that uses the virtual file provided
authored
186 }
Something went wrong with that request. Please try again.