#define FLAC__VERSION_STRING VERSION
#endif
+/** The vendor string inserted by the encoder into the VORBIS_COMMENT block.
+ * This is a nulL-terminated ASCII string; when inserted into the
+ * VORBIS_COMMENT the trailing null is stripped.
+ */
+extern const FLAC__byte *FLAC__VENDOR_STRING;
+
/** The byte string representation of the beginning of a FLAC stream. */
-extern const FLAC__byte FLAC__STREAM_SYNC_STRING[4]; /* = "fLaC" */;
+extern const FLAC__byte FLAC__STREAM_SYNC_STRING[4]; /* = "fLaC" */
/** The 32-bit integer big-endian representation of the beginning of
* a FLAC stream.
*/
-extern const unsigned FLAC__STREAM_SYNC; /* = 0x664C6143 */;
+extern const unsigned FLAC__STREAM_SYNC; /* = 0x664C6143 */
/** The length of the FLAC signature in bits. */
-extern const unsigned FLAC__STREAM_SYNC_LEN; /* = 32 bits */;
+extern const unsigned FLAC__STREAM_SYNC_LEN; /* = 32 bits */
/** The length of the FLAC signature in bytes. */
#define FLAC__STREAM_SYNC_LENGTH (4u)
FLAC__StreamEncoderState FLAC__stream_encoder_init(FLAC__StreamEncoder *encoder)
{
unsigned i;
+ FLAC__bool metadata_has_seektable, metadata_has_vorbis_comment;
FLAC__ASSERT(0 != encoder);
/* validate metadata */
if(0 == encoder->protected_->metadata && encoder->protected_->num_metadata_blocks > 0)
return encoder->protected_->state = FLAC__STREAM_ENCODER_INVALID_METADATA;
+ metadata_has_seektable = false;
+ metadata_has_vorbis_comment = false;
for(i = 0; i < encoder->protected_->num_metadata_blocks; i++) {
if(encoder->protected_->metadata[i]->type == FLAC__METADATA_TYPE_STREAMINFO)
return encoder->protected_->state = FLAC__STREAM_ENCODER_INVALID_METADATA;
else if(encoder->protected_->metadata[i]->type == FLAC__METADATA_TYPE_SEEKTABLE) {
+ if(metadata_has_seektable) /* only one is allowed */
+ return encoder->protected_->state = FLAC__STREAM_ENCODER_INVALID_METADATA;
+ metadata_has_seektable = true;
if(!FLAC__format_seektable_is_legal(&encoder->protected_->metadata[i]->data.seek_table))
return encoder->protected_->state = FLAC__STREAM_ENCODER_INVALID_METADATA;
}
+ else if(encoder->protected_->metadata[i]->type == FLAC__METADATA_TYPE_VORBIS_COMMENT) {
+ if(metadata_has_vorbis_comment) /* only one is allowed */
+ return encoder->protected_->state = FLAC__STREAM_ENCODER_INVALID_METADATA;
+ metadata_has_vorbis_comment = true;
+ }
}
encoder->private_->input_capacity = 0;
if(encoder->protected_->verify)
encoder->private_->verify.state_hint = ENCODER_IN_METADATA;
encoder->private_->metadata.type = FLAC__METADATA_TYPE_STREAMINFO;
- encoder->private_->metadata.is_last = (encoder->protected_->num_metadata_blocks == 0);
+ encoder->private_->metadata.is_last = false; /* we will have at a minimum a VORBIS_COMMENT afterwards */
encoder->private_->metadata.length = FLAC__STREAM_METADATA_STREAMINFO_LENGTH;
encoder->private_->metadata.data.stream_info.min_blocksize = encoder->protected_->blocksize; /* this encoder uses the same blocksize for the whole stream */
encoder->private_->metadata.data.stream_info.max_blocksize = encoder->protected_->blocksize;
encoder->private_->metadata.data.stream_info.total_samples = 0;
/*
+ * Check to see if the supplied metadata contains a VORBIS_COMMENT;
+ * if not, we will write an empty one (FLAC__add_metadata_block()
+ * automatically supplies the vendor string).
+ */
+ if(!metadata_has_vorbis_comment) {
+ FLAC__StreamMetadata vorbis_comment;
+ vorbis_comment.type = FLAC__METADATA_TYPE_VORBIS_COMMENT;
+ vorbis_comment.is_last = (encoder->protected_->num_metadata_blocks == 0);
+ vorbis_comment.length = 4 + 4; /* MAGIC NUMBER */
+ vorbis_comment.data.vorbis_comment.vendor_string.length = 0;
+ vorbis_comment.data.vorbis_comment.vendor_string.entry = 0;
+ vorbis_comment.data.vorbis_comment.num_comments = 0;
+ vorbis_comment.data.vorbis_comment.comments = 0;
+ if(!FLAC__bitbuffer_clear(encoder->private_->frame)) {
+ encoder->protected_->state = FLAC__STREAM_ENCODER_MEMORY_ALLOCATION_ERROR;
+ return false;
+ }
+ if(!FLAC__add_metadata_block(&vorbis_comment, encoder->private_->frame))
+ return encoder->protected_->state = FLAC__STREAM_ENCODER_FRAMING_ERROR;
+ if(!write_bitbuffer_(encoder, 0)) {
+ /* the above function sets the state for us in case of an error */
+ return encoder->protected_->state;
+ }
+ }
+
+ /*
* write the user's metadata blocks
*/
for(i = 0; i < encoder->protected_->num_metadata_blocks; i++) {
*/
#include <stdio.h>
+#include <string.h> /* for strlen() */
#include "private/stream_encoder_framing.h"
#include "private/crc.h"
#include "FLAC/assert.h"
FLAC__bool FLAC__add_metadata_block(const FLAC__StreamMetadata *metadata, FLAC__BitBuffer *bb)
{
unsigned i;
+ const unsigned vendor_string_length = (unsigned)strlen(FLAC__VENDOR_STRING);
if(!FLAC__bitbuffer_write_raw_uint32(bb, metadata->is_last, FLAC__STREAM_METADATA_IS_LAST_LEN))
return false;
if(!FLAC__bitbuffer_write_raw_uint32(bb, metadata->type, FLAC__STREAM_METADATA_TYPE_LEN))
return false;
- FLAC__ASSERT(metadata->length < (1u << FLAC__STREAM_METADATA_LENGTH_LEN));
- if(!FLAC__bitbuffer_write_raw_uint32(bb, metadata->length, FLAC__STREAM_METADATA_LENGTH_LEN))
+ /*
+ * First, for VORBIS_COMMENTs, adjust the length to reflect our vendor string
+ */
+ i = metadata->length;
+ if(metadata->type == FLAC__METADATA_TYPE_VORBIS_COMMENT) {
+ i -= metadata->data.vorbis_comment.vendor_string.length;
+ i += vendor_string_length;
+ }
+ FLAC__ASSERT(i < (1u << FLAC__STREAM_METADATA_LENGTH_LEN));
+ if(!FLAC__bitbuffer_write_raw_uint32(bb, i, FLAC__STREAM_METADATA_LENGTH_LEN))
return false;
switch(metadata->type) {
}
break;
case FLAC__METADATA_TYPE_VORBIS_COMMENT:
- if(!FLAC__bitbuffer_write_raw_uint32_little_endian(bb, metadata->data.vorbis_comment.vendor_string.length))
+ if(!FLAC__bitbuffer_write_raw_uint32_little_endian(bb, vendor_string_length))
return false;
- if(!FLAC__bitbuffer_write_byte_block(bb, metadata->data.vorbis_comment.vendor_string.entry, metadata->data.vorbis_comment.vendor_string.length))
+ if(!FLAC__bitbuffer_write_byte_block(bb, FLAC__VENDOR_STRING, vendor_string_length))
return false;
if(!FLAC__bitbuffer_write_raw_uint32_little_endian(bb, metadata->data.vorbis_comment.num_comments))
return false;