Permalink
Browse files

o write to a file instead of a buffer.

o some more pretty naming.
  • Loading branch information...
1 parent 6e895e3 commit fd4ed76e2ec83255824a82a965f7c6b7e44b1a28 @hzeller committed Sep 10, 2012
Showing with 114 additions and 104 deletions.
  1. +44 −51 conversion-buffer.cc
  2. +39 −30 conversion-buffer.h
  3. +31 −23 convolver.cc
View
95 conversion-buffer.cc
@@ -13,32 +13,44 @@
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
+#include <errno.h>
+#include <fcntl.h>
#include <stdio.h>
#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
#include "conversion-buffer.h"
-ConversionBuffer::ConversionBuffer(int buffer_size, SoundfileFiller *callback)
- : buffer_size_(buffer_size), fill_data_(callback),
- buffer_(new char[buffer_size_]), buffer_pos_(0),
- buffer_global_offset_(0) {
+ConversionBuffer::ConversionBuffer(SoundSource *source, const SF_INFO &info)
+ : source_(source), tmpfile_(-1), total_written_(0) {
+ // We need to be able to skip backwards but we don't want to fill our
+ // memory. So lets create a temporary file.
+ const char *filename = tempnam(NULL, "fuse-");
+ tmpfile_ = open(filename, O_RDWR|O_CREAT|O_NOATIME, S_IRUSR|S_IWUSR);
+ unlink(filename);
+
+ // After file-open: SetOutputSoundfile() already might attempt to write data.
+ source_->SetOutputSoundfile(CreateOutputSoundfile(info));
}
ConversionBuffer::~ConversionBuffer() {
- delete [] buffer_;
+ close(tmpfile_);
}
sf_count_t ConversionBuffer::SndTell(void *userdata) {
return reinterpret_cast<ConversionBuffer*>(userdata)->Tell();
}
sf_count_t ConversionBuffer::SndWrite(const void *ptr, sf_count_t count,
void *userdata) {
- return reinterpret_cast<ConversionBuffer*>(userdata)
- ->RefillBuffer(ptr, count);
+ return reinterpret_cast<ConversionBuffer*>(userdata)->Append(ptr, count);
}
// These callbacks we don't care about.
static sf_count_t DummySeek(sf_count_t offset, int whence, void *user_data) {
+ // This seems to be called after we're closing, probably to change
+ // skipping data or something.
fprintf(stderr, "DummySeek called %ld\n", offset);
return 0;
}
@@ -47,63 +59,44 @@ static sf_count_t DummyRead(void *ptr, sf_count_t count, void *user_data) {
return 0;
}
-SNDFILE *ConversionBuffer::CreateOutputSoundfile(SF_INFO *out_info) {
+SNDFILE *ConversionBuffer::CreateOutputSoundfile(const SF_INFO &out_info) {
+ SF_INFO info_copy = out_info;
SF_VIRTUAL_IO virtual_io;
memset(&virtual_io, 0, sizeof(virtual_io));
virtual_io.get_filelen = &ConversionBuffer::SndTell;
virtual_io.write = &ConversionBuffer::SndWrite;
virtual_io.tell = &ConversionBuffer::SndTell;
virtual_io.read = &DummyRead;
virtual_io.seek = &DummySeek;
- return sf_open_virtual(&virtual_io, SFM_WRITE, out_info, this);
+ return sf_open_virtual(&virtual_io, SFM_WRITE, &info_copy, this);
}
-
-size_t ConversionBuffer::RefillBuffer(const void *data, size_t count) {
- fprintf(stderr, "Fill buffer with %ld bytes\n", count);
- if (buffer_pos_ + count <= buffer_size_) {
- memcpy(buffer_ + buffer_pos_, data, count);
- buffer_pos_ += count;
- } else {
- // Reached end of buffer. Start from the beginning.
- // If we find that people are skipping backwards a bit, we might need
- // to have a double-buffer to always have a bit of 'older' history.
- // Or actually use a temporary file.
- buffer_global_offset_ += buffer_pos_;
- if (count > buffer_size_) {
- // Uh, too much data. Let's trim that a bit.
- count = buffer_size_;
- }
- memcpy(buffer_, data, count);
- buffer_pos_ = count;
+ssize_t ConversionBuffer::Append(const void *data, size_t count) {
+ if (tmpfile_ < 0) return -1;
+ fprintf(stderr, "Extend horizon by %ld bytes.\n", count);
+ int remaining = count;
+ const char *buf = (const char*)data;
+ while (remaining > 0) {
+ int w = write(tmpfile_, data, count);
+ if (w < 0) return -errno;
+ remaining -= w;
+ buf += w;
}
+ total_written_ += count;
return count;
}
ssize_t ConversionBuffer::Read(char *buf, size_t size, off_t offset) {
- if (offset < buffer_global_offset_) {
- fprintf(stderr, "Uh, skipped backwards; %ld vs. %ld\n", offset,
- buffer_global_offset_);
- return -1; // TODO(hzeller): return an errno saying closest problem.
- }
- while (buffer_pos_ == 0 || offset >= Tell()) {
- if (!fill_data_->WriteToSoundfile())
- return 0; // done.
+ const off_t required_min_written = offset + 1;
+ // As soon as someone tries to read beyond of what we already have, we call
+ // our WriteToSoundfile() callback that fills more of it.
+ while (total_written_ < required_min_written) {
+ if (!source_->AddMoreSoundData())
+ break;
}
- if (offset < buffer_global_offset_) {
- fprintf(stderr, "Looks like WriteToSoundfile() filled buffer beyond "
- "expectation; want=%ld, min-pos=%ld\n",
- offset, buffer_global_offset_);
- }
- int bytes_to_write = size;
- const bool not_enough_in_buffer = Tell() - offset < bytes_to_write;
- if (not_enough_in_buffer) {
- bytes_to_write = Tell() - offset;
- }
- const off_t read_pos = offset - buffer_global_offset_;
- memcpy(buf, buffer_ + read_pos, bytes_to_write);
- fprintf(stderr, "Read(%ld @ %ld) = %d %s\n",
- size, offset, bytes_to_write,
- not_enough_in_buffer ? "(buffer now empty)" : "");
- return bytes_to_write;
+ ssize_t result = pread(tmpfile_, buf, size, offset);
+ fprintf(stderr, "Read(%ld @ %ld) = %ld\n",
+ size, offset, result);
+
+ return result;
}
View
69 conversion-buffer.h
@@ -15,48 +15,57 @@
#include <sndfile.h>
-// A buffer that keeps track
+// A file-backed buffer for a SNDFILE, that is only filled on demand via
+// a SoundSource.
+// If Read() is called beyond the current available data, a callback is
+// called to write more into the SNDFILE.
class ConversionBuffer {
public:
- // Callback called by the conversion buffer requesting write to the
- // SNDFILE. Returns 'true', if there is more, 'false' when done.
- class SoundfileFiller {
+ // SoundSource, a instance of which needs to be passed to the
+ // ConversionBuffer.
+ class SoundSource {
public:
- virtual ~SoundfileFiller() {}
- virtual bool WriteToSoundfile() = 0;
+ virtual ~SoundSource() {}
+
+ // The soundfile is set by this conversion buffer and to be filled when
+ // requested. There can be an error in opening the sound-file, in that
+ // case SetOutputSoundfile() will be called with NULL.
+ // Ask sf_strerror() to find out why.
+ virtual void SetOutputSoundfile(SNDFILE *sndfile) = 0;
+
+ // This callback is called by the ConversionBuffer if it needs more data.
+ // Rerturns 'true' if there is more, 'false' if that was the last available
+ // data.
+ virtual bool AddMoreSoundData() = 0;
};
- // Create a conversion buffer that holds "buffer_size" bytes. The
- // "callback" will be called if we need more write operations on
- // the SNDFILE. The user needs to call CreateOutputSoundfile(), otherwise
- // they don't know where to write to :)
- ConversionBuffer(int buffer_size, SoundfileFiller *callback);
+ // Create a conversion buffer that holds "buffer_size" bytes.
+ // The "source" will be informed to what SNDFILE to write to and whenever
+ // this ConversionBuffer lusts for more data (it then calls
+ // AddMoreSoundData()).
+ // Ownership is not taken over for source.
+ ConversionBuffer(SoundSource *source, const SF_INFO &info);
~ConversionBuffer();
- // Create a SNDFILE the user has to write to in the WriteToSoundfile callback.
- // Can be NULL on error (call sf_strerror() to find out why).
- SNDFILE *CreateOutputSoundfile(SF_INFO *info);
-
- // Read data from internal buffer that has been filled by write operations to
- // the SNDFILE.
- // If the internal buffer is exhausted, it will call the WriteToSoundfile
- // callback to fill more into our buffer.
- //
- // Attempts to skip backwards might fail, we only guarantee forward looking.
+ // Read data from buffer. Can block and call the SoundSource first to get
+ // more data if needed.
ssize_t Read(char *buf, size_t size, off_t offset);
- // Current file position.
- off_t Tell() const { return buffer_global_offset_ + buffer_pos_; }
-
private:
static sf_count_t SndTell(void *userdata);
static sf_count_t SndWrite(const void *ptr, sf_count_t count, void *userdata);
- size_t RefillBuffer(const void *data, size_t count);
+ // Append data. Called via the SndWrite() virtual file callback.
+ ssize_t Append(const void *data, size_t count);
+
+ // Current max file position.
+ off_t Tell() const { return total_written_; }
+
+ // Create a SNDFILE the user has to write to in the WriteToSoundfile callback.
+ // Can be NULL on error.
+ SNDFILE *CreateOutputSoundfile(const SF_INFO &info);
- const size_t buffer_size_;
- SoundfileFiller *const fill_data_;
- char *buffer_;
- size_t buffer_pos_;
- off_t buffer_global_offset_; // since the beginning of writing.
+ SoundSource *const source_;
+ int tmpfile_;
+ off_t total_written_;
};
View
54 convolver.cc
@@ -55,7 +55,7 @@ class PassThroughFilter : public FileFilter {
class SndFileFilter :
public FileFilter,
- public ConversionBuffer::SoundfileFiller {
+ public ConversionBuffer::SoundSource {
public:
SndFileFilter(int filedes, const char *path, int chunk_size)
: filedes_(filedes), conversion_chunk_size_(chunk_size), error_(false),
@@ -72,32 +72,19 @@ class SndFileFilter :
fprintf(stderr, "Opening input: %s\n", sf_strerror(NULL));
return;
}
-
+
+ raw_sample_buffer_ = new float[conversion_chunk_size_ * in_info.channels];
+ input_frames_left_ = in_info.frames;
+
+ // Create a conversion buffer that creates a soundfile of a particular
+ // format that we choose here. Essentially we want to have mostly what
+ // our input is.
struct SF_INFO out_info = in_info;
out_info.format = SF_FORMAT_FLAC;
// same number of bits format as input.
out_info.format |= in_info.format & SF_FORMAT_SUBMASK;
out_info.seekable = 0; // no point in making it seekable.
- output_buffer_ = new ConversionBuffer(1 << 20 /* 1 MB */, this);
- snd_out_ = output_buffer_->CreateOutputSoundfile(&out_info);
- if (snd_out_ == NULL) {
- error_ = true;
- fprintf(stderr, "Opening output: %s\n", sf_strerror(NULL));
- return;
- }
-
- // Copy header. Everything else that follows will be stream bytes.
- for (int i = SF_STR_FIRST; i <= SF_STR_LAST; ++i) {
- const char *s = sf_get_string(snd_in_, i);
- if (s != NULL) {
- sf_set_string(snd_out_, i, s);
- }
- }
- sf_command(snd_out_, SFC_UPDATE_HEADER_NOW, NULL, 0);
- fprintf(stderr, "Header copy done.\n");
-
- raw_sample_buffer_ = new float[conversion_chunk_size_ * in_info.channels];
- input_frames_left_ = in_info.frames;
+ output_buffer_ = new ConversionBuffer(this, out_info);
}
virtual ~SndFileFilter() {
@@ -119,14 +106,35 @@ class SndFileFilter :
}
private:
- virtual bool WriteToSoundfile() {
+ virtual void SetOutputSoundfile(SNDFILE *sndfile) {
+ snd_out_ = sndfile;
+ if (snd_out_ == NULL) {
+ error_ = true;
+ fprintf(stderr, "Opening output: %s\n", sf_strerror(NULL));
+ return;
+ }
+ // Copy header. Everything else that follows will be stream bytes.
+ for (int i = SF_STR_FIRST; i <= SF_STR_LAST; ++i) {
+ const char *s = sf_get_string(snd_in_, i);
+ if (s != NULL) {
+ sf_set_string(snd_out_, i, s);
+ }
+ }
+ // Now flush the header: that way if someone only reads the metadata, then
+ // our AddMoreSoundData() is never called.
+ sf_command(snd_out_, SFC_UPDATE_HEADER_NOW, NULL, 0);
+ fprintf(stderr, "Header copy done.\n");
+ }
+
+ virtual bool AddMoreSoundData() {
fprintf(stderr, "** conversion callback **\n");
int r = sf_readf_float(snd_in_, raw_sample_buffer_, conversion_chunk_size_);
// Do convolution here.
sf_writef_float(snd_out_, raw_sample_buffer_, r);
input_frames_left_ -= r;
return (input_frames_left_ > 0);
}
+
const int filedes_;
const int conversion_chunk_size_;
bool error_;

0 comments on commit fd4ed76

Please sign in to comment.