3
** This program is distributed under the GNU General Public License, version 2.
4
** A copy of this license is included with this source.
6
** This particular file may also be distributed under (at your option) any
7
** later version of the GNU General Public License.
9
** Copyright 2008, ogg.k.ogg.k <ogg.k.ogg.k@googlemail.com>
11
** Portions from ffmpeg2theora, (c) j <j@v2v.cc>
24
#include <kate/oggkate.h>
39
static char *fgets2(char *s,size_t sz,FILE *f)
41
char *ret = fgets(s, sz, f);
43
/* fixup DOS newline character */
44
char *ptr=strchr(ret, '\r');
53
static double hmsms2s(int h,int m,int s,int ms)
55
return h*3600+m*60+s+ms/1000.0;
58
static int add_lyrics(oe_lyrics *lyrics, char *text, kate_motion *km, double t0,double t1)
64
ret=utf8_encode(text,&utf8);
66
fprintf(stderr,_("Failed to convert to UTF-8: %s\n"),text);
70
lyrics->lyrics = (oe_lyrics_item*)realloc(lyrics->lyrics, (lyrics->count+1)*sizeof(oe_lyrics_item));
71
if (!lyrics->lyrics) {
73
fprintf(stderr, _("Out of memory\n"));
77
ret=kate_text_validate(kate_utf8,utf8,len+1);
79
fprintf(stderr,_("WARNING: subtitle %s is not valid UTF-8\n"),utf8);
83
/* kill off trailing \n characters */
85
if (utf8[len-1]=='\n') utf8[--len]=0; else break;
87
lyrics->lyrics[lyrics->count].text = utf8;
88
lyrics->lyrics[lyrics->count].len = len;
89
lyrics->lyrics[lyrics->count].t0 = t0;
90
lyrics->lyrics[lyrics->count].t1 = t1;
91
lyrics->lyrics[lyrics->count].km = km;
97
static int is_line_empty(const char *s)
99
/* will work fine with UTF-8 despite the appearance */
101
if (!strchr(" \t\r\n",*s)) return 0;
107
static oe_lyrics *load_srt_lyrics(FILE *f)
109
enum { need_id, need_timing, need_text };
114
static char text[4096];
115
static char str[4096];
116
int h0,m0,s0,ms0,h1,m1,s1,ms1;
124
lyrics=(oe_lyrics*)malloc(sizeof(oe_lyrics));
125
if (!lyrics) return NULL;
127
lyrics->lyrics = NULL;
130
fgets2(str,sizeof(str),f);
135
if (is_line_empty(str)) {
136
/* be nice and ignore extra empty lines between records */
139
ret=sscanf(str,"%d\n",&id);
140
if (ret!=1 || id<0) {
141
fprintf(stderr,_("ERROR - line %u: Syntax error: %s\n"),line,str);
145
if (id!=last_seen_id+1) {
146
fprintf(stderr,_("WARNING - line %u: non consecutive ids: %s - pretending not to have noticed\n"),line,str);
154
/* we could use %u, but glibc accepts minus signs for %u for some reason */
155
ret=sscanf(str,"%d:%d:%d%*[.,]%d --> %d:%d:%d%*[.,]%d\n",&h0,&m0,&s0,&ms0,&h1,&m1,&s1,&ms1);
156
if (ret!=8 || (h0|m0|s0|ms0)<0 || (h1|m1|s1|ms1)<0) {
157
fprintf(stderr,_("ERROR - line %u: Syntax error: %s\n"),line,str);
162
fprintf(stderr,_("ERROR - line %u: end time must not be less than start time: %s\n"),line,str);
167
t0=hmsms2s(h0,m0,s0,ms0);
168
t1=hmsms2s(h1,m1,s1,ms1);
174
if (add_lyrics(lyrics,text,NULL,t0,t1) < 0) {
181
/* in case of very long lines */
182
size_t len=strlen(text);
183
if (len+strlen(str) >= sizeof(text)) {
184
fprintf(stderr, _("WARNING - line %u: text is too long - truncated\n"),line);
186
strncpy(text+len,str,sizeof(text)-len);
187
text[sizeof(text)-1]=0;
191
fgets2(str,sizeof(str),f);
196
/* shouldn't be a problem though, but warn */
197
fprintf(stderr, _("WARNING - line %u: missing data - truncated file?\n"),line);
203
static void add_kate_karaoke_tag(kate_motion *km,kate_float dt,const char *str,size_t len,int line)
206
kate_float ptr=(kate_float)-0.5;
210
fprintf(stderr, _("WARNING - line %d: lyrics times must not be decreasing\n"), line);
214
/* work out how many glyphs we have */
216
ret=kate_text_get_character(kate_utf8,&str,&len);
218
fprintf(stderr, _("WARNING - line %d: failed to get UTF-8 glyph from string\n"), line);
221
ptr+=(kate_float)1.0;
223
/* ptr now points to the middle of the glyph we're at */
225
kc=(kate_curve*)malloc(sizeof(kate_curve));
227
kc->type=kate_curve_static;
229
kc->pts=(kate_float*)malloc(2*sizeof(kate_float));
231
kc->pts[1]=(kate_float)0;
234
km->curves=(kate_curve**)realloc(km->curves,km->ncurves*sizeof(kate_curve*));
235
km->durations=(kate_float*)realloc(km->durations,km->ncurves*sizeof(kate_float));
236
km->curves[km->ncurves-1]=kc;
237
km->durations[km->ncurves-1]=dt;
240
static int fraction_to_milliseconds(int fraction,int digits)
253
static kate_motion *process_enhanced_lrc_tags(char *str,kate_float start_time,kate_float end_time,int line)
258
kate_motion *km=NULL;
259
kate_float current_time = start_time;
262
if (!str) return NULL;
266
start=strchr(start,'<');
268
end=strchr(start+1,'>');
271
/* we found a <> pair, parse it */
273
ret=sscanf(start,"<%d:%d.%n%d%n>",&m,&s,&f0,&fs,&f1);
275
/* remove the <> tag from input to get raw text */
276
memmove(start,end+1,strlen(end+1)+1);
278
if (ret<3 || (f0|f1)<0 || f0>=f1 || (m|s|fs)<0) {
279
fprintf(stderr, _("WARNING - line %d: failed to process enhanced LRC tag (%*.*s) - ignored\n"),line,(int)(end-start+1),(int)(end-start+1),start);
282
kate_float tag_time=hmsms2s(0,m,s,fraction_to_milliseconds(fs,f1-f0));
284
/* if this is the first tag in this line, create a kate motion */
286
km=(kate_motion*)malloc(sizeof(kate_motion));
288
fprintf(stderr, _("WARNING: failed to allocate memory - enhanced LRC tag will be ignored\n"));
291
kate_motion_init(km);
292
km->semantics=kate_motion_semantics_glyph_pointer_1;
295
/* add to the kate motion */
297
add_kate_karaoke_tag(km,tag_time-current_time,str,start-str,line);
298
current_time = tag_time;
303
/* if we've found karaoke info, extend the motion to the end time */
305
add_kate_karaoke_tag(km,end_time-current_time,str,strlen(str),line);
311
static oe_lyrics *load_lrc_lyrics(FILE *f)
314
static char str[4096];
315
static char lyrics_line[4096]="";
317
double t,start_time = -1.0;
327
fgets2(str,sizeof(str),f);
330
ret = sscanf(str, "[%d:%d.%d]%n\n",&m,&s,&fs,&offset);
333
fgets2(str,sizeof(str),f);
337
fprintf(stderr,_("ERROR - line %u: Syntax error: %s\n"),line,str);
341
lyrics=(oe_lyrics*)malloc(sizeof(oe_lyrics));
342
if (!lyrics) return NULL;
344
lyrics->lyrics = NULL;
348
/* ignore empty lines */
349
if (!is_line_empty(str)) {
351
ret=sscanf(str, "[%d:%d.%n%d%n]%n\n",&m,&s,&f0,&fs,&f1,&offset);
352
if (ret<3 || (f0|f1)<0 || f1<=f0 || (m|s|fs)<0) {
353
fprintf(stderr,_("ERROR - line %u: Syntax error: %s\n"),line,str);
357
t=hmsms2s(0,m,s,fraction_to_milliseconds(fs,f1-f0));
359
if (start_time>=0.0 && !is_line_empty(lyrics_line)) {
360
km=process_enhanced_lrc_tags(lyrics_line,start_time,t,line);
364
if (add_lyrics(lyrics,lyrics_line,km,start_time,t) < 0) {
370
strncpy(lyrics_line,str+offset,sizeof(lyrics_line));
371
lyrics_line[sizeof(lyrics_line)-1]=0;
375
fgets2(str,sizeof(str),f);
382
/* very weak checks, but we only support two formats, so it's ok */
383
lyrics_format probe_lyrics_format(FILE *f)
386
static char str[4096];
387
lyrics_format format=lf_unknown;
390
if (!f) return lf_unknown;
393
fgets2(str,sizeof(str),f);
396
if (sscanf(str, "%d\n", &dummy_int) == 1 && dummy_int>=0)
403
fseek(f,pos,SEEK_SET);
410
oe_lyrics *load_lyrics(const char *filename)
413
static char str[4096];
415
oe_lyrics *lyrics=NULL;
419
fprintf(stderr,_("ERROR: No lyrics filename to load from\n"));
423
f = fopen(filename, "r");
425
fprintf(stderr,_("ERROR: Failed to open lyrics file %s (%s)\n"), filename, strerror(errno));
429
/* first, check for a BOM */
430
ret=fread(str,1,3,f);
431
if (ret<3 || memcmp(str,"\xef\xbb\xbf",3)) {
436
switch (probe_lyrics_format(f)) {
438
lyrics = load_srt_lyrics(f);
441
lyrics = load_lrc_lyrics(f);
444
fprintf(stderr, _("ERROR: Failed to load %s - can't determine format\n"), filename);
456
void free_lyrics(oe_lyrics *lyrics)
461
for (n=0; n<lyrics->count; ++n) {
462
oe_lyrics_item *li=&lyrics->lyrics[n];
465
for (c=0; c<li->km->ncurves; ++c) {
466
free(li->km->curves[c]->pts);
467
free(li->km->curves[c]);
469
free(li->km->curves);
470
free(li->km->durations);
474
free(lyrics->lyrics);
480
const oe_lyrics_item *get_lyrics(const oe_lyrics *lyrics, double t, size_t *idx)
483
if (!lyrics || *idx>=lyrics->count) return NULL;
484
if (lyrics->lyrics[*idx].t0 > t) return NULL;
485
return &lyrics->lyrics[(*idx)++];