libmp3splt
src/cue.c
Go to the documentation of this file.
00001 /**********************************************************
00002  * libmp3splt -- library based on mp3splt,
00003  *               for mp3/ogg splitting without decoding
00004  *
00005  * Copyright (c) 2002-2005 M. Trotta - <mtrotta@users.sourceforge.net>
00006  * Copyright (c) 2005-2011 Alexandru Munteanu - io_fx@yahoo.fr
00007  *
00008  *********************************************************/
00009 
00010 /**********************************************************
00011  * This program is free software; you can redistribute it and/or
00012  * modify it under the terms of the GNU General Public License
00013  * as published by the Free Software Foundation; either version 2
00014  * of the License, or (at your option) any later version.
00015  *
00016  * This program is distributed in the hope that it will be useful,
00017  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00018  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00019  * GNU General Public License for more details.
00020  *
00021  * You should have received a copy of the GNU General Public License
00022  * along with this program; if not, write to the Free Software
00023  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
00024  * 02111-1307, USA.
00025  *********************************************************/
00026 
00031 #include <string.h>
00032 #include <ctype.h>
00033 
00034 #include "splt.h"
00035 #include "cddb_cue_common.h"
00036 
00037 #include "cue.h"
00038 
00040 static void splt_cue_process_track_line(char *line_content, cue_utils *cu, splt_state *state)
00041 {
00042   // Skip the word TRACK
00043   line_content += 5;
00044 
00045   if (cu->tracks == -1) 
00046   {
00047     cu->tracks = 0;
00048   }
00049 
00050   if (!cu->time_for_track) 
00051   {
00052     splt_e_set_error_data(state, cu->file);
00053     cu->error = SPLT_INVALID_CUE_FILE;
00054   }
00055 
00056   cu->performer = SPLT_FALSE;
00057   cu->title = SPLT_FALSE;
00058   cu->time_for_track = SPLT_FALSE;
00059   cu->tracks++;
00060   cu->current_track_type = SPLT_SPLITPOINT;
00061 
00062   splt_tu_new_tags_if_necessary(state, cu->tracks - 1);
00063 }
00064 
00065 static void remove_trailing_spaces_and_quote(char *ptr_e, char *in)
00066 {
00067   if (ptr_e)
00068   {
00069     ptr_e--;
00070 
00071     while (*ptr_e == ' ' && ptr_e > in)
00072     {
00073       ptr_e--;
00074     }
00075 
00076     if (ptr_e > in)
00077     {
00078       if (*ptr_e == '"')
00079       {
00080         *ptr_e = '\0';
00081       }
00082       else
00083       {
00084         *(ptr_e + 1) = '\0';
00085       }
00086     }
00087   }
00088 }
00089 
00090 static const char *splt_cue_parse_value(char *in, int skip_last_word)
00091 {
00092   char *ptr_b = in;
00093   char *ptr_e = NULL;
00094 
00095   while (*ptr_b == ' ')
00096   {
00097     ptr_b++;
00098   }
00099 
00100   if (*ptr_b == '"')
00101   {
00102     ptr_b++;
00103   }
00104 
00105   ptr_e = strchr(ptr_b + 1, '\0');
00106 
00107   remove_trailing_spaces_and_quote(ptr_e, in);
00108 
00109   if (skip_last_word)
00110   {
00111     ptr_e = strrchr(ptr_b, ' ');
00112     remove_trailing_spaces_and_quote(ptr_e, in);
00113   }
00114 
00115   return ptr_b;
00116 }
00117 
00119 static int splt_cue_store_value(splt_state *state, char *in,
00120     int index, int tag_field)
00121 {
00122   if (!in)
00123   {
00124     return SPLT_OK;
00125   }
00126 
00127   const char *ptr_b = splt_cue_parse_value(in, SPLT_FALSE);
00128 
00129   char *out = NULL;
00130   int error = splt_su_append(&out, ptr_b, strlen(ptr_b) + 1, NULL);
00131   if (error < 0) { return error; }
00132 
00133   if (tag_field == SPLT_TAGS_ARTIST)
00134   {
00135     splt_c_put_info_message_to_client(state, _("\n  Artist: %s\n"), out);
00136   }
00137   else if (tag_field == SPLT_TAGS_ALBUM)
00138   {
00139     splt_c_put_info_message_to_client(state, _("  Album: %s\n"), out);
00140   }
00141 
00142   error = splt_tu_set_tags_field(state, index, tag_field, out);
00143   if (out)
00144   {
00145     free(out);
00146     out = NULL;
00147   }
00148 
00149   return error;
00150 }
00151 
00153 static void splt_cue_process_title_line(char *line_content, cue_utils *cu, splt_state *state)
00154 {
00155   int err = SPLT_OK;
00156 
00157   // Skip the word TITLE
00158   line_content += 5;
00159 
00160   if (cu->tracks == -1)
00161   {
00162     if ((err = splt_cue_store_value(state, line_content, 
00163             0, SPLT_TAGS_ALBUM)) != SPLT_OK)
00164     {
00165       cu->error = err;
00166       return;
00167     }
00168   }
00169   else
00170   {
00171     if (cu->tracks > 0)
00172     {
00173       if ((err = splt_cue_store_value(state, line_content,
00174               cu->tracks-1, SPLT_TAGS_TITLE)) != SPLT_OK)
00175       {
00176         cu->error = err;
00177         return;
00178       }
00179     }
00180 
00181     cu->title = SPLT_TRUE;
00182   }
00183 }
00184 
00186 static void splt_cue_process_performer_line(char *line_content, cue_utils *cu, splt_state *state)
00187 {
00188   int err = SPLT_OK;
00189 
00190   // Skip the word PERFORMER
00191   line_content += 9;
00192 
00193   if (cu->tracks == -1)
00194   {
00195     //we always have one artist in a cue file, we
00196     //put the performers if more than one artist
00197     if ((err = splt_cue_store_value(state, line_content,
00198             0, SPLT_TAGS_ARTIST)) != SPLT_OK)
00199     {
00200       cu->error = err;
00201       return;
00202     }
00203   }
00204   else
00205   {
00206     if (cu->tracks > 0)
00207     {
00208       if ((err = splt_cue_store_value(state, line_content,
00209               cu->tracks - 1, SPLT_TAGS_PERFORMER)) != SPLT_OK)
00210       {
00211         cu->error = err;
00212         return;
00213       }
00214     }
00215  
00216     cu->performer = SPLT_TRUE;
00217   }
00218 
00219 }
00220 
00221 
00223 static void splt_cue_process_index_line(char *line_content, cue_utils *cu, splt_state *state)
00224 {
00225   int err = SPLT_OK;
00226 
00227   line_content += 9;
00228 
00229   if (cu->tracks <= 0)
00230   {
00231     return;
00232   }
00233 
00234   char *trimmed_line = splt_su_trim_spaces(line_content);
00235 
00236   long hundr_seconds = splt_co_convert_to_hundreths(trimmed_line);
00237   if (hundr_seconds == -1)
00238   {
00239     splt_e_set_error_data(state, cu->file);
00240     cu->error = SPLT_INVALID_CUE_FILE;
00241     return;
00242   }
00243 
00244   err = splt_sp_append_splitpoint(state, hundr_seconds, NULL, 
00245                                   cu->current_track_type);
00246   if (err < 0) { cu->error = err; return; }
00247 
00248   cu->time_for_track = SPLT_TRUE;
00249   cu->counter++;
00250 }
00251 
00253 static void splt_cue_process_rem_line(char *line_content, cue_utils *cu, splt_state *state)
00254 {
00255   char *linetail;
00256 
00257   // Skip the word REM
00258   line_content += 3;
00259 
00260   // Skip all leading whitespace after the word REM
00261   while ((*line_content==' ')||(*line_content=='\t')) line_content++;
00262 
00263   if((linetail=strstr(line_content,"CREATOR"))!=NULL)
00264   {
00265     // Skip the word "CREATOR"
00266     linetail += 7;
00267 
00268     if(strstr(linetail,"MP3SPLT_GTK")!=NULL)
00269     {
00270       cu->file_has_been_created_by_us = SPLT_TRUE;
00271     }
00272   }
00273   else if((linetail=strstr(line_content,"SPLT_TITLE_IS_FILENAME"))!=NULL)
00274   {
00275     cu->title_is_filename = SPLT_TRUE;
00276   }
00277   else if((linetail=strstr(line_content,"NOKEEP"))!=NULL)
00278   {
00279     if (cu->tracks >= 0)
00280       cu->current_track_type=SPLT_SKIPPOINT;
00281   }
00282 }
00283 
00285 static void splt_cue_process_file_line(char *line_content, cue_utils *cu, splt_state *state)
00286 {
00287   if (!splt_o_get_int_option(state, SPLT_OPT_SET_FILE_FROM_CUE_IF_FILE_TAG_FOUND))
00288   {
00289     return;
00290   }
00291 
00292   // Skip the word FILE
00293   line_content += 4;
00294 
00295   int err = SPLT_OK;
00296 
00297   const char *file_from_cue = splt_cue_parse_value(line_content, SPLT_TRUE);
00298 
00299   if (splt_io_check_if_file(NULL, file_from_cue))
00300   {
00301     err = splt_t_set_filename_to_split(state, file_from_cue);
00302     if (err < 0) { cu->error = err; }
00303     return;
00304   }
00305 
00306   char *file_from_cue_with_path = NULL;
00307   splt_su_copy(cu->file, &file_from_cue_with_path);
00308   splt_su_keep_path_and_remove_filename(file_from_cue_with_path);
00309   splt_su_append_str(&file_from_cue_with_path, SPLT_DIRSTR, file_from_cue, NULL);
00310 
00311   if (splt_io_check_if_file(NULL, file_from_cue_with_path))
00312   {
00313     err = splt_t_set_filename_to_split(state, file_from_cue_with_path);
00314   }
00315 
00316   if (file_from_cue_with_path)
00317   {
00318     free(file_from_cue_with_path);
00319     file_from_cue_with_path = NULL;
00320   }
00321 
00322   if (err < 0) { cu->error = err; return; }
00323 }
00324 
00328 static void splt_cue_process_line(char **l, cue_utils *cu, splt_state *state)
00329 {
00330   if (!l || !*l) { return; }
00331 
00332   char *line = *l;
00333 
00334   splt_su_line_to_unix(line);
00335   splt_su_str_cut_last_char(line);
00336 
00337   splt_t_clean_one_split_data(state, cu->tracks);
00338 
00339   char *line_content = NULL;
00340   if (((line_content = strstr(line, "TRACK")) != NULL)
00341       && (strstr(line, "AUDIO") != NULL))
00342   {
00343     splt_cue_process_track_line(line_content, cu, state);
00344   }
00345   else if ((line_content = strstr(line, "REM")) != NULL)
00346   {
00347     splt_cue_process_rem_line(line_content, cu, state);
00348   }
00349   else if ((line_content = strstr(line, "TITLE")) != NULL)
00350   {
00351     splt_cue_process_title_line(line_content, cu, state);
00352   }
00353   else if ((line_content = strstr(line, "PERFORMER")) != NULL)
00354   {
00355     splt_cue_process_performer_line(line_content, cu, state);
00356   }
00357   else if ((line_content = strstr(line, "INDEX 01")) != NULL)
00358   {
00359     splt_cue_process_index_line(line_content, cu, state);
00360   }
00361   else if ((line_content = strstr(line, "FILE")) != NULL)
00362   {
00363     splt_cue_process_file_line(line_content, cu, state);
00364   }
00365 
00366   free(*l);
00367   *l = NULL;
00368 }
00369 
00370 
00371 /* Malloc memory for and initialize a cue_utils structure
00372 
00373 \param error Contains the libmp3splt error number if any error
00374        occoured in this step.
00375 \return The address of the structure
00376  */
00377 static cue_utils *splt_cue_cu_new(int *error)
00378 {
00379   cue_utils *cu = malloc(sizeof(cue_utils));
00380   if (cu == NULL)
00381   {
00382     *error = SPLT_ERROR_CANNOT_ALLOCATE_MEMORY;
00383     return NULL;
00384   }
00385 
00386   cu->tracks = -1;
00387   cu->time_for_track = SPLT_TRUE;
00388   cu->performer = SPLT_FALSE;
00389   cu->title = SPLT_FALSE;
00390   cu->counter = 0;
00391   cu->file = NULL;
00392   cu->error = SPLT_OK;
00393   cu->current_track_type = SPLT_SPLITPOINT;
00394   cu->title_is_filename = SPLT_FALSE;
00395   cu->file_has_been_created_by_us = SPLT_FALSE;
00396 
00397   return cu;
00398 }
00399 
00405 static void splt_cue_cu_free(cue_utils **cu)
00406 {
00407   if (!cu || !*cu)
00408   {
00409     return;
00410   }
00411 
00412   free(*cu);
00413   *cu = NULL;
00414 }
00415 
00425 int splt_cue_put_splitpoints(const char *file, splt_state *state, int *error)
00426 {
00427   if (file == NULL)
00428   { 
00429     *error = SPLT_INVALID_CUE_FILE;
00430     return 0;
00431   }
00432 
00433   splt_c_put_info_message_to_client(state, 
00434       _(" reading informations from CUE file %s ...\n"),file);
00435 
00436   splt_t_free_splitpoints_tags(state);
00437 
00438   *error = SPLT_CUE_OK;
00439 
00440   int err = SPLT_OK;
00441   FILE *file_input = NULL;
00442   char *line = NULL;
00443   int tracks = -1;
00444 
00445   cue_utils *cu = splt_cue_cu_new(&err);
00446   
00447   if (err < 0) { *error = err; return tracks; }
00448   cu->file = file;
00449 
00450   //TODO: .cue #REM GENRE support
00451   err = splt_tu_set_tags_field(state, 0, SPLT_TAGS_GENRE, SPLT_UNDEFINED_GENRE);
00452   if (err != SPLT_OK)
00453   {
00454     *error = err;
00455     return tracks;
00456   }
00457 
00458   if (!(file_input=splt_io_fopen(file, "r")))
00459   {
00460     splt_e_set_strerror_msg_with_data(state, file);
00461     *error = SPLT_ERROR_CANNOT_OPEN_FILE;
00462     return tracks;
00463   }
00464 
00465   if (fseek(file_input, 0, SEEK_SET) != 0)
00466   {
00467     splt_e_set_strerror_msg_with_data(state, file);
00468     *error = SPLT_ERROR_SEEKING_FILE;
00469     goto function_end;
00470   }
00471 
00472   while ((line = splt_io_readline(file_input, error)) != NULL)
00473   {
00474     if (*error < 0) { goto function_end; }
00475 
00476     splt_cue_process_line(&line, cu, state);
00477     tracks = cu->tracks;
00478     if (cu->error < 0) { *error = cu->error; goto function_end; }
00479   }
00480 
00481   // Append a split point at the end of the file
00482   // If the file has been created by us this has already been done
00483   // and we can skip this step.
00484   if(!cu->file_has_been_created_by_us)
00485     err = splt_sp_append_splitpoint(state, LONG_MAX,
00486                                     _("description here"), cu->current_track_type);
00487   
00488   if (cu->counter == 0)
00489   {
00490     splt_e_set_error_data(state, file);
00491     *error = SPLT_INVALID_CUE_FILE;
00492     goto function_end;
00493   }
00494 
00495   if (!cu->time_for_track) 
00496   {
00497     tracks--;
00498   }
00499 
00500   // Generate filenames using the tags we got
00501   splt_cc_put_filenames_from_tags(state, tracks, error);
00502   
00503   // If mp3splt_gtk has generated the cue file 
00504   // splt_cc_put_filenames_from_tags has now auto-generated
00505   // filenames that contain the intended filename (that was
00506   // written to the TITLE tag).
00507   // Which means we have to correct this by copying the
00508   // TITLE tags to the filenames.
00509 
00510     if(cu->title_is_filename)
00511       {
00512         int i;
00513         for(i=0;i<tracks;i++)
00514           {
00515             splt_sp_set_splitpoint_name(state, i,
00516                                         splt_tu_get_tags_field(state, i, SPLT_TAGS_TITLE));
00517           }
00518       }
00519 
00520 function_end:
00521   splt_cue_cu_free(&cu);
00522 
00523   if (line)
00524   {
00525     free(line);
00526     line = NULL;
00527   }
00528 
00529   if (fclose(file_input) != 0)
00530   {
00531     splt_e_set_strerror_msg_with_data(state, file);
00532     *error = SPLT_ERROR_CANNOT_CLOSE_FILE;
00533   }
00534   file_input = NULL;
00535 
00536   if (*error >= 0)
00537   {
00538     splt_c_put_info_message_to_client(state, _("  Tracks: %d\n\n"), tracks);
00539   }
00540 
00541   return tracks;
00542 }
00543 
00553 static void splt_cue_write_title_performer(splt_state *state, FILE *file_output,
00554     int tags_index, short with_spaces, short write_album)
00555 {
00556   splt_tags *tags = NULL;
00557   if (tags_index >= 0)
00558   {
00559     tags = splt_tu_get_tags_at(state, tags_index);
00560   }
00561   else
00562   {
00563     tags = splt_tu_get_current_tags(state);
00564   }
00565 
00566   if (tags)
00567   {
00568     if (write_album)
00569     {
00570       if (tags->album)
00571       {
00572         if (with_spaces) { fprintf(file_output, "    "); }
00573         fprintf(file_output, "TITLE \"%s\"\n", tags->album);
00574       }
00575     }
00576     else
00577     {
00578       if (tags->title)
00579       {
00580         if (with_spaces) { fprintf(file_output, "    "); }
00581         fprintf(file_output, "TITLE \"%s\"\n", tags->title);
00582       }
00583     }
00584 
00585     char *performer = splt_tu_get_artist_or_performer_ptr(tags);
00586     if (performer)
00587     {
00588       if (with_spaces) { fprintf(file_output, "    "); }
00589       fprintf(file_output, "PERFORMER \"%s\"\n", performer);
00590     }
00591   }
00592   else
00593   {
00594     if (with_spaces) { fprintf(file_output, "    "); }
00595     fprintf(file_output, "TITLE \"\"\n");
00596     if (with_spaces) { fprintf(file_output, "    "); }
00597     fprintf(file_output, "PERFORMER \"\"\n");
00598   }
00599 }
00600 
00611 void splt_cue_export_to_file(splt_state *state, const char *out_file,
00612     short stop_at_total_time, int *error)
00613 {
00614   int err = SPLT_OK;
00615 
00616   int num_of_splitpoints = splt_t_get_splitnumber(state);
00617   if (num_of_splitpoints <= 0)
00618   {
00619     return;
00620   }
00621 
00622   long total_time = splt_t_get_total_time(state);
00623   FILE *file_output = NULL;
00624 
00625   splt_d_print_debug(state, "Cue output file without output path = _%s_\n", out_file);
00626 
00627   char *dup_out_file = NULL;
00628   err = splt_su_copy(out_file, &dup_out_file);
00629   if (err < 0) { *error = err; return; }
00630   char *cue_out_file = splt_su_get_file_with_output_path(state, dup_out_file, &err);
00631   free(dup_out_file);
00632   dup_out_file = NULL;
00633   if (err < 0) { *error = err; goto end; }
00634 
00635   splt_d_print_debug(state, "Cue output file with output path = _%s_\n", cue_out_file);
00636 
00637   if (!(file_output = splt_io_fopen(cue_out_file, "w")))
00638   {
00639     splt_e_set_strerror_msg_with_data(state, cue_out_file);
00640     *error = SPLT_ERROR_CANT_WRITE_TO_OUTPUT_FILE;
00641     goto end;
00642   }
00643 
00644   splt_cue_write_title_performer(state, file_output, 0, SPLT_FALSE, SPLT_TRUE);
00645 
00646   char *fname = splt_t_get_filename_to_split(state);
00647 
00648   char new_upper_ext[10] = { '\0' };
00649   const char *upper_ext = splt_p_get_upper_extension(state, &err);
00650   int i = 0;
00651   for (i = 1;i < strlen(upper_ext);i++)
00652   {
00653     new_upper_ext[i-1] = upper_ext[i];
00654   }
00655 
00656   fprintf(file_output, "FILE \"%s\" %s\n", fname, new_upper_ext);
00657   if (err < 0) { *error = err; goto end; }
00658 
00659   splt_t_set_current_split(state, 0);
00660   for (i = 0;i < num_of_splitpoints;i++)
00661   {
00662     long splitpoint = splt_sp_get_splitpoint_value(state, i, &err);
00663     if (err < 0) { *error = err; break; }
00664 
00665     //todo: splitpoint can be slightly != than total_time sometimes
00666     // (test with silence and cue)
00667     if (stop_at_total_time &&
00668         (total_time > 0  && splitpoint >= total_time))
00669     {
00670       break;
00671     }
00672 
00673     fprintf(file_output, "  TRACK %02d AUDIO\n", i+1);
00674 
00675     splt_cue_write_title_performer(state, file_output, -1, SPLT_TRUE, SPLT_FALSE);
00676 
00677     long mins = 0, secs = 0, hundr = 0;
00678     splt_sp_get_mins_secs_hundr_from_splitpoint(splitpoint, &mins, &secs, &hundr);
00679     fprintf(file_output, "    INDEX 01 %02ld:%02ld:%02ld\n", mins, secs, hundr);
00680 
00681     splt_t_current_split_next(state);
00682   }
00683 
00684 end:
00685   fflush(file_output);
00686   if (fclose(file_output) != 0)
00687   {
00688     splt_e_set_strerror_msg_with_data(state, cue_out_file);
00689     *error = SPLT_ERROR_CANNOT_CLOSE_FILE;
00690   }
00691   file_output = NULL;
00692 
00693   splt_c_put_info_message_to_client(state, 
00694       _(" CUE file '%s' created.\n"), cue_out_file);
00695 
00696   if (cue_out_file)
00697   {
00698     free(cue_out_file);
00699     cue_out_file = NULL;
00700   }
00701 }
00702