Commit 0d6a0f00 authored by ivo's avatar ivo

Move preliminary Kate support to repository to avoid losing work.

svn path=/trunk/vorbis-tools/; revision=15395
parent 4cecf2f4
......@@ -25,9 +25,9 @@ DEPENDENCIES:
All of the tools require libogg and libvorbis to be installed (along
with the header files). Additionally, ogg123 requires libao, libcurl,
and a POSIX-compatible thread library. Ogg123 can optionally compiled
to use libFLAC, libOggFLAC, and libspeex. Oggenc can be optionally
compiled with libFLAC and libOggFLAC. The libraries libogg,
libvorbis, and libao are all available at
to use libFLAC, and libspeex. Oggenc can be optionally compiled with
libFLAC, and libkate. The libraries libogg, libvorbis, and libao are
all available at
http://www.vorbis.com/download.psp
The libcurl library is packaged with most Linux distributions. The
......@@ -40,6 +40,9 @@ FLAC is available at:
Speex is available at:
http://www.speex.org/
libkate is available at:
http://libkate.googlecode.com/
CONTACT:
The Ogg Vorbis homepage is located at 'http://www.vorbis.com'. Up to
......
......@@ -414,7 +414,7 @@ will NOT be built with Speex read support.])
will NOT be built with http support.])
fi
if test "x$HAVE_KATE" != xyes; then
AC_MSG_WARN([Kate libraries and/or headers missing, ogginfo
will be built with LIMITED Kate read support.])
AC_MSG_WARN([Kate libraries and/or headers missing, oggenc
will NOT be built with Kate lyrics support.])
fi
fi
......@@ -13,16 +13,16 @@ DEFS = -DLOCALEDIR=\"$(localedir)\" @DEFS@
bin_PROGRAMS = oggenc
INCLUDES = @SHARE_CFLAGS@ @OGG_CFLAGS@ @VORBIS_CFLAGS@ @I18N_CFLAGS@
INCLUDES = @SHARE_CFLAGS@ @OGG_CFLAGS@ @VORBIS_CFLAGS@ @KATE_CFLAGS@ @I18N_CFLAGS@
oggenc_LDADD = @SHARE_LIBS@ \
@VORBISENC_LIBS@ @VORBIS_LIBS@ @OGG_LIBS@ \
@VORBISENC_LIBS@ @VORBIS_LIBS@ @KATE_LIBS@ @OGG_LIBS@ \
@LIBICONV@ @I18N_LIBS@ @FLAC_LIBS@
oggenc_DEPENDENCIES = @SHARE_LIBS@
oggenc_SOURCES = $(flac_sources) \
oggenc.c audio.c encode.c platform.c \
oggenc.c audio.c encode.c platform.c lyrics.c \
audio.h encode.h platform.h resample.c resample.h skeleton.c skeleton.h
......
......@@ -47,7 +47,7 @@
/* Define the supported formats here */
input_format formats[] = {
{wav_id, 12, wav_open, wav_close, "wav", N_("WAV file reader")},
{wav_id, 12, wav_open, wav_close, "wav", N_("Wave file reader")},
{aiff_id, 12, aiff_open, wav_close, "aiff", N_("AIFF/AIFC file reader")},
#ifdef HAVE_LIBFLAC
{flac_id, 4, flac_open, flac_close, "flac", N_("FLAC file reader")},
......
......@@ -25,6 +25,11 @@
#include "i18n.h"
#include "skeleton.h"
#ifdef HAVE_KATE
#include "lyrics.h"
#include <kate/oggkate.h>
#endif
#define READSIZE 1024
......@@ -116,7 +121,7 @@ static void set_advanced_encoder_options(adv_opt *opts, int count,
#endif
}
void add_fishead_packet (ogg_stream_state *os) {
static void add_fishead_packet (ogg_stream_state *os) {
fishead_packet fp;
......@@ -130,9 +135,9 @@ void add_fishead_packet (ogg_stream_state *os) {
}
/*
* Adds the fishead packets in the skeleton output stream along with the e_o_s packet
* Adds the fishead packets in the skeleton output stream
*/
void add_fisbone_packet (ogg_stream_state *os, oe_enc_opt *opt) {
static void add_vorbis_fisbone_packet (ogg_stream_state *os, oe_enc_opt *opt) {
fisbone_packet fp;
......@@ -150,11 +155,53 @@ void add_fisbone_packet (ogg_stream_state *os, oe_enc_opt *opt) {
add_fisbone_to_stream(os, &fp);
}
#ifdef HAVE_KATE
static void add_kate_fisbone_packet (ogg_stream_state *os, oe_enc_opt *opt, kate_info *ki) {
fisbone_packet fp;
memset(&fp, 0, sizeof(fp));
fp.serial_no = opt->kate_serialno;
fp.nr_header_packet = ki->num_headers;
fp.granule_rate_n = ki->gps_numerator;
fp.granule_rate_d = ki->gps_denominator;
fp.start_granule = 0;
fp.preroll = 0;
fp.granule_shift = ki->granule_shift;
add_message_header_field(&fp, "Content-Type", "application/x-kate");
add_fisbone_to_stream(os, &fp);
}
#endif
#ifdef HAVE_KATE
static void add_kate_karaoke_style(kate_info *ki,unsigned char r,unsigned char g,unsigned char b,unsigned char a)
{
kate_style *ks;
int ret;
if (!ki) return;
ks=(kate_style*)malloc(sizeof(kate_style));
kate_style_init(ks);
ks->text_color.r = r;
ks->text_color.g = g;
ks->text_color.b = b;
ks->text_color.a = a;
ret=kate_info_add_style(ki,ks);
if (ret<0) {
fprintf(stderr, _("WARNING: failed to add Kate karaoke style\n"));
}
}
#endif
int oe_encode(oe_enc_opt *opt)
{
ogg_stream_state os;
ogg_stream_state so; /* stream for skeleton bitstream */
ogg_stream_state ko; /* stream for kate bitstream */
ogg_page og;
ogg_packet op;
......@@ -162,6 +209,15 @@ int oe_encode(oe_enc_opt *opt)
vorbis_block vb;
vorbis_info vi;
#ifdef HAVE_KATE
kate_info ki;
kate_comment kc;
kate_state k;
oe_lyrics *lyrics=NULL;
size_t lyrics_index=0;
double vorbis_time = 0.0;
#endif
long samplesdone=0;
int eos;
long bytes_written = 0, packetsdone=0;
......@@ -293,13 +349,41 @@ int oe_encode(oe_enc_opt *opt)
vorbis_analysis_init(&vd,&vi);
vorbis_block_init(&vd,&vb);
#ifdef HAVE_KATE
if (opt->lyrics) {
/* load lyrics */
lyrics=load_lyrics(opt->lyrics);
/* if it fails, don't do anything else for lyrics */
if (!lyrics) {
opt->lyrics = NULL;
} else {
/* init kate for encoding */
kate_info_init(&ki);
kate_info_set_category(&ki, "lyrics");
if (opt->lyrics_language)
kate_info_set_language(&ki, opt->lyrics_language);
else
fprintf(stderr, _("WARNING: no language specified for %s\n"), opt->lyrics);
kate_comment_init(&kc);
kate_encode_init(&k,&ki);
/* if we're in karaoke mode (we have syllable level timing info),
add style info in case some graphical player is used */
add_kate_karaoke_style(&ki, 255, 255, 255, 255);
add_kate_karaoke_style(&ki, 255, 128, 128, 255);
}
}
#endif
ogg_stream_init(&os, opt->serialno);
if (opt->with_skeleton)
if (opt->with_skeleton)
ogg_stream_init(&so, opt->skeleton_serialno);
if (opt->lyrics)
ogg_stream_init(&ko, opt->kate_serialno);
/* create the skeleton fishead packet and output it */
if (opt->with_skeleton) {
add_fishead_packet(&so);
if (opt->with_skeleton) {
add_fishead_packet(&so);
if ((ret = flush_ogg_stream_to_file(&so, opt->out))) {
opt->error(_("Failed writing fishead packet to output stream\n"));
goto cleanup;
......@@ -319,9 +403,9 @@ int oe_encode(oe_enc_opt *opt)
&header_main,&header_comments,&header_codebooks);
/* And stream them out */
/* output the vorbis bos first, then the fisbone packets */
/* output the vorbis bos first, then the kate bos, then the fisbone packets */
ogg_stream_packetin(&os,&header_main);
while((result = ogg_stream_flush(&os, &og)))
while((result = ogg_stream_flush(&os, &og)))
{
if(!result) break;
ret = oe_write_page(&og, opt->out);
......@@ -333,13 +417,48 @@ int oe_encode(oe_enc_opt *opt)
}
}
#ifdef HAVE_KATE
if (opt->lyrics) {
ogg_packet kate_op;
ret = kate_ogg_encode_headers(&k, &kc, &kate_op);
if (ret < 0) {
opt->error(_("Failed encoding Kate header\n"));
goto cleanup;
}
ogg_stream_packetin(&ko,&kate_op);
while((result = ogg_stream_flush(&ko, &og)))
{
if(!result) break;
ret = oe_write_page(&og, opt->out);
if(ret != og.header_len + og.body_len)
{
opt->error(_("Failed writing header to output stream\n"));
ret = 1;
goto cleanup; /* Bail and try to clean up stuff */
}
}
ogg_packet_clear(&kate_op);
}
#endif
if (opt->with_skeleton) {
add_fisbone_packet(&so, opt);
add_vorbis_fisbone_packet(&so, opt);
if ((ret = flush_ogg_stream_to_file(&so, opt->out))) {
opt->error(_("Failed writing fisbone header packet to output stream\n"));
goto cleanup;
}
goto cleanup;
}
#ifdef HAVE_KATE
if (opt->lyrics) {
add_kate_fisbone_packet(&so, opt, &ki);
if ((ret = flush_ogg_stream_to_file(&so, opt->out))) {
opt->error(_("Failed writing fisbone header packet to output stream\n"));
goto cleanup;
}
}
#endif
}
/* write the next Vorbis headers */
ogg_stream_packetin(&os,&header_comments);
ogg_stream_packetin(&os,&header_codebooks);
......@@ -356,9 +475,30 @@ int oe_encode(oe_enc_opt *opt)
}
}
if (opt->with_skeleton) {
add_eos_packet_to_stream(&so);
if ((ret = flush_ogg_stream_to_file(&so, opt->out))) {
/* build kate headers if requested */
#ifdef HAVE_KATE
if (opt->lyrics) {
while (kate_ogg_encode_headers(&k,&kc,&op)==0) {
ogg_stream_packetin(&ko,&op);
ogg_packet_clear(&op);
}
while((result = ogg_stream_flush(&ko, &og)))
{
if(!result) break;
ret = oe_write_page(&og, opt->out);
if(ret != og.header_len + og.body_len)
{
opt->error(_("Failed writing header to output stream\n"));
ret = 1;
goto cleanup; /* Bail and try to clean up stuff */
}
}
}
#endif
if (opt->with_skeleton) {
add_eos_packet_to_stream(&so);
if ((ret = flush_ogg_stream_to_file(&so, opt->out))) {
opt->error(_("Failed writing skeleton eos packet to output stream\n"));
goto cleanup;
}
......@@ -420,6 +560,53 @@ int oe_encode(oe_enc_opt *opt)
int result = ogg_stream_pageout(&os,&og);
if(!result) break;
/* now that we have a new Vorbis page, we scan lyrics for any that is due */
#ifdef HAVE_KATE
if (opt->lyrics && ogg_page_granulepos(&og)>=0) {
vorbis_time = vorbis_granule_time(&vd, ogg_page_granulepos(&og));
const oe_lyrics_item *item;
while ((item = get_lyrics(lyrics, vorbis_time, &lyrics_index))) {
ogg_packet kate_op;
if (item->km) {
ret = kate_encode_set_style_index(&k, 0);
if (ret < 0) {
opt->error(_("Failed encoding karaoke style - continuing anyway\n"));
}
ret = kate_encode_set_secondary_style_index(&k, 1);
if (ret < 0) {
opt->error(_("Failed encoding karaoke style - continuing anyway\n"));
}
ret = kate_encode_add_motion(&k, item->km, 0);
if (ret < 0) {
opt->error(_("Failed encoding karaoke motion - continuing anyway\n"));
}
}
ret = kate_ogg_encode_text(&k, item->t0, item->t1, item->text, strlen(item->text)+1, &kate_op);
if (ret < 0) {
opt->error(_("Failed encoding lyrics - continuing anyway\n"));
}
else {
ogg_stream_packetin(&ko, &kate_op);
ogg_packet_clear(&kate_op);
while (1) {
ogg_page ogk;
int result=ogg_stream_flush(&ko,&ogk);
if (!result) break;
ret = oe_write_page(&ogk, opt->out);
if(ret != ogk.header_len + ogk.body_len)
{
opt->error(_("Failed writing data to output stream\n"));
ret = 1;
goto cleanup; /* Bail */
}
else
bytes_written += ret;
}
}
}
}
#endif
ret = oe_write_page(&og, opt->out);
if(ret != og.header_len + og.body_len)
{
......@@ -437,12 +624,58 @@ int oe_encode(oe_enc_opt *opt)
}
}
ret = 0; /* Success, set return value to 0 since other things reuse it
/* if encoding lyrics, signal EOS and cleanup the kate state */
#ifdef HAVE_KATE
if (opt->lyrics) {
ogg_packet kate_op;
ret = kate_ogg_encode_finish(&k, vorbis_time, &kate_op);
if (ret < 0) {
opt->error(_("Failed encoding Kate EOS packet\n"));
}
else {
ogg_stream_packetin(&ko,&kate_op);
packetsdone++;
ogg_packet_clear(&kate_op);
eos = 0;
while(!eos)
{
int result = ogg_stream_pageout(&ko,&og);
if(!result) break;
ret = oe_write_page(&og, opt->out);
if(ret != og.header_len + og.body_len)
{
opt->error(_("Failed writing data to output stream\n"));
ret = 1;
goto cleanup; /* Bail */
}
else
bytes_written += ret;
if(ogg_page_eos(&og))
eos = 1;
}
}
}
#endif
ret = 0; /* Success. Set return value to 0 since other things reuse it
* for nefarious purposes. */
/* Cleanup time */
cleanup:
#ifdef HAVE_KATE
if (opt->lyrics) {
ogg_stream_clear(&ko);
kate_clear(&k);
kate_info_clear(&ki);
kate_comment_clear(&kc);
free_lyrics(lyrics);
}
#endif
ogg_stream_clear(&os);
vorbis_block_clear(&vb);
......
......@@ -54,14 +54,18 @@ typedef struct
int date_count;
char **genre;
int genre_count;
char **lyrics;
int lyrics_count;
char **lyrics_language;
int lyrics_language_count;
adv_opt *advopt;
int advopt_count;
int copy_comments;
int with_skeleton;
int quiet;
int rawmode;
int raw_samplesize;
int raw_samplerate;
int raw_channels;
......@@ -70,6 +74,7 @@ typedef struct
char *namefmt;
char *namefmt_remove;
char *namefmt_replace;
char *outfile;
/* All 3 in kbps */
......@@ -88,7 +93,8 @@ typedef struct
unsigned int serial;
unsigned int skeleton_serial;
int fixedserial;
unsigned int kate_serial;
int fixedserial;
int ignorelength;
int isutf8;
......@@ -99,6 +105,7 @@ typedef struct
vorbis_comment *comments;
unsigned int serialno;
unsigned int skeleton_serialno;
unsigned int kate_serialno;
audio_read_func read_samples;
progress_func progress_update;
......@@ -131,6 +138,9 @@ typedef struct
char *filename;
char *infilename;
int ignorelength;
char *lyrics;
char *lyrics_language;
} oe_enc_opt;
......
.\" Process this file with
.\" groff -man -Tascii oggenc.1
.\"
.TH oggenc 1 "2008 September 9" "Xiph.Org Foundation" "Vorbis Tools"
.TH oggenc 1 "2008 October 05" "Xiph.Org Foundation" "Vorbis Tools"
.SH NAME
oggenc \- encode audio into the Ogg Vorbis format
......@@ -78,6 +78,14 @@ oggenc \- encode audio into the Ogg Vorbis format
.B -G
.I genre
]
[
.B -L
.I lyrics file
]
[
.B -Y
.I language-string
]
.I input_files \fR...
.SH DESCRIPTION
......@@ -94,14 +102,20 @@ unless the
.B -o
option is used to redirect the output. By default, disk files are
output to Ogg Vorbis files of the same name, with the extension
changed to ".ogg". This naming convention can be overridden by the
changed to ".ogg" or ".oga". This naming convention can be overridden
by the
.B -o
option (in the case of one file) or the
.B -n
option (in the case of several files). Finally, if none of these
are available, the output filename will be the input filename with the
extension (that part after the final dot) replaced with ogg, so file.wav
will become file.ogg
will become file.ogg.
.br
Optionally, lyrics may be embedded in the Ogg file, if Kate support was compiled in.
.br
Note that some old players mail fail to play streams with more than a single Vorbis stream
(the so called "Vorbis I" simple profile).
.SH OPTIONS
.IP "-h, --help"
......@@ -199,11 +213,35 @@ Set the track title comment field to
.IP "-l album, --album album"
Set the album comment field to
.I album.
.IP "-L filename, --lyrics filename"
Loads lyrics from
.I filename
and encodes them into a Kate stream multiplexed with the Vorbis stream.
Lyrics may be in LRC or SRT format, and should be encoded in UTF-8 or
plain ASCII. Other encodings may be converted using tools such as iconv
or recode. Alternatively, the same system as for comments will be used
for conversion between encodings.
So called "enhanced LRC" files are supported, and a simple karaoke style
change will be saved with the lyrics. For more complex karaoke setups,
.B kateenc(1)
should be used instead.
When embedding lyrics, the default output file extention is ".oga".
.IP "-Y language-string, --lyrics-language language-string"
Sets the language for the corresponding lyrics file to
.I language-string.
This should be an ISO 639-1 language code (eg, "en"), or a RFC 3066 language tag
(eg, "en_US"),
.B not
a free form language name. Players will typically recognize this standard tag
and display the language name in your own language.
Note that the maximum length of this tag is 15 characters.
.PP
Note that the \fB-a\fR, \fB-t\fR, and \fB-l\fR options can be given
multiple times. They will be applied, one to each file, in the order
given. If there are fewer album, title, or artist comments given than
Note that the \fB-a\fR, \fB-t\fR, \fB-l\fR, \fB-L\fR, and \fB-Y\fR options
can be given multiple times. They will be applied, one to each file, in the
order given. If there are fewer album, title, or artist comments given than
there are input files,
.B oggenc
will reuse the final one for the remaining files, and issue a warning
......@@ -299,27 +337,27 @@ oggenc somefile.wav -o out.ogg
.RE
.PP
Specifying a high-quality encoding averaging 256 kbps (but still VBR).
Specifying a high-quality encoding averaging 256 kbps (but still VBR):
.RS
oggenc infile.wav -b 256 out.ogg
oggenc infile.wav -b 256 -o out.ogg
.RE
.PP
Specifying a maximum and average bitrate, and enforcing these.
Specifying a maximum and average bitrate, and enforcing these:
.RS
oggenc infile.wav --managed -b 128 -M 160 out.ogg
oggenc infile.wav --managed -b 128 -M 160 -o out.ogg
.RE
.PP
Specifying quality rather than bitrate (to a very high quality mode)
Specifying quality rather than bitrate (to a very high quality mode):
.RS
oggenc infile.wav -q 6 out.ogg
oggenc infile.wav -q 6 -o out.ogg
.RE
.PP
Downsampling and downmixing to 11 kHz mono before encoding.
Downsampling and downmixing to 11 kHz mono before encoding:
.RS
oggenc --resample 11025 --downmix infile.wav -q 1 out.ogg
oggenc --resample 11025 --downmix infile.wav -q 1 -o out.ogg
.RE
.PP
......@@ -331,6 +369,12 @@ oggenc somefile.wav -t "The track title" -a "artist who performed this" -l
.RE
.PP
Adding embedded lyrics:
.RS
oggenc somefile.wav --lyrics lyrics.lrc --lyrics-language en -o out.oga
.RE
.PP
This encodes the three files, each with the
same artist/album tag, but with different title tags on each one. The
string given as an argument to -n is used to generate filenames, as shown
......@@ -367,4 +411,4 @@ Reading type 3 Wave files (floating point samples) probably doesn't work other t
.SH "SEE ALSO"
.PP
\fBvorbiscomment\fR(1), \fBogg123\fR(1), \fBflac\fR(1), \fBspeexenc\fR(1), \fBffmpeg2theora\fR(1)
\fBvorbiscomment\fR(1), \fBogg123\fR(1), \fBoggdec\fR(1), \fBflac\fR(1), \fBspeexenc\fR(1), \fBffmpeg2theora\fR(1), \fBkateenc\fR(1)
......@@ -69,6 +69,8 @@ struct option long_options[] = {
{"discard-comments", 0, 0, 0},
{"utf8", 0,0,0},
{"ignorelength", 0, 0, 0},
{"lyrics",1,0,'L'},
{"lyrics-language",1,0,'Y'},
{NULL,0,0,0}
};
......@@ -84,10 +86,17 @@ static void usage(void);
int main(int argc, char **argv)
{
/* Default values */
oe_options opt = {NULL, 0, NULL, 0, NULL, 0, NULL, 0, NULL,
0, NULL, 0, NULL, 0, NULL, 0, 1, 0, 0, 0,16,44100,2, 0, NULL,
DEFAULT_NAMEFMT_REMOVE, DEFAULT_NAMEFMT_REPLACE,
NULL, 0, -1,-1,-1,.3,-1,0, 0,0.f, 0, 0, 0, 0};
oe_options opt = {
NULL, 0, NULL, 0, NULL, 0, NULL, 0, NULL, 0,
NULL, 0, NULL, 0, NULL, 0, NULL, 0, NULL, 0,
1, 0, 0, 0,
16,44100,2, 0,
NULL, DEFAULT_NAMEFMT_REMOVE, DEFAULT_NAMEFMT_REPLACE,
NULL,
0, -1,-1,-1,
.3,-1,
0,0,0.f,
0, 0, 0, 0, 0};
int i;
......@@ -139,6 +148,7 @@ int main(int argc, char **argv)
srand(time(NULL) ^ getpid());
opt.serial = rand();
opt.skeleton_serial = opt.serial + numfiles;
opt.kate_serial = opt.skeleton_serial + numfiles;
}
for(i = 0; i < numfiles; i++)
......@@ -153,13 +163,14 @@ int main(int argc, char **argv)
int closeout = 0, closein = 0;
char *artist=NULL, *album=NULL, *title=NULL, *track=NULL;
char *date=NULL, *genre=NULL;
char *lyrics=NULL, *lyrics_language=NULL;
input_format *format;
int resampled = 0;
/* Set various encoding defaults */
enc_opts.serialno = opt.serial++;