return song;
}
-int nsub_write(FILE *out, song_t *song, NSUB_FORMAT fmt, int apply_offset) {
+int nsub_write(FILE *out, song_t *song, NSUB_FORMAT fmt, int apply_offset,
+ int add_offset, double conv) {
switch (fmt) {
case NSUB_FMT_LRC:
- return nsub_write_lrc(out, song, fmt, apply_offset);
+ return nsub_write_lrc(out, song, fmt, apply_offset,
+ add_offset, conv);
case NSUB_FMT_WEBVTT:
- return nsub_write_webvtt(out, song, fmt, apply_offset);
+ return nsub_write_webvtt(out, song, fmt, apply_offset,
+ add_offset, conv);
case NSUB_FMT_SRT:
- return nsub_write_srt(out, song, fmt, apply_offset);
+ return nsub_write_srt(out, song, fmt, apply_offset,
+ add_offset, conv);
default:
fprintf(stderr, "Unsupported write format %d\n", fmt);
return 0;
return 1;
}
+
+int apply_conv(int time, double conv) {
+ // Just so we don't require -lm...
+ double tmp = time * conv;
+ double diff = tmp - (int)tmp;
+
+ if (diff >= 0.5)
+ return (int)tmp + 1;
+ return (int)tmp;
+}
+
*/
int nsub_is_timing(const char line[], char deci_sym, int max_deci);
+/**
+ * Apply a conversion ratio to the given time.
+ *
+ * @param time the initial time to compute from
+ * @param conv the conversion ratio to apply (i.e., 1 = no conversion)
+ *
+ * @return the converted time
+ */
+int apply_conv(int time, double conv);
+
song_t *nsub_read(FILE *in, NSUB_FORMAT fmt);
int nsub_read_lrc(song_t *song, char *line);
int nsub_read_webvtt(song_t *song, char *line);
/* Write */
-int nsub_write(FILE *out, song_t *song, NSUB_FORMAT fmt, int apply_offset);
-int nsub_write_lrc(FILE *out, song_t *song, NSUB_FORMAT fmt, int apply_offset);
+// conv = time conversion ratio
+int nsub_write(FILE *out, song_t *song, NSUB_FORMAT fmt, int apply_offset,
+ int add_offset, double conv);
+int nsub_write_lrc(FILE *out, song_t *song, NSUB_FORMAT fmt, int apply_offset,
+ int add_offset, double conv);
int nsub_write_webvtt(FILE *out, song_t *song, NSUB_FORMAT fmt,
- int apply_offset);
-int nsub_write_srt(FILE *out, song_t *song, NSUB_FORMAT fmt, int apply_offset);
+ int apply_offset, int add_offset, double conv);
+int nsub_write_srt(FILE *out, song_t *song, NSUB_FORMAT fmt, int apply_offset,
+ int add_offset, double conv);
#endif /* NSUB_H */
char *in_file = NULL;
char *out_file = NULL;
int apply_offset = 0;
+ int add_offset = 0;
+ double conv = 1;
if (argc <= 1) {
help(argv[0]);
} else if (!strcmp("--from", arg) || !strcmp("-f", arg)) {
if (i + 1 >= argc) {
fprintf(stderr,
- "The parameter --from/-f requires an argument\n");
+ "The parameter --from/-f "
+ "requires an argument\n"
+ );
return 5;
}
from = nsub_parse_fmt(argv[++i], 1);
if (from == NSUB_FMT_ERROR) {
- fprintf(stderr, "Unsupported input format: %s\n", argv[i]);
+ fprintf(stderr,
+ "Unsupported input "
+ "format: %s\n", argv[i]
+ );
return 8;
}
} else if (!strcmp("--to", arg) || !strcmp("-t", arg)) {
if (i + 1 >= argc) {
- fprintf(stderr, "The parameter --to/-t requires an argument\n");
+ fprintf(stderr,
+ "The parameter --to/-t "
+ "requires an argument\n"
+ );
return 5;
}
to = nsub_parse_fmt(argv[++i], 1);
if (to == NSUB_FMT_ERROR) {
- fprintf(stderr, "Unsupported output format: %s\n", argv[i]);
+ fprintf(stderr,
+ "Unsupported output "
+ "format: %s\n", argv[i]
+ );
return 9;
}
- } else if (!strcmp("--apply-offset", arg) || !strcmp("-a", arg)) {
+ } else if (!strcmp("--apply-offset", arg)
+ || !strcmp("-a", arg)) {
apply_offset = 1;
+ } else if (!strcmp("--ntsc", arg)
+ || !strcmp("-n", arg)) {
+ conv = 25.00 / 29.97;
+ } else if (!strcmp("--pal", arg)
+ || !strcmp("-p", arg)) {
+ conv = 29.97 / 25.00;
+ } else if (!strcmp("--offset", arg)
+ || !strcmp("-o", arg)) {
+ if (i + 1 >= argc) {
+ fprintf(stderr,
+ "The parameter --offset/-o requires "
+ "an argument\n"
+ );
+ return 5;
+ }
+
+ if (sscanf(argv[++i], "%i", &add_offset) == EOF) {
+ fprintf(stderr,
+ "Bad parameter to %s: %s\n",
+ arg, argv[i-1]
+ );
+ return 5;
+ }
+ } else if (!strcmp("--ratio", arg)
+ || !strcmp("-r", arg)) {
+ if (i + 1 >= argc) {
+ fprintf(stderr,
+ "The parameter --ratio/-r requires "
+ "an argument\n"
+ );
+ return 5;
+ }
+
+ if (sscanf(argv[++i], "%lf", &conv) == EOF) {
+ fprintf(stderr,
+ "Bad parameter to %s: %s\n",
+ arg, argv[i-1]
+ );
+ return 5;
+ }
} else if (!strcmp("--output", arg) || !strcmp("-o", arg)) {
if (i + 1 >= argc) {
fprintf(stderr,
- "The parameter --output/-o requires an argument\n");
+ "The parameter --output/-o requires "
+ "an argument\n"
+ );
return 5;
}
out_file = argv[++i];
if (from == NSUB_FMT_UNKNOWN) {
fprintf(stderr,
- "Cannot detect input format, please specify it with '--from'\n");
+ "Cannot detect input format, "
+ "please specify it with '--from'\n"
+ );
return 6;
}
if (to == NSUB_FMT_UNKNOWN) {
fprintf(stderr,
- "Cannot detect output format, please specify it with '--to'\n");
+ "Cannot detect output format, "
+ "please specify it with '--to'\n"
+ );
return 7;
}
if (in_file && !(in_file[0] == '-' && !in_file[1])) {
in = fopen(in_file, "r");
if (!in) {
- fprintf(stderr, "Cannot open input file: %s\n", in_file);
+ fprintf(stderr,
+ "Cannot open input file: %s\n", in_file
+ );
rep = 2;
}
}
if (!rep && out_file && !(out_file[0] == '-' && !out_file[1])) {
out = fopen(out_file, "w");
if (!in) {
- fprintf(stderr, "Cannot create output file: %s\n", out_file);
+ fprintf(stderr,
+ "Cannot create output file: %s\n", out_file
+ );
rep = 3;
}
}
if (!song)
rep = 22;
- if (!rep && !nsub_write(out, song, to, apply_offset))
+ if (!rep && !nsub_write(out, song, to, apply_offset,
+ add_offset, conv))
rep = 33;
free_song(song);
void help(char *program) {
printf("NSub subtitles conversion program\n");
printf("Syntax:\n");
- printf("\t%s (--from FMT) (--to FMT) (--apply-offset)\n"
- "\t\t (--output OUT_FILE) (IN_FILE)\n", program);
- printf("\t> --help (or -h): this help message\n");
- printf("\t> --from (or -f) FMT: select the input format FMT\n");
- printf("\t> --to (or -t) FMT: select the output format FMT\n");
+ printf("\t%s (--from FMT) (--to FMT) (--apply-offset) (--offset MSEC)\n"
+ "\t\t (--ntsc) (--pal) (--ratio RATIO)\n"
+ "\t\t (--output OUT_FILE) (IN_FILE)\n",
+ program
+ );
+
+ printf("\nOptions:\n");
+ printf("\t-h/--help : this help message\n");
+ printf("\t-f/--from FMT : select the input format FMT\n");
+ printf("\t-t/--to FMT : select the output format FMT\n");
+ printf("\t-a/--apply-offset : apply the offset tag "
+ "value to the lyrics\n"
+ );
+ printf("\t-o/--offset MSEC : add a manual offset to all timings\n");
+ printf("\t-n/--ntsc : Convert timings from NTSC to PAL\n");
+ printf("\t-p/--pal : Convert timings from PAL to NTSC\n");
+ printf("\t-r/--ratio RATIO : Convert timings with a "
+ "custom ratio\n");
+
+ printf("\nArguments:\n");
printf(
- "\t> --apply-offset (or -a): apply the offset tag value to the lyrics\n");
+ "\tIN_FILE : the input file or '-' for stdin "
+ "(which is the default)\n"
+ );
printf(
- "\t> IN_FILE: the input file or '-' for stdin (which is the default)\n");
+ "\tOUT_FILE : the output file or '-' for stdout "
+ "(which is the default)\n"
+ );
+ printf("\tRATIO : the ratio to apply to timings (1 = no change)\n");
printf(
- "\t> OUT_FILE: the output file or '-' for stdout (which is the default)\n");
+ "\tMSEC : the offset to add to all timings in "
+ "milliseconds\n"
+ );
printf("\n");
printf(
- "Note: the in/out formats will be guessed from the extension if needed/possible\n");
+ "Note: the in/out formats will be guessed from the "
+ "extension if needed/possible\n"
+ );
printf(
- "Note: to specify a file named dash (-), prefix it with a path (e.g., './-')\n");
+ "Note: to specify a file named dash (-), prefix it with a path"
+ " (e.g., './-')\n"
+ );
printf("\n");
printf("Supported formats:\n");
- printf("\t> lrc: lyrics files\n");
- printf("\t> srt: SubRip subtitles files\n");
- printf("\t> vtt/webvtt: Web Video Text Tracks\n");
+ printf("\tlrc: lyrics files\n");
+ printf("\tsrt: SubRip subtitles files\n");
+ printf("\tvtt/webvtt: Web Video Text Tracks\n");
}
if (*offset)
return 0;
- // skip spaces then ':'
+ // skip spaces then ':', then spaces
while (*line == ' ')
line++;
if (*line && *line != ':')
return 0;
line++;
+ while (*line == ' ')
+ line++;
// allow sign
if (*line == '-' || *line == '+')
// skip [offset:
while (*line != ':')
line++;
+ line++;
while (*line == ' ')
line++;
} else {
/* should not happen! */
fprintf(stderr,
- "Warning: called lrc_millisec with bad input [%s], ignoring...\n",
- line);
+ "Warning: called lrc_millisec with bad input"
+ " [%s], ignoring...\n",
+ line);
return 0;
}
int new_count = atoi(line);
if (new_count != count + 1) {
fprintf(stderr,
- "Warning: line %zu is out of order (it is numbered %i), ignoring order...\n",
- count, new_count);
+ "Warning: line %zu is out of order "
+ "(it is numbered %i), ignoring order...\n",
+ count, new_count
+ );
}
song_add_lyric(song, 0, 0, NULL, NULL);
if (!is_srt_timing(line)) {
/* should not happen! */
fprintf(stderr,
- "Warning: called get_start with bad input [%s], ignoring...\n",
- line);
+ "Warning: called get_start with bad input "
+ "[%s], ignoring...\n",
+ line
+ );
return 0;
}
if (!is_srt_timing(line)) {
/* should not happen! */
fprintf(stderr,
- "Warning: called get_stop with bad input [%s], ignoring...\n",
- line);
+ "Warning: called get_stop with bad input "
+ "[%s], ignoring...\n",
+ line
+ );
return 0;
}
/* Declarations */
char *nsub_lrc_time_str(int time, int show_sign);
-void nsub_write_lrc_lyric(FILE *out, lyric_t *lyric, int offset);
+void nsub_write_lrc_lyric(FILE *out, lyric_t *lyric, int offset, double conv);
/* Public */
-int nsub_write_lrc(FILE *out, song_t *song, NSUB_FORMAT fmt, int apply_offset) {
- int offset;
+int nsub_write_lrc(FILE *out, song_t *song, NSUB_FORMAT fmt, int apply_offset,
+ int add_offset, double conv) {
+ int offset = add_offset;
// header: none
{
char *offset_str;
if (apply_offset) {
- offset = song->offset;
+ offset += song->offset;
offset_str = nsub_lrc_time_str(0, 1);
} else {
- offset = 0;
offset_str = nsub_lrc_time_str(song->offset, 1);
}
fprintf(out, "[offset: %s]\n", offset_str);
// lyrics
array_loop(song->lyrics, lyric, lyric_t)
{
- nsub_write_lrc_lyric(out, lyric, offset);
+ nsub_write_lrc_lyric(out, lyric, offset, conv);
}
return 1;
/* Private */
-void nsub_write_lrc_lyric(FILE *out, lyric_t *lyric, int offset) {
+void nsub_write_lrc_lyric(FILE *out, lyric_t *lyric, int offset, double conv) {
static int lrc_last_stop = 0;
if (lyric->type == NSUB_EMPTY) {
- if (lrc_last_stop) {
- char *time = nsub_lrc_time_str(lrc_last_stop, 0);
- fprintf(out, "[%s]\n", time);
- free(time);
- } else {
- fprintf(out, "\n");
- }
-
- lrc_last_stop = 0;
+ fprintf(out, "\n");
return;
}
}
fprintf(out, "-- %s\n", tmp->string);
- lrc_last_stop = 0;
free_cstring(tmp);
return;
}
+
+ int start_time = apply_conv(lyric->start, conv) + offset;
+ if (lrc_last_stop && lrc_last_stop != start_time) {
+ char *time = nsub_lrc_time_str(lrc_last_stop, 0);
+ fprintf(out, "[%s]\n", time);
+ fprintf(out, "\n");
+ free(time);
+ lrc_last_stop = 0;
+ }
+
if (lyric->name) {
cstring_t *tmp = cstring_clone(lyric->name);
free_cstring(tmp);
}
-
- char *time = nsub_lrc_time_str(lyric->start + offset, 0);
+
+ char *time = nsub_lrc_time_str(start_time, 0);
cstring_t *tmp = cstring_clone(lyric->text);
if (tmp) {
cstring_replace(tmp, "\n", "\\n");
free(time);
free_cstring(tmp);
- lrc_last_stop = lyric->stop + offset;
+ lrc_last_stop = apply_conv(lyric->stop, conv) + offset;
}
char *nsub_lrc_time_str(int time, int show_sign) {
/* Declarations */
char *nsub_srt_time_str(int time, int show_sign);
-void nsub_write_srt_lyric(FILE *out, lyric_t *lyric, int offset);
+void nsub_write_srt_lyric(FILE *out, lyric_t *lyric, int offset, double conv);
/* Public */
-int nsub_write_srt(FILE *out, song_t *song, NSUB_FORMAT fmt, int apply_offset) {
- int offset;
+int nsub_write_srt(FILE *out, song_t *song, NSUB_FORMAT fmt, int apply_offset,
+ int add_offset, double conv) {
+ int offset = add_offset;
// header: none
// offset is not supported in SRT (so, always applied)
{
- offset = song->offset;
+ offset += song->offset;
}
// other metas: none
// lyrics
array_loop(song->lyrics, lyric, lyric_t)
{
- nsub_write_srt_lyric(out, lyric, offset);
+ nsub_write_srt_lyric(out, lyric, offset, conv);
}
return 1;
/* Private */
-void nsub_write_srt_lyric(FILE *out, lyric_t *lyric, int offset) {
+void nsub_write_srt_lyric(FILE *out, lyric_t *lyric, int offset, double conv) {
if (lyric->type == NSUB_EMPTY) {
// not supported, ignored
return;
//if (lyric->name)
// not supported, ignored
- char *start = nsub_srt_time_str(lyric->start + offset, 0);
- char *stop = nsub_srt_time_str(lyric->stop + offset, 0);
+ char *start = nsub_srt_time_str(
+ apply_conv(lyric->start, conv) + offset,
+ 0);
+ char *stop = nsub_srt_time_str(
+ apply_conv(lyric->stop , conv) + offset,
+ 0);
fprintf(out, "%s --> %s\n%s\n\n", start, stop, lyric->text);
free(start);
free(stop);
/* Declarations */
char *nsub_webvtt_time_str(int time, int show_sign);
-void nsub_write_webvtt_lyric(FILE *out, lyric_t *lyric, int offset);
+void nsub_write_webvtt_lyric(FILE *out, lyric_t *lyric, int offset,
+ double conv);
/* Public */
int nsub_write_webvtt(FILE *out, song_t *song, NSUB_FORMAT fmt,
- int apply_offset) {
- int offset;
+ int apply_offset, int add_offset, double conv) {
+ int offset = add_offset;
// header
{
// offset is not supported in WebVTT (so, always applied)
{
- offset = song->offset;
+ offset += song->offset;
}
// other metas
// lyrics
array_loop(song->lyrics, lyric, lyric_t)
{
- nsub_write_webvtt_lyric(out, lyric, offset);
+ nsub_write_webvtt_lyric(out, lyric, offset, conv);
}
return 1;
/* Private */
-void nsub_write_webvtt_lyric(FILE *out, lyric_t *lyric, int offset) {
+void nsub_write_webvtt_lyric(FILE *out, lyric_t *lyric, int offset,
+ double conv) {
if (lyric->type == NSUB_EMPTY) {
fprintf(out, "\n\n");
return;
// Not always supported by clients, so disabled:
// if (lyric->name)
//fprintf(out, "%s\n", lyric->name);
-
- char *start = nsub_webvtt_time_str(lyric->start + offset, 0);
- char *stop = nsub_webvtt_time_str(lyric->stop + offset, 0);
+
+ char *start = nsub_webvtt_time_str(
+ apply_conv(lyric->start, conv) + offset,
+ 0);
+ char *stop = nsub_webvtt_time_str(
+ apply_conv(lyric->stop , conv) + offset,
+ 0);
fprintf(out, "%s --> %s\n%s\n\n", start, stop, lyric->text);
free(start);
free(stop);