int EOF_IMPORT_VIA_LC(EOF_VOCAL_TRACK *tp, struct Lyric_Format **lp, int format, char *inputfilename, char *string2) { char * returnedfn = NULL; //Return string from dialog window FILE *inf; //Used to open the input file struct Lyric_Format *detectionlist; unsigned long i; int jumpcode = 0; eof_log("EOF_IMPORT_VIA_LC() entered", 1); //Validate parameters if((tp == NULL) || (inputfilename == NULL)) return 0; //Return failure if((format == 0) && (lp == NULL)) return 0; //Return failure //Perform detection logic InitLyrics(); //Initialize all variables in the Lyrics structure InitMIDI(); //Initialize all variables in the MIDI structure if(format == 0) //Auto-detect lyric format { detectionlist=DetectLyricFormat(inputfilename); if(detectionlist == NULL) return 0; //Return invalid lyric file if(detectionlist->format == PITCHED_LYRIC_FORMAT) { //If the detection format is Pitched Lyrics, the user must specify the corresponding Vocal Rhythm MIDI *lp=detectionlist; //Return the detected lyric information via the lp pointer return -1; //Return prompt for user selection } if(detectionlist->next != NULL) { //If there was more MIDI track with lyrics, the user must specify which track to import *lp=detectionlist; //Return the detected lyric information via the lp pointer return -2; //Return prompt for user selection } Lyrics.in_format=detectionlist->format; //Format to import if(detectionlist->track != NULL) Lyrics.inputtrack=detectionlist->track; //Track to import from DestroyLyricFormatList(detectionlist); //Deallocate the linked list returned by DetectLyricFormat() detectionlist=NULL; } else //Import specific format { Lyrics.in_format=format; if(Lyrics.in_format == KAR_FORMAT) { //If this is a format for which string2 (pitched file or track name) must be specified if(string2 == NULL) //If the track name to import is not given return 0; //Return failure Lyrics.inputtrack=DuplicateString(string2); //Make a duplicate, so its de-allocation won't affect calling function } else if(Lyrics.in_format == PITCHED_LYRIC_FORMAT) { //If importing Pitched Lyrics, user must provide the Vocal Rhythm MIDI returnedfn = ncd_file_select(0, eof_filename, "Select Vocal Rhythm MIDI", eof_filter_midi_files); eof_clear_input(); if(!returnedfn) return 0; //Return error or user canceled } } Lyrics.infilename=DuplicateString(inputfilename); //Make a duplicate, so it's de-allocation won't affect calling function jumpcode=setjmp(jumpbuffer); //Store environment/stack/etc. info in the jmp_buf array if(jumpcode!=0) //if program control returned to the setjmp() call above returning any nonzero value { (void) puts("Assert() handled sucessfully!"); free(Lyrics.infilename); Lyrics.infilename = NULL; ReleaseMemory(1); //Release memory allocated during lyric import return 0; //Return error } //Import lyrics switch(Lyrics.in_format) { case SCRIPT_FORMAT: //Load script.txt format file as input inf=fopen_err(Lyrics.infilename,"rt"); //Script is a text format Script_Load(inf); break; case VL_FORMAT: //Load VL format file as input inf=fopen_err(Lyrics.infilename,"rb"); //VL is a binary format VL_Load(inf); break; case MIDI_FORMAT: //Load MIDI format file as input if(string2 == NULL) //If no track name was given Lyrics.inputtrack=DuplicateString("PART VOCALS"); //Default to PART VOCALS else Lyrics.inputtrack=DuplicateString(string2); //Make a duplicate, so its de-allocation won't affect calling function inf=fopen_err(Lyrics.infilename,"rb"); //MIDI is a binary format Parse_Song_Ini(Lyrics.infilename,1,1); //Load ALL tags from song.ini first, as the delay tag will affect timestamps MIDI_Load(inf,Lyric_handler,0); //Call MIDI_Load, specifying the new KAR-compatible Lyric Event handler break; case USTAR_FORMAT: //Load UltraStar format file as input inf=fopen_err(Lyrics.infilename,"rt"); //UltraStar is a text format UStar_Load(inf); break; case LRC_FORMAT: //Load LRC format file as input case ELRC_FORMAT: inf=fopen_err(Lyrics.infilename,"rt"); //LRC is a text format LRC_Load(inf); break; case VRHYTHM_FORMAT: //Load vocal rhythm (MIDI) and pitched lyrics inf=fopen_err(returnedfn,"rb"); //Vrhythm is a binary format VRhythm_Load(inputfilename,returnedfn,inf); break; case PITCHED_LYRIC_FORMAT: inf=fopen_err(returnedfn,"rb"); //Vrhythm is a binary format VRhythm_Load(eof_filename,returnedfn,inf); break; case KAR_FORMAT: //Load KAR MIDI file inf=fopen_err(Lyrics.infilename,"rb"); //KAR is a binary format MIDI_Load(inf,Lyric_handler,0); //Call MIDI_Load, specifying the new KAR-compatible Lyric Event handler break; case SKAR_FORMAT: //Load SKAR MIDI file inf=fopen_err(Lyrics.infilename,"rb"); //KAR is a binary format Lyrics.inputtrack=DuplicateString("Words"); MIDI_Load(inf,SKAR_handler,0); //Call MIDI_Load, specifying the Simple Karaoke Event handler EndLyricLine(); //KAR files do not mark the end of the last line of lyrics break; case ID3_FORMAT: //Load MP3 ID3 tag inf=fopen_err(Lyrics.infilename,"rb"); //MP3 is a binary format ID3_Load(inf); break; case SRT_FORMAT: //Load SRT file inf=fopen_err(Lyrics.infilename,"rt"); //SRT is a text format SRT_Load(inf); break; case XML_FORMAT: //Load XML file inf=fopen_err(Lyrics.infilename,"rt"); //XML is a text format XML_Load(inf); break; case C9C_FORMAT: //Load JamBand file inf=fopen_err(Lyrics.infilename,"rt"); //JamBand is a text format JB_Load(inf); break; case RS_FORMAT: //Load Rocksmith XML file case RS2_FORMAT: inf=fopen_err(Lyrics.infilename,"rt"); //Rocksmith XML is a text format RS_Load(inf); break; default: return 0; //Return failure }//switch(Lyrics.in_format) free(Lyrics.infilename); Lyrics.infilename = NULL; //Validate imported lyrics if((Lyrics.piececount == 0) || (MIDI_Lyrics.head != NULL)) //If the imported MIDI track had no valid lyrics or otherwise was incorrectly formatted { ReleaseMemory(1); //Release memory allocated during lyric import fclose_err(inf); return 0; //Return no EOF lyric structure } PostProcessLyrics(); //Perform validation of pointers, counters, etc. if(Lyrics.pitch_tracking) { //Only perform input pitch validation and remapping if the import lyrics had pitch information RemapPitches(); //Ensure pitches are within the correct range (except for pitchless lyrics) } //Delete any existing lyrics and lines for(i = 0; i < tp->lyrics; i++) { free(tp->lyric[i]); } tp->lyrics = 0; tp->lines = 0; fclose_err(inf); //Ensure this file gets closed inf=NULL; if(EOF_TRANSFER_FROM_LC(tp,&Lyrics) != 0) //Pass the Lyrics global variable by reference { ReleaseMemory(1); //Release memory allocated during lyric import return 0; //Return error (failed to import into EOF lyric structure) } ReleaseMemory(1); //Release memory allocated during lyric import return 1; //Return finished EOF lyric structure }
void JB_Load(FILE *inf) { size_t maxlinelength; //I will count the length of the longest line (including NULL char/newline) in the //input file so I can create a buffer large enough to read any line into char *buffer; //Will be an array large enough to hold the largest line of text from input file unsigned long index=0; //Used to index within a line of input text unsigned long index2=0; //Used to index within an output buffer char textbuffer[101]={0}; //Allow for a 100 character lyric text unsigned long processedctr=0; //The current line number being processed in the text file unsigned long bufferctr=0; //Ensure textbuffer[] isn't overflowed char notename=0; //Used for parsing note names double timestamp=0.0; //Used to read timestamp char linetype=0; //Is set to one of the following: 1 = lyric, 2 = line break, 3 = end of file unsigned char pitch=0; //Stores the lyric pitch transposed to middle octave int readerrordetected = 0; assert_wrapper(inf != NULL); //This must not be NULL //Find the length of the longest line maxlinelength=FindLongestLineLength(inf,1); //Allocate memory buffer large enough to hold any line in this file buffer=(char *)malloc_err(maxlinelength); (void) fgets_err(buffer,(int)maxlinelength,inf); //Read first line of text, capping it to prevent buffer overflow if(Lyrics.verbose) printf("\nImporting C9C lyrics from file \"%s\"\n\n",Lyrics.infilename); processedctr=0; //This will be set to 1 at the beginning of the main while loop while(!feof(inf) && !readerrordetected) //Until end of file is reached or fgets() returns an I/O error { processedctr++; if(Lyrics.verbose) printf("\tProcessing line %lu\n",processedctr); index = 0; //Read end of file if(strcasestr_spec(buffer,"ENDFILE")) { //A line starting with "ENDFILE" denotes the end of the lyric entries linetype = 3; } //Read the lyric pitch else if(isalpha(buffer[index])) { //A line starting with an alphabetical letter is a normal lyric entry linetype = 1; notename = toupper(buffer[index++]); if(isalpha(buffer[index])) index++; //The first lyric entry seems to repeat the note name pitch=60; //The pitches will be interpreted as ranging from C4 to B4 switch(notename) //Add value of note in current octave { case 'B': pitch+=11; break; case 'A': pitch+=9; break; case 'G': pitch+=7; break; case 'F': pitch+=5; break; case 'E': pitch+=4; break; case 'D': pitch+=2; break; default: break; } if(buffer[index] == '#') { //If the note name is followed by a sharp character, pitch++; //increase the pitch by one half step index++; //Seek past the sharp character } while(buffer[index] != ':') { //Seek to the expected colon character if(buffer[index] == '\0') { //The line ends unexpectedly printf("Error: Invalid lyric entry in line %lu during C9C lyric import (colon missing)\nAborting\n",processedctr); exit_wrapper(1); } index++; } index++; //Seek beyond the colon //Read the lyric text index2=bufferctr=0; while(!isspace(buffer[index])) { //Until whitespace is reached if(buffer[index] == '\0') { //The line ends unexpectedly printf("Error: Invalid lyric entry in line %lu during C9C lyric import (whitespace missing)\nAborting\n",processedctr); exit_wrapper(2); } textbuffer[index2++] = buffer[index++]; //Copy the character to a buffer bufferctr++; if(bufferctr == 100) { //Unexpectedly long lyric reached printf("Error: Invalid lyric entry in line %lu during C9C lyric import (lyric is too long)\nAborting\n",processedctr); exit_wrapper(3); } } textbuffer[index2++] = '\0'; //Terminate the string }//A line starting with an alphabetical letter is a normal lyric entry //Read line break else if(buffer[index] == '-') { //A line starting with "--:S" is the start of a period of silence between lyrics (will be treated as a line break) linetype = 2; } else { //Invalid input printf("Error: Invalid input \"%s\" in line %lu during C9C import\nAborting\n",&(buffer[index]),processedctr); exit_wrapper(4); } //Seek to timestamp while(!isdigit(buffer[index])) { //Until a number (the timestamp) is reached if(buffer[index] == '\0') { //The line ends unexpectedly printf("Error: Invalid line break entry in line %lu during C9C lyric import (timestamp missing)\nAborting\n",processedctr); exit_wrapper(5); } index++; } //Read timestamp if(sscanf(&(buffer[index]), "%20lf", ×tamp) != 1) { //Double floating point value didn't parse printf("Error: Invalid lyric entry in line %lu during C9C lyric import (error parsing timestamp)\nAborting\n",processedctr); exit_wrapper(6); } timestamp *= 1000.0; //Convert to milliseconds //Adjust previous lyric's end position if(Lyrics.lastpiece) { //If there was a previous lyric unsigned long length; assert_wrapper(Lyrics.lastpiece->lyric != NULL); length = (unsigned long)strlen(Lyrics.lastpiece->lyric); Lyrics.lastpiece->duration = timestamp + 0.5 - Lyrics.realoffset - Lyrics.lastpiece->start; //Remember to offset start by realoffset, otherwise Lyrics.lastpiece->start could be the larger operand, causing an overflow if(Lyrics.lastpiece->lyric[length - 1] == '-') { //If the previous lyric ended in a hyphen, the previous lyric lasts all the way up to the start of this one Lyrics.lastpiece->groupswithnext=1; //The previous lyric piece will group with this one } else { //Otherwise space out the lyrics a bit, 1/32 second was suggested if(Lyrics.lastpiece->duration > 31) Lyrics.lastpiece->duration -= 31; //31ms ~= 1 sec/32 } } //Add lyric if(linetype == 1) //If this line defined a new lyric { //Track for pitch changes, enabling Lyrics.pitch_tracking if applicable if((Lyrics.last_pitch != 0) && (Lyrics.last_pitch != pitch)) //There's a pitch change Lyrics.pitch_tracking=1; Lyrics.last_pitch=pitch; //Consider this the last defined pitch if(Lyrics.line_on != 1) //If we're at this point, there should be a line of lyrics in progress CreateLyricLine(); AddLyricPiece(textbuffer,timestamp + 0.5,timestamp + 0.5,pitch,0); //Add lyric with no defined duration } //Add line break else if(linetype == 2) { //If this line defined a line break EndLyricLine(); Lyrics.lastpiece = NULL; //Prevent the first lyric from the new line from altering the previous lyric's duration, which was set by the line break position } //End processing else break; if(fgets(buffer, (int)maxlinelength,inf) == NULL) //Read next line of text, so the EOF condition can be checked, don't exit on EOF readerrordetected = 1; }//while(!feof(inf) && !readerrordetected) free(buffer); //No longer needed, release the memory before exiting function ForceEndLyricLine(); RecountLineVars(Lyrics.lines); //Rebuild line durations since this lyric format required adjusting timestamps after lines were parsed if(Lyrics.verbose) printf("C9C import complete. %lu lyrics loaded\n\n",Lyrics.piececount); }
void VL_Load(FILE *inf) { unsigned long ctr=0; //Generic counter unsigned long start_off=0; //Starting offset of a lyric piece in milliseconds unsigned long end_off=0; //Ending offset of a lyric piece in milliseconds char *temp=NULL; //Pointer for string manipulation struct VL_Text_entry *curtext=NULL; //Conductor for text chunk linked list struct VL_Sync_entry *cursync=NULL; //Conductor for sync chunk linked list unsigned short cur_line_len=0; //The length of the currently line of lyrics unsigned short start_char=0; //The starting character offset for the current sync entry unsigned short end_char=0; //The ending character offset for the current sync entry char groupswithnext=0; //Tracks grouping, passed to AddLyricPiece() assert_wrapper(inf != NULL); //This must not be NULL Lyrics.freestyle_on=1; //VL is a pitch-less format, so import it as freestyle if(Lyrics.verbose) printf("Importing VL lyrics from file \"%s\"\n\n",Lyrics.infilename); //Build the VL storage structure (void) VL_PreLoad(inf,0); //Process offset if(Lyrics.offsetoverride == 0) { if(Lyrics.Offset == NULL) { if(Lyrics.verbose) (void) puts("No offset defined in VL file, applying offset of 0"); Lyrics.realoffset=0; } else if(strcmp(Lyrics.Offset,"0") != 0) { //If the VL file's offset is not zero and the command line offset is not specified assert_wrapper(Lyrics.Offset != NULL); //atol() crashes if NULL is passed to it Lyrics.realoffset=atol(Lyrics.Offset); //convert to number if(Lyrics.realoffset == 0) //atol returns 0 on error { printf("Error converting \"%s\" to integer value\nAborting\n",Lyrics.Offset); exit_wrapper(1); } if(Lyrics.verbose) printf("Applying offset defined in VL file: %ldms\n",Lyrics.realoffset); } //if the VL file's offset is defined as 0, that's what Lyrics.realoffset is initialized to already } if(Lyrics.verbose) (void) puts("Processing lyrics and sync entries"); //Process sync points, adding lyrics to Lyrics structure cursync=VL.Syncs; //Begin with first sync entry while(cursync != NULL) //For each sync point { groupswithnext=0; //Reset this condition start_off=cursync->start_time*10; //VL stores offsets as increments of 10 milliseconds each end_off=cursync->end_time*10; //Validate the lyric line number if(cursync->lyric_number >= VL.numlines) //lyric_number count starts w/ 0 instead of 1 and should never meet/exceed numlines { (void) puts("Error: Invalid line number detected during VL load\nAborting"); exit_wrapper(2); } //Validate the start and end character numbers in the sync entry start_char=cursync->start_char; end_char=cursync->end_char; if(start_char == 0xFFFF) { (void) puts("Error: Sync entry has no valid lyric during VL load\nAborting"); exit_wrapper(3); } //Seek to the correct lyric entry curtext=VL.Lyrics; //Point conductor to first text entry for(ctr=0; ctr<cursync->lyric_number; ctr++) if(curtext->next == NULL) { (void) puts("Error: Unexpected end of text piece linked list\nAborting"); exit_wrapper(4); } else curtext=curtext->next; //Seek forward to next piece cur_line_len = (unsigned short) strlen(curtext->text); //This value will be used several times in the rest of the loop if(start_char >= cur_line_len) //The starting offset cannot be past the end of the line of lyrics { (void) puts("Error: Sync entry has invalid starting offset during VL load\nAborting"); exit_wrapper(5); } if((end_char!=0xFFFF) && (end_char >= cur_line_len)) { //The ending offset cannot be past the end of the line of lyrics (void) puts("Error: Sync entry has invalid ending offset during VL load\nAborting"); exit_wrapper(6); } //Build the lyric based on the start and end character offsets temp=DuplicateString(curtext->text+start_char); //Copy the current text piece into a new string, starting from the character indexed by the sync entry if(Lyrics.verbose>=2) printf("\tProcessing sync entry #%lu: \"%s\"\tstart char=%u\tend char=%u\t\n",ctr,curtext->text,start_char,end_char); if(end_char != 0xFFFF) //If the sync entry ends before the end of the text piece { //"abcdef" strlen=6 st=2,en=4->"cde" if((isspace((unsigned char)temp[end_char-start_char-1])==0) && (isspace((unsigned char)temp[end_char-start_char])==0)) //if this sync entry's text doesn't end in whitespace and the next entry's doesn't begin in whitespace groupswithnext=1; //Allow AddLyricPiece to handle grouping and optional hyphen insertion //I've had to go back and forth on this line, but currently, end_char-start_char seems to mark the location at which to truncate, not the last character to keep before truncating temp[end_char-start_char]='\0'; //Truncate the string as indicated by the sync entry's end index (after the end_char) } //Add lyric to Lyric structure if(cursync->start_char == 0) //If this piece is the beginning of a line of lyrics { //Ensure a line of lyrics isn't already in progress if(Lyrics.line_on == 1) { (void) puts("Error: Lyric lines overlap during VL load\nAborting"); exit_wrapper(7); } if(Lyrics.verbose>=2) (void) puts("New line of lyrics:"); CreateLyricLine(); //Initialize the line } //Add lyric to Lyrics structure. AddLyricPiece(temp,start_off,end_off,PITCHLESS,groupswithnext); //Add the lyric piece to the Lyric structure, no defined pitch free(temp); //Release memory for this temporary string if((end_char == 0xFFFF) || (end_char == cur_line_len)) //If this piece ends a line of lyrics { //Ensure a line of lyrics is in progress if(Lyrics.line_on == 0) { (void) puts("Error: End of lyric line detected when none is started during VL load\nAborting"); exit_wrapper(8); } if(Lyrics.verbose>=2) (void) puts("End line of lyrics"); EndLyricLine(); //End the line } cursync=cursync->next; }//end while(cursync != NULL) ForceEndLyricLine(); if(Lyrics.verbose) printf("VL import complete. %lu lyrics loaded\n",Lyrics.piececount); ReleaseVL(); //Release memory used to build the VL structure }
void SRT_Load(FILE *inf) { char *buffer; //Buffer used to read from input file char *temp=NULL; //Used for string processing unsigned long processedctr=0; //The current line number being processed in the text file size_t maxlinelength; //I will count the length of the longest line (including NULL char/newline) in the //input file so I can create a buffer large enough to read any line into unsigned long startstamp=0,endstamp=0; unsigned long ctr=0; assert_wrapper(inf != NULL); //This must not be NULL //Find the length of the longest line maxlinelength=FindLongestLineLength(inf,1); //Allocate buffers to read file line by line buffer=malloc_err(maxlinelength); //Process each line of input file if(Lyrics.verbose) printf("\nImporting SRT subtitles from file \"%s\"\n\n",Lyrics.infilename); processedctr=0; //This will be set to 1 at the beginning of the main while loop while(fgets(buffer,(int)maxlinelength,inf) != NULL) //Read lines until end of file is reached, don't exit on EOF { processedctr++; //Find first timestamp in this line temp=SeekNextSRTTimestamp(buffer); //Point temp to first timestamp if(temp == NULL) //If there is none, skip this line continue; //Skip processing and read next line startstamp=ConvertSRTTimestamp(&temp,NULL); //Find second timestamp in this line temp=SeekNextSRTTimestamp(temp); //Point temp to second timestamp if(temp == NULL) { if(Lyrics.verbose) printf("Warning: Line #%lu does not contain the ending timestamp. Ignoring\n",processedctr); continue; //Skip processing and read next line } endstamp=ConvertSRTTimestamp(&temp,NULL); //Read next line, which is expected to be the subtitle entry if(fgets(buffer,(int)maxlinelength,inf) == NULL) break; //If another line couldn't be read, exit loop ctr = (unsigned long)strlen(buffer); //Find index of the string's NULL terminator while((ctr > 0) && ((buffer[ctr-1] == '\n') || (buffer[ctr-1] == '\r'))) { //If the string isn't empty and the last character is a newline or carriage return buffer[ctr-1] = '\0'; //Truncate it from the string ctr--; //Track the position of the end of the string } //Add lyric piece as a lyric line CreateLyricLine(); AddLyricPiece(buffer,startstamp,endstamp,PITCHLESS,0); //Write lyric with no defined pitch EndLyricLine(); }//end while(fgets(buffer,maxlinelength,inf) != NULL) ForceEndLyricLine(); //Release memory buffers and return free(buffer); if(Lyrics.verbose) printf("SRT import complete. %lu subtitles loaded\n\n",Lyrics.piececount); }
void SYLT_Parse(struct ID3Tag *tag) { unsigned char frameheader[10]={0}; //This is the ID3 frame header unsigned char syltheader[6]={0}; //This is the SYLT frame header, excluding the terminated descriptor string that follows char *contentdescriptor=NULL; //The null terminated string located after the SYLT frame header char timestampformat=0; //Will be set based on the SYLT header (1= MPEG Frames, 2=Milliseconds) char *string=NULL; //Used to store SYLT strings that are read char *string2=NULL; //Used to build a display version of the string for debug output (minus newline) unsigned char timestamparray[4]={0};//Used to store the timestamp for a lyric string unsigned long timestamp=0; //The timestamp converted to milliseconds unsigned long breakpos; //Will be set to the first byte beyond the SYLT frame unsigned long framesize=0; char groupswithnext=0; //Used for grouping logic char linebreaks=0; //Tracks whether the imported lyrics defined linebreaks (if not, each ID3 lyric should be treated as one line) struct Lyric_Piece *ptr=NULL,*ptr2=NULL; //Used to insert line breaks as necessary struct Lyric_Line *lineptr=NULL; //Used to insert line breaks as necessary unsigned long length=0; //Used to store the length of each string that is read from file unsigned long filepos=0; //Used to track the current file position during SYLT lyric parsing //Validate input assert_wrapper((tag != NULL) && (tag->fp != NULL)); breakpos=ftell_err(tag->fp); //Load and validate ID3 frame header and SYLT frame header fread_err(frameheader,10,1,tag->fp); //Load ID3 frame header fread_err(syltheader,6,1,tag->fp); //Load SYLT frame header if((frameheader[9] & 192) != 0) { (void) puts("ID3 Compression and Encryption are not supported\nAborting"); exit_wrapper(3); } if(syltheader[0] != 0) { (void) puts("Unicode ID3 lyrics are not supported\nAborting"); exit_wrapper(4); } //Load and validate content descriptor string contentdescriptor=ReadString(tag->fp,NULL,0); //Load content descriptor string if(contentdescriptor == NULL) //If the content descriptor string couldn't be read { (void) puts("Damaged Content Descriptor String\nAborting"); exit_wrapper(1); } //Validate timestamp format timestampformat=syltheader[4]; if((timestampformat != 1) && (timestampformat != 2)) { printf("Warning: Invalid timestamp format (%d) specified, ms timing assumed\n",timestampformat); timestampformat=2; //Assume millisecond timing } //Process framesize as a 4 byte Big Endian integer framesize=((unsigned long)frameheader[4]<<24) | ((unsigned long)frameheader[5]<<16) | ((unsigned long)frameheader[6]<<8) | ((unsigned long)frameheader[7]); //Convert to 4 byte integer if(framesize & 0x80808080) //According to the ID3v2 specification, the MSB of each of the 4 bytes defining the tag size must be zero exit_wrapper(5); //If this isn't the case, the size is invalid assert(framesize < 0x80000000); //Redundant assert() to resolve a false positive with Coverity (this assertion will never be triggered because the above exit_wrapper() call would be triggered first) breakpos=breakpos + framesize + 10; //Find the position that is one byte past the end of the SYLT frame if(Lyrics.verbose>=2) printf("SYLT frame info:\n\tFrame size is %lu bytes\n\tEnds after byte 0x%lX\n\tTimestamp format: %s\n\tLanguage: %c%c%c\n\tContent Type %d\n\tContent Descriptor: \"%s\"\n\n",framesize,breakpos-1,timestampformat == 1 ? "MPEG frames" : "Milliseconds",syltheader[1],syltheader[2],syltheader[3],syltheader[5],contentdescriptor != NULL ? contentdescriptor : "(none)"); if(Lyrics.verbose) (void) puts("Parsing SYLT frame:"); free(contentdescriptor); //Release this, it's not going to be used contentdescriptor=NULL; //Load SYLT lyrics filepos=ftell_err(tag->fp); while(filepos < breakpos) //While we haven't reached the end of the SYLT frame { //Load the lyric text string=ReadString(tag->fp,&length,0); //Load SYLT lyric string, save the string length if(string == NULL) { (void) puts("Invalid SYLT lyric string\nAborting"); exit_wrapper(6); } //Load the timestamp if(fread(timestamparray,4,1,tag->fp) != 1) //Read timestamp { (void) puts("Error reading SYLT timestamp\nAborting"); exit_wrapper(7); } filepos+=length+4; //The number of bytes read from the input file during this iteration is the string length and the timestamp length //Process the timestamp as a 4 byte Big Endian integer timestamp=(unsigned long)((timestamparray[0]<<24) | (timestamparray[1]<<16) | (timestamparray[2]<<8) | timestamparray[3]); if(timestampformat == 1) //If this timestamp is in MPEG frames instead of milliseconds timestamp=((double)timestamp * tag->frameduration + 0.5); //Convert to milliseconds, rounding up //Perform line break logic assert(string != NULL); //(check string for NULL again to satisfy cppcheck) if((string[0] == '\r') || (string[0] == '\n')) //If this lyric begins with a newline or carriage return { EndLyricLine(); //End the lyric line before the lyric is added linebreaks=1; //Track that line break character(s) were found in the lyrics } if(Lyrics.verbose >= 2) { string2=DuplicateString(string); //Make a copy of the string for display purposes string2=TruncateString(string2,1); //Remove leading/trailing whitespace, newline chars, etc. printf("Timestamp: 0x%X%X%X%X\t%lu %s\t\"%s\"\t%s",timestamparray[0],timestamparray[1],timestamparray[2],timestamparray[3],timestamp,(timestampformat == 1) ? "MPEG frames" : "Milliseconds",string2,(string[0]=='\n') ? "(newline)\n" : "\n"); free(string2); string2=NULL; } //Perform grouping logic //Handle whitespace at the beginning of the parsed lyric piece as a signal that the piece will not group with previous piece if(isspace(string[0])) if(Lyrics.curline->pieces != NULL) //If there was a previous lyric piece on this line Lyrics.lastpiece->groupswithnext=0; //Ensure it is set to not group with this lyric piece if(isspace(string[strlen(string)-1])) //If the lyric ends in a space groupswithnext=0; else groupswithnext=1; if(Lyrics.line_on == 0) //Ensure that a line phrase is started CreateLyricLine(); //Add lyric piece, during testing, I'll just write it with a duration of 1ms AddLyricPiece(string,timestamp,timestamp+1,PITCHLESS,groupswithnext); //Write lyric with no defined pitch free(string); //Free string if((Lyrics.lastpiece != NULL) && (Lyrics.lastpiece->prev != NULL) && (Lyrics.lastpiece->prev->groupswithnext)) //If this piece groups with the previous piece Lyrics.lastpiece->prev->duration=Lyrics.lastpiece->start-Lyrics.realoffset-Lyrics.lastpiece->prev->start; //Extend previous piece's length to reach this piece, take the current offset into account }//While we haven't reached the end of the SYLT frame //If the imported lyrics did not contain line breaks, they must be inserted manually if(!linebreaks && Lyrics.piececount) { if(Lyrics.verbose) (void) puts("\nImported ID3 lyrics did not contain line breaks, adding..."); ptr=Lyrics.lines->pieces; lineptr=Lyrics.lines; //Point to first line of lyrics (should be the only line) assert_wrapper((lineptr != NULL) && (ptr != NULL)); //This shouldn't be possible if Lyrics.piececount is nonzero if(lineptr->next != NULL) //If there is another line of lyrics defined return; //abort the insertion of automatic line breaks while((ptr != NULL) && (ptr->next != NULL)) //For each lyric { ptr2=ptr->next; //Store pointer to next lyric lineptr=InsertLyricLineBreak(lineptr,ptr2); //Insert a line break between this lyric and the next, line conductor points to new line ptr=ptr2; //lyric conductor points to the next lyric, which is at the beginning of the new line } } }