Commit c9ed238c authored by Josh Coalson's avatar Josh Coalson

Allow MM:SS:FF and MM:SS.SS time formats in non-CD-DA cuesheets (SF#1947353,...

Allow MM:SS:FF and MM:SS.SS time formats in non-CD-DA cuesheets (SF#1947353, SF#2182432: https://sourceforge.net/tracker2/?func=detail&aid=1947353&group_id=13478&atid=363478 https://sourceforge.net/tracker2/index.php?func=detail&aid=2182432&group_id=13478&atid=113478)
parent f36d4567
......@@ -63,6 +63,7 @@
<ul>
<li>The <span class="argument"><a href="documentation_tools_flac.html#flac_options_sector_align">--sector-align</a></span> option of <span class="commandname">flac</span> has been deprecated and may not exist in future versions. <a href="http://www.etree.org/shnutils/shntool/">shntool</a> provides similar functionality.</li>
<li>Support for the RF64 and Wave64 formats in <span class="commandname">flac</span> (see below).</li>
<li>Better handling of cuesheets with non-CD-DA sample rates.</li>
</ul>
</li>
<li>
......@@ -83,6 +84,7 @@
<li>Added support for encoding from and decoding to the RF64 format, and a new corresponding option <span class="argument"><a href="#flac_options_force_rf64_format" />--force-rf64-format</a></span>. (<a href="https://sourceforge.net/tracker2/?func=detail&amp;aid=1762502&amp;group_id=13478&amp;atid=363478">SF #1762502</a>). <span class="argument"><a href="documentation_tools_flac.html#flac_options_keep_foreign_metadata">--keep-foreign-metadata</a></span> is also supported.</li>
<li>Added support for encoding from and decoding to the Sony Wave64 format, and a new corresponding option <span class="argument"><a href="#flac_options_force_wave64_format" />--force-wave64-format</a></span>. (<a href="https://sourceforge.net/tracker2/?func=detail&amp;aid=1769582&amp;group_id=13478&amp;atid=363478">SF #1769582</a>). <span class="argument"><a href="documentation_tools_flac.html#flac_options_keep_foreign_metadata">--keep-foreign-metadata</a></span> is also supported.</li>
<li>Added new options <span class="argument"><a href="documentation_tools_flac.html#flac_options_preserve_modtime">--preserve-modtime</a></span> and <span class="argument"><a href="documentation_tools_flac.html#flac_options_no_preserve_modtime">--no-preserve-modtime</a></span> to specify whether or not output files should copy the timestamp and permissions from their input files. The default is <span class="argument"><a href="documentation_tools_flac.html#flac_options_preserve_modtime">--preserve-modtime</a></span> as in previous versions. (<a href="https://sourceforge.net/tracker2/?func=detail&amp;aid=1805428&amp;group_id=13478&amp;atid=363478">SF #1805428</a>).</li>
<li>Allow MM:SS:FF and MM:SS.SS time formats in non-CD-DA cuesheets. (<a href="https://sourceforge.net/tracker2/?func=detail&amp;aid=1947353&amp;group_id=13478&amp;atid=363478">SF #1947353</a>, <a href="https://sourceforge.net/tracker2/index.php?func=detail&amp;aid=2182432&amp;group_id=13478&amp;atid=113478">SF #2182432</a>)</li>
<li>The <span class="argument"><a href="documentation_tools_flac.html#flac_options_sector_align">--sector-align</a></span> option of <span class="commandname">flac</span> has been deprecated and may not exist in future versions. <a href="http://www.etree.org/shnutils/shntool/">shntool</a> provides similar functionality. (<a href="https://sourceforge.net/tracker2/?func=detail&amp;aid=1805946&amp;group_id=13478&amp;atid=363478">SF #1805946</a>)</li>
<li>Improved error message when user attempts to decode a non-FLAC file (<a href="https://sourceforge.net/tracker2/?func=detail&amp;aid=2222789&amp;group_id=13478&amp;atid=113478">SF #2222789</a>).</li>
<li>Fix bug where <span class="commandname">flac</span> was disallowing use of <span class="argument">--replay-gain</span> when encoding from stdin (<a href="https://sourceforge.net/tracker2/?func=detail&amp;aid=1840124&amp;group_id=13478&amp;atid=113478">SF #1840124</a>).</li>
......@@ -92,7 +94,7 @@
<li>
metaflac:
<ul>
<li>(none)</li>
<li>Allow MM:SS:FF and MM:SS.SS time formats in non-CD-DA cuesheets. (<a href="https://sourceforge.net/tracker2/?func=detail&amp;aid=1947353&amp;group_id=13478&amp;atid=363478">SF #1947353</a>, <a href="https://sourceforge.net/tracker2/index.php?func=detail&amp;aid=2182432&amp;group_id=13478&amp;atid=113478">SF #2182432</a>)</li>
</ul>
</li>
<li>
......
......@@ -31,7 +31,7 @@ extern "C" {
unsigned grabbag__cuesheet_msf_to_frame(unsigned minutes, unsigned seconds, unsigned frames);
void grabbag__cuesheet_frame_to_msf(unsigned frame, unsigned *minutes, unsigned *seconds, unsigned *frames);
FLAC__StreamMetadata *grabbag__cuesheet_parse(FILE *file, const char **error_message, unsigned *last_line_read, FLAC__bool is_cdda, FLAC__uint64 lead_out_offset);
FLAC__StreamMetadata *grabbag__cuesheet_parse(FILE *file, const char **error_message, unsigned *last_line_read, unsigned sample_rate, FLAC__bool is_cdda, FLAC__uint64 lead_out_offset);
void grabbag__cuesheet_emit(FILE *file, const FLAC__StreamMetadata *cuesheet, const char *file_reference);
......
......@@ -162,7 +162,7 @@ static FLAC__bool flac_decoder_eof_callback(const FLAC__StreamDecoder *decoder,
static FLAC__StreamDecoderWriteStatus flac_decoder_write_callback(const FLAC__StreamDecoder *decoder, const FLAC__Frame *frame, const FLAC__int32 * const buffer[], void *client_data);
static void flac_decoder_metadata_callback(const FLAC__StreamDecoder *decoder, const FLAC__StreamMetadata *metadata, void *client_data);
static void flac_decoder_error_callback(const FLAC__StreamDecoder *decoder, FLAC__StreamDecoderErrorStatus status, void *client_data);
static FLAC__bool parse_cuesheet(FLAC__StreamMetadata **cuesheet, const char *cuesheet_filename, const char *inbasefilename, FLAC__bool is_cdda, FLAC__uint64 lead_out_offset, FLAC__bool treat_warnings_as_errors);
static FLAC__bool parse_cuesheet(FLAC__StreamMetadata **cuesheet, const char *cuesheet_filename, const char *inbasefilename, unsigned sample_rate, FLAC__bool is_cdda, FLAC__uint64 lead_out_offset, FLAC__bool treat_warnings_as_errors);
static void print_stats(const EncoderSession *encoder_session);
static void print_error_with_init_status(const EncoderSession *e, const char *message, FLAC__StreamEncoderInitStatus init_status);
static void print_error_with_state(const EncoderSession *e, const char *message);
......@@ -1770,7 +1770,7 @@ FLAC__bool EncoderSession_init_encoder(EncoderSession *e, encode_options_t optio
}
}
if(!parse_cuesheet(&static_metadata.cuesheet, options.cuesheet_filename, e->inbasefilename, is_cdda, e->total_samples_to_encode, e->treat_warnings_as_errors))
if(!parse_cuesheet(&static_metadata.cuesheet, options.cuesheet_filename, e->inbasefilename, sample_rate, is_cdda, e->total_samples_to_encode, e->treat_warnings_as_errors))
return false;
if(!convert_to_seek_table_template(options.requested_seek_points, options.num_requested_seek_points, options.cued_seekpoints? static_metadata.cuesheet : 0, e)) {
......@@ -2560,7 +2560,7 @@ void flac_decoder_error_callback(const FLAC__StreamDecoder *decoder, FLAC__Strea
data->fatal_error = true;
}
FLAC__bool parse_cuesheet(FLAC__StreamMetadata **cuesheet, const char *cuesheet_filename, const char *inbasefilename, FLAC__bool is_cdda, FLAC__uint64 lead_out_offset, FLAC__bool treat_warnings_as_errors)
FLAC__bool parse_cuesheet(FLAC__StreamMetadata **cuesheet, const char *cuesheet_filename, const char *inbasefilename, unsigned sample_rate, FLAC__bool is_cdda, FLAC__uint64 lead_out_offset, FLAC__bool treat_warnings_as_errors)
{
FILE *f;
unsigned last_line_read;
......@@ -2579,7 +2579,7 @@ FLAC__bool parse_cuesheet(FLAC__StreamMetadata **cuesheet, const char *cuesheet_
return false;
}
*cuesheet = grabbag__cuesheet_parse(f, &error_message, &last_line_read, is_cdda, lead_out_offset);
*cuesheet = grabbag__cuesheet_parse(f, &error_message, &last_line_read, sample_rate, is_cdda, lead_out_offset);
fclose(f);
......
......@@ -42,6 +42,9 @@
#include <stdio.h>
#endif
/* OPT: #undef'ing this may improve the speed on some architectures */
#define FLAC__LPC_UNROLLED_FILTER_LOOPS
#ifndef FLAC__INTEGER_ONLY_LIBRARY
#ifndef M_LN2
......@@ -49,9 +52,6 @@
#define M_LN2 0.69314718055994530942
#endif
/* OPT: #undef'ing this may improve the speed on some architectures */
#define FLAC__LPC_UNROLLED_FILTER_LOOPS
void FLAC__lpc_window_data(const FLAC__int32 in[], const FLAC__real window[], FLAC__real out[], unsigned data_len)
{
......
......@@ -29,7 +29,7 @@
#include "share/grabbag.h"
#include "operations_shorthand.h"
static FLAC__bool import_cs_from(const char *filename, FLAC__StreamMetadata **cuesheet, const char *cs_filename, FLAC__bool *needs_write, FLAC__uint64 lead_out_offset, FLAC__bool is_cdda, Argument_AddSeekpoint *add_seekpoint_link);
static FLAC__bool import_cs_from(const char *filename, FLAC__StreamMetadata **cuesheet, const char *cs_filename, FLAC__bool *needs_write, FLAC__uint64 lead_out_offset, unsigned sample_rate, FLAC__bool is_cdda, Argument_AddSeekpoint *add_seekpoint_link);
static FLAC__bool export_cs_to(const char *filename, const FLAC__StreamMetadata *cuesheet, const char *cs_filename);
FLAC__bool do_shorthand_operation__cuesheet(const char *filename, FLAC__Metadata_Chain *chain, const Operation *operation, FLAC__bool *needs_write)
......@@ -39,6 +39,7 @@ FLAC__bool do_shorthand_operation__cuesheet(const char *filename, FLAC__Metadata
FLAC__Metadata_Iterator *iterator = FLAC__metadata_iterator_new();
FLAC__uint64 lead_out_offset = 0;
FLAC__bool is_cdda = false;
unsigned sample_rate = 0;
if(0 == iterator)
die("out of memory allocating iterator");
......@@ -54,7 +55,8 @@ FLAC__bool do_shorthand_operation__cuesheet(const char *filename, FLAC__Metadata
FLAC__metadata_iterator_delete(iterator);
return false;
}
is_cdda = (block->data.stream_info.channels == 1 || block->data.stream_info.channels == 2) && (block->data.stream_info.bits_per_sample == 16) && (block->data.stream_info.sample_rate == 44100);
sample_rate = block->data.stream_info.sample_rate;
is_cdda = (block->data.stream_info.channels == 1 || block->data.stream_info.channels == 2) && (block->data.stream_info.bits_per_sample == 16) && (sample_rate == 44100);
}
else if(block->type == FLAC__METADATA_TYPE_CUESHEET)
cuesheet = block;
......@@ -73,7 +75,7 @@ FLAC__bool do_shorthand_operation__cuesheet(const char *filename, FLAC__Metadata
ok = false;
}
else {
ok = import_cs_from(filename, &cuesheet, operation->argument.import_cuesheet_from.filename, needs_write, lead_out_offset, is_cdda, operation->argument.import_cuesheet_from.add_seekpoint_link);
ok = import_cs_from(filename, &cuesheet, operation->argument.import_cuesheet_from.filename, needs_write, lead_out_offset, sample_rate, is_cdda, operation->argument.import_cuesheet_from.add_seekpoint_link);
if(ok) {
/* append CUESHEET block */
while(FLAC__metadata_iterator_next(iterator))
......@@ -108,7 +110,7 @@ FLAC__bool do_shorthand_operation__cuesheet(const char *filename, FLAC__Metadata
* local routines
*/
FLAC__bool import_cs_from(const char *filename, FLAC__StreamMetadata **cuesheet, const char *cs_filename, FLAC__bool *needs_write, FLAC__uint64 lead_out_offset, FLAC__bool is_cdda, Argument_AddSeekpoint *add_seekpoint_link)
FLAC__bool import_cs_from(const char *filename, FLAC__StreamMetadata **cuesheet, const char *cs_filename, FLAC__bool *needs_write, FLAC__uint64 lead_out_offset, unsigned sample_rate, FLAC__bool is_cdda, Argument_AddSeekpoint *add_seekpoint_link)
{
FILE *f;
const char *error_message;
......@@ -129,7 +131,7 @@ FLAC__bool import_cs_from(const char *filename, FLAC__StreamMetadata **cuesheet,
return false;
}
*cuesheet = grabbag__cuesheet_parse(f, &error_message, &last_line_read, is_cdda, lead_out_offset);
*cuesheet = grabbag__cuesheet_parse(f, &error_message, &last_line_read, sample_rate, is_cdda, lead_out_offset);
if(f != stdin)
fclose(f);
......
......@@ -76,10 +76,11 @@ static FLAC__int64 local__parse_int64_(const char *s)
return ret;
}
/* accept '[0-9]+:[0-9][0-9]?:[0-9][0-9]?', but max second of 59 and max frame of 74, e.g. 0:0:0, 123:45:67
/* accept minute:second:frame syntax of '[0-9]+:[0-9][0-9]?:[0-9][0-9]?', but max second of 59 and max frame of 74, e.g. 0:0:0, 123:45:67
* return sample number or <0 for error
* WATCHOUT: if sample rate is not evenly divisible by 75, the resulting sample number will be approximate
*/
static FLAC__int64 local__parse_msf_(const char *s)
static FLAC__int64 local__parse_msf_(const char *s, unsigned sample_rate)
{
FLAC__int64 ret, field;
char c;
......@@ -96,7 +97,7 @@ static FLAC__int64 local__parse_msf_(const char *s)
return -1;
}
ret = field * 60 * 44100;
ret = field * 60 * sample_rate;
c = *s++;
if(c >= '0' && c <= '9')
......@@ -117,7 +118,7 @@ static FLAC__int64 local__parse_msf_(const char *s)
if(field >= 60)
return -1;
ret += field * 44100;
ret += field * sample_rate;
c = *s++;
if(c >= '0' && c <= '9')
......@@ -139,7 +140,45 @@ static FLAC__int64 local__parse_msf_(const char *s)
if(field >= 75)
return -1;
ret += field * (44100 / 75);
ret += field * (sample_rate / 75);
return ret;
}
/* accept minute:second syntax of '[0-9]+:[0-9][0-9]?{,.[0-9]+}', but second < 60, e.g. 0:0.0, 3:5, 15:31.731
* return sample number or <0 for error
* WATCHOUT: depending on the sample rate, the resulting sample number may be approximate with fractional seconds
*/
static FLAC__int64 local__parse_ms_(const char *s, unsigned sample_rate)
{
FLAC__int64 ret, field;
double x;
char c, *end;
c = *s++;
if(c >= '0' && c <= '9')
field = (c - '0');
else
return -1;
while(':' != (c = *s++)) {
if(c >= '0' && c <= '9')
field = field * 10 + (c - '0');
else
return -1;
}
ret = field * 60 * sample_rate;
s++; /* skip the ':' */
if(strspn(s, "0123456789.") != strlen(s))
return -1;
x = strtod(s, &end);
if(*end || end == s)
return -1;
if(x < 0.0 || x >= 60.0)
return -1;
ret += (FLAC__int64)(x * sample_rate);
return ret;
}
......@@ -198,7 +237,7 @@ static char *local__get_field_(char **s, FLAC__bool allow_quotes)
return p;
}
static FLAC__bool local__cuesheet_parse_(FILE *file, const char **error_message, unsigned *last_line_read, FLAC__StreamMetadata *cuesheet, FLAC__bool is_cdda, FLAC__uint64 lead_out_offset)
static FLAC__bool local__cuesheet_parse_(FILE *file, const char **error_message, unsigned *last_line_read, FLAC__StreamMetadata *cuesheet, unsigned sample_rate, FLAC__bool is_cdda, FLAC__uint64 lead_out_offset)
{
#if defined _MSC_VER || defined __MINGW32__ || defined __EMX__
#define FLAC__STRCASECMP stricmp
......@@ -212,6 +251,13 @@ static FLAC__bool local__cuesheet_parse_(FILE *file, const char **error_message,
FLAC__bool disc_has_catalog = false, track_has_flags = false, track_has_isrc = false, has_forced_leadout = false;
FLAC__StreamMetadata_CueSheet *cs = &cuesheet->data.cue_sheet;
FLAC__ASSERT(!is_cdda || sample_rate == 44100);
/* double protection */
if(is_cdda && sample_rate != 44100) {
*error_message = "CD-DA cuesheet only allowed with 44.1kHz sample rate";
return false;
}
cs->lead_in = is_cdda? 2 * 44100 /* The default lead-in size for CD-DA */ : 0;
cs->is_cd = is_cdda;
......@@ -302,18 +348,28 @@ static FLAC__bool local__cuesheet_parse_(FILE *file, const char **error_message,
*error_message = "INDEX is missing an offset after the index number";
return false;
}
xx = local__parse_msf_(field);
/* first parse as minute:second:frame format */
xx = local__parse_msf_(field, sample_rate);
if(xx < 0) {
/* CD-DA must use only MM:SS:FF format */
if(is_cdda) {
*error_message = "illegal INDEX offset (not of the form MM:SS:FF)";
return false;
}
xx = local__parse_int64_(field);
/* as an extension for non-CD-DA we allow MM:SS.SS or raw sample number */
xx = local__parse_ms_(field, sample_rate);
if(xx < 0) {
*error_message = "illegal INDEX offset";
return false;
xx = local__parse_int64_(field);
if(xx < 0) {
*error_message = "illegal INDEX offset";
return false;
}
}
}
else if(sample_rate % 75) {
*error_message = "illegal INDEX offset (MM:SS:FF form not allowed if sample rate is not a multiple of 75)";
return false;
}
if(is_cdda && cs->num_tracks == 1 && cs->tracks[0].num_indices == 0 && xx != 0) {
*error_message = "first INDEX of first TRACK must have an offset of 00:00:00";
return false;
......@@ -533,7 +589,7 @@ static FLAC__bool local__cuesheet_parse_(FILE *file, const char **error_message,
#undef FLAC__STRCASECMP
}
FLAC__StreamMetadata *grabbag__cuesheet_parse(FILE *file, const char **error_message, unsigned *last_line_read, FLAC__bool is_cdda, FLAC__uint64 lead_out_offset)
FLAC__StreamMetadata *grabbag__cuesheet_parse(FILE *file, const char **error_message, unsigned *last_line_read, unsigned sample_rate, FLAC__bool is_cdda, FLAC__uint64 lead_out_offset)
{
FLAC__StreamMetadata *cuesheet;
......@@ -549,7 +605,7 @@ FLAC__StreamMetadata *grabbag__cuesheet_parse(FILE *file, const char **error_mes
return 0;
}
if(!local__cuesheet_parse_(file, error_message, last_line_read, cuesheet, is_cdda, lead_out_offset)) {
if(!local__cuesheet_parse_(file, error_message, last_line_read, cuesheet, sample_rate, is_cdda, lead_out_offset)) {
FLAC__metadata_object_delete(cuesheet);
return 0;
}
......
......@@ -28,7 +28,7 @@
#include "FLAC/metadata.h"
#include "share/grabbag.h"
static int do_cuesheet(const char *infilename, FLAC__bool is_cdda, FLAC__uint64 lead_out_offset)
static int do_cuesheet(const char *infilename, unsigned sample_rate, FLAC__bool is_cdda, FLAC__uint64 lead_out_offset)
{
FILE *fin, *fout;
const char *error_message;
......@@ -48,7 +48,7 @@ static int do_cuesheet(const char *infilename, FLAC__bool is_cdda, FLAC__uint64
fprintf(stderr, "can't open file %s for reading: %s\n", infilename, strerror(errno));
return 255;
}
if(0 != (cuesheet = grabbag__cuesheet_parse(fin, &error_message, &last_line_read, is_cdda, lead_out_offset))) {
if(0 != (cuesheet = grabbag__cuesheet_parse(fin, &error_message, &last_line_read, sample_rate, is_cdda, lead_out_offset))) {
if(fin != stdin)
fclose(fin);
}
......@@ -80,7 +80,7 @@ static int do_cuesheet(const char *infilename, FLAC__bool is_cdda, FLAC__uint64
fprintf(stderr, "can't open file %s for reading: %s\n", tmpfilename, strerror(errno));
return 255;
}
if(0 != (cuesheet = grabbag__cuesheet_parse(fin, &error_message, &last_line_read, is_cdda, lead_out_offset))) {
if(0 != (cuesheet = grabbag__cuesheet_parse(fin, &error_message, &last_line_read, sample_rate, is_cdda, lead_out_offset))) {
if(fin != stdin)
fclose(fin);
}
......@@ -111,28 +111,32 @@ static int do_cuesheet(const char *infilename, FLAC__bool is_cdda, FLAC__uint64
int main(int argc, char *argv[])
{
FLAC__uint64 lead_out_offset;
unsigned sample_rate;
FLAC__bool is_cdda = false;
const char *usage = "usage: test_cuesheet cuesheet_file lead_out_offset [ cdda ]\n";
const char *usage = "usage: test_cuesheet cuesheet_file lead_out_offset [ [ sample_rate ] cdda ]\n";
if(argc > 1 && 0 == strcmp(argv[1], "-h")) {
printf(usage);
return 0;
}
if(argc < 3 || argc > 4) {
if(argc < 3 || argc > 5) {
fprintf(stderr, usage);
return 255;
}
lead_out_offset = (FLAC__uint64)strtoul(argv[2], 0, 10);
if(argc == 4) {
if(0 == strcmp(argv[3], "cdda"))
is_cdda = true;
else {
fprintf(stderr, usage);
return 255;
if(argc >= 4) {
sample_rate = (unsigned)atoi(argv[3]);
if(argc >= 5) {
if(0 == strcmp(argv[4], "cdda"))
is_cdda = true;
else {
fprintf(stderr, usage);
return 255;
}
}
}
return do_cuesheet(argv[1], is_cdda, lead_out_offset);
return do_cuesheet(argv[1], sample_rate, is_cdda, lead_out_offset);
}
......@@ -111,7 +111,7 @@ rm -f $log
#
for cuesheet in $bad_cuesheets ; do
echo "NEGATIVE $cuesheet" >> $log 2>&1
run_test_cuesheet $cuesheet $good_leadout cdda >> $log 2>&1
run_test_cuesheet $cuesheet $good_leadout 44100 cdda >> $log 2>&1
exit_code=$?
if [ "$exit_code" = 255 ] ; then
die "Error: test script is broken"
......@@ -126,7 +126,7 @@ done
#
for cuesheet in $good_cuesheets ; do
echo "POSITIVE $cuesheet" >> $log 2>&1
run_test_cuesheet $cuesheet $good_leadout cdda >> $log 2>&1
run_test_cuesheet $cuesheet $good_leadout 44100 cdda >> $log 2>&1
exit_code=$?
if [ "$exit_code" = 255 ] ; then
die "Error: test script is broken"
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment