From 2b85ca216778190d94b2f97d21eaa82f4a989145 Mon Sep 17 00:00:00 2001 From: Svend Sorensen Date: Thu, 24 Jun 2004 23:32:16 +0000 Subject: [PATCH] imported sources --- COPYING | 340 ++++++++++++++++++++++++++++++++++++++++ Makefile | 6 + README | 22 +++ lib/Makefile | 44 ++++++ lib/cd.c | 329 +++++++++++++++++++++++++++++++++++++++ lib/cd.h | 147 ++++++++++++++++++ lib/cdtext.c | 153 ++++++++++++++++++ lib/cdtext.h | 65 ++++++++ lib/cue.h | 6 + lib/cue_parse.y | 277 +++++++++++++++++++++++++++++++++ lib/cue_print.c | 146 +++++++++++++++++ lib/cue_scan.l | 94 +++++++++++ lib/cuefile.c | 90 +++++++++++ lib/cuefile.h | 16 ++ lib/time.c | 43 +++++ lib/time.h | 15 ++ lib/toc.h | 6 + lib/toc_parse.y | 345 +++++++++++++++++++++++++++++++++++++++++ lib/toc_print.c | 149 ++++++++++++++++++ lib/toc_scan.l | 102 ++++++++++++ man/Makefile | 28 ++++ man/cuebreakpoints.1 | 23 +++ man/cueconvert.1 | 24 +++ man/cueprint.1 | 89 +++++++++++ test/complete.cue | 52 +++++++ test/empty.cue | 0 test/empty.toc | 0 test/errors.cue | 60 +++++++ test/test001.toc | 336 +++++++++++++++++++++++++++++++++++++++ test/test002.toc | 208 +++++++++++++++++++++++++ tools/Makefile | 39 +++++ tools/cuebreakpoints.c | 109 +++++++++++++ tools/cueconvert.c | 108 +++++++++++++ tools/cueprint.c | 240 ++++++++++++++++++++++++++++ 34 files changed, 3711 insertions(+) create mode 100644 COPYING create mode 100644 Makefile create mode 100644 README create mode 100644 lib/Makefile create mode 100644 lib/cd.c create mode 100644 lib/cd.h create mode 100644 lib/cdtext.c create mode 100644 lib/cdtext.h create mode 100644 lib/cue.h create mode 100644 lib/cue_parse.y create mode 100644 lib/cue_print.c create mode 100644 lib/cue_scan.l create mode 100644 lib/cuefile.c create mode 100644 lib/cuefile.h create mode 100644 lib/time.c create mode 100644 lib/time.h create mode 100644 lib/toc.h create mode 100644 lib/toc_parse.y create mode 100644 lib/toc_print.c create mode 100644 lib/toc_scan.l create mode 100644 man/Makefile create mode 100644 man/cuebreakpoints.1 create mode 100644 man/cueconvert.1 create mode 100644 man/cueprint.1 create mode 100644 test/complete.cue create mode 100644 test/empty.cue create mode 100644 test/empty.toc create mode 100644 test/errors.cue create mode 100644 test/test001.toc create mode 100644 test/test002.toc create mode 100644 tools/Makefile create mode 100644 tools/cuebreakpoints.c create mode 100644 tools/cueconvert.c create mode 100644 tools/cueprint.c diff --git a/COPYING b/COPYING new file mode 100644 index 0000000..5b6e7c6 --- /dev/null +++ b/COPYING @@ -0,0 +1,340 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 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. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + +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 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. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..a089fc0 --- /dev/null +++ b/Makefile @@ -0,0 +1,6 @@ +SUBDIRS= lib tools man + +all install uninstall clean: + for dir in $(SUBDIRS) ; do \ + (cd $$dir && $(MAKE) $@) ; \ + done diff --git a/README b/README new file mode 100644 index 0000000..2f42be9 --- /dev/null +++ b/README @@ -0,0 +1,22 @@ +cuetools README +cuetools is a set of utilites for working with cue and toc files. + +RELEASE NOTES +* cuetools-1.x is a significant code rewrite of 0.x. The cue/toc parsing code +has been reimplimented in lex/yacc. +* cuerename has been replaced by the more general utility cueprint. +* Usage syntax of the utilites has changed. See the respective man pages. + +INSTALL +Building requires GNU Make. Run `make install' to install cuetools into the +default location of /usr/local. The Makefiles also support both the prefix and +DESTDIR variables. + +AUTHOR +Svend Sorensen + +SEE ALSO +cuetools website - http://cuetools.sourceforge.net +Cdrdao website - http://cdrdao.sourceforge.net +cdrdao(1) man page - description of toc file format +shntool website - http://www.etree.org/shnutils/shntool/ diff --git a/lib/Makefile b/lib/Makefile new file mode 100644 index 0000000..c1229b8 --- /dev/null +++ b/lib/Makefile @@ -0,0 +1,44 @@ +YFLAGS+= -d + +OBJS= cd.o cdtext.o time.o cuefile.o +CUE_OBJS= cue_parse.o cue_scan.o cue_print.o +TOC_OBJS= toc_parse.o toc_scan.o toc_print.o + +toc_scan.c: LFLAGS+= -Ptoc_yy +cue_scan.c: LFLAGS+= -Pcue_yy +toc_parse.c: YFLAGS+= -p toc_yy +cue_parse.c: YFLAGS+= -p cue_yy + +TARGETS= libcuefile.a + +all: $(TARGETS) + +libcuefile.a: libcuefile.a($(OBJS) $(CUE_OBJS) $(TOC_OBJS)) + +%.c: %.y + $(YACC) -o $@ $(YFLAGS) $< + +install: all + +uninstall: + +clean: + rm -f $(TARGETS) + rm -f *.o *.a *.core *.output + rm -f toc_scan.c toc_parse.c toc_parse.h + rm -f cue_scan.c cue_parse.c cue_parse.h + +# dependancies +# generated with `gcc -MM *.c' +# after building lex and yacc .c files + +cd.o: cd.c cd.h cdtext.h +cdtext.o: cdtext.c cdtext.h +cuefile.o: cuefile.c cuefile.h cd.h cdtext.h cue.h toc.h +cue_parse.o: cue_parse.c cd.h cdtext.h time.h +cue_print.o: cue_print.c cd.h cdtext.h time.h +cue_scan.o: cue_scan.c cd.h cdtext.h cue_parse.h +time.o: time.c +toc_parse.o: toc_parse.c cd.h cdtext.h time.h +toc_print.o: toc_print.c cd.h cdtext.h time.h +toc_scan.o: toc_scan.c cd.h cdtext.h toc_parse.h diff --git a/lib/cd.c b/lib/cd.c new file mode 100644 index 0000000..3f25c97 --- /dev/null +++ b/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_print(stdout, track->cdtext); */ + } +} + +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_print(stdout, cd->cdtext); */ + } + + for (i = 0; i < cd->ntrack; ++i) { + printf("Track %d Info\n", i + 1); + cd_track_dump(cd->track[i]); + } +} diff --git a/lib/cd.h b/lib/cd.h new file mode 100644 index 0000000..a69dee7 --- /dev/null +++ b/lib/cd.h @@ -0,0 +1,147 @@ +/* + * 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 +}; + +typedef struct Cd Cd; +typedef struct Track Track; + +Cd *cd_init (); +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 + */ + +void track_set_filename (Track *track, char *filename); +char *track_get_filename (Track *track); + +void track_set_start (Track *track, long start); +long track_get_start (Track *track); + +void track_set_length (Track *track, long length); +long track_get_length (Track *track); + +void track_set_mode (Track *track, int mode); +int track_get_mode (Track *track); + +void track_set_sub_mode (Track *track, int sub_mode); +int track_get_sub_mode (Track *track); + +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); + +void track_set_zero_pre (Track *track, long length); +long track_get_zero_pre (Track *track); + +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/lib/cdtext.c b/lib/cdtext.c new file mode 100644 index 0000000..608a47c --- /dev/null +++ b/lib/cdtext.c @@ -0,0 +1,153 @@ +/* + * 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; +} diff --git a/lib/cdtext.h b/lib/cdtext.h new file mode 100644 index 0000000..e62f13b --- /dev/null +++ b/lib/cdtext.h @@ -0,0 +1,65 @@ +/* + * 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); + +#endif diff --git a/lib/cue.h b/lib/cue.h new file mode 100644 index 0000000..7665917 --- /dev/null +++ b/lib/cue.h @@ -0,0 +1,6 @@ +/* + * cue.h -- cue function declarations + */ + +Cd *cue_parse (FILE *fp); +void cue_print (FILE *fp, Cd *cd); diff --git a/lib/cue_parse.y b/lib/cue_parse.y new file mode 100644 index 0000000..f185488 --- /dev/null +++ b/lib/cue_parse.y @@ -0,0 +1,277 @@ +%{ +/* + * 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" + +#define YYDEBUG 1 + +extern int cue_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 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); } + | 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 cue_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; + cue_yydebug = 0; + + if (0 == cue_yyparse()) + return cd; + + return NULL; +} diff --git a/lib/cue_print.c b/lib/cue_print.c new file mode 100644 index 0000000..c558b4a --- /dev/null +++ b/lib/cue_print.c @@ -0,0 +1,146 @@ +/* + * 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/lib/cue_scan.l b/lib/cue_scan.l new file mode 100644 index 0000000..d5e7f90 --- /dev/null +++ b/lib/cue_scan.l @@ -0,0 +1,94 @@ +%{ +/* + * 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.h" + +int cue_lineno = 1; +%} + +ws [ \t\r] +nonws [^ \t\r\n] + +%option noyywrap + +%s NAME + +%% + +\"(\\.|[^\\"])*\" { + cue_yylval.sval = strdup(yytext + 1); + cue_yylval.sval[strlen(cue_yylval.sval) - 1] = '\0'; + BEGIN(INITIAL); + return STRING; + } + +{nonws}+ { + cue_yylval.sval = strdup(yytext); + BEGIN(INITIAL); + return STRING; + } + +CATALOG { BEGIN(NAME); return CATALOG; } + +FILE { BEGIN(NAME); return FFILE; } +BINARY { return BINARY; } +MOTOROLA { return MOTOROLA; } +AIFF { return AIFF; } +WAVE { return WAVE; } +MP3 { return MP3; } + +TRACK { return TRACK; } +AUDIO { cue_yylval.ival = MODE_AUDIO; return AUDIO; } +MODE1\/2048 { cue_yylval.ival = MODE_MODE1; return MODE1_2048; } +MODE1\/2352 { cue_yylval.ival = MODE_MODE1_RAW; return MODE1_2352; } +MODE2\/2336 { cue_yylval.ival = MODE_MODE2; return MODE2_2336; } +MODE2\/2048 { cue_yylval.ival = MODE_MODE2_FORM1; return MODE2_2048; } +MODE2\/2342 { cue_yylval.ival = MODE_MODE2_FORM2; return MODE2_2342; } +MODE2\/2332 { cue_yylval.ival = MODE_MODE2_FORM_MIX; return MODE2_2332; } +MODE2\/2352 { cue_yylval.ival = MODE_MODE2_RAW; return MODE2_2352; } + +FLAGS { return FLAGS; } +PRE { cue_yylval.ival = FLAG_PRE_EMPHASIS; return PRE; } +DCP { cue_yylval.ival = FLAG_COPY_PERMITTED; return DCP; } +4CH { cue_yylval.ival = FLAG_FOUR_CHANNEL; return FOUR_CH; } +SCMS { cue_yylval.ival = FLAG_SCMS; return SCMS; } + +PREGAP { return PREGAP; } +INDEX { return INDEX; } +POSTGAP { return POSTGAP; } + +TITLE { BEGIN(NAME); cue_yylval.ival = PTI_TITLE; return TITLE; } +PERFORMER { BEGIN(NAME); cue_yylval.ival = PTI_PERFORMER; return PERFORMER; } +SONGWRITER { BEGIN(NAME); cue_yylval.ival = PTI_SONGWRITER; return SONGWRITER; } +COMPOSER { BEGIN(NAME); cue_yylval.ival = PTI_COMPOSER; return COMPOSER; } +ARRANGER { BEGIN(NAME); cue_yylval.ival = PTI_ARRANGER; return ARRANGER; } +MESSAGE { BEGIN(NAME); cue_yylval.ival = PTI_MESSAGE; return MESSAGE; } +DISC_ID { BEGIN(NAME); cue_yylval.ival = PTI_DISC_ID; return DISC_ID; } +GENRE { BEGIN(NAME); cue_yylval.ival = PTI_GENRE; return GENRE; } +TOC_INFO1 { BEGIN(NAME); cue_yylval.ival = PTI_TOC_INFO1; return TOC_INFO1; } +TOC_INFO2 { BEGIN(NAME); cue_yylval.ival = PTI_TOC_INFO2; return TOC_INFO2; } +UPC_EAN { BEGIN(NAME); cue_yylval.ival = PTI_UPC_ISRC; return UPC_EAN; } +ISRC/{ws}+\" { BEGIN(NAME); cue_yylval.ival = PTI_UPC_ISRC; return ISRC; } +SIZE_INFO { BEGIN(NAME); cue_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:]]+ { cue_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/lib/cuefile.c b/lib/cuefile.c new file mode 100644 index 0000000..52fe942 --- /dev/null +++ b/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/lib/cuefile.h b/lib/cuefile.h new file mode 100644 index 0000000..e56b741 --- /dev/null +++ b/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/lib/time.c b/lib/time.c new file mode 100644 index 0000000..0b0c899 --- /dev/null +++ b/lib/time.c @@ -0,0 +1,43 @@ +/* + * 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/lib/time.h b/lib/time.h new file mode 100644 index 0000000..3bb28cd --- /dev/null +++ b/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/lib/toc.h b/lib/toc.h new file mode 100644 index 0000000..26609c1 --- /dev/null +++ b/lib/toc.h @@ -0,0 +1,6 @@ +/* + * toc.h -- toc function declarations + */ + +Cd *toc_parse (FILE *fp); +void toc_print (FILE *fp, Cd *cd); diff --git a/lib/toc_parse.y b/lib/toc_parse.y new file mode 100644 index 0000000..c52be6c --- /dev/null +++ b/lib/toc_parse.y @@ -0,0 +1,345 @@ +%{ +/* + * 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" + +#define YYDEBUG 1 + +extern int toc_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 + ; + +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 { $$ = $1; } + | NUMBER ':' NUMBER ':' NUMBER { $$ = time_msf_to_frame($1, $3, $5); } + ; + +opt_nl + : /* empty */ + | '\n' + ; + +%% + +/* lexer interface */ +extern int toc_lineno; +extern int toc_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; + toc_yydebug = 0; + + if (0 == toc_yyparse()) + return cd; + + return NULL; +} diff --git a/lib/toc_print.c b/lib/toc_print.c new file mode 100644 index 0000000..fa8c942 --- /dev/null +++ b/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/lib/toc_scan.l b/lib/toc_scan.l new file mode 100644 index 0000000..4c15bb8 --- /dev/null +++ b/lib/toc_scan.l @@ -0,0 +1,102 @@ +%{ +/* + * 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.h" + +int toc_lineno = 1; +%} + +ws [ \t\r] +nonws [^ \t\r\n] + +%option noyywrap + +%s NAME + +%% + +\"(\\.|[^\\"])*\" { + toc_yylval.sval = strdup(yytext + 1); + toc_yylval.sval[strlen(toc_yylval.sval) - 1] = '\0'; + BEGIN(INITIAL); + return STRING; + } + +{nonws}+ { + toc_yylval.sval = strdup(yytext); + BEGIN(INITIAL); + return STRING; + } + +CATALOG { BEGIN(NAME); return CATALOG; } + +CD_DA { toc_yylval.ival = MODE_CD_DA; return CD_DA; } +CD_ROM { toc_yylval.ival = MODE_CD_ROM; return CD_ROM; } +CD_ROM_XA { toc_yylval.ival = MODE_CD_ROM_XA; return CD_ROM_XA; } + +TRACK { return TRACK; } +AUDIO { toc_yylval.ival = MODE_AUDIO; return AUDIO; } +MODE1 { toc_yylval.ival = MODE_MODE1; return MODE1; } +MODE1_RAW { toc_yylval.ival = MODE_MODE1_RAW; return MODE1_RAW; } +MODE2 { toc_yylval.ival = MODE_MODE2; return MODE2; } +MODE2_FORM1 { toc_yylval.ival = MODE_MODE2_FORM1; return MODE2_FORM1; } +MODE2_FORM2 { toc_yylval.ival = MODE_MODE2_FORM2; return MODE2_FORM2; } +MODE2_FORM_MIX { toc_yylval.ival = MODE_MODE2_FORM_MIX; return MODE2_FORM_MIX; } +MODE2_RAW { toc_yylval.ival = MODE_MODE2_RAW; return MODE2_RAW; } +RW { toc_yylval.ival = SUB_MODE_RW; return RW; } +RW_RAW { toc_yylval.ival = SUB_MODE_RW_RAW; return RW_RAW; } + +NO { return NO; } +COPY { toc_yylval.ival = FLAG_PRE_EMPHASIS; return COPY; } +PRE_EMPHASIS { toc_yylval.ival = FLAG_COPY_PERMITTED; return PRE_EMPHASIS; } +FOUR_CHANNEL_AUDIO { toc_yylval.ival = FLAG_FOUR_CHANNEL; return FOUR_CHANNEL_AUDIO; } +TWO_CHANNEL_AUDIO { toc_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); toc_yylval.ival = PTI_TITLE; return TITLE; } +PERFORMER { BEGIN(NAME); toc_yylval.ival = PTI_PERFORMER; return PERFORMER; } +SONGWRITER { BEGIN(NAME); toc_yylval.ival = PTI_SONGWRITER; return SONGWRITER; } +COMPOSER { BEGIN(NAME); toc_yylval.ival = PTI_COMPOSER; return COMPOSER; } +ARRANGER { BEGIN(NAME); toc_yylval.ival = PTI_ARRANGER; return ARRANGER; } +MESSAGE { BEGIN(NAME); toc_yylval.ival = PTI_MESSAGE; return MESSAGE; } +DISC_ID { BEGIN(NAME); toc_yylval.ival = PTI_DISC_ID; return DISC_ID; } +GENRE { BEGIN(NAME); toc_yylval.ival = PTI_GENRE; return GENRE; } +TOC_INFO1 { BEGIN(NAME); toc_yylval.ival = PTI_TOC_INFO1; return TOC_INFO1; } +TOC_INFO2 { BEGIN(NAME); toc_yylval.ival = PTI_TOC_INFO2; return TOC_INFO2; } +UPC_EAN { BEGIN(NAME); toc_yylval.ival = PTI_UPC_ISRC; return UPC_EAN; } +ISRC { BEGIN(NAME); toc_yylval.ival = PTI_UPC_ISRC; return ISRC; } +SIZE_INFO { BEGIN(NAME); toc_yylval.ival = PTI_SIZE_INFO; return SIZE_INFO; } + +"//".*\n { toc_lineno++; /* ignore comments */ } +{ws}+ { /* ignore whitespace */ } + +[[:digit:]]+ { toc_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/man/Makefile b/man/Makefile new file mode 100644 index 0000000..3a120d4 --- /dev/null +++ b/man/Makefile @@ -0,0 +1,28 @@ +# Makefile + +INSTALL= install +INSTALL_PROGRAM= $(INSTALL) +INSTALL_DATA= $(INSTALL) -m 644 + +prefix= /usr/local +mandir= $(prefix)/man +DESTDIR= + +TARGETS= cuebreakpoints.1 cueconvert.1 cueprint.1 + +all: + @true + +install: all + $(INSTALL) -d $(DESTDIR)$(mandir)/man1 + for i in $(TARGETS) ; do \ + $(INSTALL_DATA) $$i $(DESTDIR)$(mandir)/man1/$$i ; \ + done + +uninstall: + -for i in $(TARGETS) ; do \ + rm -f $(DESTDIR)$(mandir)/man1/$$i ; \ + done + +clean: + @true diff --git a/man/cuebreakpoints.1 b/man/cuebreakpoints.1 new file mode 100644 index 0000000..0a4f621 --- /dev/null +++ b/man/cuebreakpoints.1 @@ -0,0 +1,23 @@ +.TH cuetools 1 +.SH NAME +cuebreakpoints +.SH DESCRIPTION +cuebreakpoints outputs the breakpoints from a cue or toc file +.SH SYNTAX +cueconvert [-h] [-i cue|toc] [-o cue|toc] [file...] +.SH OPTIONS +.TP +.B \-h +print usage information +.TP +.B \-i cue|toc +set format of file(s) +.SH NOTES +The breakpoints are in a format usable by shnsplit (part of the shntool package). A track breakpoint is at index 1. +.PP +If no files are specified, stdin is used. If a filename is specified and the format is not specified, the format will be set based on a ".cue" or ".toc" suffix. +.SH AUTHOR +Svend Sorensen +.SH "SEE ALSO" +cueconvert(1), +cueprint(1) diff --git a/man/cueconvert.1 b/man/cueconvert.1 new file mode 100644 index 0000000..4819301 --- /dev/null +++ b/man/cueconvert.1 @@ -0,0 +1,24 @@ +.TH cuetools 1 +.SH NAME +cueconvert +.SH DESCRIPTION +cueconvert converts between the cue and toc formats +.SH SYNTAX +cueconvert [-h] [-i cue|toc] [-o cue|toc] [infile [outfile]] +.SH OPTIONS +.TP +.B \-h +print usage information +.TP +.B \-i cue|toc +set format of input file +.TP +.B \-i cue|toc +set format of output file +.SH NOTES +If infile or outfile is not specified, stdin and stdout are used, respectively. If a filename is specified and the format is not specified, the format will be set based on a ".cue" or ".toc" suffix. If the output file format is not specified, and it cannot be determined from the suffix, it will be set to the opposite of the input format. +.SH AUTHOR +Svend Sorensen +.SH "SEE ALSO" +cuebreakpoints(1), +cueprint(1) diff --git a/man/cueprint.1 b/man/cueprint.1 new file mode 100644 index 0000000..8f7090e --- /dev/null +++ b/man/cueprint.1 @@ -0,0 +1,89 @@ +.TH cuetools 1 +.SH NAME +cueprint +.SH DESCRIPTION +cueprint \- print disc and track infomation for a cue or toc file +.SH SYNTAX +cueprint [\-h] [\-i cue|toc] [\-d TEMPLATE] [\-t TEMPLATE] [file...] +.SH OPTIONS +.TP +.B \-h +print usage information +.TP +.B \-i cue|toc +set format of file(s) +.TP +.B -d TEMPLATE +set disc template (see TEMPLATE EXPANSION) +.TP +.B -t TEMPLATE +set track template (see TEMPLATE EXPANSION) +.SH TEMPLATE EXPANSION +Disc template expansion characters are valid for both the disc and track templates. +.PP +Disc: +.TP +.B %A +album arranger +.TP +.B %C +album composer +.TP +.B %G +album genre +.TP +.B %M +album message +.TP +.B %N +number of tracks +.TP +.B %P +album performer +.TP +.B %S +album songwriter +.TP +.B %T +album title +.TP +.B %U +album UPC/EAN +.PP +Track: +.TP +.B %a +track arranger +.TP +.B %c +track composer +.TP +.B %g +track genre +.TP +.B %i +track ISRC +.TP +.B %m +track message +.TP +.B %n +track number +.TP +.B %p +track perfomer +.TP +.B %t +track title +.TP +.B %u +track ISRC (CD-TEXT) +.PP +Any other % is expanded to that character. For example, %% expands to a literal `%'. +.SH NOTES +If no files are specified, stdin is used. If a filename is specified and the format is not specified, the format will be set based on a ".cue" or ".toc" suffix. +.SH AUTHOR +Svend Sorensen +.SH "SEE ALSO" +cueconvert(1), +cuebreakpoints(1) diff --git a/test/complete.cue b/test/complete.cue new file mode 100644 index 0000000..49f76c1 --- /dev/null +++ b/test/complete.cue @@ -0,0 +1,52 @@ +CATALOG 1234567890123 +TITLE "Album Title" +PERFORMER "Album Performer" +SONGWRITER "Album Songwriter" +COMPOSER "Album Composer" +ARRANGER "Album Arranger" +MESSAGE "Album Message" +UPC_EAN "Album UPC/EAN" + +FILE "track1.wav" WAVE +TRACK 01 AUDIO +TITLE "Track 1 Title" +PERFORMER "Track 1 Performer" +SONGWRITER "Track 1 Songwriter" +COMPOSER "Track 1 Composer" +ARRANGER "Track 1 Arranger" +MESSAGE "Track 1 Message" +ISRC "CC-OWNR-99-12345" +ISRC CCOWNR9912345 +FLAGS PRE DCP 4CH SCMS +INDEX 01 00:00:00 + +REM zero data pregap with index pregap +TRACK 02 AUDIO +TITLE "Track 2 Title" +PERFORMER "Track 2 Performer" +SONGWRITER "Track 2 Songwriter" +COMPOSER "Track 2 Composer" +ARRANGER "Track 2 Arranger" +MESSAGE "Track 2 Message" +ISRC "CC-OWNR-99-12345" +ISRC CCOWNR9912345 +FLAGS PRE DCP 4CH SCMS +PREGAP 00:00:02 +INDEX 00 05:00:00 +INDEX 01 05:02:00 + +REM new file +FILE "track3.wav" WAVE +TRACK 03 AUDIO +TITLE "Track 3 Title" +PERFORMER "Track 3 Performer" +SONGWRITER "Track 3 Songwriter" +COMPOSER "Track 3 Composer" +ARRANGER "Track 3 Arranger" +MESSAGE "Track 3 Message" +ISRC "CC-OWNR-99-12345" +ISRC CCOWNR9912345 +FLAGS PRE DCP 4CH SCMS +INDEX 00 00:00:00 +INDEX 01 00:03:00 +POSTGAP 00:00:02 diff --git a/test/empty.cue b/test/empty.cue new file mode 100644 index 0000000..e69de29 diff --git a/test/empty.toc b/test/empty.toc new file mode 100644 index 0000000..e69de29 diff --git a/test/errors.cue b/test/errors.cue new file mode 100644 index 0000000..659ffc0 --- /dev/null +++ b/test/errors.cue @@ -0,0 +1,60 @@ +CATALOG 0000000000000 +PERFORMER "Marvin Gaye and Freddie Jackson" +TITLE "On Tour - Disc 1" +FILE "marvin_gaye_and_freddie_jackson-2001-on_tour-cd1-kmf.wav" WAVE +TRACK 01 AUDIO + TITLE "I Heard It Through The Grapevine" + PERFORMER "Marvin Gaye and Freddie Jackson" + FLAGS DCP + INDEX 01 00:00:00 + TRACK 02 AUDIO + TITLE "Come Get To This" + PERFORMER "Marvin Gaye and Freddie Jackson" + FLAGS DCP + INDEX 01 03:57:64 + TRACK 03 AUDIO + TITLE "Let's Get In On" + PERFORMER "Marvin Gaye and Freddie Jackson" + FLAGS DCP + INDEX 01 07:34:61 + TRACK 04 AUDIO + TITLE "God Is My Friend" + PERFORMER "Marvin Gaye and Freddie Jackson" + FLAGS DCP + INDEX 01 12:56:41 + TRACK 05 AUDIO + TITLE "What's Going On" + PERFORMER "Marvin Gaye and Freddie Jackson" + FLAGS DCP + INDEX 01 15:11:54 + TRACK 06 AUDIO + TITLE "Inner City Blues" + PERFORMER "Marvin Gaye and Freddie Jackson" + FLAGS DCP + INDEX 01 19:10:33 + TRACK 07 AUDIO + TITLE "Joy" + PERFORMER "Marvin Gaye and Freddie Jackson" + FLAGS DCP + INDEX 00 24:32:65 + INDEX 01 24:34:35 + TRACK 08 AUDIO + TITLE "Medley (Ain't Nothing Like The Real Thing...)" + PERFORMER "Marvin Gaye and Freddie Jackson" + FLAGS DCP + INDEX 01 29:03:56 + TRACK 09 AUDIO + TITLE "Rockin' After Midnight" + PERFORMER "Marvin Gaye and Freddie Jackson" + FLAGS DCP + INDEX 01 37:23:42 + TRACK 10 AUDIO + TITLE "Distant Lover" + PERFORMER "Marvin Gaye and Freddie Jackson" + FLAGS DCP + INDEX 01 46:21:21 + TRACK 11 AUDIO + TITLE "Sexual Healing" + PERFORMER "Marvin Gaye and Freddie Jackson" + FLAGS DCP + INDEX 01 56:00:42 diff --git a/test/test001.toc b/test/test001.toc new file mode 100644 index 0000000..a8a0347 --- /dev/null +++ b/test/test001.toc @@ -0,0 +1,336 @@ +CD_DA + +CATALOG "0008811307523" + +CD_TEXT { + LANGUAGE_MAP { + 0: 9 + } + LANGUAGE 0 { + TITLE "Red Hot + Riot" + PERFORMER "Various Artists" + MESSAGE "Net Profits from the sale of this album will suport Red Jot's AIDS prevention and relief efforts in Africa and around the world. Produced By: John Carlin, Paul Heck and Andr\351s Levin" + } +} + +// Track 1 +TRACK AUDIO +NO COPY +NO PRE_EMPHASIS +TWO_CHANNEL_AUDIO +ISRC "USRH20214001" +CD_TEXT { + LANGUAGE 0 { + TITLE "[intro] Fela Mentality" + PERFORMER "Mix Master Mike and Mario Caldato, Jr." + MESSAGE "" + } +} +FILE "data.bin" 0 01:09:45 + + +// Track 2 +TRACK AUDIO +NO COPY +NO PRE_EMPHASIS +TWO_CHANNEL_AUDIO +ISRC "USRH20214002" +CD_TEXT { + LANGUAGE 0 { + TITLE "Kalakuta Show" + PERFORMER "Mix Master Mike, Lateef, and The Gift Of Gab" + MESSAGE "" + } +} +FILE "data.bin" 01:09:45 02:31:17 + + +// Track 3 +TRACK AUDIO +NO COPY +NO PRE_EMPHASIS +TWO_CHANNEL_AUDIO +ISRC "USRH20214003" +CD_TEXT { + LANGUAGE 0 { + TITLE "[interlude] Live at Kalakuta" + PERFORMER "Posma, Remedies, Taiwo, Segun, Tosin, Andres Levin" + MESSAGE "" + } +} +FILE "data.bin" 03:40:62 00:32:15 + + +// Track 4 +TRACK AUDIO +NO COPY +NO PRE_EMPHASIS +TWO_CHANNEL_AUDIO +ISRC "USRH20214004" +CD_TEXT { + LANGUAGE 0 { + TITLE "Shuffering and Shmiling" + PERFORMER "Dead Prez, Jorge Ben Jor, Talib Kweli, Bilal and Postive Force" + MESSAGE "" + } +} +FILE "data.bin" 04:13:02 07:50:05 + + +// Track 5 +TRACK AUDIO +NO COPY +NO PRE_EMPHASIS +TWO_CHANNEL_AUDIO +ISRC "USRH20214005" +CD_TEXT { + LANGUAGE 0 { + TITLE "[interlude] Gimme Shit" + PERFORMER "Mix Master Mike and Mario Caldato, Jr." + MESSAGE "" + } +} +FILE "data.bin" 12:03:07 00:31:43 + + +// Track 6 +TRACK AUDIO +NO COPY +NO PRE_EMPHASIS +TWO_CHANNEL_AUDIO +ISRC "USRH20214006" +CD_TEXT { + LANGUAGE 0 { + TITLE "Water Get No Enemy [intro]" + PERFORMER "D'Angelo and Femi Kuti" + MESSAGE "" + } +} +FILE "data.bin" 12:34:50 04:04:30 + + +// Track 7 +TRACK AUDIO +NO COPY +NO PRE_EMPHASIS +TWO_CHANNEL_AUDIO +ISRC "USRH20214007" +CD_TEXT { + LANGUAGE 0 { + TITLE "Water Get No Enemy" + PERFORMER "D'Angelo, Femi Kuti, and Macy Gray featuring Roy Hargrove, Nile Rodgers, The Soultronics, and Positive Force" + MESSAGE "" + } +} +FILE "data.bin" 16:39:05 06:05:70 + + +// Track 8 +TRACK AUDIO +NO COPY +NO PRE_EMPHASIS +TWO_CHANNEL_AUDIO +ISRC "USRH20214007" +CD_TEXT { + LANGUAGE 0 { + TITLE "Gentleman" + PERFORMER "Meshell Ndegeocello and Yerba Buena! featuring Ron Blake" + MESSAGE "" + } +} +FILE "data.bin" 22:45:00 07:02:70 + + +// Track 9 +TRACK AUDIO +NO COPY +NO PRE_EMPHASIS +TWO_CHANNEL_AUDIO +ISRC "USRH20214009" +CD_TEXT { + LANGUAGE 0 { + TITLE "Tears and Sorrow" + PERFORMER "Common, Meshell Ndegeocello, and Djelimady Tounkara" + MESSAGE "" + } +} +FILE "data.bin" 29:47:70 01:31:27 + + +// Track 10 +TRACK AUDIO +NO COPY +NO PRE_EMPHASIS +TWO_CHANNEL_AUDIO +ISRC "USRH20214010" +CD_TEXT { + LANGUAGE 0 { + TITLE "Shakara Lady (part one)" + PERFORMER "Cheikh Lo" + MESSAGE "" + } +} +FILE "data.bin" 31:19:22 02:20:05 + + +// Track 11 +TRACK AUDIO +NO COPY +NO PRE_EMPHASIS +TWO_CHANNEL_AUDIO +ISRC "USRH20214011" +CD_TEXT { + LANGUAGE 0 { + TITLE "Shakara Lady (part two)" + PERFORMER "Cheikh Lo, Les Nubians, and Manu Dibango featuring Chateau Flight" + MESSAGE "" + } +} +FILE "data.bin" 33:39:27 04:47:13 + + +// Track 12 +TRACK AUDIO +NO COPY +NO PRE_EMPHASIS +TWO_CHANNEL_AUDIO +ISRC "USRH20214012" +CD_TEXT { + LANGUAGE 0 { + TITLE "[interlude] Don't Worry About My Mouth-O" + PERFORMER "Monoaural" + MESSAGE "" + } +} +FILE "data.bin" 38:26:40 01:03:32 + + +// Track 13 +TRACK AUDIO +NO COPY +NO PRE_EMPHASIS +TWO_CHANNEL_AUDIO +ISRC "USRH20214013" +CD_TEXT { + LANGUAGE 0 { + TITLE "Zombie (part one)" + PERFORMER "Bugz in the Attic featuring Wunmi" + MESSAGE "" + } +} +FILE "data.bin" 39:29:72 02:51:70 + + +// Track 14 +TRACK AUDIO +NO COPY +NO PRE_EMPHASIS +TWO_CHANNEL_AUDIO +ISRC "USRH20214014" +CD_TEXT { + LANGUAGE 0 { + TITLE "Zombie (part two)" + PERFORMER "Nile Rodgers, Roy Hargrove, and Money Mark" + MESSAGE "" + } +} +FILE "data.bin" 42:21:67 04:27:38 + + +// Track 15 +TRACK AUDIO +NO COPY +NO PRE_EMPHASIS +TWO_CHANNEL_AUDIO +ISRC "USRH20214015" +CD_TEXT { + LANGUAGE 0 { + TITLE "No Agreement" + PERFORMER "Tony Allen, Res, Ray Lema, Baaba Maal, Postive Black Soul, and Archie Shepp" + MESSAGE "" + } +} +FILE "data.bin" 46:49:30 08:12:02 + + +// Track 16 +TRACK AUDIO +NO COPY +NO PRE_EMPHASIS +TWO_CHANNEL_AUDIO +ISRC "USRH20214015" +CD_TEXT { + LANGUAGE 0 { + TITLE "So Be It" + PERFORMER "Kelis" + MESSAGE "" + } +} +FILE "data.bin" 55:01:32 03:52:15 + + +// Track 17 +TRACK AUDIO +NO COPY +NO PRE_EMPHASIS +TWO_CHANNEL_AUDIO +ISRC "USRH20214016" +CD_TEXT { + LANGUAGE 0 { + TITLE "[interlude] This is an Ashanti Proverb" + PERFORMER "Monoaural" + MESSAGE "" + } +} +FILE "data.bin" 58:53:47 00:09:58 + + +// Track 18 +TRACK AUDIO +NO COPY +NO PRE_EMPHASIS +TWO_CHANNEL_AUDIO +ISRC "GBBBM0002184" +CD_TEXT { + LANGUAGE 0 { + TITLE "By Your Side (Cottonbelly's Fola Mix Edit)" + PERFORMER "Sade" + MESSAGE "" + } +} +FILE "data.bin" 59:03:30 04:39:67 + + +// Track 19 +TRACK AUDIO RW_RAW +COPY +PRE_EMPHASIS +TWO_CHANNEL_AUDIO +ISRC "USRH20214019" +CD_TEXT { + LANGUAGE 0 { + TITLE "Colonial Mentality" + PERFORMER "Yerba Buena! and Lenine" + MESSAGE "" + } +} +FILE "data.bin" 63:43:22 01:34:10 +START 00:00:01 +INDEX 00:10:01 + + +// Track 20 +TRACK AUDIO +NO COPY +NO PRE_EMPHASIS +TWO_CHANNEL_AUDIO +ISRC "USRH20214020" +CD_TEXT { + LANGUAGE 0 { + TITLE "Trouble Sleep Yanga Wake Am" + PERFORMER "Baaba Maal and Taj Mahal featuring Kaouding Cissoko and Antibalas" + MESSAGE "" + } +} +FILE "data.bin" 65:17:32 10:38:20 + diff --git a/test/test002.toc b/test/test002.toc new file mode 100644 index 0000000..b04acc4 --- /dev/null +++ b/test/test002.toc @@ -0,0 +1,208 @@ +CD_DA + +CATALOG "0075994590127" +CD_TEXT { + LANGUAGE_MAP { + 0: 9 + } + LANGUAGE 0 { + TITLE "Jagged Little Pill" + PERFORMER "Alanis Morissette" + MESSAGE "1995" + } +} + +// Track 1 +TRACK AUDIO +NO COPY +NO PRE_EMPHASIS +TWO_CHANNEL_AUDIO +CD_TEXT { + LANGUAGE 0 { + TITLE "All I Really Want" + PERFORMER "Alanis Morissette" + } +} +SILENCE 00:00:32 +FILE "alanis_morissette-1998-jagged_little_pill-kmf.wav" 0 04:43:28 +START 00:00:32 + + +// Track 2 +TRACK AUDIO +NO COPY +NO PRE_EMPHASIS +TWO_CHANNEL_AUDIO +CD_TEXT { + LANGUAGE 0 { + TITLE "You Oughta Know" + PERFORMER "Alanis Morissette" + } +} +FILE "alanis_morissette-1998-jagged_little_pill-kmf.wav" 04:43:28 04:09:00 +START 00:01:20 + + +// Track 3 +TRACK AUDIO +NO COPY +NO PRE_EMPHASIS +TWO_CHANNEL_AUDIO +CD_TEXT { + LANGUAGE 0 { + TITLE "Perfect" + PERFORMER "Alanis Morissette" + } +} +FILE "alanis_morissette-1998-jagged_little_pill-kmf.wav" 08:52:28 03:07:65 +START 00:01:35 + + +// Track 4 +TRACK AUDIO +NO COPY +NO PRE_EMPHASIS +TWO_CHANNEL_AUDIO +CD_TEXT { + LANGUAGE 0 { + TITLE "Hand in My Pocket" + PERFORMER "Alanis Morissette" + } +} +FILE "alanis_morissette-1998-jagged_little_pill-kmf.wav" 12:00:18 03:40:35 +START 00:01:37 + + +// Track 5 +TRACK AUDIO +NO COPY +NO PRE_EMPHASIS +TWO_CHANNEL_AUDIO +CD_TEXT { + LANGUAGE 0 { + TITLE "Right Through You" + PERFORMER "Alanis Morissette" + } +} +FILE "alanis_morissette-1998-jagged_little_pill-kmf.wav" 15:40:53 02:57:00 +START 00:02:55 + + +// Track 6 +TRACK AUDIO +NO COPY +NO PRE_EMPHASIS +TWO_CHANNEL_AUDIO +CD_TEXT { + LANGUAGE 0 { + TITLE "Forgiven" + PERFORMER "Alanis Morissette" + } +} +FILE "alanis_morissette-1998-jagged_little_pill-kmf.wav" 18:37:53 05:00:02 +START 00:01:42 + + +// Track 7 +TRACK AUDIO +NO COPY +NO PRE_EMPHASIS +TWO_CHANNEL_AUDIO +CD_TEXT { + LANGUAGE 0 { + TITLE "You Learn" + PERFORMER "Alanis Morissette" + } +} +FILE "alanis_morissette-1998-jagged_little_pill-kmf.wav" 23:37:55 04:00:33 +START 00:01:60 + + +// Track 8 +TRACK AUDIO +NO COPY +NO PRE_EMPHASIS +TWO_CHANNEL_AUDIO +CD_TEXT { + LANGUAGE 0 { + TITLE "Head Over Feet" + PERFORMER "Alanis Morissette" + } +} +FILE "alanis_morissette-1998-jagged_little_pill-kmf.wav" 27:38:13 04:25:40 +START 00:01:00 + + +// Track 9 +TRACK AUDIO +NO COPY +NO PRE_EMPHASIS +TWO_CHANNEL_AUDIO +CD_TEXT { + LANGUAGE 0 { + TITLE "Mary Jane" + PERFORMER "Alanis Morissette" + } +} +FILE "alanis_morissette-1998-jagged_little_pill-kmf.wav" 32:03:53 04:41:57 +START 00:02:55 + + +// Track 10 +TRACK AUDIO +NO COPY +NO PRE_EMPHASIS +TWO_CHANNEL_AUDIO +CD_TEXT { + LANGUAGE 0 { + TITLE "Ironic" + PERFORMER "Alanis Morissette" + } +} +FILE "alanis_morissette-1998-jagged_little_pill-kmf.wav" 36:45:35 03:50:03 +START 00:01:45 + + +// Track 11 +TRACK AUDIO +NO COPY +NO PRE_EMPHASIS +TWO_CHANNEL_AUDIO +CD_TEXT { + LANGUAGE 0 { + TITLE "Not the Doctor" + PERFORMER "Alanis Morissette" + } +} +FILE "alanis_morissette-1998-jagged_little_pill-kmf.wav" 40:35:38 03:46:25 +START 00:01:20 + + +// Track 12 +TRACK AUDIO +NO COPY +NO PRE_EMPHASIS +TWO_CHANNEL_AUDIO +CD_TEXT { + LANGUAGE 0 { + TITLE "Wake Up" + PERFORMER "Alanis Morissette" + } +} +FILE "alanis_morissette-1998-jagged_little_pill-kmf.wav" 44:21:63 04:56:03 +START 00:02:40 + + +// Track 13 +TRACK AUDIO +NO COPY +NO PRE_EMPHASIS +TWO_CHANNEL_AUDIO +CD_TEXT { + LANGUAGE 0 { + TITLE "You Oughta Know (Alternate Take)" + PERFORMER "Alanis Morissette" + } +} +FILE "alanis_morissette-1998-jagged_little_pill-kmf.wav" 49:17:66 08:13:19 +START 00:00:22 diff --git a/tools/Makefile b/tools/Makefile new file mode 100644 index 0000000..26e7e40 --- /dev/null +++ b/tools/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 +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/tools/cuebreakpoints.c b/tools/cuebreakpoints.c new file mode 100644 index 0000000..ae610f3 --- /dev/null +++ b/tools/cuebreakpoints.c @@ -0,0 +1,109 @@ +/* + * 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; + +void usage (int status) +{ + if (0 == status) { + fprintf(stdout, "%s: usage: cuebreakpoints [-h] [-i cue|toc] [file...]\n", progname); + fputs("\ +\n\ +OPTIONS\n\ +-h print usage\n\ +-i cue|toc set format of file(s)\n\ +", stdout); + } else { + fprintf(stderr, "%s: syntax error\n", progname); + fprintf(stderr, "run `%s -h' 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_breaks (Cd *cd) +{ + int i; + long b; + Track *track; + + for (i = 1; i < cd_get_ntrack(cd); i++) { + track = cd_get_track(cd, i); + /* don't print zero indexes */ + b = track_get_start(track) + track_get_index(track, 1) - track_get_zero_pre(track); + if (0 != b) + print_m_ss_ff(b); + } +} + +int breaks (char *name, int format) +{ + Cd *cd = NULL; + + if (NULL == (cd = cf_parse(name, &format))) { + fprintf(stderr, "%s: input file error\n", name); + return -1; + } + + print_breaks(cd); + + return 0; +} + +int main (int argc, char **argv) +{ + int format = UNKNOWN; + + /* option variables */ + char c; + /* getopt() variables */ + extern char *optarg; + extern int optind; + + progname = *argv; + + while (-1 != (c = getopt(argc, argv, "hi:"))) { + switch (c) { + case 'h': + usage(0); + break; + case 'i': + if (0 == strcmp("cue", optarg)) + format = CUE; + else if (0 == strcmp("toc", optarg)) + format = TOC; + break; + default: + usage(1); + break; + } + } + + if (optind == argc) { + breaks("-", format); + } else { + for (; optind < argc; optind++) + breaks(argv[optind], format); + } + + return 0; +} diff --git a/tools/cueconvert.c b/tools/cueconvert.c new file mode 100644 index 0000000..d4bd2d5 --- /dev/null +++ b/tools/cueconvert.c @@ -0,0 +1,108 @@ +/* + * 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 [-h] [-i cue|toc] [-o cue|toc] [infile [outfile]]\n", progname); + fputs("\ +\n\ +OPTIONS\n\ +-h print usage\n\ +-i cue|toc set format of input file\n\ +-o cue|toc set format of output file\n\ +", stdout); + } else { + fprintf(stderr, "%s: syntax error\n", progname); + fprintf(stderr, "run `%s -h' 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() variables */ + extern char *optarg; + extern int optind; + + progname = *argv; + + while (-1 != (c = getopt(argc, argv, "hi:o:"))) { + switch (c) { + case 'h': + usage(0); + break; + case 'i': + if (0 == strcmp("cue", optarg)) + iformat = CUE; + else if (0 == strcmp("toc", optarg)) + iformat = TOC; + break; + case 'o': + if (0 == strcmp("cue", optarg)) + oformat = CUE; + else if (0 == strcmp("toc", optarg)) + oformat = TOC; + 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/tools/cueprint.c b/tools/cueprint.c new file mode 100644 index 0000000..2a83a71 --- /dev/null +++ b/tools/cueprint.c @@ -0,0 +1,240 @@ +/* + * 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 /* getopt() */ +#include /* isdigit() */ +#include "cuefile.h" + +/* default templates */ +#define D_TEMPLATE "%P \"%T\" (%N tracks)\n" +#define T_TEMPLATE "%n: %p \"%t\"\n" + +char *progname; +char *d_template = D_TEMPLATE; /* disc template */ +char *t_template = T_TEMPLATE; /* track template */ + +void usage (int status) +{ + if (0 == status) { + fprintf(stdout, "%s: usage: cueprint [-h] [-i cue|toc] [-d TEMPLATE] [-t TEMPLATE] [file...]\n", progname); + fputs("\ +\n\ +OPTIONS\n\ +-h print usage\n\ +-i cue|toc set format of file(s)\n\ +-d TEMPLATE set disc template (see TEMPLATE EXPANSION)\n\ +-t TEMPLATE set track template (see TEMPLATE EXPANSION)\n\ +\n\ +Template Expansion\n\ +Disc:\n\ +%A - album arranger\n\ +%C - album composer\n\ +%G - album genre\n\ +%M - album message\n\ +%N - number of tracks\n\ +%P - album performer\n\ +%S - album songwriter\n\ +%T - album title\n\ +%U - album UPC/EAN\n\ +Track:\n\ +%a - track arranger\n\ +%c - track composer\n\ +%g - track genre\n\ +%i - track ISRC\n\ +%m - track message\n\ +%n - track number\n\ +%p - track perfomer\n\ +%t - track title\n\ +%u - track ISRC (CD-TEXT)\n\ +\n\ +Any other % is expanded to that character. For example, to get a\n\ +'%', use %%.\n\ +", stdout); + fprintf(stdout, "default disc template is:\n\"%s\"\n", D_TEMPLATE); + fprintf(stdout, "default track template is:\n\"%s\"\n", T_TEMPLATE); + } else { + fprintf(stderr, "%s: syntax error\n", progname); + fprintf(stderr, "run `%s -h' for usage\n", progname); + } + + exit (status); +} + +void disc_field (char *c, Cd *cd) +{ + Cdtext *cdtext = NULL; + cdtext = cd_get_cdtext(cd); + + switch (*c) { + case 'A': + printf("%s", cdtext_get(PTI_ARRANGER, cdtext)); + break; + case 'C': + printf("%s", cdtext_get(PTI_COMPOSER, cdtext)); + break; + case 'G': + printf("%s", cdtext_get(PTI_GENRE, cdtext)); + break; + case 'M': + printf("%s", cdtext_get(PTI_MESSAGE, cdtext)); + break; + case 'N': + printf("%0*d", 2, cd_get_ntrack(cd)); + break; + case 'P': + printf("%s", cdtext_get(PTI_PERFORMER, cdtext)); + break; + case 'R': + printf("%s", cdtext_get(PTI_ARRANGER, cdtext)); + break; + case 'S': + printf("%s", cdtext_get(PTI_SONGWRITER, cdtext)); + break; + case 'T': + printf("%s", cdtext_get(PTI_TITLE, cdtext)); + break; + case 'U': + printf("%s", cdtext_get(PTI_UPC_ISRC, cdtext)); + break; + default: + putchar(*c); + break; + } +} + +void track_field (char *c, Cd *cd, int trackno) +{ + Track *track = NULL; + Cdtext *cdtext = NULL; + track = cd_get_track(cd, trackno); + cdtext = track_get_cdtext(track); + + switch (*c) { + case 'a': + printf("%s", cdtext_get(PTI_ARRANGER, cdtext)); + break; + case 'c': + printf("%s", cdtext_get(PTI_COMPOSER, cdtext)); + break; + case 'f': + printf("%s", track_get_filename(track)); + break; + case 'g': + printf("%s", cdtext_get(PTI_GENRE, cdtext)); + break; + case 'i': + printf("%s", track_get_isrc(track)); + break; + case 'm': + printf("%s", cdtext_get(PTI_MESSAGE, cdtext)); + break; + case 'n': + printf("%0*d", 2, trackno); + break; + case 'p': + printf("%s", cdtext_get(PTI_PERFORMER, cdtext)); + break; + case 's': + printf("%s", cdtext_get(PTI_SONGWRITER, cdtext)); + break; + case 't': + printf("%s", cdtext_get(PTI_TITLE, cdtext)); + break; + case 'u': + printf("%s", cdtext_get(PTI_UPC_ISRC, cdtext)); + break; + default: + disc_field(c, cd); + break; + } +} + +void print_info (Cd *cd) +{ + int i; /* track */ + char *c; + + for (c = d_template; '\0' != *c; c++) { + if ('%' == *c) { + c++; + disc_field(c, cd); + } else { + putchar(*c); + } + } + + for (i = 1; i <= cd_get_ntrack(cd); i++) { + for (c = t_template; '\0' != *c; c++) { + if ('%' == *c) { + c++; + track_field(c, cd, i); + } else { + putchar(*c); + } + } + } +} + +int info (char *name, int format) +{ + Cd *cd = NULL; + + if (NULL == (cd = cf_parse(name, &format))) { + fprintf(stderr, "%s: input file error\n", name); + return -1; + } + + print_info(cd); + + return 0; +} + +int main (int argc, char **argv) +{ + int format = UNKNOWN; + /* getopt () variables */ + char c; + extern char *optarg; + extern int optind; + + progname = *argv; + + while (-1 != (c = getopt(argc, argv, "hi:d:t:"))) { + switch (c) { + case 'h': + usage(0); + break; + case 'i': + if (0 == strcmp("cue", optarg)) + format = CUE; + else if (0 == strcmp("toc", optarg)) + format = TOC; + break; + case 'd': + d_template = optarg; + break; + case 't': + t_template = optarg; + break; + default: + usage(1); + break; + } + } + + if (optind == argc) { + info("-", format); + } else { + for (; optind < argc; optind++) + info(argv[optind], format); + } + + return 0; +}