예제 #1
0
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
}
예제 #2
0
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", &timestamp) != 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);
}
예제 #3
0
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
}
예제 #4
0
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);
}
예제 #5
0
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
		}
	}
}