diff --git a/include/FLAC++/encoder.h b/include/FLAC++/encoder.h index 39eddb0d..c5e17093 100644 --- a/include/FLAC++/encoder.h +++ b/include/FLAC++/encoder.h @@ -131,6 +131,7 @@ namespace FLAC { unsigned get_min_residual_partition_order() const; unsigned get_max_residual_partition_order() const; unsigned get_rice_parameter_search_dist() const; + FLAC__uint64 get_total_samples_estimate() const; // Initialize the instance; as with the C interface, // init() should be called after construction and 'set' @@ -221,6 +222,7 @@ namespace FLAC { unsigned get_min_residual_partition_order() const; unsigned get_max_residual_partition_order() const; unsigned get_rice_parameter_search_dist() const; + FLAC__uint64 get_total_samples_estimate() const; // Initialize the instance; as with the C interface, // init() should be called after construction and 'set' @@ -313,6 +315,7 @@ namespace FLAC { unsigned get_min_residual_partition_order() const; unsigned get_max_residual_partition_order() const; unsigned get_rice_parameter_search_dist() const; + FLAC__uint64 get_total_samples_estimate() const; // Initialize the instance; as with the C interface, // init() should be called after construction and 'set' @@ -324,11 +327,11 @@ namespace FLAC { bool process(const FLAC__int32 * const buffer[], unsigned samples); bool process_interleaved(const FLAC__int32 buffer[], unsigned samples); protected: - //@@@@ progress callback + virtual void progress_callback(unsigned current_frame, unsigned total_frames_estimate); ::FLAC__FileEncoder *encoder_; private: - //@@@@ progress callback + static void progress_callback_(const ::FLAC__FileEncoder *encoder, unsigned current_frame, unsigned total_frames_estimate, void *client_data); // Private and undefined so you can't use them: File(const Stream &); diff --git a/include/FLAC/file_encoder.h b/include/FLAC/file_encoder.h index 66b3bdf1..bf43114c 100644 --- a/include/FLAC/file_encoder.h +++ b/include/FLAC/file_encoder.h @@ -150,6 +150,9 @@ typedef struct { struct FLAC__FileEncoderPrivate *private_; /* avoid the C++ keyword 'private' */ } FLAC__FileEncoder; +/*@@@ document: */ +typedef void (*FLAC__FileEncoderProgressCallback)(const FLAC__FileEncoder *encoder, unsigned current_frame, unsigned total_frames_estimate, void *client_data); + /*********************************************************************** * @@ -431,6 +434,41 @@ FLAC__bool FLAC__file_encoder_set_metadata(FLAC__FileEncoder *encoder, FLAC__Str */ FLAC__bool FLAC__file_encoder_set_filename(FLAC__FileEncoder *encoder, const char *value); +/** Set the progress callback. + * The supplied function will be called when the encoder has finished + * writing a frame. The \c total_frames_estimate argument to the callback + * will be based on the value from + * FLAC__file_encoder_set_total_samples_estimate(). + * + * \note + * Unlike most other callbacks, the progress callback is \b not mandatory + * and need not be set before initialization. + * + * \default \c NULL + * \param encoder An encoder instance to set. + * \param value See above. + * \assert + * \code encoder != NULL \endcode + * \code value != NULL \endcode + * \retval FLAC__bool + * \c false if the encoder is already initialized, else \c true. + */ +FLAC__bool FLAC__file_encoder_set_progress_callback(FLAC__FileEncoder *encoder, FLAC__FileEncoderProgressCallback value); + +/** Set the client data to be passed back to callbacks. + * This value will be supplied to callbacks in their \a client_data + * argument. + * + * \default \c NULL + * \param encoder An encoder instance to set. + * \param value See above. + * \assert + * \code encoder != NULL \endcode + * \retval FLAC__bool + * \c false if the encoder is already initialized, else \c true. + */ +FLAC__bool FLAC__file_encoder_set_client_data(FLAC__FileEncoder *encoder, void *value); + /** Get the current encoder state. * * \param encoder An encoder instance to query. @@ -509,7 +547,7 @@ FLAC__bool FLAC__file_encoder_get_loose_mid_side_stereo(const FLAC__FileEncoder * \param encoder An encoder instance to query. * \assert * \code encoder != NULL \endcode - * \retval FLAC__bool + * \retval unsigned * See FLAC__file_encoder_set_channels(). */ unsigned FLAC__file_encoder_get_channels(const FLAC__FileEncoder *encoder); @@ -521,7 +559,7 @@ unsigned FLAC__file_encoder_get_channels(const FLAC__FileEncoder *encoder); * \param encoder An encoder instance to query. * \assert * \code encoder != NULL \endcode - * \retval FLAC__bool + * \retval unsigned * See FLAC__file_encoder_set_bits_per_sample(). */ unsigned FLAC__file_encoder_get_bits_per_sample(const FLAC__FileEncoder *encoder); @@ -533,7 +571,7 @@ unsigned FLAC__file_encoder_get_bits_per_sample(const FLAC__FileEncoder *encoder * \param encoder An encoder instance to query. * \assert * \code encoder != NULL \endcode - * \retval FLAC__bool + * \retval unsigned * See FLAC__file_encoder_set_sample_rate(). */ unsigned FLAC__file_encoder_get_sample_rate(const FLAC__FileEncoder *encoder); @@ -545,7 +583,7 @@ unsigned FLAC__file_encoder_get_sample_rate(const FLAC__FileEncoder *encoder); * \param encoder An encoder instance to query. * \assert * \code encoder != NULL \endcode - * \retval FLAC__bool + * \retval unsigned * See FLAC__file_encoder_set_blocksize(). */ unsigned FLAC__file_encoder_get_blocksize(const FLAC__FileEncoder *encoder); @@ -557,7 +595,7 @@ unsigned FLAC__file_encoder_get_blocksize(const FLAC__FileEncoder *encoder); * \param encoder An encoder instance to query. * \assert * \code encoder != NULL \endcode - * \retval FLAC__bool + * \retval unsigned * See FLAC__file_encoder_set_max_lpc_order(). */ unsigned FLAC__file_encoder_get_max_lpc_order(const FLAC__FileEncoder *encoder); @@ -569,7 +607,7 @@ unsigned FLAC__file_encoder_get_max_lpc_order(const FLAC__FileEncoder *encoder); * \param encoder An encoder instance to query. * \assert * \code encoder != NULL \endcode - * \retval FLAC__bool + * \retval unsigned * See FLAC__file_encoder_set_qlp_coeff_precision(). */ unsigned FLAC__file_encoder_get_qlp_coeff_precision(const FLAC__FileEncoder *encoder); @@ -617,7 +655,7 @@ FLAC__bool FLAC__file_encoder_get_do_exhaustive_model_search(const FLAC__FileEnc * \param encoder An encoder instance to query. * \assert * \code encoder != NULL \endcode - * \retval FLAC__bool + * \retval unsigned * See FLAC__file_encoder_set_min_residual_partition_order(). */ unsigned FLAC__file_encoder_get_min_residual_partition_order(const FLAC__FileEncoder *encoder); @@ -629,7 +667,7 @@ unsigned FLAC__file_encoder_get_min_residual_partition_order(const FLAC__FileEnc * \param encoder An encoder instance to query. * \assert * \code encoder != NULL \endcode - * \retval FLAC__bool + * \retval unsigned * See FLAC__file_encoder_set_max_residual_partition_order(). */ unsigned FLAC__file_encoder_get_max_residual_partition_order(const FLAC__FileEncoder *encoder); @@ -641,11 +679,23 @@ unsigned FLAC__file_encoder_get_max_residual_partition_order(const FLAC__FileEnc * \param encoder An encoder instance to query. * \assert * \code encoder != NULL \endcode - * \retval FLAC__bool + * \retval unsigned * See FLAC__file_encoder_set_rice_parameter_search_dist(). */ unsigned FLAC__file_encoder_get_rice_parameter_search_dist(const FLAC__FileEncoder *encoder); +/** Get the previously set estimate of the total samples to be encoded. + * This is inherited from FLAC__SeekableStreamEncoder; see + * FLAC__seekable_stream_encoder_get_total_samples_estimate(). + * + * \param encoder An encoder instance to query. + * \assert + * \code encoder != NULL \endcode + * \retval FLAC__uint64 + * See FLAC__file_encoder_set_total_samples_estimate(). + */ +FLAC__uint64 FLAC__file_encoder_get_total_samples_estimate(const FLAC__FileEncoder *encoder); + /** Initialize the encoder instance. * Should be called after FLAC__file_encoder_new() and * FLAC__file_encoder_set_*() but before FLAC__file_encoder_process() diff --git a/src/libFLAC++/file_encoder.cc b/src/libFLAC++/file_encoder.cc index 9d24cfe2..5723ae96 100644 --- a/src/libFLAC++/file_encoder.cc +++ b/src/libFLAC++/file_encoder.cc @@ -256,9 +256,17 @@ namespace FLAC { return ::FLAC__file_encoder_get_rice_parameter_search_dist(encoder_); } + FLAC__uint64 File::get_total_samples_estimate() const + { + FLAC__ASSERT(is_valid()); + return ::FLAC__file_encoder_get_total_samples_estimate(encoder_); + } + File::State File::init() { FLAC__ASSERT(is_valid()); + ::FLAC__file_encoder_set_progress_callback(encoder_, progress_callback_); + ::FLAC__file_encoder_set_client_data(encoder_, (void*)this); return State(::FLAC__file_encoder_init(encoder_)); } @@ -280,5 +288,19 @@ namespace FLAC { return (bool)::FLAC__file_encoder_process_interleaved(encoder_, buffer, samples); } + void File::progress_callback(unsigned current_frame, unsigned total_frames_estimate) + { + (void)current_frame, (void)total_frames_estimate; + } + + void File::progress_callback_(const ::FLAC__FileEncoder *encoder, unsigned current_frame, unsigned total_frames_estimate, void *client_data) + { + (void)encoder; + FLAC__ASSERT(0 != client_data); + File *instance = reinterpret_cast(client_data); + FLAC__ASSERT(0 != instance); + instance->progress_callback(current_frame, total_frames_estimate); + } + }; }; diff --git a/src/libFLAC/file_encoder.c b/src/libFLAC/file_encoder.c index 3bfbaab3..01bf3c49 100644 --- a/src/libFLAC/file_encoder.c +++ b/src/libFLAC/file_encoder.c @@ -41,9 +41,12 @@ static FLAC__StreamEncoderWriteStatus write_callback_(const FLAC__SeekableStream ***********************************************************************/ typedef struct FLAC__FileEncoderPrivate { - FILE *file; + FLAC__FileEncoderProgressCallback progress_callback; + void *client_data; char *filename; + unsigned total_frames_estimate; FLAC__SeekableStreamEncoder *seekable_stream_encoder; + FILE *file; } FLAC__FileEncoderPrivate; /*********************************************************************** @@ -154,6 +157,13 @@ FLAC__FileEncoderState FLAC__file_encoder_init(FLAC__FileEncoder *encoder) if(FLAC__seekable_stream_encoder_init(encoder->private_->seekable_stream_encoder) != FLAC__SEEKABLE_STREAM_ENCODER_OK) return encoder->protected_->state = FLAC__FILE_ENCODER_SEEKABLE_STREAM_ENCODER_ERROR; + { + unsigned blocksize = FLAC__file_encoder_get_blocksize(encoder); + + FLAC__ASSERT(blocksize != 0); + encoder->private_->total_frames_estimate = (FLAC__file_encoder_get_total_samples_estimate(encoder) + blocksize - 1) / blocksize; + } + return encoder->protected_->state = FLAC__FILE_ENCODER_OK; } @@ -392,6 +402,28 @@ FLAC__bool FLAC__file_encoder_set_filename(FLAC__FileEncoder *encoder, const cha return true; } +FLAC__bool FLAC__file_encoder_set_progress_callback(FLAC__FileEncoder *encoder, FLAC__FileEncoderProgressCallback value) +{ + FLAC__ASSERT(0 != encoder); + FLAC__ASSERT(0 != encoder->private_); + FLAC__ASSERT(0 != encoder->protected_); + if(encoder->protected_->state != FLAC__FILE_ENCODER_UNINITIALIZED) + return false; + encoder->private_->progress_callback = value; + return true; +} + +FLAC__bool FLAC__file_encoder_set_client_data(FLAC__FileEncoder *encoder, void *value) +{ + FLAC__ASSERT(0 != encoder); + FLAC__ASSERT(0 != encoder->private_); + FLAC__ASSERT(0 != encoder->protected_); + if(encoder->protected_->state != FLAC__FILE_ENCODER_UNINITIALIZED) + return false; + encoder->private_->client_data = value; + return true; +} + FLAC__FileEncoderState FLAC__file_encoder_get_state(const FLAC__FileEncoder *encoder) { FLAC__ASSERT(0 != encoder); @@ -518,6 +550,13 @@ unsigned FLAC__file_encoder_get_rice_parameter_search_dist(const FLAC__FileEncod return FLAC__seekable_stream_encoder_get_rice_parameter_search_dist(encoder->private_->seekable_stream_encoder); } +FLAC__uint64 FLAC__file_encoder_get_total_samples_estimate(const FLAC__FileEncoder *encoder) +{ + FLAC__ASSERT(0 != encoder); + FLAC__ASSERT(0 != encoder->private_); + return FLAC__seekable_stream_encoder_get_total_samples_estimate(encoder->private_->seekable_stream_encoder); +} + FLAC__bool FLAC__file_encoder_process(FLAC__FileEncoder *encoder, const FLAC__int32 * const buffer[], unsigned samples) { FLAC__ASSERT(0 != encoder); @@ -545,6 +584,9 @@ void set_defaults_(FLAC__FileEncoder *encoder) FLAC__ASSERT(0 != encoder); FLAC__ASSERT(0 != encoder->private_); + encoder->private_->progress_callback = 0; + encoder->private_->client_data = 0; + encoder->private_->total_frames_estimate = 0; encoder->private_->filename = 0; } @@ -570,8 +612,11 @@ FLAC__StreamEncoderWriteStatus write_callback_(const FLAC__SeekableStreamEncoder FLAC__ASSERT(0 != file_encoder); - if(fwrite(buffer, sizeof(FLAC__byte), bytes, file_encoder->private_->file) == bytes) + if(fwrite(buffer, sizeof(FLAC__byte), bytes, file_encoder->private_->file) == bytes) { + if(0 != file_encoder->private_->progress_callback) + file_encoder->private_->progress_callback(file_encoder, current_frame, file_encoder->private_->total_frames_estimate, file_encoder->private_->client_data); return FLAC__STREAM_ENCODER_WRITE_STATUS_OK; + } else return FLAC__STREAM_ENCODER_WRITE_STATUS_FATAL_ERROR; } diff --git a/src/test_libFLAC++/encoders.cc b/src/test_libFLAC++/encoders.cc index 1175e577..854ed702 100644 --- a/src/test_libFLAC++/encoders.cc +++ b/src/test_libFLAC++/encoders.cc @@ -377,6 +377,13 @@ static bool test_stream_encoder() } printf("OK\n"); + printf("testing get_total_samples_estimate()... "); + if(encoder->get_total_samples_estimate() != streaminfo_.data.stream_info.total_samples) { + printf("FAILED, expected %llu, got %llu\n", streaminfo_.data.stream_info.total_samples, encoder->get_total_samples_estimate()); + return false; + } + printf("OK\n"); + /* init the dummy sample buffer */ for(i = 0; i < sizeof(samples) / sizeof(FLAC__int32); i++) samples[i] = i & 7; @@ -673,6 +680,13 @@ static bool test_seekable_stream_encoder() } printf("OK\n"); + printf("testing get_total_samples_estimate()... "); + if(encoder->get_total_samples_estimate() != streaminfo_.data.stream_info.total_samples) { + printf("FAILED, expected %llu, got %llu\n", streaminfo_.data.stream_info.total_samples, encoder->get_total_samples_estimate()); + return false; + } + printf("OK\n"); + /* init the dummy sample buffer */ for(i = 0; i < sizeof(samples) / sizeof(FLAC__int32); i++) samples[i] = i & 7; @@ -706,11 +720,16 @@ public: ~FileEncoder() { } // from FLAC::Encoder::File - //@@@@ progress callback + void progress_callback(unsigned current_frame, unsigned total_frames_estimate); bool die(const char *msg = 0) const; }; +void FileEncoder::progress_callback(unsigned current_frame, unsigned total_frames_estimate) +{ + (void)current_frame, (void)total_frames_estimate; +} + bool FileEncoder::die(const char *msg) const { State state = get_state(); @@ -967,6 +986,13 @@ static bool test_file_encoder() } printf("OK\n"); + printf("testing get_total_samples_estimate()... "); + if(encoder->get_total_samples_estimate() != streaminfo_.data.stream_info.total_samples) { + printf("FAILED, expected %llu, got %llu\n", streaminfo_.data.stream_info.total_samples, encoder->get_total_samples_estimate()); + return false; + } + printf("OK\n"); + /* init the dummy sample buffer */ for(i = 0; i < sizeof(samples) / sizeof(FLAC__int32); i++) samples[i] = i & 7; diff --git a/src/test_libFLAC/encoders.c b/src/test_libFLAC/encoders.c index 4b6f0181..5660b82d 100644 --- a/src/test_libFLAC/encoders.c +++ b/src/test_libFLAC/encoders.c @@ -413,6 +413,13 @@ static FLAC__bool test_stream_encoder() } printf("OK\n"); + printf("testing FLAC__stream_encoder_get_total_samples_estimate()... "); + if(FLAC__stream_encoder_get_total_samples_estimate(encoder) != streaminfo_.data.stream_info.total_samples) { + printf("FAILED, expected %llu, got %llu\n", streaminfo_.data.stream_info.total_samples, FLAC__stream_encoder_get_total_samples_estimate(encoder)); + return false; + } + printf("OK\n"); + /* init the dummy sample buffer */ for(i = 0; i < sizeof(samples) / sizeof(FLAC__int32); i++) samples[i] = i & 7; @@ -682,6 +689,13 @@ static FLAC__bool test_seekable_stream_encoder() } printf("OK\n"); + printf("testing FLAC__seekable_stream_encoder_get_total_samples_estimate()... "); + if(FLAC__seekable_stream_encoder_get_total_samples_estimate(encoder) != streaminfo_.data.stream_info.total_samples) { + printf("FAILED, expected %llu, got %llu\n", streaminfo_.data.stream_info.total_samples, FLAC__seekable_stream_encoder_get_total_samples_estimate(encoder)); + return false; + } + printf("OK\n"); + /* init the dummy sample buffer */ for(i = 0; i < sizeof(samples) / sizeof(FLAC__int32); i++) samples[i] = i & 7; @@ -709,6 +723,11 @@ static FLAC__bool test_seekable_stream_encoder() return true; } +static void file_encoder_progress_callback_(const FLAC__FileEncoder *encoder, unsigned current_frame, unsigned total_frames_estimate, void *client_data) +{ + (void)encoder, (void)current_frame, (void)total_frames_estimate, (void)client_data; +} + static FLAC__bool test_file_encoder() { FLAC__FileEncoder *encoder; @@ -817,6 +836,16 @@ static FLAC__bool test_file_encoder() return die_f_("returned false", encoder); printf("OK\n"); + printf("testing FLAC__file_encoder_set_progress_callback()... "); + if(!FLAC__file_encoder_set_progress_callback(encoder, file_encoder_progress_callback_)) + return die_f_("returned false", encoder); + printf("OK\n"); + + printf("testing FLAC__file_encoder_set_client_data()... "); + if(!FLAC__file_encoder_set_client_data(encoder, 0)) + return die_f_("returned false", encoder); + printf("OK\n"); + printf("testing FLAC__file_encoder_init()... "); if(FLAC__file_encoder_init(encoder) != FLAC__FILE_ENCODER_OK) return die_f_(0, encoder); @@ -929,6 +958,13 @@ static FLAC__bool test_file_encoder() } printf("OK\n"); + printf("testing FLAC__file_encoder_get_total_samples_estimate()... "); + if(FLAC__file_encoder_get_total_samples_estimate(encoder) != streaminfo_.data.stream_info.total_samples) { + printf("FAILED, expected %llu, got %llu\n", streaminfo_.data.stream_info.total_samples, FLAC__file_encoder_get_total_samples_estimate(encoder)); + return false; + } + printf("OK\n"); + /* init the dummy sample buffer */ for(i = 0; i < sizeof(samples) / sizeof(FLAC__int32); i++) samples[i] = i & 7;