Permalink
Browse files

o initial version of copying header. Not sure yet though if

  it is according to spec: kaffeine does not know how to play the
  stream.
  • Loading branch information...
1 parent 2f12396 commit 7e27dab7b7d48f4e6d96da56b4481ddbcfc17b48 @hzeller committed Sep 11, 2012
Showing with 95 additions and 14 deletions.
  1. +12 −4 conversion-buffer.cc
  2. +14 −4 conversion-buffer.h
  3. +69 −6 convolver.cc
View
@@ -24,15 +24,15 @@
#include "conversion-buffer.h"
ConversionBuffer::ConversionBuffer(SoundSource *source, const SF_INFO &info)
- : source_(source), tmpfile_(-1), total_written_(0) {
+ : source_(source), tmpfile_(-1), snd_writing_enabled_(true), 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));
+ source_->SetOutputSoundfile(this, CreateOutputSoundfile(info));
}
ConversionBuffer::~ConversionBuffer() {
@@ -44,7 +44,7 @@ sf_count_t ConversionBuffer::SndTell(void *userdata) {
}
sf_count_t ConversionBuffer::SndWrite(const void *ptr, sf_count_t count,
void *userdata) {
- return reinterpret_cast<ConversionBuffer*>(userdata)->Append(ptr, count);
+ return reinterpret_cast<ConversionBuffer*>(userdata)->SndAppend(ptr, count);
}
// These callbacks we don't care about.
@@ -53,7 +53,7 @@ static sf_count_t DummySeek(sf_count_t offset, int whence, void *user_data) {
// header. It actually attempts to write that end up at the end of the
// file. We don't care, it is not accessed for reading anymore.
// TODO(hzeller): Suppress writing after close() and really warn
- //fprintf(stderr, "DummySeek called %ld\n", offset);
+ fprintf(stderr, "DummySeek called %ld\n", offset);
return 0;
}
static sf_count_t DummyRead(void *ptr, sf_count_t count, void *user_data) {
@@ -88,6 +88,14 @@ ssize_t ConversionBuffer::Append(const void *data, size_t count) {
return count;
}
+ssize_t ConversionBuffer::SndAppend(const void *data, size_t count) {
+ if (!snd_writing_enabled_) {
+ fprintf(stderr, "Skipping %ld\n", count);
+ return count;
+ }
+ return Append(data, count);
+}
+
ssize_t ConversionBuffer::Read(char *buf, size_t size, off_t offset) {
const off_t required_min_written = offset + 1;
// As soon as someone tries to read beyond of what we already have, we call
View
@@ -33,7 +33,7 @@ class ConversionBuffer {
// Ask sf_strerror() to find out why.
// Ownership is passed to the SoundSource, receiver needs to
// sf_close() the file.
- virtual void SetOutputSoundfile(SNDFILE *sndfile) = 0;
+ virtual void SetOutputSoundfile(ConversionBuffer *parent, 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
@@ -53,21 +53,31 @@ class ConversionBuffer {
// more data if needed.
ssize_t Read(char *buf, size_t size, off_t offset);
+ // Append data. Usually called via the SndWrite() virtual-SNFFILE callback,
+ // but can be used to write raw data as well.
+ ssize_t Append(const void *data, size_t count);
+
+ // Enable writing. If set to 'false', no writing through the SNDFILE is
+ // making it through. To be used to suppress writing of the header or
+ // footer if we want to handle that on our own.
+ void allow_sndfile_writes(bool b) { snd_writing_enabled_ = b; }
+
private:
static sf_count_t SndTell(void *userdata);
static sf_count_t SndWrite(const void *ptr, sf_count_t count, void *userdata);
- // 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_; }
+ // Append for the SndWrite callback.
+ ssize_t SndAppend(const void *data, size_t count);
+
// Create a SNDFILE the user has to write to in the WriteToSoundfile callback.
// Can be NULL on error.
SNDFILE *CreateOutputSoundfile(const SF_INFO &info);
SoundSource *const source_;
int tmpfile_;
+ bool snd_writing_enabled_;
off_t total_written_;
};
View
@@ -19,6 +19,7 @@
#include <string.h>
#include <strings.h>
#include <unistd.h>
+#include <FLAC/metadata.h>
#include <string>
@@ -123,6 +124,10 @@ class SndFileFilter :
error_(false), output_buffer_(NULL), channels_(0), snd_out_(NULL),
raw_sample_buffer_(NULL), input_frames_left_(0) {
+ // The flac header we get is more rich than what we can do
+ // with sndfile. In that case, just copy that.
+ copy_flac_header_ = (in_info.format & SF_FORMAT_TYPEMASK) == SF_FORMAT_FLAC;
+
// Initialize zita config, but don't allocate converter quite yet.
memset(&zita_, 0, sizeof(zita_));
zita_.fsamp = in_info.samplerate;
@@ -158,23 +163,31 @@ class SndFileFilter :
output_buffer_ = new ConversionBuffer(this, out_info);
}
- virtual void SetOutputSoundfile(SNDFILE *sndfile) {
+ virtual void SetOutputSoundfile(ConversionBuffer *parent,
+ 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);
+ if (copy_flac_header_) {
+ parent->allow_sndfile_writes(false);
+ CopyFlacHeader(parent);
+ } else {
+ parent->allow_sndfile_writes(true);
+ // Copy strings. 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);
+ parent->allow_sndfile_writes(true);
fprintf(stderr, "Header init done.\n");
}
@@ -229,11 +242,61 @@ class SndFileFilter :
return input_frames_left_;
}
+ off_t CopyBytes(int fd, off_t pos, ConversionBuffer *out, size_t len) {
+ char buf[256];
+ while (len > 0) {
+ //fprintf(stderr, "read at %ld\n", pos);
+ ssize_t r = pread(fd, buf, std::min(sizeof(buf), len), pos);
+ if (r <= 0) return pos;
+ //fprintf(stderr, "append %ld bytes\n", r);
+ out->Append(buf, r);
+ len -= r;
+ pos += r;
+ }
+ return pos;
+ }
+
+ void CopyFlacHeader(ConversionBuffer *parent) {
+ fprintf(stderr, "Copy raw flac header\n");
+ parent->Append("fLaC", 4);
+ off_t pos = 4;
+ unsigned char header[4];
+ bool need_padding = false;
+ while (pread(filedes_, header, sizeof(header), pos) == sizeof(header)) {
+ pos += sizeof(header);
+ bool is_last = header[0] & 0x80;
+ unsigned int type = header[0] & 0x7F;
+ unsigned int byte_len = (header[1] << 16) + (header[2] << 8) + header[3];
+ fprintf(stderr, " type: %d, len: %u %s ", type,
+ byte_len, is_last ? "(last-header)" : "(cont)");
+ // The SEEKTABLE header we skip, because it is bogus after encoding.
+ if (type == FLAC__METADATA_TYPE_SEEKTABLE) {
+ fprintf(stderr, " (this is the seektable; skipped)\n");
+ pos += byte_len;
+ need_padding = is_last; // if we were last, force finish block.
+ } else {
+ parent->Append(&header, sizeof(header));
+ pos = CopyBytes(filedes_, pos, parent, byte_len);
+ need_padding = false;
+ fprintf(stderr, " (ok)\n");
+ }
+ if (is_last)
+ break;
+ }
+ if (need_padding) { // if the last block was not is_last: pad.
+ fprintf(stderr, "write padding\n");
+ memset(&header, 0, sizeof(header));
+ header[0] = 0x80 /* is last */ | FLAC__METADATA_TYPE_PADDING;
+ parent->Append(&header, sizeof(header));
+ }
+ }
+
const int filedes_;
SNDFILE *const snd_in_;
const std::string config_path_;
bool error_;
+ bool copy_flac_header_;
ConversionBuffer *output_buffer_;
int channels_;
SNDFILE *snd_out_;

0 comments on commit 7e27dab

Please sign in to comment.