diff --git a/AUTHORS b/AUTHORS new file mode 100644 index 0000000..ff656e4 --- /dev/null +++ b/AUTHORS @@ -0,0 +1 @@ +Svend Sorensen diff --git a/COPYING b/COPYING index 5b6e7c6..d60c31a 100644 --- a/COPYING +++ b/COPYING @@ -2,7 +2,7 @@ Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. - 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. @@ -313,7 +313,7 @@ Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: - Gnomovision version 69, Copyright (C) year name of author + Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. diff --git a/ChangeLog b/ChangeLog new file mode 100644 index 0000000..e69de29 diff --git a/Makefile b/Makefile deleted file mode 100644 index 33c3114..0000000 --- a/Makefile +++ /dev/null @@ -1,38 +0,0 @@ -SUBDIRS= lib tools man - -all install uninstall: - for dir in $(SUBDIRS) ; do \ - (cd $$dir && $(MAKE) $@) ; \ - done - -# reStructuredText Makefile - -RST2HTML= rst2html.py -RST2LATEX= rst2latex.py -LATEX2PDF= pdflatex - -RSTFILES= $(wildcard *.txt) -HTMLFILES= $(RSTFILES:.txt=.html) -LATEXFILES= $(RSTFILES:.txt=.tex) -PDFFILES= $(RSTFILES:.txt=.pdf) - -html: $(HTMLFILES) - cd man && $(MAKE) $@ -latex: $(LATEXFILES) -pdf: $(PDFFILES) - -%.html: %.txt - $(RST2HTML) $< > $@ - -%.tex: %.txt - $(RST2LATEX) $< > $@ - -%.pdf: %.tex - $(LATEX2PDF) $< - -clean: - for dir in $(SUBDIRS) ; do \ - (cd $$dir && $(MAKE) $@) ; \ - done - rm -f $(HTMLFILES) $(LATEXFILES) $(PDFFILES) - rm -f *.aux *.log *.out diff --git a/Makefile.am b/Makefile.am new file mode 100644 index 0000000..26b17ac --- /dev/null +++ b/Makefile.am @@ -0,0 +1 @@ +SUBDIRS = doc src extras diff --git a/CHANGES.txt b/NEWS similarity index 83% rename from CHANGES.txt rename to NEWS index 28ae4ed..bb99ba7 100644 --- a/CHANGES.txt +++ b/NEWS @@ -1,15 +1,6 @@ -================ -cuetools CHANGES -================ - -:Author: Svend Sorensen -:Contact: sorensen@users.berlios.de - Changes since 1.2 ------------------ -* prefix options have been removed from the lib/Makefile for Lex and Yacc (to - prepare for automake). +* Build system has been rewritten using automake/autoconf. * Added append, prepend, and split pregap modes to cuebreakpoints. @@ -20,7 +11,6 @@ Changes since 1.2 * Programs exit if --input-format or --output-format is an illegal value. Changes since 1.1 ------------------ * cuebreakpoints was not printing the last track breakpoint. This has been fixed. @@ -35,7 +25,6 @@ Changes since 1.1 * Added a track selection flag to cueprint. Changes since 1.0 ------------------ * File formats documentation has been readded and updated (docs/formats.txt). @@ -48,7 +37,6 @@ Changes since 1.0 expansion (like the printf command). Changes since 0.6 ------------------ * cuetools-1.x is a significant code rewrite of 0.x. The cue/toc parsing code has been reimplemented in lex/yacc. diff --git a/README b/README new file mode 100644 index 0000000..564e064 --- /dev/null +++ b/README @@ -0,0 +1,28 @@ +cuetools - cue and toc file parsers and utilities + +Copyright (C) 2004, 2005 Svend Sorensen + +cuetools is a set of utilities for working with Cue Sheet (cue) and Table of +Contents (toc) files. + +It includes: + +cueconvert + convert between the cue and toc formats +cuebreakpoints + print the breakpoints from a cue or toc file +cueprint + print disc and track infomation for a cue or toc file + +Directory layout: + +doc/ + documentation (including man pages) +src/ + all source files +src/lib + scanning, parsing, and printing library +src/tools + cue and toc tools + +Cuetools is hosted by BerliOS (http://cuetools.berlios.de). diff --git a/README.txt b/README.txt deleted file mode 100644 index 18d0fa1..0000000 --- a/README.txt +++ /dev/null @@ -1,34 +0,0 @@ -=============== -cuetools README -=============== - -:Author: Svend Sorensen -:Contact: sorensen@users.berlios.de -:Website: http://cuetools.berlios.de - -Description ------------ - -cuetools is a set of utilities for working with Cue Sheet (cue) and Table of -Contents (toc) files. - -It includes: - -cueconvert - convert between the cue and toc formats - -cuebreakpoints - print the breakpoints from a cue or toc file - -cueprint - print disc and track infomation for a cue or toc file - - -Installation ------------- - -Building requires GNU Make, Lex, and Yacc. GNU Bison works in Yacc mode -(``make YACC="bison -y"``). - -Run ``make install`` to install cuetools into the default location of -/usr/local. The Makefiles also support both the prefix and DESTDIR variables. diff --git a/BUGS.txt b/TODO similarity index 75% rename from BUGS.txt rename to TODO index 99ca72f..272b967 100644 --- a/BUGS.txt +++ b/TODO @@ -1,10 +1,3 @@ -================== -cuetools Bugs/TODO -================== - -:Author: Svend Sorensen -:Contact: sorensen@users.berlios.de - * (cue/toc) Line numbers in error messages are off sometimes. * (cue/toc) Double quotes must be escaped in a string, otherwise they will not be escaped when printed. diff --git a/configure.ac b/configure.ac new file mode 100644 index 0000000..ab929b2 --- /dev/null +++ b/configure.ac @@ -0,0 +1,14 @@ +AC_INIT(cuetools,1.3pre,sorensen@users.berlios.de) +AM_INIT_AUTOMAKE($PACKAGE_NAME,$PACKAGE_VERSION) +# getting conflicting errors about config.h.in +#AM_CONFIG_HEADER(config.h) +#AC_CONFIG_HEADERS([config.h]) + +AC_PROG_INSTALL +AC_PROG_RANLIB +AC_PROG_CC +AM_PROG_LEX +AC_PROG_YACC + +AC_CONFIG_FILES([Makefile doc/Makefile src/Makefile src/lib/Makefile src/tools/Makefile extras/Makefile]) +AC_OUTPUT diff --git a/doc/Makefile.am b/doc/Makefile.am new file mode 100644 index 0000000..21dcc06 --- /dev/null +++ b/doc/Makefile.am @@ -0,0 +1,2 @@ +man_MANS = cuebreakpoints.1 cueconvert.1 cueprint.1 +EXTRA_DIST = $(man_MANS) diff --git a/man/cuebreakpoints.man b/doc/cuebreakpoints.1 similarity index 100% rename from man/cuebreakpoints.man rename to doc/cuebreakpoints.1 diff --git a/man/cueconvert.man b/doc/cueconvert.1 similarity index 100% rename from man/cueconvert.man rename to doc/cueconvert.1 diff --git a/man/cueprint.man b/doc/cueprint.1 similarity index 100% rename from man/cueprint.man rename to doc/cueprint.1 diff --git a/formats.txt b/extras/formats.txt similarity index 100% rename from formats.txt rename to extras/formats.txt diff --git a/index.txt b/extras/index.txt similarity index 100% rename from index.txt rename to extras/index.txt diff --git a/man/Makefile b/man/Makefile deleted file mode 100644 index e565203..0000000 --- a/man/Makefile +++ /dev/null @@ -1,35 +0,0 @@ -# Makefile - -INSTALL= install -c -INSTALL_PROGRAM= $(INSTALL) -INSTALL_DATA= $(INSTALL) -m 644 -GROFF= groff - -prefix= /usr/local -mandir= $(prefix)/man -DESTDIR= - -MANFILES= cuebreakpoints.man cueconvert.man cueprint.man -HTMLFILES= $(MANFILES:.man=.html) - -html: $(HTMLFILES) - -all: - @true - -install: all - $(INSTALL) -d $(DESTDIR)$(mandir)/man1 - for i in $(MANFILES) ; do \ - $(INSTALL_DATA) $$i $(DESTDIR)$(mandir)/man1/`basename $$i .man`.1 ; \ - done - -uninstall: - -for i in $(MANFILES) ; do \ - rm -f $(DESTDIR)$(mandir)/man1/`basename $$i .man`.1 ; \ - done - -clean: - rm -f *.html - -%.html: %.man - ${GROFF} -man -T html $< > $@ diff --git a/src/Makefile b/src/Makefile new file mode 100644 index 0000000..fe117b3 --- /dev/null +++ b/src/Makefile @@ -0,0 +1,39 @@ +CPPFLAGS+= -I../lib +LDFLAGS+= -L../lib +LDLIBS+= -lcuefile + +TARGETS= cuebreakpoints cueconvert cueprint + +all: $(TARGETS) + +clean: + rm -f $(TARGETS) + rm -f *.o + +INSTALL= install -c +INSTALL_PROGRAM= $(INSTALL) +INSTALL_DATA= $(INSTALL) -m 644 + +prefix= /usr/local +exec_prefix= $(prefix) +bindir= $(exec_prefix)/bin +DESTDIR= + +install: all + $(INSTALL) -d $(DESTDIR)$(bindir) + for i in $(TARGETS) ; do \ + $(INSTALL_PROGRAM) $$i $(DESTDIR)$(bindir)/$$i ; \ + done + +uninstall: + -for i in $(TARGETS) ; do \ + rm -f $(DESTDIR)$(bindir)/$$i ; \ + done + +# dependancies +# generated by `gcc -MM *.c' + +cuebreakpoints.o: cuebreakpoints.c ../lib/cuefile.h ../lib/cd.h \ + ../lib/cdtext.h +cueconvert.o: cueconvert.c ../lib/cuefile.h ../lib/cd.h ../lib/cdtext.h +cueprint.o: cueprint.c ../lib/cuefile.h ../lib/cd.h ../lib/cdtext.h diff --git a/src/Makefile.am b/src/Makefile.am new file mode 100644 index 0000000..6086ffb --- /dev/null +++ b/src/Makefile.am @@ -0,0 +1 @@ +SUBDIRS = lib tools diff --git a/src/lib/Makefile.am b/src/lib/Makefile.am new file mode 100644 index 0000000..7168386 --- /dev/null +++ b/src/lib/Makefile.am @@ -0,0 +1,7 @@ +BUILT_SOURCES = cue_parse.h toc_parse.h +EXTRA_DIST = $(BUILT_SOURCES) +AM_YFLAGS = -d +AM_LFLAGS = -olex.yy.c +noinst_HEADERS = cd.h cdtext.h cue_parse_prefix.h toc_parse_prefix.h cuefile.h cue.h time.h toc.h +noinst_LIBRARIES = libcuefile.a +libcuefile_a_SOURCES = cd.c cdtext.c time.c cuefile.c cue_print.c toc_print.c cue_parse.y cue_scan.l toc_parse.y toc_scan.l diff --git a/src/lib/cd.c b/src/lib/cd.c new file mode 100644 index 0000000..c8fba6e --- /dev/null +++ b/src/lib/cd.c @@ -0,0 +1,329 @@ +/* + * cd.c -- cd functions + * + * Copyright (C) 2004 Svend Sorensen + * For license terms, see the file COPYING in this distribution. + */ + +#include +#include +#include +#include "cd.h" + +typedef struct Data Data; +struct Data { + int type; /* DataType */ + char *name; /* data source name */ + long start; /* start time for data */ + long length; /* length of data */ +}; + +struct Track { + Data zero_pre; /* pre-gap generated with zero data */ + Data file; /* track data file */ + Data zero_post; /* post-gap generated with zero data */ + int mode; /* track mode */ + int sub_mode; /* sub-channel mode */ + int flags; /* flags */ + char *isrc; /* IRSC Code (5.22.4) 12 bytes */ + Cdtext *cdtext; /* CD-TEXT */ + int nindex; /* number of indexes */ + long index[MAXINDEX]; /* indexes (in frames) (5.29.2.5) + * relative to start of track + * index[0] should always be zero */ +}; + +struct Cd { + int mode; /* disc mode */ + char *catalog; /* Media Catalog Number (5.22.3) */ + Cdtext *cdtext; /* CD-TEXT */ + int ntrack; /* number of tracks in album */ + Track *track[MAXTRACK]; /* array of tracks */ +}; + +Cd *cd_init () +{ + Cd *cd = NULL; + cd = malloc(sizeof(Cd)); + + if(NULL == cd) { + fprintf(stderr, "unable to create cd\n"); + } else { + cd->mode = MODE_CD_DA; + cd->catalog = NULL; + cd->cdtext = cdtext_init(); + cd->ntrack = 0; + } + + return cd; +} + +Track *track_init () +{ + Track *track = NULL; + track = malloc(sizeof(Track)); + + if (NULL == track) { + fprintf(stderr, "unable to create track\n"); + } else { + track->zero_pre.type = DATA_ZERO; + track->zero_pre.name = NULL; + track->zero_pre.start = 0; + track->zero_pre.length = 0; + + track->file.type = DATA_AUDIO; + track->file.name = NULL; + track->file.start = 0; + track->file.length = 0; + + track->zero_post.type = DATA_ZERO; + track->zero_post.name = NULL; + track->zero_post.start = 0; + track->zero_post.length = 0; + + track->mode = MODE_AUDIO; + track->sub_mode = SUB_MODE_RW; + track->flags = FLAG_NONE; + track->isrc = NULL; + track->cdtext = cdtext_init(); + track->nindex = 0; + } + + return track; +} + +/* + * cd structure functions + */ +void cd_set_mode (Cd *cd, int mode) +{ + cd->mode = mode; +} + +int cd_get_mode (Cd *cd) +{ + return cd->mode; +} + +void cd_set_catalog (Cd *cd, char *catalog) +{ + if (cd->catalog) + free(cd->catalog); + + cd->catalog = strdup(catalog); +} + +char *cd_get_catalog (Cd *cd) +{ + return cd->catalog; +} + +Cdtext *cd_get_cdtext (Cd *cd) +{ + return cd->cdtext; +} + +Track *cd_add_track (Cd *cd) +{ + if (MAXTRACK - 1 > cd->ntrack) + cd->ntrack++; + else + fprintf(stderr, "too many tracks\n"); + + /* this will reinit last track if there were too many */ + cd->track[cd->ntrack - 1] = track_init(); + + return cd->track[cd->ntrack - 1]; +} + + +int cd_get_ntrack (Cd *cd) +{ + return cd->ntrack; +} + +Track *cd_get_track (Cd *cd, int i) +{ + if (0 < i <= cd->ntrack) + return cd->track[i - 1]; + + return NULL; +} + +/* + * track structure functions + */ + +void track_set_filename (Track *track, char *filename) +{ + if (track->file.name) + free(track->file.name); + + track->file.name = strdup(filename); +} + +char *track_get_filename (Track *track) +{ + return track->file.name; +} + +void track_set_start (Track *track, long start) +{ + track->file.start = start; +} + +long track_get_start (Track *track) +{ + return track->file.start; +} + +void track_set_length (Track *track, long length) +{ + track->file.length = length; +} + +long track_get_length (Track *track) +{ + return track->file.length; +} + +void track_set_mode (Track *track, int mode) +{ + track->mode = mode; +} + +int track_get_mode (Track *track) +{ + return track->mode; +} + +void track_set_sub_mode (Track *track, int sub_mode) +{ + track->sub_mode = sub_mode; +} + +int track_get_sub_mode (Track *track) +{ + return track->sub_mode; +} + +void track_set_flag (Track *track, int flag) +{ + track->flags |= flag; +} + +void track_clear_flag (Track *track, int flag) +{ + track->flags &= ~flag; +} + +int track_is_set_flag (Track *track, int flag) +{ + return track->flags & flag; +} + +void track_set_zero_pre (Track *track, long length) +{ + track->zero_pre.length = length; +} + +long track_get_zero_pre (Track *track) +{ + return track->zero_pre.length; +} + +void track_set_zero_post (Track *track, long length) +{ + track->zero_post.length = length; +} + +long track_get_zero_post (Track *track) +{ + return track->zero_post.length; +} +void track_set_isrc (Track *track, char *isrc) +{ + if (track->isrc) + free(track->isrc); + + track->isrc = strdup(isrc); +} + +char *track_get_isrc (Track *track) +{ + return track->isrc; +} + +Cdtext *track_get_cdtext (Track *track) +{ + return track->cdtext; +} + +void track_add_index (Track *track, long index) +{ + if (MAXTRACK - 1 > track->nindex) + track->nindex++; + else + fprintf(stderr, "too many indexes\n"); + + /* this will overwrite last index if there were too many */ + track->index[track->nindex - 1] = index; +} + +int track_get_nindex (Track *track) +{ + return track->nindex; +} + +long track_get_index (Track *track, int i) +{ + if (0 <= i < track->nindex) + return track->index[i]; + + return -1; +} + +/* + * dump cd information + */ +void cd_track_dump (Track *track) +{ + int i; + + printf("zero_pre: %ld\n", track->zero_pre.length); + printf("filename: %s\n", track->file.name); + printf("start: %ld\n", track->file.start); + printf("length: %ld\n", track->file.length); + printf("zero_post: %ld\n", track->zero_post.length); + printf("mode: %d\n", track->mode); + printf("sub_mode: %d\n", track->sub_mode); + printf("flags: 0x%x\n", track->flags); + printf("isrc: %s\n", track->isrc); + printf("indexes: %d\n", track->nindex); + + for (i = 0; i < track->nindex; ++i) + printf("index %d: %ld\n", i, track->index[i]); + + if (NULL != track->cdtext) { + printf("cdtext:\n"); + cdtext_dump(track->cdtext, 1); + } +} + +void cd_dump (Cd *cd) +{ + int i; + + printf("Disc Info\n"); + printf("mode: %d\n", cd->mode); + printf("catalog: %s\n", cd->catalog); + if (NULL != cd->cdtext) { + printf("cdtext:\n"); + cdtext_dump(cd->cdtext, 0); + } + + for (i = 0; i < cd->ntrack; ++i) { + printf("Track %d Info\n", i + 1); + cd_track_dump(cd->track[i]); + } +} diff --git a/src/lib/cd.h b/src/lib/cd.h new file mode 100644 index 0000000..c08743a --- /dev/null +++ b/src/lib/cd.h @@ -0,0 +1,161 @@ +/* + * cd.h -- cd structure + * + * Copyright (C) 2004 Svend Sorensen + * For license terms, see the file COPYING in this distribution. + */ + +/* references: MMC-3 draft revsion - 10g */ + +#ifndef CD_H +#define CD_H + +#include "cdtext.h" + +#define MAXTRACK 99 /* Red Book track limit */ +#define MAXINDEX 99 /* Red Book index limit */ + +/* + * disc modes + * DATA FORM OF MAIN DATA (5.29.2.8) + */ +enum DiscMode { + MODE_CD_DA, /* CD-DA */ + MODE_CD_ROM, /* CD-ROM mode 1 */ + MODE_CD_ROM_XA /* CD-ROM XA and CD-I */ +}; + +/* + * track modes + * 5.29.2.8 DATA FORM OF MAIN DATA + * Table 350 - Data Block Type Codes + */ +enum TrackMode { + MODE_AUDIO, /* 2352 byte block length */ + MODE_MODE1, /* 2048 byte block length */ + MODE_MODE1_RAW, /* 2352 byte block length */ + MODE_MODE2, /* 2336 byte block length */ + MODE_MODE2_FORM1, /* 2048 byte block length */ + MODE_MODE2_FORM2, /* 2324 byte block length */ + MODE_MODE2_FORM_MIX, /* 2332 byte block length */ + MODE_MODE2_RAW /* 2352 byte block length */ +}; + +/* + * sub-channel mode + * 5.29.2.13 Data Form of Sub-channel + * NOTE: not sure if this applies to cue files + */ +enum TrackSubMode { + SUB_MODE_RW, /* RAW Data */ + SUB_MODE_RW_RAW /* PACK DATA (written R-W */ +}; + +/* + * track flags + * Q Sub-channel Control Field (4.2.3.3, 5.29.2.2) + */ +enum TrackFlag { + FLAG_NONE =0x00, /* no flags set */ + FLAG_PRE_EMPHASIS =0x01, /* audio recorded with pre-emphasis */ + FLAG_COPY_PERMITTED =0x02, /* digital copy permitted */ + FLAG_DATA =0x04, /* data track */ + FLAG_FOUR_CHANNEL =0x08, /* 4 audio channels */ + FLAG_SCMS =0x10, /* SCMS (not Q Sub-ch.) (5.29.2.7) */ + FLAG_ANY =0xff /* any flags set */ +}; + +enum DataType { + DATA_AUDIO, + DATA_DATA, + DATA_FIFO, + DATA_ZERO +}; + +/* ADTs */ +typedef struct Cd Cd; +typedef struct Track Track; + +/* return pointer to CD structure */ +Cd *cd_init (); + +/* dump all info from CD structure + * in human readable format (for debugging) + */ +void cd_dump (Cd *cd); + +/* + * Cd functions + */ + +void cd_set_mode (Cd *cd, int mode); +int cd_get_mode (Cd *cd); + +void cd_set_catalog (Cd *cd, char *catalog); +char *cd_get_catalog (Cd *cd); + +/* + * return pointer to cd's Cdtext + */ +Cdtext *cd_get_cdtext (Cd *cd); + +/* + * add a new track to cd, increment number of tracks + * and return pointer to new track + */ +Track *cd_add_track (Cd *cd); + +/* + * return number of tracks in cd + */ +int cd_get_ntrack (Cd *cd); + +Track *cd_get_track (Cd *cd, int i); + +/* + * Track functions + */ + +/* filename of data file */ +void track_set_filename (Track *track, char *filename); +char *track_get_filename (Track *track); + +/* track start is starting position in data file */ +void track_set_start (Track *track, long start); +long track_get_start (Track *track); + +/* track length is length of data file to use */ +void track_set_length (Track *track, long length); +long track_get_length (Track *track); + +/* see enum TrackMode */ +void track_set_mode (Track *track, int mode); +int track_get_mode (Track *track); + +/* see enum TrackSubMode */ +void track_set_sub_mode (Track *track, int sub_mode); +int track_get_sub_mode (Track *track); + +/* see enum TrackFlag */ +void track_set_flag (Track *track, int flag); +void track_clear_flag (Track *track, int flag); +int track_is_set_flag (Track *track, int flag); + +/* zero data pregap */ +void track_set_zero_pre (Track *track, long length); +long track_get_zero_pre (Track *track); + +/* zero data postgap */ +void track_set_zero_post (Track *track, long length); +long track_get_zero_post (Track *track); + +void track_set_isrc (Track *track, char *isrc); +char *track_get_isrc (Track *track); + +Cdtext *track_get_cdtext (Track *track); + +void track_add_index (Track *track, long index); +int track_get_nindex (Track *track); +long track_get_index (Track *track, int i); + +#endif diff --git a/src/lib/cdtext.c b/src/lib/cdtext.c new file mode 100644 index 0000000..3a4ba7a --- /dev/null +++ b/src/lib/cdtext.c @@ -0,0 +1,166 @@ +/* + * cdtext.c -- cdtext data structure and functions + * + * Copyright (C) 2004 Svend Sorensen + * For license terms, see the file COPYING in this distribution. + */ + +#include +#include +#include +#include "cdtext.h" + +struct Cdtext { + int pti; + int format; + char *value; +}; + +Cdtext *cdtext_init () +{ + Cdtext *new_cdtext = NULL; + + Cdtext cdtext[] = { + {PTI_TITLE, FORMAT_CHAR, NULL}, + {PTI_PERFORMER, FORMAT_CHAR, NULL}, + {PTI_SONGWRITER, FORMAT_CHAR, NULL}, + {PTI_COMPOSER, FORMAT_CHAR, NULL}, + {PTI_ARRANGER, FORMAT_CHAR, NULL}, + {PTI_MESSAGE, FORMAT_CHAR, NULL}, + {PTI_DISC_ID, FORMAT_BINARY, NULL}, + {PTI_GENRE, FORMAT_BINARY, NULL}, + {PTI_TOC_INFO1, FORMAT_BINARY, NULL}, + {PTI_TOC_INFO2, FORMAT_BINARY, NULL}, + {PTI_RESERVED1, FORMAT_CHAR, NULL}, + {PTI_RESERVED2, FORMAT_CHAR, NULL}, + {PTI_RESERVED3, FORMAT_CHAR, NULL}, + {PTI_RESERVED4, FORMAT_CHAR, NULL}, + {PTI_UPC_ISRC, FORMAT_CHAR, NULL}, + {PTI_SIZE_INFO, FORMAT_BINARY, NULL}, + {PTI_END, FORMAT_CHAR, NULL} + }; + + new_cdtext = (Cdtext *) calloc (sizeof (cdtext) / sizeof (Cdtext), sizeof (Cdtext)); + if (NULL == new_cdtext) + fprintf (stderr, "problem allocating memory\n"); + else + memcpy (new_cdtext, cdtext, sizeof(cdtext)); + + return new_cdtext; +} + +void cdtext_delete (Cdtext *cdtext) +{ + int i; + + if (NULL != cdtext) { + for (i = 0; PTI_END != cdtext[i].pti; i++) + free (cdtext[i].value); + free (cdtext); + } +} + +/* return 0 if there is no cdtext, returns non-zero otherwise */ +int cdtext_is_empty (Cdtext *cdtext) +{ + for (; PTI_END != cdtext->pti; cdtext++) + if (NULL != cdtext->value) + return -1; + + return 0; +} + +/* sets cdtext's pti entry to field */ +void cdtext_set (int pti, char *value, Cdtext *cdtext) +{ + if (NULL != value) /* don't pass NULL to strdup */ + for (; PTI_END != cdtext->pti; cdtext++) + if (pti == cdtext->pti) { + free (cdtext->value); + cdtext->value = strdup (value); + } +} + +/* returns value for pti, NULL if pti is not found */ +char *cdtext_get (int pti, Cdtext *cdtext) +{ + for (; PTI_END != cdtext->pti; cdtext++) + if (pti == cdtext->pti) + return cdtext->value; + + return NULL; +} + +const char *cdtext_get_key (int pti, int istrack) +{ + char *key = NULL; + + switch (pti) { + case PTI_TITLE: + key = "TITLE"; + break; + case PTI_PERFORMER: + key = "PERFORMER"; + break; + case PTI_SONGWRITER: + key = "SONGWRITER"; + break; + case PTI_COMPOSER: + key = "COMPOSER"; + break; + case PTI_ARRANGER: + key = "ARRANGER"; + break; + case PTI_MESSAGE: + key = "MESSAGE"; + break; + case PTI_DISC_ID: + key = "DISC_ID"; + break; + case PTI_GENRE: + key = "GENRE"; + break; + case PTI_TOC_INFO1: + key = "TOC_INFO1"; + break; + case PTI_TOC_INFO2: + key = "TOC_INFO1"; + break; + case PTI_RESERVED1: + /* reserved */ + break; + case PTI_RESERVED2: + /* reserved */ + break; + case PTI_RESERVED3: + /* reserved */ + break; + case PTI_RESERVED4: + /* reserved */ + break; + case PTI_UPC_ISRC: + if (0 == istrack) + key = "UPC_EAN"; + else + key = "ISRC"; + break; + case PTI_SIZE_INFO: + key = "SIZE_INFO"; + break; + } + + return key; +} + +void cdtext_dump (Cdtext *cdtext, int istrack) +{ + int pti; + char *value = NULL; + + for (pti = 0; PTI_END != pti; pti++) { + if (NULL != (value = cdtext_get(pti, cdtext))) { + printf("%s: ", cdtext_get_key(pti, istrack)); + printf("%s\n", value); + } + } +} diff --git a/src/lib/cdtext.h b/src/lib/cdtext.h new file mode 100644 index 0000000..e7e8e6a --- /dev/null +++ b/src/lib/cdtext.h @@ -0,0 +1,71 @@ +/* + * cdtext.h + * + * Copyright (C) 2004 Svend Sorensen + * For license terms, see the file COPYING in this distribution. + */ + +/* references: MMC-3 draft revsion - 10g */ + +#ifndef CDTEXT_H +#define CDTEXT_H + +#include + +/* cdtext pack type indicators */ +enum Pti { + PTI_TITLE, /* title of album or track titles */ + PTI_PERFORMER, /* name(s) of the performer(s) */ + PTI_SONGWRITER, /* name(s) of the songwriter(s) */ + PTI_COMPOSER, /* name(s) of the composer(s) */ + PTI_ARRANGER, /* name(s) of the arranger(s) */ + PTI_MESSAGE, /* message(s) from the content provider and/or artist */ + PTI_DISC_ID, /* (binary) disc identification information */ + PTI_GENRE, /* (binary) genre identification and genre information */ + PTI_TOC_INFO1, /* (binary) table of contents information */ + PTI_TOC_INFO2, /* (binary) second table of contents information */ + PTI_RESERVED1, /* reserved */ + PTI_RESERVED2, /* reserved */ + PTI_RESERVED3, /* reserved */ + PTI_RESERVED4, /* reserved for content provider only */ + PTI_UPC_ISRC, /* UPC/EAN code of the album and ISRC code of each track */ + PTI_SIZE_INFO, /* (binary) size information of the block */ + PTI_END /* terminating PTI (for stepping through PTIs) */ +}; + +enum PtiFormat { + FORMAT_CHAR, /* single or double byte character string */ + FORMAT_BINARY /* binary data */ +}; + +typedef struct Cdtext Cdtext; + +/* return a pointer to a new Cdtext */ +Cdtext *cdtext_init (); + +/* release a Cdtext */ +void cdtext_delete (Cdtext *cdtext); + +/* returns non-zero if there are no CD-TEXT fields set, zero otherwise */ +int cdtext_is_empty (Cdtext *cdtext); + +/* set CD-TEXT field to value for PTI pti */ +void cdtext_set (int pti, char *value, Cdtext *cdtext); + +/* returns pointer to CD-TEXT value for PTI pti */ +char *cdtext_get (int pti, Cdtext *cdtext); + +/* + * returns appropriate string for PTI pti + * if istrack is zero, UPC/EAN string will be returned for PTI_UPC_ISRC + * othwise ISRC string will be returned + */ +const char *cdtext_get_key (int pti, int istrack); + +/* + * dump all cdtext info + * in human readable format (for debugging) + */ +void cdtext_dump (Cdtext *cdtext, int istrack); + +#endif diff --git a/src/lib/cue.h b/src/lib/cue.h new file mode 100644 index 0000000..b85fdfc --- /dev/null +++ b/src/lib/cue.h @@ -0,0 +1,9 @@ +/* + * cue.h -- cue function declarations + * + * Copyright (C) 2004 Svend Sorensen + * For license terms, see the file COPYING in this distribution. + */ + +Cd *cue_parse (FILE *fp); +void cue_print (FILE *fp, Cd *cd); diff --git a/src/lib/cue_parse.y b/src/lib/cue_parse.y new file mode 100644 index 0000000..62f43f5 --- /dev/null +++ b/src/lib/cue_parse.y @@ -0,0 +1,280 @@ +%{ +/* + * cue_parse.y -- parser for cue files + * + * Copyright (C) 2004 Svend Sorensen + * For license terms, see the file COPYING in this distribution. + */ + +#include +#include +#include +#include "cd.h" +#include "time.h" +#include "cue_parse_prefix.h" + +#define YYDEBUG 1 + +extern int yylex(); +void yyerror (char *s); + +static Cd *cd = NULL; +static Track *track = NULL; +static Track *prev_track = NULL; +static Cdtext *cdtext = NULL; +static char *prev_filename = NULL; /* last file in or before last track */ +static char *cur_filename = NULL; /* last file in the last track */ +static char *new_filename = NULL; /* last file in this track */ +%} + +%start cuefile + +%union { + long ival; + char *sval; +} + +%token NUMBER +%token STRING + +/* global (header) */ +%token CATALOG +%token CDTEXTFILE + +%token FFILE +%token BINARY +%token MOTOROLA +%token AIFF +%token WAVE +%token MP3 + +/* track */ +%token TRACK + +%token AUDIO +%token MODE1_2048 +%token MODE1_2352 +%token MODE2_2336 +%token MODE2_2048 +%token MODE2_2342 +%token MODE2_2332 +%token MODE2_2352 + +/* ISRC is with CD_TEXT */ +%token TRACK_ISRC + +%token FLAGS +%token PRE +%token DCP +%token FOUR_CH +%token SCMS + +%token PREGAP +%token INDEX +%token POSTGAP + +/* CD-TEXT */ +%token TITLE +%token PERFORMER +%token SONGWRITER +%token COMPOSER +%token ARRANGER +%token MESSAGE +%token DISC_ID +%token GENRE +%token TOC_INFO1 +%token TOC_INFO2 +%token UPC_EAN +%token ISRC +%token SIZE_INFO + +%type track_mode +%type track_flag +%type time +%type cdtext_item + +%% + +cuefile + : new_cd global_statements track_list + ; + +new_cd + : /* empty */ { + cd = cd_init(); + cdtext = cd_get_cdtext(cd); + } + ; + +global_statements + : /* empty */ + | global_statements global_statement + ; + +global_statement + : CATALOG STRING '\n' { cd_set_catalog(cd, $2); } + | CDTEXTFILE STRING '\n' { /* ignored */ } + | cdtext + | track_data + | error '\n' + ; + +track_data + : FFILE STRING file_format '\n' { + if (NULL != new_filename) { + yyerror("too many files specified\n"); + free(new_filename); + } + new_filename = strdup($2); + } + ; + +track_list + : track + | track_list track + ; + +track + : new_track track_def track_statements + ; + +file_format + : BINARY + | MOTOROLA + | AIFF + | WAVE + | MP3 + ; + +new_track + : /*empty */ { + /* save previous track, to later set length */ + prev_track = track; + + track = cd_add_track(cd); + cdtext = track_get_cdtext(track); + + cur_filename = new_filename; + if (NULL != cur_filename) + prev_filename = cur_filename; + + if (NULL == prev_filename) + yyerror("no file specified for track"); + else + track_set_filename(track, prev_filename); + + new_filename = NULL; + } + ; + +track_def + : TRACK NUMBER track_mode '\n' { + track_set_mode(track, $3); + } + ; + +track_mode + : AUDIO + | MODE1_2048 + | MODE1_2352 + | MODE2_2336 + | MODE2_2048 + | MODE2_2342 + | MODE2_2332 + | MODE2_2352 + ; + +track_statements + : track_statement + | track_statements track_statement + ; + +track_statement + : cdtext + | FLAGS track_flags '\n' + | TRACK_ISRC STRING '\n' { track_set_isrc(track, $2); } + | PREGAP time '\n' { track_set_zero_pre(track, $2); } + | INDEX NUMBER time '\n' { + int i = track_get_nindex(track); + long prev_length; + + if (0 == i) { + /* first index */ + track_set_start(track, $3); + + if (NULL != prev_track && NULL == cur_filename) { + /* track shares file with previous track */ + prev_length = $3 - track_get_start(prev_track); + track_set_length(prev_track, prev_length); + } + } + + for (; i <= $2; i++) + track_add_index(track, \ + track_get_zero_pre(track) + $3 \ + - track_get_start(track)); + } + | POSTGAP time '\n' { track_set_zero_post(track, $2); } + | track_data + | error '\n' + ; + +track_flags + : /* empty */ + | track_flags track_flag { track_set_flag(track, $2); } + ; + +track_flag + : PRE + | DCP + | FOUR_CH + | SCMS + ; + +cdtext + : cdtext_item STRING '\n' { cdtext_set ($1, $2, cdtext); } + ; + +cdtext_item + : TITLE + | PERFORMER + | SONGWRITER + | COMPOSER + | ARRANGER + | MESSAGE + | DISC_ID + | GENRE + | TOC_INFO1 + | TOC_INFO2 + | UPC_EAN + | ISRC + | SIZE_INFO + ; + +time + : NUMBER + | NUMBER ':' NUMBER ':' NUMBER { $$ = time_msf_to_frame($1, $3, $5); } + ; + +%% + +/* lexer interface */ +extern int cue_lineno; +extern int yydebug; +extern FILE *cue_yyin; + +void yyerror (char *s) +{ + fprintf(stderr, "%d: %s\n", cue_lineno, s); +} + +Cd *cue_parse (FILE *fp) +{ + cue_yyin = fp; + yydebug = 0; + + if (0 == yyparse()) + return cd; + + return NULL; +} diff --git a/src/lib/cue_parse_prefix.h b/src/lib/cue_parse_prefix.h new file mode 100644 index 0000000..31b2d38 --- /dev/null +++ b/src/lib/cue_parse_prefix.h @@ -0,0 +1,44 @@ +/* Remap normal yacc names so we can have multiple parsers + * see http://www.gnu.org/software/automake/manual/html_node/Yacc-and-Lex.html + */ + +#define yymaxdepth cue_yymaxdepth +#define yyparse cue_yyparse +#define yylex cue_yylex +#define yyerror cue_yyerror +#define yylval cue_yylval +#define yychar cue_yychar +#define yydebug cue_yydebug +#define yypact cue_yypact +#define yyr1 cue_yyr1 +#define yyr2 cue_yyr2 +#define yydef cue_yydef +#define yychk cue_yychk +#define yypgo cue_yypgo +#define yyact cue_yyact +#define yyexca cue_yyexca +#define yyerrflag cue_yyerrflag +#define yynerrs cue_yynerrs +#define yyps cue_yyps +#define yypv cue_yypv +#define yys cue_yys +#define yy_yys cue_yy_yys +#define yystate cue_yystate +#define yytmp cue_yytmp +#define yyv cue_yyv +#define yy_yyv cue_yy_yyv +#define yyval cue_yyval +#define yylloc cue_yylloc +#define yyreds cue_yyreds +#define yytoks cue_yytoks +#define yylhs cue_yylhs +#define yylen cue_yylen +#define yydefred cue_yydefred +#define yydgoto cue_yydgoto +#define yysinde cue_yysindex +#define yyrindex cue_yyrindex +#define yygindex cue_yygindex +#define yytable cue_yytable +#define yycheck cue_yycheck +#define yyname cue_yyname +#define yyrule cue_yyrule diff --git a/src/lib/cue_print.c b/src/lib/cue_print.c new file mode 100644 index 0000000..17efcca --- /dev/null +++ b/src/lib/cue_print.c @@ -0,0 +1,147 @@ +/* + * cue_print.y -- print cue file + * + * Copyright (C) 2004 Svend Sorensen + * For license terms, see the file COPYING in this distribution. + */ + +#include +#include +#include "cd.h" +#include "time.h" + +void cue_print_track (FILE *fp, Track *track, int trackno); +void cue_print_cdtext (Cdtext *cdtext, FILE *fp, int istrack); +void cue_print_index (long i, FILE *fp); +char *filename = ""; /* last track datafile */ +long prev_length = 0; /* last track length */ + +/* prints cd in cue format */ +void cue_print (FILE *fp, Cd *cd) +{ + Cdtext *cdtext = cd_get_cdtext(cd); + int i; /* track */ + Track *track = NULL; + + /* print global information */ + if (NULL != cd_get_catalog(cd)) + fprintf(fp, "CATALOG %s\n", cd_get_catalog(cd)); + + cue_print_cdtext(cdtext, fp, 0); + + /* print track information */ + for (i = 1; i <= cd_get_ntrack(cd); i++) { + track = cd_get_track(cd, i); + fprintf(fp, "\n"); + cue_print_track(fp, track, i); + } +} + +void cue_print_track (FILE *fp, Track *track, int trackno) +{ + Cdtext *cdtext = track_get_cdtext(track); + int i; /* index */ + + if (NULL != track_get_filename(track)) { + /* + * always print filename for track 1, afterwards only + * print filename if it differs from the previous track + */ + if (0 != strcmp(track_get_filename(track), filename)) { + filename = track_get_filename(track); + fprintf(fp, "FILE \"%s\" ", filename); + + /* NOTE: what to do with other formats (MP3, etc)? */ + if (MODE_AUDIO == track_get_mode(track)) + fprintf(fp, "WAVE\n"); + else + fprintf(fp, "BINARY\n"); + } + } + + fprintf(fp, "TRACK %02d ", trackno); + switch (track_get_mode(track)) { + case MODE_AUDIO: + fprintf(fp, "AUDIO\n"); + break; + case MODE_MODE1: + fprintf(fp, "MODE1/2048\n"); + break; + case MODE_MODE1_RAW: + fprintf(fp, "MODE1/2352\n"); + break; + case MODE_MODE2: + fprintf(fp, "MODE2/2048\n"); + break; + case MODE_MODE2_FORM1: + fprintf(fp, "MODE2/2336\n"); + break; + case MODE_MODE2_FORM2: + fprintf(fp, "MODE2/2324\n"); + break; + case MODE_MODE2_FORM_MIX: + fprintf(fp, "MODE2/2336\n"); + break; + case MODE_MODE2_RAW: + fprintf(fp, "MODE2/2352\n"); + break; + } + + cue_print_cdtext(cdtext, fp, 1); + + if (0 != track_is_set_flag(track, FLAG_ANY)) { + fprintf(fp, "FLAGS"); + if (0 != track_is_set_flag(track, FLAG_PRE_EMPHASIS)) + fprintf(fp, " PRE"); + if (0 != track_is_set_flag(track, FLAG_COPY_PERMITTED)) + fprintf(fp, " DCP"); + if (0 != track_is_set_flag(track, FLAG_FOUR_CHANNEL)) + fprintf(fp, " 4CH"); + if (0 != track_is_set_flag(track, FLAG_SCMS)) + fprintf(fp, " SCMS"); + fprintf(fp, "\n"); + } + + if (NULL != track_get_isrc(track)) + fprintf(fp, "ISRC %s\n", track_get_isrc(track)); + + if (0 != track_get_zero_pre(track)) + fprintf (fp, "PREGAP %s\n", time_frame_to_mmssff(track_get_zero_pre(track))); + + /* don't print index 0 if index 1 = 0 */ + if (track_get_index(track, 1) == 0) + i = 1; + else + i = 0; + + for (; i < track_get_nindex(track); i++) { + fprintf(fp, "INDEX %02d ", i); + cue_print_index( \ + track_get_index(track, i) \ + + track_get_start(track) \ + - track_get_zero_pre(track) , fp); + } + + if (0 != track_get_zero_post(track)) + fprintf (fp, "POSTGAP %s\n", time_frame_to_mmssff(track_get_zero_post(track))); + + prev_length = track_get_length(track); +} + +void cue_print_cdtext (Cdtext *cdtext, FILE *fp, int istrack) +{ + int pti; + char *value = NULL; + + for (pti = 0; PTI_END != pti; pti++) { + if (NULL != (value = cdtext_get(pti, cdtext))) { + fprintf(fp, "%s", cdtext_get_key(pti, istrack)); + fprintf(fp, " \"%s\"\n", value); + } + } +} + +void cue_print_index (long i, FILE *fp) +{ + fprintf (fp, "%s\n", time_frame_to_mmssff(i)); +} diff --git a/src/lib/cue_scan.l b/src/lib/cue_scan.l new file mode 100644 index 0000000..7884830 --- /dev/null +++ b/src/lib/cue_scan.l @@ -0,0 +1,98 @@ +%{ +/* + * cue_scan.l -- lexer for cue files + * + * Copyright (C) 2004 Svend Sorensen + * For license terms, see the file COPYING in this distribution. + */ + +#include +#include +#include "cd.h" +#include "cue_parse_prefix.h" +#include "cue_parse.h" + +int cue_lineno = 1; +%} + +ws [ \t\r] +nonws [^ \t\r\n] + +%option noyywrap +%option prefix="cue_yy" + +%s NAME + +%% + +\'([^\']|\\\')*\' | +\"([^\"]|\\\")*\" { + yylval.sval = strdup(yytext + 1); + yylval.sval[strlen(yylval.sval) - 1] = '\0'; + BEGIN(INITIAL); + return STRING; + } + +{nonws}+ { + yylval.sval = strdup(yytext); + BEGIN(INITIAL); + return STRING; + } + +CATALOG { BEGIN(NAME); return CATALOG; } +CDTEXTFILE { BEGIN(NAME); return CDTEXTFILE; } + +FILE { BEGIN(NAME); return FFILE; } +BINARY { return BINARY; } +MOTOROLA { return MOTOROLA; } +AIFF { return AIFF; } +WAVE { return WAVE; } +MP3 { return MP3; } + +TRACK { return TRACK; } +AUDIO { yylval.ival = MODE_AUDIO; return AUDIO; } +MODE1\/2048 { yylval.ival = MODE_MODE1; return MODE1_2048; } +MODE1\/2352 { yylval.ival = MODE_MODE1_RAW; return MODE1_2352; } +MODE2\/2336 { yylval.ival = MODE_MODE2; return MODE2_2336; } +MODE2\/2048 { yylval.ival = MODE_MODE2_FORM1; return MODE2_2048; } +MODE2\/2342 { yylval.ival = MODE_MODE2_FORM2; return MODE2_2342; } +MODE2\/2332 { yylval.ival = MODE_MODE2_FORM_MIX; return MODE2_2332; } +MODE2\/2352 { yylval.ival = MODE_MODE2_RAW; return MODE2_2352; } + +FLAGS { return FLAGS; } +PRE { yylval.ival = FLAG_PRE_EMPHASIS; return PRE; } +DCP { yylval.ival = FLAG_COPY_PERMITTED; return DCP; } +4CH { yylval.ival = FLAG_FOUR_CHANNEL; return FOUR_CH; } +SCMS { yylval.ival = FLAG_SCMS; return SCMS; } + +PREGAP { return PREGAP; } +INDEX { return INDEX; } +POSTGAP { return POSTGAP; } + +TITLE { BEGIN(NAME); yylval.ival = PTI_TITLE; return TITLE; } +PERFORMER { BEGIN(NAME); yylval.ival = PTI_PERFORMER; return PERFORMER; } +SONGWRITER { BEGIN(NAME); yylval.ival = PTI_SONGWRITER; return SONGWRITER; } +COMPOSER { BEGIN(NAME); yylval.ival = PTI_COMPOSER; return COMPOSER; } +ARRANGER { BEGIN(NAME); yylval.ival = PTI_ARRANGER; return ARRANGER; } +MESSAGE { BEGIN(NAME); yylval.ival = PTI_MESSAGE; return MESSAGE; } +DISC_ID { BEGIN(NAME); yylval.ival = PTI_DISC_ID; return DISC_ID; } +GENRE { BEGIN(NAME); yylval.ival = PTI_GENRE; return GENRE; } +TOC_INFO1 { BEGIN(NAME); yylval.ival = PTI_TOC_INFO1; return TOC_INFO1; } +TOC_INFO2 { BEGIN(NAME); yylval.ival = PTI_TOC_INFO2; return TOC_INFO2; } +UPC_EAN { BEGIN(NAME); yylval.ival = PTI_UPC_ISRC; return UPC_EAN; } +ISRC/{ws}+\" { BEGIN(NAME); yylval.ival = PTI_UPC_ISRC; return ISRC; } +SIZE_INFO { BEGIN(NAME); yylval.ival = PTI_SIZE_INFO; return SIZE_INFO; } + +ISRC { BEGIN(NAME); return TRACK_ISRC; } + +^{ws}*REM.*\n { cue_lineno++; /* ignore comments */ } +{ws}+ { /* ignore whitespace */ } + +[[:digit:]]+ { yylval.ival = atoi(yytext); return NUMBER; } +: { return yytext[0]; } + +^{ws}*\n { cue_lineno++; /* blank line */ } +\n { cue_lineno++; return '\n'; } +. { fprintf(stderr, "bad character '%c'\n", yytext[0]); } + +%% diff --git a/src/lib/cuefile.c b/src/lib/cuefile.c new file mode 100644 index 0000000..d65d9f2 --- /dev/null +++ b/src/lib/cuefile.c @@ -0,0 +1,90 @@ +/* + * cuefile.c -- cue/toc functions + * + * Copyright (C) 2004 Svend Sorensen + * For license terms, see the file COPYING in this distribution. + */ + +#include +#include +#include "cuefile.h" +#include "cue.h" +#include "toc.h" + +Cd *cf_parse (char *name, int *format) +{ + FILE *fp = NULL; + Cd *cd = NULL; + + if (UNKNOWN == *format) + if (UNKNOWN == (*format = cf_format_from_suffix(name))) { + fprintf(stderr, "%s: unknown format\n", name); + return NULL; + } + + if (0 == strcmp("-", name)) { + fp = stdin; + } else if (NULL == (fp = fopen(name, "r"))) { + fprintf(stderr, "%s: error opening file\n", name); + return NULL; + } + + switch (*format) { + case CUE: + cd = cue_parse(fp); + break; + case TOC: + cd = toc_parse(fp); + break; + } + + if(stdin != fp) + fclose(fp); + + return cd; +} + +int cf_print (char *name, int *format, Cd *cd) +{ + FILE *fp = NULL; + + if (UNKNOWN == *format) + if (UNKNOWN == (*format = cf_format_from_suffix(name))) { + fprintf(stderr, "%s: unknown format\n", name); + return -1; + } + + if (0 == strcmp("-", name)) { + fp = stdout; + } else if (NULL == (fp = fopen(name, "w"))) { + fprintf(stderr, "%s: error opening file\n", name); + return -1; + } + + switch (*format) { + case CUE: + cue_print(fp, cd); + break; + case TOC: + toc_print(fp, cd); + break; + } + + if(stdout != fp) + fclose(fp); + + return 0; +} + +int cf_format_from_suffix (char *name) +{ + char *suffix; + if (0 != (suffix = strrchr(name, '.'))) { + if (0 == strcasecmp(".cue", suffix)) + return CUE; + else if (0 == strcasecmp(".toc", suffix)) + return TOC; + } + + return UNKNOWN; +} diff --git a/src/lib/cuefile.h b/src/lib/cuefile.h new file mode 100644 index 0000000..f3efb67 --- /dev/null +++ b/src/lib/cuefile.h @@ -0,0 +1,16 @@ +/* + * cuefile.h -- cue/toc public declarations + * + * Copyright (C) 2004 Svend Sorensen + * For license terms, see the file COPYING in this distribution. + */ + +#include "cd.h" + +enum {CUE, TOC, UNKNOWN}; + +typedef struct Cue Cue; + +Cd *cf_parse (char *fname, int *format); +int cf_print (char *fname, int *format, Cd *cue); +int cf_format_from_suffix (char *fname); diff --git a/src/lib/time.c b/src/lib/time.c new file mode 100644 index 0000000..9c31b2d --- /dev/null +++ b/src/lib/time.c @@ -0,0 +1,44 @@ +/* + * time.c -- time functions + * + * Copyright (C) 2004 Svend Sorensen + * For license terms, see the file COPYING in this distribution. + */ + +#include +#include + +long time_msf_to_frame (int m, int s, int f) +{ + return (m * 60 + s) * 75 + f; +} + +void msf_frame_to_msf (long frame, int *m, int *s, int *f) +{ + *f = frame % 75; /* 0 <= frames <= 74 */ + frame /= 75; + *s = frame % 60; /* 0 <= seconds <= 59 */ + frame /= 60; + *m = frame; /* 0 <= minutes */ +} + +void time_frame_to_msf (long frame, int *m, int *s, int *f) +{ + *f = frame % 75; /* 0 <= frames <= 74 */ + frame /= 75; + *s = frame % 60; /* 0 <= seconds <= 59 */ + frame /= 60; + *m = frame; /* 0 <= minutes */ +} + +/* print frame in mm:ss:ff format */ +char *time_frame_to_mmssff (long f) +{ + static char msf[9]; + int minutes, seconds, frames; + + msf_frame_to_msf(f, &minutes, &seconds, &frames); + sprintf(msf, "%02d:%02d:%02d", minutes, seconds, frames); + + return msf; +} diff --git a/src/lib/time.h b/src/lib/time.h new file mode 100644 index 0000000..8719635 --- /dev/null +++ b/src/lib/time.h @@ -0,0 +1,15 @@ +/* time.h -- time declarations + * + * Copyright (C) 2004 Svend Sorensen + * For license terms, see the file COPYING in this distribution. + */ + +#ifndef TIME_H +#define TIME_H + +long time_msf_to_frame (int m, int s, int f); +long time_mmssff_to_frame (char *mmssff); +void time_frame_to_msf (long frame, int *m, int *s, int *f); +char *time_frame_to_mmssff (long f); + +#endif diff --git a/src/lib/toc.h b/src/lib/toc.h new file mode 100644 index 0000000..5d100cb --- /dev/null +++ b/src/lib/toc.h @@ -0,0 +1,9 @@ +/* + * toc.h -- toc function declarations + * + * Copyright (C) 2004 Svend Sorensen + * For license terms, see the file COPYING in this distribution. + */ + +Cd *toc_parse (FILE *fp); +void toc_print (FILE *fp, Cd *cd); diff --git a/src/lib/toc_parse.y b/src/lib/toc_parse.y new file mode 100644 index 0000000..a2d932b --- /dev/null +++ b/src/lib/toc_parse.y @@ -0,0 +1,346 @@ +%{ +/* + * toc_parse.y -- parser for toc files + * + * Copyright (C) 2004 Svend Sorensen + * For license terms, see the file COPYING in this distribution. + */ + +#include +#include +#include +#include "cd.h" +#include "time.h" +#include "toc_parse_prefix.h" + +#define YYDEBUG 1 + +extern int yylex(); +void yyerror (char *s); + +static Cd *cd = NULL; +static Track *track = NULL; +static Cdtext *cdtext = NULL; +%} + +%start tocfile + +%union { + long ival; + char *sval; +} + +%token NUMBER +%token STRING + +/* global (header) */ +%token CATALOG + +%token CD_DA +%token CD_ROM +%token CD_ROM_XA + +/* track */ +%token TRACK +%token AUDIO +%token MODE1 +%token MODE1_RAW +%token MODE2 +%token MODE2_FORM1 +%token MODE2_FORM2 +%token MODE2_FORM_MIX +%token MODE2_RAW +%token RW +%token RW_RAW + +%token NO +%token COPY +%token PRE_EMPHASIS +%token TWO_CHANNEL_AUDIO +%token FOUR_CHANNEL_AUDIO + +%token ISRC +%token SILENCE +%token ZERO +%token AUDIOFILE +%token DATAFILE +%token FIFO +%token START +%token PREGAP +%token INDEX + +/* CD-TEXT */ +%token CD_TEXT +%token LANGUAGE_MAP +%token LANGUAGE + +%token TITLE +%token PERFORMER +%token SONGWRITER +%token COMPOSER +%token ARRANGER +%token MESSAGE +%token DISC_ID +%token GENRE +%token TOC_INFO1 +%token TOC_INFO2 +%token UPC_EAN +%token ISRC +%token SIZE_INFO + +%type disc_mode +%type track_modes +%type track_mode +%type track_sub_mode +%type track_set_flag +%type track_clear_flag +%type time +%type cdtext_item + +%% + +tocfile + : new_cd global_statements track_list + ; + +new_cd + : /* empty */ { + cd = cd_init(); + cdtext = cd_get_cdtext(cd); + } + ; + +global_statements + : /* empty */ + | global_statements global_statement + ; + +global_statement + : CATALOG STRING '\n' { cd_set_catalog(cd, $2); } + | disc_mode '\n' { cd_set_mode(cd, $1); } + | CD_TEXT '{' opt_nl language_map cdtext_langs '}' '\n' + | error '\n' + ; + +disc_mode + : CD_DA + | CD_ROM + | CD_ROM_XA + ; + +track_list + : track + | track_list track + ; + +track + : new_track track_def track_statements { + while (2 > track_get_nindex(track)) + track_add_index(track, 0); + } + ; + +new_track + : /* empty */ { + track = cd_add_track(cd); + cdtext = track_get_cdtext(track); + /* add 0 index */ + track_add_index(track, 0); + } + ; + +track_def + : TRACK track_modes '\n' { track_set_mode(track, $2); } + ; + +track_modes + : track_mode + | track_mode track_sub_mode { track_set_sub_mode(track, $2); } + ; + +track_mode + : AUDIO + | MODE1 + | MODE1_RAW + | MODE2 + | MODE2_FORM1 + | MODE2_FORM2 + | MODE2_FORM_MIX + | MODE2_RAW + ; + +track_sub_mode + : RW + | RW_RAW + ; + +track_statements + : track_statement + | track_statements track_statement + ; + +track_statement + : track_flags + | ISRC STRING '\n' { track_set_isrc(track, $2); } + | CD_TEXT '{' opt_nl cdtext_langs '}' '\n' + | track_data + | track_pregap + | track_index + | error '\n' + ; + +track_flags + : track_set_flag { track_set_flag(track, $1); } + | track_clear_flag { track_clear_flag(track, $1); } + ; + +track_set_flag + : COPY '\n' + | PRE_EMPHASIS '\n' + | FOUR_CHANNEL_AUDIO '\n' + ; + +track_clear_flag + : NO PRE_EMPHASIS '\n' { $$ = $2; } + | NO COPY '\n' { $$ = $2; } + | TWO_CHANNEL_AUDIO '\n' + ; + +track_data + : zero_data time '\n' { + if (NULL == track_get_filename(track)) + track_set_zero_pre(track, $2); + else + track_set_zero_post(track, $2); + } + | AUDIOFILE STRING time '\n' { + track_set_filename(track, $2); + track_set_start(track, $3); + } + | AUDIOFILE STRING time time '\n' { + track_set_filename(track, $2); + track_set_start(track, $3); + track_set_length(track, $4); + } + | DATAFILE STRING '\n' { + track_set_filename(track, $2); + } + | DATAFILE STRING time '\n' { + track_set_filename(track, $2); + track_set_start(track, $3); + } + | FIFO STRING time '\n' { + track_set_filename(track, $2); + track_set_start(track, $3); + } + ; + +zero_data + : SILENCE + | ZERO + ; + +track_pregap + : START '\n' + | START time '\n' { + track_add_index(track, $2); + } + | PREGAP time '\n' { + track_set_zero_pre(track, $2); + track_add_index(track, $2); + } + ; + +track_index + : INDEX time '\n' { track_add_index(track, $2); } + ; + +language_map + : LANGUAGE_MAP '{' opt_nl languages '}' '\n' + ; + +languages + : language + | languages language + ; + +language + : NUMBER ':' NUMBER opt_nl { /* not implemented */ } + ; + +cdtext_langs + : cdtext_lang + | cdtext_langs cdtext_lang + ; + +cdtext_lang + : LANGUAGE NUMBER '{' opt_nl cdtext_defs '}' '\n' + ; + +cdtext_defs + : /* empty */ + | cdtext_defs cdtext_def + ; + +cdtext_def + : cdtext_item STRING '\n' { + cdtext_set ($1, $2, cdtext); + } + | cdtext_item '{' bytes '}' '\n' { + yyerror("binary CD-TEXT data not supported\n"); + } + ; + +cdtext_item + : TITLE + | PERFORMER + | SONGWRITER + | COMPOSER + | ARRANGER + | MESSAGE + | DISC_ID + | GENRE + | TOC_INFO1 + | TOC_INFO2 + | UPC_EAN + | ISRC + | SIZE_INFO + ; + +bytes + : /* empty */ + | bytes ',' NUMBER + ; + +time + : NUMBER + | NUMBER ':' NUMBER ':' NUMBER { $$ = time_msf_to_frame($1, $3, $5); } + ; + +opt_nl + : /* empty */ + | '\n' + ; + +%% + +/* lexer interface */ +extern int toc_lineno; +extern int yydebug; +extern FILE *toc_yyin; + +void yyerror (char *s) +{ + fprintf(stderr, "%d: %s\n", toc_lineno, s); +} + +Cd *toc_parse (FILE *fp) +{ + toc_yyin = fp; + yydebug = 0; + + if (0 == yyparse()) + return cd; + + return NULL; +} diff --git a/src/lib/toc_parse_prefix.h b/src/lib/toc_parse_prefix.h new file mode 100644 index 0000000..02aa31e --- /dev/null +++ b/src/lib/toc_parse_prefix.h @@ -0,0 +1,44 @@ +/* Remap normal yacc names so we can have multiple parsers + * see http://www.gnu.org/software/automake/manual/html_node/Yacc-and-Lex.html + */ + +#define yymaxdepth toc_yymaxdepth +#define yyparse toc_yyparse +#define yylex toc_yylex +#define yyerror toc_yyerror +#define yylval toc_yylval +#define yychar toc_ychar +#define yydebug toc_yydebug +#define yypact toc_yypact +#define yyr1 toc_yyr1 +#define yyr2 toc_yyr2 +#define yydef toc_yydef +#define yychk toc_yychk +#define yypgo toc_yypgo +#define yyact toc_yyact +#define yyexca toc_yyexca +#define yyerrflag toc_yyerrflag +#define yynerrs toc_yynerrs +#define yyps toc_yyps +#define yypv toc_yypv +#define yys toc_yys +#define yy_yys toc_yy_yys +#define yystate toc_yystate +#define yytmp toc_yytmp +#define yyv toc_yyv +#define yy_yyv toc_yy_yyv +#define yyval toc_yyval +#define yylloc toc_yylloc +#define yyreds toc_yyreds +#define yytoks toc_yytoks +#define yylhs toc_yylhs +#define yylen toc_yylen +#define yydefred toc_yydefred +#define yydgoto toc_yydgoto +#define yysinde toc_yysindex +#define yyrindex toc_yyrindex +#define yygindex toc_yygindex +#define yytable toc_yytable +#define yycheck toc_yycheck +#define yyname toc_yyname +#define yyrule toc_yyrule diff --git a/src/lib/toc_print.c b/src/lib/toc_print.c new file mode 100644 index 0000000..c38d4a6 --- /dev/null +++ b/src/lib/toc_print.c @@ -0,0 +1,149 @@ +/* + * toc_print.c -- print toc file + * + * Copyright (C) 2004 Svend Sorensen + * For license terms, see the file COPYING in this distribution. + */ + +#include +#include +#include "cd.h" +#include "time.h" + +void toc_print_track (FILE *fp, Track *track); +void toc_print_cdtext (Cdtext *cdtext, FILE *fp, int istrack); + +void toc_print (FILE *fp, Cd *cd) +{ + Cdtext *cdtext = cd_get_cdtext(cd); + int i; /* track */ + Track *track; + + switch(cd_get_mode(cd)) { + case MODE_CD_DA: + fprintf(fp, "CD_DA\n"); + break; + case MODE_CD_ROM: + fprintf(fp, "CD_ROM\n"); + break; + case MODE_CD_ROM_XA: + fprintf(fp, "CD_ROM_XA\n"); + break; + } + + if (NULL != cd_get_catalog(cd)) + fprintf(fp, "CATALOG \"%s\"\n", cd_get_catalog(cd)); + + if(0 != cdtext_is_empty(cdtext)) { + fprintf(fp, "CD_TEXT {\n"); + fprintf(fp, "\tLANGUAGE_MAP { 0:9 }\n"); + fprintf(fp, "\tLANGUAGE 0 {\n"); + toc_print_cdtext(cdtext, fp, 0); + fprintf(fp, "\t}\n"); + fprintf(fp, "}\n"); + } + + for (i = 1; i <= cd_get_ntrack(cd); i++) { + track = cd_get_track(cd, i); + fprintf(fp, "\n"); + toc_print_track(fp, track); + } +} + +void toc_print_track (FILE *fp, Track *track) +{ + Cdtext *cdtext = track_get_cdtext(track); + int i; /* index */ + + fprintf(fp, "TRACK "); + switch (track_get_mode(track)) { + case MODE_AUDIO: + fprintf(fp, "AUDIO"); + break; + case MODE_MODE1: + fprintf(fp, "MODE1"); + break; + case MODE_MODE1_RAW: + fprintf(fp, "MODE1_RAW"); + break; + case MODE_MODE2: + fprintf(fp, "MODE2"); + break; + case MODE_MODE2_FORM1: + fprintf(fp, "MODE2_FORM1"); + break; + case MODE_MODE2_FORM2: + fprintf(fp, "MODE2_FORM2"); + break; + case MODE_MODE2_FORM_MIX: + fprintf(fp, "MODE2_FORM_MIX"); + break; + } + fprintf(fp, "\n"); + + if (0 != track_is_set_flag(track, FLAG_PRE_EMPHASIS)) + fprintf(fp, "PRE_EMPHASIS\n"); + if (0 != track_is_set_flag(track, FLAG_COPY_PERMITTED)) + fprintf(fp, "COPY\n"); + if (0 != track_is_set_flag(track, FLAG_FOUR_CHANNEL)) + fprintf(fp, "FOUR_CHANNEL_AUDIO\n"); + + if (NULL != track_get_isrc(track)) + fprintf(fp, "ISRC \"%s\"\n", track_get_isrc(track)); + + if (0 != cdtext_is_empty(cdtext)) { + fprintf(fp, "CD_TEXT {\n"); + fprintf(fp, "\tLANGUAGE 0 {\n"); + toc_print_cdtext(cdtext, fp, 1); + fprintf(fp, "\t}\n"); + fprintf(fp, "}\n"); + } + + if (0 != track_get_zero_pre(track)) { + fprintf(fp, "ZERO "); + fprintf(fp, "%s", time_frame_to_mmssff(track_get_zero_pre(track))); + fprintf(fp, "\n"); + } + + fprintf(fp, "FILE "); + fprintf(fp, "\"%s\" ", track_get_filename(track)); + if (0 == track_get_start(track)) + fprintf(fp, "0"); + else + fprintf(fp, "%s", time_frame_to_mmssff(track_get_start(track))); + if (0 != track_get_length(track)) + fprintf(fp, " %s", time_frame_to_mmssff(track_get_length(track))); + fprintf(fp, "\n"); + + if (0 != track_get_zero_post(track)) { + fprintf(fp, "ZERO "); + fprintf(fp, "%s", time_frame_to_mmssff(track_get_zero_post(track))); + fprintf(fp, "\n"); + } + + if (track_get_index(track, 1) != 0) { + fprintf(fp, "START "); + fprintf(fp, "%s\n", time_frame_to_mmssff(track_get_index(track, 1))); + } + + for (i = 2; i < track_get_nindex(track); i++) { + fprintf(fp, "INDEX "); + fprintf(fp, "%s\n", time_frame_to_mmssff( \ + track_get_index(track, i) - track_get_index(track, 0) \ + )); + } +} + +void toc_print_cdtext (Cdtext *cdtext, FILE *fp, int istrack) +{ + int pti; + char *value = NULL; + + for (pti = 0; PTI_END != pti; pti++) { + if (NULL != (value = cdtext_get(pti, cdtext))) { + fprintf(fp, "\t\t"); + fprintf(fp, "%s", cdtext_get_key(pti, istrack)); + fprintf(fp, " \"%s\"\n", value); + } + } +} diff --git a/src/lib/toc_scan.l b/src/lib/toc_scan.l new file mode 100644 index 0000000..30a477e --- /dev/null +++ b/src/lib/toc_scan.l @@ -0,0 +1,105 @@ +%{ +/* + * toc_scan.l -- lexer for toc files + * + * Copyright (C) 2004 Svend Sorensen + * For license terms, see the file COPYING in this distribution. + */ + +#include +#include +#include "cd.h" +#include "toc_parse_prefix.h" +#include "toc_parse.h" + +int toc_lineno = 1; +%} + +ws [ \t\r] +nonws [^ \t\r\n] + +%option noyywrap +%option prefix="toc_yy" + +%s NAME + +%% + +\'([^\']|\\\')*\' | +\"([^\"]|\\\")*\" { + yylval.sval = strdup(yytext + 1); + yylval.sval[strlen(yylval.sval) - 1] = '\0'; + BEGIN(INITIAL); + return STRING; + } + +{nonws}+ { + yylval.sval = strdup(yytext); + BEGIN(INITIAL); + return STRING; + } + +CATALOG { BEGIN(NAME); return CATALOG; } + +CD_DA { yylval.ival = MODE_CD_DA; return CD_DA; } +CD_ROM { yylval.ival = MODE_CD_ROM; return CD_ROM; } +CD_ROM_XA { yylval.ival = MODE_CD_ROM_XA; return CD_ROM_XA; } + +TRACK { return TRACK; } +AUDIO { yylval.ival = MODE_AUDIO; return AUDIO; } +MODE1 { yylval.ival = MODE_MODE1; return MODE1; } +MODE1_RAW { yylval.ival = MODE_MODE1_RAW; return MODE1_RAW; } +MODE2 { yylval.ival = MODE_MODE2; return MODE2; } +MODE2_FORM1 { yylval.ival = MODE_MODE2_FORM1; return MODE2_FORM1; } +MODE2_FORM2 { yylval.ival = MODE_MODE2_FORM2; return MODE2_FORM2; } +MODE2_FORM_MIX { yylval.ival = MODE_MODE2_FORM_MIX; return MODE2_FORM_MIX; } +MODE2_RAW { yylval.ival = MODE_MODE2_RAW; return MODE2_RAW; } +RW { yylval.ival = SUB_MODE_RW; return RW; } +RW_RAW { yylval.ival = SUB_MODE_RW_RAW; return RW_RAW; } + +NO { return NO; } +COPY { yylval.ival = FLAG_PRE_EMPHASIS; return COPY; } +PRE_EMPHASIS { yylval.ival = FLAG_COPY_PERMITTED; return PRE_EMPHASIS; } +FOUR_CHANNEL_AUDIO { yylval.ival = FLAG_FOUR_CHANNEL; return FOUR_CHANNEL_AUDIO; } +TWO_CHANNEL_AUDIO { yylval.ival = FLAG_FOUR_CHANNEL; return TWO_CHANNEL_AUDIO; } + + /* ISRC is with CD-TEXT items */ + +SILENCE { return SILENCE; } +ZERO { return ZERO; } +(AUDIO)?FILE { BEGIN(NAME); return AUDIOFILE; } +DATAFILE { BEGIN(NAME); return DATAFILE; } +FIFO { BEGIN(NAME); return FIFO; } +START { return START; } +PREGAP { return PREGAP; } +INDEX { return INDEX; } + +CD_TEXT { return CD_TEXT; } +LANGUAGE_MAP { return LANGUAGE_MAP; } +LANGUAGE { return LANGUAGE; } + +TITLE { BEGIN(NAME); yylval.ival = PTI_TITLE; return TITLE; } +PERFORMER { BEGIN(NAME); yylval.ival = PTI_PERFORMER; return PERFORMER; } +SONGWRITER { BEGIN(NAME); yylval.ival = PTI_SONGWRITER; return SONGWRITER; } +COMPOSER { BEGIN(NAME); yylval.ival = PTI_COMPOSER; return COMPOSER; } +ARRANGER { BEGIN(NAME); yylval.ival = PTI_ARRANGER; return ARRANGER; } +MESSAGE { BEGIN(NAME); yylval.ival = PTI_MESSAGE; return MESSAGE; } +DISC_ID { BEGIN(NAME); yylval.ival = PTI_DISC_ID; return DISC_ID; } +GENRE { BEGIN(NAME); yylval.ival = PTI_GENRE; return GENRE; } +TOC_INFO1 { BEGIN(NAME); yylval.ival = PTI_TOC_INFO1; return TOC_INFO1; } +TOC_INFO2 { BEGIN(NAME); yylval.ival = PTI_TOC_INFO2; return TOC_INFO2; } +UPC_EAN { BEGIN(NAME); yylval.ival = PTI_UPC_ISRC; return UPC_EAN; } +ISRC { BEGIN(NAME); yylval.ival = PTI_UPC_ISRC; return ISRC; } +SIZE_INFO { BEGIN(NAME); yylval.ival = PTI_SIZE_INFO; return SIZE_INFO; } + +"//".*\n { toc_lineno++; /* ignore comments */ } +{ws}+ { /* ignore whitespace */ } + +[[:digit:]]+ { yylval.ival = atoi(yytext); return NUMBER; } +:|,|\{|\} { return yytext[0]; } + +^{ws}*\n { toc_lineno++; /* blank line */ } +\n { toc_lineno++; return '\n'; } +. { fprintf(stderr, "bad character '%c'\n", yytext[0]); } + +%% diff --git a/src/tools/Makefile.am b/src/tools/Makefile.am new file mode 100644 index 0000000..85af42d --- /dev/null +++ b/src/tools/Makefile.am @@ -0,0 +1,3 @@ +bin_PROGRAMS = cuebreakpoints cueconvert cueprint +LDADD = ../lib/libcuefile.a +AM_CPPFLAGS = -I$(srcdir)/../lib diff --git a/src/tools/cuebreakpoints.c b/src/tools/cuebreakpoints.c new file mode 100644 index 0000000..6c89364 --- /dev/null +++ b/src/tools/cuebreakpoints.c @@ -0,0 +1,163 @@ +/* + * cuebreakpoints.c -- print track break points + * + * Copyright (C) 2004 Svend Sorensen + * For license terms, see the file COPYING in this distribution. + */ + +#include +#include +#include +#include +#include "cuefile.h" +#include "time.h" + +char *progname; + +/* pregap correction modes + * APPEND - append pregap to previous track (except for first track) + * PREPEND - prefix pregap to current track + * SPLIT - print breakpoints for beginning and end of pregap + */ +enum GapMode {APPEND, PREPEND, SPLIT}; + +void usage (int status) +{ + if (0 == status) { + fprintf(stdout, "%s: usage: cuebreakpoints [option...] [file...]\n", progname); + fputs("\ +\n\ +OPTIONS\n\ +-h, --help print usage\n\ +-i, --input-format cue|toc set format of file(s)\n\ +--append-gaps append pregaps to previous track (default)\n\ +--prepend-gaps prefix pregaps to track\n\ +--split-gaps split at beginning and end of pregaps\n\ +", stdout); + } else { + fprintf(stderr, "run `%s --help' for usage\n", progname); + } + + exit (status); +} + +void print_m_ss_ff (long frame) +{ + int m, s, f; + + time_frame_to_msf(frame, &m, &s, &f); + printf ("%d:%02d.%02d\n", m, s, f); +} + +void print_breakpoint (long b) +{ + /* do not print zero breakpoints */ + if (0 != b) + print_m_ss_ff(b); +} + +void print_breaks (Cd *cd, int gaps) +{ + int i; + long b; + Track *track; + + for (i = 1; i <= cd_get_ntrack(cd); i++) { + track = cd_get_track(cd, i); + /* when breakpoint is at: + * index 0: gap is prepended to track + * index 1: gap is appended to previous track + */ + b = track_get_start(track); + + if (gaps == PREPEND || gaps == SPLIT) { + print_breakpoint(b); + } + + if (gaps == APPEND || gaps == SPLIT) { + /* there is no previous track to append the first tracks pregap to */ + /* TODO: should first track's pregap be split when appending? + * this could be a suprising default + */ + if (1 < i) { + b += track_get_index(track, 1) - track_get_zero_pre(track); + print_breakpoint(b); + } + } + } +} + +int breaks (char *name, int format, int gaps) +{ + Cd *cd = NULL; + + if (NULL == (cd = cf_parse(name, &format))) { + fprintf(stderr, "%s: input file error\n", name); + return -1; + } + + print_breaks(cd, gaps); + + return 0; +} + +int main (int argc, char **argv) +{ + int format = UNKNOWN; + int gaps = APPEND; + + /* option variables */ + char c; + /* getopt_long() variables */ + extern char *optarg; + extern int optind; + + static struct option longopts[] = { + {"help", no_argument, NULL, 'h'}, + {"input-format", required_argument, NULL, 'i'}, + {"append-gaps", no_argument, NULL, 'a'}, + {"prepend-gaps", no_argument, NULL, 'p'}, + {"split-gaps", no_argument, NULL, 's'}, + {NULL, 0, NULL, 0} + }; + + progname = *argv; + + while (-1 != (c = getopt_long(argc, argv, "hi:", longopts, NULL))) { + switch (c) { + case 'h': + usage(0); + break; + case 'i': + if (0 == strcmp("cue", optarg)) + format = CUE; + else if (0 == strcmp("toc", optarg)) + format = TOC; + else + fprintf(stderr, "%s: illegal format `%s'\n", progname, optarg); + usage(1); + break; + case 'a': + gaps = APPEND; + break; + case 'p': + gaps = PREPEND; + break; + case 's': + gaps = SPLIT; + break; + default: + usage(1); + break; + } + } + + if (optind == argc) { + breaks("-", format, gaps); + } else { + for (; optind < argc; optind++) + breaks(argv[optind], format, gaps); + } + + return 0; +} diff --git a/src/tools/cueconvert.c b/src/tools/cueconvert.c new file mode 100644 index 0000000..65c301f --- /dev/null +++ b/src/tools/cueconvert.c @@ -0,0 +1,120 @@ +/* + * cueconvert.c -- convert between cue/toc formats + * + * Copyright (C) 2004 Svend Sorensen + * For license terms, see the file COPYING in this distribution. + */ + +#include +#include +#include +#include +#include "cuefile.h" + +char *progname; + +void usage (int status) +{ + if (0 == status) { + fprintf(stdout, "%s: usage: cueconvert [option...] [infile [outfile]]\n", progname); + fputs("\ +\n\ +OPTIONS\n\ +-h, --help print usage\n\ +-i, --input-format cue|toc set format of input file\n\ +-o, --output-format cue|toc set format of output file\n\ +", stdout); + } else { + fprintf(stderr, "run `%s --help' for usage\n", progname); + } + + exit (status); +} + +int convert (char *iname, int iformat, char *oname, int oformat) +{ + Cd *cd = NULL; + + if (NULL == (cd = cf_parse(iname, &iformat))) { + fprintf(stderr, "input file error\n"); + return -1; + } + + if (UNKNOWN == oformat) { + /* first use file suffix */ + if (UNKNOWN == (oformat = cf_format_from_suffix(oname))) { + /* then use opposite of input format */ + switch(iformat) { + case CUE: + oformat = TOC; + break; + case TOC: + oformat = CUE; + break; + } + } + } + + return cf_print(oname, &oformat, cd); +} + +int main (int argc, char **argv) +{ + int iformat = UNKNOWN; + int oformat = UNKNOWN; + /* option variables */ + char c; + /* getopt_long() variables */ + extern char *optarg; + extern int optind; + + static struct option longopts[] = { + {"help", no_argument, NULL, 'h'}, + {"input-format", required_argument, NULL, 'i'}, + {"output-format", required_argument, NULL, 'o'}, + {NULL, 0, NULL, 0} + }; + + progname = *argv; + + while (-1 != (c = getopt_long(argc, argv, "hi:o:", longopts, NULL))) { + switch (c) { + case 'h': + usage(0); + break; + case 'i': + if (0 == strcmp("cue", optarg)) + iformat = CUE; + else if (0 == strcmp("toc", optarg)) + iformat = TOC; + else + fprintf(stderr, "%s: illegal format `%s'\n", progname, optarg); + usage(1); + break; + case 'o': + if (0 == strcmp("cue", optarg)) + oformat = CUE; + else if (0 == strcmp("toc", optarg)) + oformat = TOC; + else + fprintf(stderr, "%s: illegal format `%s'\n", progname, optarg); + usage(1); + break; + default: + usage(1); + break; + } + } + + if (optind == argc) { + convert("-", iformat, "-", oformat); + } else if (optind == argc - 1) { + convert(argv[optind], iformat, "-", oformat); + } else if (optind == argc - 2) { + convert(argv[optind], iformat, argv[optind + 1], oformat); + } else { + usage(1); + } + + return 0; +} diff --git a/src/tools/cueprint.c b/src/tools/cueprint.c new file mode 100644 index 0000000..159993e --- /dev/null +++ b/src/tools/cueprint.c @@ -0,0 +1,496 @@ +/* + * cueprint.c -- print cd information based on a template + * + * Copyright (C) 2004 Svend Sorensen + * For license terms, see the file COPYING in this distribution. + */ + +#include +#include /* exit() */ +#include /* strcmp() */ +#include +#include /* isdigit() */ +#include "cuefile.h" + +/* default templates */ + +#define D_TEMPLATE "\ +Disc Information\n\ +arranger: %A\n\ +composer: %C\n\ +genre: %G\n\ +message: %M\n\ +no. of tracks: %N\n\ +performer: %P\n\ +songwriter: %S\n\ +title: %T\n\ +UPC/EAN: %U\n\ +" + +#define T_TEMPLATE "\ +\n\ +Track %n Information\n\ +arranger: %a\n\ +composer: %c\n\ +genre: %g\n\ +ISRC: %i\n\ +message: %m\n\ +track number: %n\n\ +perfomer: %p\n\ +title: %t\n\ +ISRC (CD-TEXT): %u\n\ +" + +/* default string to print for unset (NULL) values */ +#define VALUE_UNSET "" + +/* + * *_get_* functions can return an int or char * + */ +typedef union { + int ival; + char *sval; + char cval; +} Value; + +char *progname; + +void usage (int status) +{ + if (0 == status) { + fprintf(stdout, "%s: usage: cueprint [option...] [file...]\n", progname); + fputs("\ +\n\ +OPTIONS\n\ +-h, --help print usage\n\ +-i, --input-format cue|toc set format of file(s)\n\ +-n, --track-number only print track information for single track\n\ +-d, --disc-template