Beispiel #1
0
void Export_ID3(FILE *inf, FILE *outf)
{
	struct ID3Tag tag={NULL,0,0,0,0,0,0.0,NULL,0,NULL,NULL,NULL,NULL};

//Validate parameters
	assert_wrapper((inf != NULL) && (outf != NULL));
	assert_wrapper(Lyrics.piececount != 0);	//This function is not to be called with an empty Lyrics structure

	tag.fp=inf;	//Store the input file pointer into the ID3 tag structure

//Seek to first MPEG frame in input file (after ID3 tag, otherwise presume it's at the beginning of the file)
	if(Lyrics.verbose)	(void) puts("Parsing input MPEG audio file");
	if(FindID3Tag(&tag))		//Find start and end of ID3 tag
		fseek_err(tag.fp,tag.tagend,SEEK_SET);	//Seek to the first MP3 frame (immediately after the ID3 tag)
	else
		rewind_err(tag.fp);	//Rewind file if no ID3 tag is found

//Load MPEG information
	if(GetMP3FrameDuration(&tag) == 0)	//Find the sample rate defined in the MP3 frame at the current file position
	{
		printf("Error loading MPEG information from file \"%s\"\nAborting",Lyrics.srcfilename);
		exit_wrapper(1);
	}

//Load any existing ID3 frames
	(void) ID3FrameProcessor(&tag);

//Build output file
	(void) BuildID3Tag(&tag,outf);

//Release memory
	DestroyID3(&tag);	//Release the ID3 structure's memory
	DestroyOmitID3framelist(Lyrics.nosrctag);	//Release ID3 frame omission list
}
Beispiel #2
0
void Export_SRT(FILE *outf)
{
	struct Lyric_Line *curline=NULL;	//Conductor of the lyric line linked list
	struct Lyric_Piece *temp=NULL;		//A conductor for the lyric pieces list
	unsigned long ctr=1;			//The lyric counter

	assert_wrapper(outf != NULL);	//This must not be NULL
	assert_wrapper(Lyrics.piececount != 0);	//This function is not to be called with an empty Lyrics structure

	if(Lyrics.verbose)	printf("\nExporting SRT subtitles to file \"%s\"\n\nWriting subtitles\n",Lyrics.outfilename);


//Export the lyric pieces
	curline=Lyrics.lines;	//Point lyric line conductor to first line of lyrics
	while(curline != NULL)	//For each line of lyrics
	{
		temp=curline->pieces;	//Starting with the first piece of lyric in this line

		if(Lyrics.verbose)	printf("\tSubtitle line %lu: ",ctr);

		while(temp != NULL)				//For each piece of lyric in this line
		{
			if(ctr != 1)
			{	//All subtitles after the first are prefixed with two newline characters
				if(fprintf(outf,"\n\n") < 0)
				{
					printf("Error writing double space between subtitles: %s\nAborting\n",strerror(errno));
					exit_wrapper(1);
				}
			}
			if((fprintf(outf,"%lu\n",ctr) < 0) || Write_SRT_Timestamp(outf,temp->start) || (fprintf(outf," --> ") < 0) || Write_SRT_Timestamp(outf,temp->start+temp->duration) || (fprintf(outf,"\n%s",temp->lyric) < 0))
			{	//Write the subtitle number, the start timestamp, the "-->" separator, the end timestamp and the subtitle text
				printf("Error exporting lyric %lu: %s\nAborting\n",ctr,strerror(errno));
				exit_wrapper(2);
			}

			if(Lyrics.verbose)	printf("'%s'",temp->lyric);

			temp=temp->next;	//Advance to next lyric piece as normal
			ctr++;
		}//end while(temp != NULL)

		curline=curline->next;	//Advance to next line of lyrics

		if(Lyrics.verbose)	(void) putchar('\n');
	}

	if(Lyrics.verbose)	printf("\nSRT export complete.  %lu subtitles written",Lyrics.piececount);
}
Beispiel #3
0
int ReadSyncEntry(struct VL_Sync_entry *ptr,FILE *inf)
{   //Portable function to read 16 bytes from the file. Returns nonzero upon error
    unsigned long buffer=0;
    unsigned long ftell_result;

    assert_wrapper((ptr != NULL) && (inf != NULL));	//These must not be NULL

//Determine if the next 16 bytes can be read without reaching end of file
    ftell_result=ftell_err(inf);

    if(ftell_result +16 > VL.filesize + 8)	//If the size of a sync entry would exceed the end of the file as defined in the VL header
        return 1;			//return EOF

//Read lyric number and reserved bytes
    ReadDWORDLE(inf,&buffer);			//Read 4 bytes from file (the lyric # and the 2 reserved bytes)
    ptr->lyric_number=buffer & 0xFF;	//Convert to 2 byte integer

//Read start char
    ReadWORDLE(inf,&(ptr->start_char));	//Read 2 bytes from file

//Read end char
    ReadWORDLE(inf,&(ptr->end_char));	//Read 2 bytes from file

//Read start time
    ReadDWORDLE(inf,&(ptr->start_time));	//Read 4 bytes from file

//Read end time
    ReadDWORDLE(inf,&(ptr->end_time));	//Read 4 bytes from file

    return 0;	//return success
}
Beispiel #4
0
void WriteTextInfoFrame(FILE *outf,const char *frameid,const char *string)
{
	size_t size=0;

//Validate input parameters
	assert_wrapper((outf != NULL) && (frameid != NULL) && (string != NULL));
	if(strlen(frameid) != 4)
	{
		printf("Error: Attempted export of invalid frame ID \"%s\"\nAborting",frameid);
		exit_wrapper(1);
	}

	if(Lyrics.verbose >= 2)	printf("\tWriting frame \"%s\"\n",frameid);

	size=strlen(string)+1;		//The frame payload size is the string and the encoding byte

//Write ID3 frame header
	fwrite_err(frameid,4,1,outf);			//Write frame ID
	WriteDWORDBE(outf,(unsigned long)size);	//Write frame size (total frame size-header size) in Big Endian format
	fputc_err(0xC0,outf);					//Write first flag byte (preserve frame for both tag and file alteration)
	fputc_err(0,outf);						//Write second flag byte (no compression, encryption or grouping identity)

//Write frame data
	fputc_err(0,outf);					//Write ASCII encoding
	fwrite_err(string,(size_t)size-1,1,outf);	//Write the string
}
Beispiel #5
0
int SearchOmitID3framelist(struct OmitID3frame *ptr,const char *frameid)
{
	assert_wrapper(frameid != NULL);
	while(ptr != NULL)
	{
		assert_wrapper(ptr->frameid != NULL);
		if(ptr->frameid[0]=='*')
			return 2;	//Return wildcard match
		if(strcasecmp(ptr->frameid,frameid) == 0)
			return 1;	//Return exact match

		ptr=ptr->next;
	}

	return 0;	//Return no match
}
Beispiel #6
0
void DisplayID3Tag(char *filename)
{
	char *buffer=NULL;	//A buffer to store strings returned from ReadTextInfoFrame()
	struct ID3Tag tag={NULL,0,0,0,0,0,0.0,NULL,0,NULL,NULL,NULL,NULL};
	struct ID3Frame *temp=NULL;	//Conductor for the ID3 frames list

//Validate parameters
	if(!filename)
		return;

	tag.fp=fopen(filename,"rb");
	if(tag.fp == NULL)
		return;

	(void) ID3FrameProcessor(&tag);
	if(tag.id3v1present > 1)	//If the ID3v1 tag exists and is populated
	{
		(void) puts("ID3v1 tag:");
		if(tag.id3v1title != NULL)
			printf("\tTitle = \"%s\"\n",tag.id3v1title);
		if(tag.id3v1artist != NULL)
			printf("\tArtist = \"%s\"\n",tag.id3v1artist);
		if(tag.id3v1album != NULL)
			printf("\tAlbum = \"%s\"\n",tag.id3v1album);
		if(tag.id3v1year != NULL)
			printf("\tYear = \"%s\"\n",tag.id3v1year);
	}

//Display ID3v2 frames
	if(tag.frames != NULL)
	{
		(void) puts("ID3v2 tag:");
		for(temp=tag.frames;temp != NULL;temp=temp->next)
		{	//For each frame that was parsed, check the tags[] array to see if it is a text info frame
			assert_wrapper(temp->frameid != NULL);
			if(IsTextInfoID3FrameID(temp->frameid))
			{
				fseek_err(tag.fp,temp->pos,SEEK_SET);	//Seek to the ID3 frame in question
				buffer=ReadTextInfoFrame(tag.fp);		//Read and store the text info frame content
				if(buffer == NULL)
					printf("\tFrame: %s = Unreadable or Unicode format\n",temp->frameid);
				else
					printf("\tFrame: %s = \"%s\"\n",temp->frameid,buffer);
				free(buffer);	//Release text info frame content
				buffer=NULL;
			}
			else
				printf("\t%s frame present\n",temp->frameid);
		}//For each frame that was parsed
	}
	else if(!tag.id3v1present)
		(void) puts("No ID3 Tag detected");

	DestroyID3(&tag);	//Release the ID3 structure's memory
	(void) fclose(tag.fp);
}
Beispiel #7
0
char *GrabID3TextFrame(struct ID3Tag *tag,const char *frameid,char *buffer,unsigned long buffersize)
{
	struct ID3Frame *frameptr=NULL;
	char *string=NULL;

//Validate parameters
	assert_wrapper((tag != NULL) && (tag->fp != NULL) && (tag->frames != NULL) && (frameid != NULL));
	if(buffer != NULL)
		assert_wrapper(buffersize != 0);

//Locate the specified frame
	frameptr=FindID3Frame(tag,frameid);
	if(frameptr == NULL)	//If no match was found
		return NULL;		//return without altering the buffer

//Seek to the frame's location in the file
	if(fseek(tag->fp,frameptr->pos,SEEK_SET) != 0)	//If there was a file I/O error seeking
		return NULL;		//return without altering the buffer

//Read the text info string
	string=ReadTextInfoFrame(tag->fp);
	if(string == NULL)	//If there was an error reading the string
		return NULL;	//return without altering the buffer

//If performing the logic to buffer the string
	if(buffer != NULL)
	{	//Verify string will fit in buffer
		if(strlen(string) + 1 > (size_t)buffersize)	//If the string (including NULL terminator) is larger than the buffer
			string[buffersize-1]='\0';		//truncate the string to fit

		strcpy(buffer,string);	//Copy string to buffer
		free(string);			//Release memory used by the string
		return buffer;			//Return success
	}

//Otherwise return the allocated string
	return string;
}
Beispiel #8
0
struct ID3Frame *FindID3Frame(struct ID3Tag *tag,const char *frameid)
{
	struct ID3Frame *ptr=NULL;

//Validate parameters
	assert_wrapper((tag != NULL) && (tag->fp != NULL) && (tag->frames != NULL) && (frameid != NULL));

//Locate the specified frame
	for(ptr=tag->frames;ptr != NULL;ptr=ptr->next)	//For each frame in the linked list
		if(strcasecmp(ptr->frameid,frameid) == 0)	//If this is the frame being searched for
			return ptr;	//Return success

	return NULL;	//If no result was found, return failure
}
Beispiel #9
0
struct OmitID3frame *AddOmitID3framelist(struct OmitID3frame *ptr,const char *frameid)
{
	struct OmitID3frame *temp;

	assert_wrapper(frameid != NULL);

//Allocate and init new link
	temp=malloc_err(sizeof(struct OmitID3frame));
	temp->frameid=DuplicateString(frameid);

//Append link to existing list if applicable
	temp->next=ptr;		//The new link becomes the head and points forward to whatever the head was

	return temp;	//Otherwise return the new linked list head
}
Beispiel #10
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);
}
Beispiel #11
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);
}
Beispiel #12
0
unsigned long ConvertSRTTimestamp(char **ptr,int *errorstatus)
{
	char *temp=NULL;
	unsigned int ctr=1;
	char failed=0;	//Boolean: Parsing indicated that ptr didn't point at a valid timestamp, abort
	char hours[SRTTIMESTAMPMAXFIELDLENGTH+1]={0};		//Allow for pre-defined # of hours chars (and NULL terminator)
	char minutes[SRTTIMESTAMPMAXFIELDLENGTH+1]={0};		//Allow for pre-defined # of minute chars (and NULL terminator)
	char seconds[SRTTIMESTAMPMAXFIELDLENGTH+1]={0};		//Allow for pre-defined # of seconds chars (and NULL terminator)
	char millis[SRTTIMESTAMPMAXFIELDLENGTH+1]={0};		//Allow for pre-defined # of milliseconds chars (and NULL terminator)
	unsigned int index=0;	//index variable into the 3 timestamp strings
	unsigned long sum=0;
	long conversion=0;	//Will store the integer conversions of each of the 3 timestamp strings

	if(ptr == NULL)
		failed=1;
	else
	{
		temp=*ptr;	//To improve readability for string parsing

		if(temp == NULL)
			failed=1;
	}

//Validate that the string has brackets
	if((temp != NULL) && !isdigit(temp[0]))	//If this character is not a beginning character for a timestamp
		failed=1;

//Parse hours portion of timestamp
	index=0;
	while(!failed)
	{
		assert_wrapper(temp != NULL);
		if((temp[ctr] == '\0') || (!isdigit(temp[ctr]) && (temp[ctr] != ':')))	//Numerical char(s) followed by delimiter are expected
			failed=1;
		else
		{
			if(isdigit(temp[ctr]))	//Is a numerical character
			{
				if(index > SRTTIMESTAMPMAXFIELDLENGTH)	//If more than the defined limit of chars have been parsed
					failed=2;	//This is more than we allow for
				else
					hours[index++]=temp[ctr++];	//copy character into hours string, increment indexes
			}
			else
			{			//this character is a delimiter
				ctr++;	//seek past the colon
				break;	//break from loop
			}
		}
	}
	assert_wrapper(index < SRTTIMESTAMPMAXFIELDLENGTH+1);	//Ensure that writing the NULL character won't overflow
	hours[index]='\0';	//Terminate hours string

//validate minutes portion of timestamp
	index=0;
	while(!failed)
	{
		assert_wrapper(temp != NULL);
		if((temp[ctr] == '\0') || (!isdigit(temp[ctr]) && (temp[ctr] != ':')))	//Numerical char(s) followed by delimiter are expected
			failed=1;
		else
		{
			if(isdigit(temp[ctr]))	//Is a numerical character
			{
				if(index > SRTTIMESTAMPMAXFIELDLENGTH)	//If more than the defined limit of chars have been parsed
					failed=2;	//This is more than we allow for
				else
					minutes[index++]=temp[ctr++];	//copy character into minutes string, increment indexes
			}
			else
			{			//this character is a delimiter
				ctr++;	//seek past the colon
				break;	//break from loop
			}
		}
	}
	assert_wrapper(index < SRTTIMESTAMPMAXFIELDLENGTH+1);	//Ensure that writing the NULL character won't overflow
	minutes[index]='\0';	//Terminate minutes string

//validate seconds portion of timestamp
	index=0;
	while(!failed)
	{
		assert_wrapper(temp != NULL);
		if((temp[ctr] == '\0') || (!isdigit(temp[ctr]) && (temp[ctr] != ',')))	//Numerical char(s) followed by delimiter are expected
			failed=1;
		else
		{
			if(isdigit(temp[ctr]))	//Is a numerical character
			{
				if(index > SRTTIMESTAMPMAXFIELDLENGTH)	//If more than the defined limit of chars have been parsed
					failed=2;	//This is more than we allow for
				else
					seconds[index++]=temp[ctr++];	//copy character into seconds string, increment indexes
			}
			else
			{			//this character is a delimiter
				ctr++;	//seek past the colon
				break;	//break from loop
			}
		}
	}
	assert_wrapper(index < SRTTIMESTAMPMAXFIELDLENGTH+1);	//Ensure that writing the NULL character won't overflow
	seconds[index]='\0';	//Terminate seconds string

//validate milliseconds portion of timestamp
	index=0;
	while(!failed)
	{
		assert_wrapper(temp != NULL);
		if((temp[ctr] == '\0') || !isdigit(temp[ctr]))	//If not a numerical character
		{
			if(isspace(temp[ctr]) || (temp[ctr] == '\0'))//If this character is a whitespace/newline/carriage return/NULL character
				break;	//break from loop
			else
				failed=1;
		}
		else
		{
			if(index > SRTTIMESTAMPMAXFIELDLENGTH)	//If more than the defined limit of chars have been parsed
				failed=2;
			else
				millis[index++]=temp[ctr++];	//copy character into milliseconds string, increment indexes
		}
	}
	assert_wrapper(index < SRTTIMESTAMPMAXFIELDLENGTH+1);	//Ensure that writing the NULL character won't overflow
	millis[index]='\0';	//Terminate milliseconds string

	if(failed)		//If parsing failed
	{
		printf("Error: Invalid timestamp \"%s\"\n",temp);
		if(errorstatus != NULL)
		{
			*errorstatus=1;
			return 0;
		}
		else
		{
			(void) puts("Aborting");
			exit_wrapper(1);
		}
	}

	assert_wrapper((ptr != NULL) && (temp != NULL));
	if((ptr != NULL) && (temp != NULL))
	{	//Redundant check to satisfy cppcheck
		*ptr=&(temp[ctr+1]);	//Store address of first character following end of timestamp
	}


//Convert hours string to integer and add to sum
	temp=RemoveLeadingZeroes(hours);
	if(temp[0] != '0')	//If hours is not 0
	{
		conversion=atol(temp);	//get integer conversion
		if(conversion<1)	//Values of 0 are errors from atol(), negative values are not allowed for timestamps
		{
			(void) puts("Error converting string to integer\nAborting");
			free(temp);
			if(errorstatus != NULL)
			{
				*errorstatus=2;
				return 0;
			}
			else
				exit_wrapper(2);
		}
		sum+=conversion*3600000;	//one hour is 3600000 milliseconds
	}
	free(temp);

//Convert minutes string to integer and add to sum
	temp=RemoveLeadingZeroes(minutes);
	if(temp[0] != '0')	//If minutes is not 0
	{
		conversion=atol(temp);	//get integer conversion
		if(conversion<1)	//Values of 0 are errors from atol(), negative values are not allowed for timestamps
		{
			(void) puts("Error converting string to integer\nAborting");
			free(temp);
			if(errorstatus != NULL)
			{
				*errorstatus=2;
				return 0;
			}
			else
				exit_wrapper(2);
		}
		sum+=conversion*60000;	//one minute is 60,000 milliseconds
	}
	free(temp);

//Convert seconds string to integer and add to sum
	temp=RemoveLeadingZeroes(seconds);
	if(temp[0] != '0')	//If minutes is not 0
	{
		conversion=atol(temp);	//get integer conversion
		if(conversion<1)	//Values of 0 are errors from atol(), negative values are not allowed for timestamps
		{
			(void) puts("Error converting string to integer\nAborting");
			free(temp);
			if(errorstatus != NULL)
			{
				*errorstatus=3;
				return 0;
			}
			else
				exit_wrapper(3);
		}
		sum+=conversion*1000;	//one second is 1,000 milliseconds
	}
	free(temp);

//Convert milliseconds string to integer and add to sum
	temp=RemoveLeadingZeroes(millis);
	if(temp[0] != '0')	//If minutes is not 0
	{
		conversion=atol(temp);	//get integer conversion
		if(conversion<1)	//Values of 0 are errors from atol(), negative values are not allowed for timestamps
		{
			(void) puts("Error converting string to integer\nAborting");
			free(temp);
			if(errorstatus != NULL)
			{
				*errorstatus=4;
				return 0;
			}
			else
				exit_wrapper(4);
		}
		sum+=conversion;
	}
	free(temp);
	return sum;
}
Beispiel #13
0
void Export_VL(FILE *outf)
{
    struct _VLSTRUCT_ *OutVL=NULL;			//The prepared VL structure to write to file
    struct VL_Text_entry *curtext=NULL;		//Conductor for the text chunk list
    struct VL_Text_entry *textnext=NULL;	//Used for OutVL deallocation
    struct VL_Sync_entry *cursync=NULL;		//Conductor for the sync chunk list
    struct VL_Sync_entry *syncnext=NULL;	//Used for OutVL deallocation
    unsigned long ctr=0;
    long filepos=0;

    assert_wrapper(outf != NULL);			//This must not be NULL
    assert_wrapper(Lyrics.piececount != 0);	//This function is not to be called with an empty Lyrics structure

    if(Lyrics.verbose)	printf("\nExporting VL lyrics to file \"%s\"\n\n",Lyrics.outfilename);

//Write VL header: 'V','L','2',0
    fputc_err('V',outf);
    fputc_err('L',outf);
    fputc_err('2',outf);
    fputc_err(0,outf);

//Pad the next four bytes, which are supposed to hold the size of the file, minus the size of the VL header (8 bytes)
    WriteDWORDLE(outf,0);

    if(Lyrics.verbose>=2)	(void) puts("Writing text chunk");

//Write Text chunk header: 'T','E','X','T'
    fputc_err('T',outf);
    fputc_err('E',outf);
    fputc_err('X',outf);
    fputc_err('T',outf);

//Pad the next four bytes, which are supposed to hold the size of the header chunk, minus the size of the chunk header (8 bytes)
    WriteDWORDLE(outf,0);

//Convert the Offset string to centiseconds from milliseconds (as this is how the VL format interprets it)
    if((Lyrics.Offset != NULL))	//If there is an offset
    {
        if(strlen(Lyrics.Offset) < 2)	//If the offset is 0 or 1 characters long, it will be lost, free the string
        {
            free(Lyrics.Offset);
            Lyrics.Offset=NULL;
        }
        else
            Lyrics.Offset[strlen(Lyrics.Offset)-1]='\0';	//Truncate last digit to effectively divide by ten
    }

//Write 8 Unicode strings, for Title, Artist, Album, Editor, Offset and 3 empty strings
    WriteUnicodeString(outf,Lyrics.Title);
    WriteUnicodeString(outf,Lyrics.Artist);
    WriteUnicodeString(outf,Lyrics.Album);
    WriteUnicodeString(outf,Lyrics.Editor);
    WriteUnicodeString(outf,NULL);	//The Offset tag should be 0 because all timestamps are written as being absolute
    WriteUnicodeString(outf,NULL);
    WriteUnicodeString(outf,NULL);
    WriteUnicodeString(outf,NULL);

//Create sync and lyric structures
    OutVL=VL_PreWrite();

//Write lyric strings
    curtext=OutVL->Lyrics;

    if(Lyrics.verbose)	(void) puts("Writing VL file");

    while(curtext != NULL)	//For each text chunk
    {
        assert_wrapper(curtext->text != NULL);

        if(Lyrics.verbose)	printf("\tText chunk entry written: \"%s\"\n",curtext->text);

        WriteUnicodeString(outf,curtext->text);
        curtext=curtext->next;					//Point to next text chunk entry
    }

//Write padding if necessary
    ctr=ftell_err(outf)%4;	//ctr=# of padding bytes needed to align output file position to a DWORD boundary
    while(ctr--)	//For each byte of padding necessary
        fputc_err(0,outf);

//Go back and write the text chunk size after the text chunk header
    filepos=ftell_err(outf);		//Store file position
    OutVL->textsize=filepos-16;		//File position - VL and Text header sizes = text chunk size
    fseek_err(outf,12,SEEK_SET);	//File position 12 is where the text chunk size is to be stored
    WriteDWORDLE(outf,OutVL->textsize);	//Write text chunk size to file
    fseek_err(outf,filepos,SEEK_SET);		//Seek back to the end of the text chunk (where the Sync chunk will begin)

    if(Lyrics.verbose>=2)	(void) puts("Writing Sync chunk");

//Write Sync chunk header: 'S','Y','N','C'
    fputc_err('S',outf);
    fputc_err('Y',outf);
    fputc_err('N',outf);
    fputc_err('C',outf);

//Pad the next four bytes, which are supposed to hold the size of the sync chunk, minus the size of the chunk header (8 bytes)
    WriteDWORDLE(outf,0);

//Write sync entries
    cursync=OutVL->Syncs;
    while(cursync != NULL)
    {
        WriteWORDLE(outf,cursync->lyric_number);//Write lyric line number this sync entry pertains to
        WriteWORDLE(outf,0);					//Write reserved Word value
        WriteWORDLE(outf,cursync->start_char);	//Write start offset
        WriteWORDLE(outf,cursync->end_char);	//Write end offset
        WriteDWORDLE(outf,cursync->start_time);	//Write start time
        WriteDWORDLE(outf,cursync->end_time);	//Write end time
        cursync=cursync->next;
    }

//Go back and write the sync chunk size after the sync chunk header
    filepos=ftell_err(outf);						//Store file position
    OutVL->syncsize=filepos - OutVL->textsize - 24;	//File position - Text chunk size - 3 header sizes = sync chunk size
    fseek_err(outf,OutVL->textsize+20,SEEK_SET);	//File position for sync chunk size is text chunk size +2.5 headers
    WriteDWORDLE(outf,OutVL->syncsize);				//Write sync chunk size to file

//Go back and write the VL file size after the VL header, which completes the VL export
    filepos-=8;							//Subtract the VL header size from the total file size
    fseek_err(outf,4,SEEK_SET);			//File posistion for VL file size is after the "VL2\0" string
    WriteDWORDLE(outf,filepos);			//Write the VL file size - header value

//Destroy the OutVL structure
    if(Lyrics.verbose>=2)	(void) puts("\tReleasing memory from export VL structure");
    curtext=OutVL->Lyrics;	//Point conductor at first text chunk entry
    while(curtext != NULL)	//For each text chunk entry
    {
        textnext=curtext->next;
        if(curtext->text != NULL)
            free(curtext->text);	//Free string
        free(curtext);				//Free text chunk structure
        curtext=textnext;
    }

    cursync=OutVL->Syncs;	//Point conductor at first sync chunk entry
    while(cursync != NULL)	//For each sync chunk entry
    {
        syncnext=cursync->next;
        free(cursync);		//Free sync chunk structure
        cursync=syncnext;
    }

    free(OutVL);
    if(Lyrics.verbose)	printf("\nVL export complete.  %lu lyrics written\n",Lyrics.piececount);
}
Beispiel #14
0
struct _VLSTRUCT_ *VL_PreWrite(void)
{
    struct _VLSTRUCT_ *OutVL=NULL;		//Create a VL structure to build the exported VL format
    char *lyrline=NULL;					//A pointer to an array large enough to store the largest line of lyrics
    char *temp=NULL;
    unsigned long maxlength=0;			//The calculated length of the longest lyric line (excluding null terminator)
    unsigned long charcount=0;			//The running sum of the length of all lyric pieces in a line
    unsigned long index=0;				//The current index into the lyric line, stored with each sync entry
    unsigned long linenum=0;			//The number of the currently-processed lyric line
    struct Lyric_Piece *curpiece=NULL;	//Conductor for lyric piece linked list
    struct Lyric_Line *curline=NULL;	//Conductor for lyric line linked list
    struct VL_Text_entry *curtext=NULL;	//Text entry for current lyric piece
    struct VL_Sync_entry *cursync=NULL;	//Sync entry for current lyric piece
    struct VL_Sync_entry *newsync=NULL;	//Used to build new sync entries to insert into list
    struct VL_Text_entry *newtext=NULL;	//Used to build new text entries to insert into list
    unsigned long lastendtime=0;		//The end time for the last lyric piece.  The next lyric's timestamp must be at least 1 more than this
    static const struct _VLSTRUCT_ empty_VLSTRUCT_;	//Auto-initialize all members to 0/NULL

    if(Lyrics.verbose)	(void) puts("Building VL structure");

//Allocate the Export_VL structure
    OutVL=malloc_err(sizeof(struct _VLSTRUCT_));
    *OutVL=empty_VLSTRUCT_;	//Reliably initialize all values to 0/NULL

//Initialize the Export_VL structure
    OutVL->numlines=Lyrics.linecount;
    OutVL->numsyncs=Lyrics.piececount;

//Find the length of the longest line of lyrics
    maxlength=0;
    curline=Lyrics.lines;		//Conductor points to first line of lyrics
    while(curline != NULL)	//For each line of lyrics
    {
        charcount=0;	//reset count
        curpiece=curline->pieces;	//Conductor points to first lyric in the line
        while(curpiece != NULL)		//For each lyric piece in the line
        {
            if(curpiece->lyric != NULL)
                charcount+=strlen(curpiece->lyric);	//Add the length of the lyric

            curpiece=curpiece->next;	//Point to next lyric piece
        }

        if(charcount>maxlength)		//If this line has more characters than any other so far
            maxlength=charcount;	//store the number of characters

        curline=curline->next;		//Point to next lyric line
    }

//Initialize lyrline
    lyrline=malloc_err((size_t)(maxlength+1+Lyrics.piececount));	//Add piececount to allow for spacing between each word

//Process lyric pieces
    curline=Lyrics.lines;	//Conductor points to first line of lyrics
    lastendtime=0;
    linenum=0;	//This will be used to index into the lyric array
    cursync=OutVL->Syncs;	//Point conductor to first link in sync chunk list

    while(curline != NULL)	//For each line of lyrics
    {
        lyrline[0]='\0';	//Empty the string buffer
        index=0;			//Reset index to 0
        curpiece=curline->pieces;	//Conductor points to first lyric in the line

        while(curpiece != NULL)	//For each lyric piece in this line
        {
            if(curpiece->lyric != NULL)	//Only process if there is actual lyric text for this lyric piece
            {
                //Allocate a new sync list link
                newsync=malloc_err(sizeof(struct VL_Sync_entry));

                //Build sync entry
                newsync->lyric_number=linenum;	//Initialize lyric number, which should be the lyric line number it refers to
                newsync->start_char=index;		//Initialize sync entry start index

                if(curpiece->next != NULL)	//If there's another lyric piece in this line
                    newsync->end_char=index+strlen(curpiece->lyric)-1;	//Find ending offset of this sync entry
                else
                    newsync->end_char=0xFFFF;	//The lyric reaches the end of the line of lyrics

                newsync->start_time=curpiece->start/10;	//VL stores timestamps in 10ms increments
                newsync->end_time=(curpiece->start+curpiece->duration)/10;

                if(newsync->end_time <= newsync->start_time)	//If the duration of the lyric was less than 10ms (became 0 during the division)
                    newsync->end_time+=1;						//add the minimum duration of 10ms

                if((lastendtime != 0) && (newsync->start_time < lastendtime))	//Ensure that this doesn't overlap with last piece
                    newsync->start_time=lastendtime+1;

                lastendtime=newsync->end_time;	//Store this to prevent overlapping with next piece
                index=newsync->end_char+1;	//Set lyric character index one higher than the end of this sync entry
                newsync->next=NULL;			//This will be the last link in the list

                //Insert sync entry into linked list
                if(OutVL->Syncs == NULL)		//This is the first sync entry in the list
                    OutVL->Syncs=newsync;
                else						//Last link points forward to this link
                    cursync->next=newsync;

                cursync=newsync;			//This becomes the new last link in the list

                //Append lyric piece to string and append a space if necessary
                strcat(lyrline,curpiece->lyric);
                if((curpiece->next != NULL) && (curpiece->groupswithnext == 0))
                {   //There is another lyric piece in this line and this piece does not group with it
                    strcat(lyrline," ");
                    index++;				//Increment the index to preserve subsequent sync piece placement
                }
            }

            curpiece=curpiece->next;	//Point to next lyric piece
        }//end while(curpiece != NULL)

        //Make a permanent copy of the combined lyric string and store it in the lyric array
        temp=DuplicateString(lyrline);	//Copy completed lyric line into new string
        linenum++;							//Iterate lyric line number
        //Allocate new text link
        newtext=malloc_err(sizeof(struct VL_Text_entry));

        //Build new link and insert into list
        newtext->text=temp;
        newtext->next=NULL;
        if(OutVL->Lyrics == NULL)	//This is the first text chunk entry
            OutVL->Lyrics=newtext;
        else							//Last link points forward to this link
        {
            assert_wrapper(curtext != NULL);
            curtext->next=newtext;
        }
        curtext=newtext;				//This becomes the last link in the list

        curline=curline->next;			//Point to next lyric line

        if(Lyrics.verbose>=2)	printf("\tStored text chunk entry \"%s\"\n",temp);
    }//end while(curline != NULL)

    free(lyrline);	//No longer needed
    return OutVL;	//Return completed structure
}
Beispiel #15
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
}
Beispiel #16
0
void ID3_Load(FILE *inf)
{	//Parses the file looking for an ID3 tag.  If found, the first MP3 frame is examined to obtain the sample rate
	//Then an SYLT frame is searched for within the ID3 tag.  If found, synchronized lyrics are imported
	struct ID3Tag tag={NULL,0,0,0,0,0,0.0,NULL,0,NULL,NULL,NULL,NULL};
	struct ID3Frame *frameptr=NULL;
	struct ID3Frame *frameptr2=NULL;

	assert_wrapper(inf != NULL);	//This must not be NULL
	tag.fp=inf;

	if(Lyrics.verbose)	printf("Importing ID3 lyrics from file \"%s\"\n\nParsing input MPEG audio file\n",Lyrics.infilename);

	if(ID3FrameProcessor(&tag) == 0)	//Build a list of the ID3 frames
	{
		DestroyID3(&tag);	//Release the ID3 structure's memory
		return;				//Return if no frames were successfully parsed
	}

//Parse MPEG frame header
	fseek_err(tag.fp,tag.tagend,SEEK_SET);	//Seek to the first MP3 frame (immediately after the ID3 tag)
	if(GetMP3FrameDuration(&tag) == 0)		//Find the sample rate defined in the MP3 frame
	{
		DestroyID3(&tag);	//Release the ID3 structure's memory
		return;				//Return if the sample rate was not found or was invalid
	}

	frameptr=FindID3Frame(&tag,"SYLT");		//Search ID3 frame list for SYLT ID3 frame
	frameptr2=FindID3Frame(&tag,"USLT");	//Search ID3 frame list for USLT ID3 frame
	if(frameptr != NULL)
	{	//Perform Synchronized ID3 Lyric import
		fseek_err(tag.fp,frameptr->pos,SEEK_SET);	//Seek to SYLT ID3 frame
		SYLT_Parse(&tag);
	}
	else if(frameptr2 != NULL)
	{	//Perform Unsynchronized ID3 Lyric import
		(void) puts("\aUnsynchronized ID3 lyric import currently not supported");
		exit_wrapper(1);
	}
	else
	{
		DestroyID3(&tag);	//Release the ID3 structure's memory
		return;				//Return if neither lyric frame is present
	}

//Load song tags
	Lyrics.Title=GrabID3TextFrame(&tag,"TIT2",NULL,0);	//Return the defined song title, if it exists
	Lyrics.Artist=GrabID3TextFrame(&tag,"TPE1",NULL,0);	//Return the defined artist, if it exists
	Lyrics.Album=GrabID3TextFrame(&tag,"TALB",NULL,0);	//Return the defined album, if it exists
	Lyrics.Year=GrabID3TextFrame(&tag,"TYER",NULL,0);	//Return the defined year, if it exists

	if(Lyrics.Title == NULL)	//If there was no Title defined in the ID3v2 tag
		Lyrics.Title=DuplicateString(tag.id3v1title);	//Use one defined in the ID3v1 tag if it exists
	if(Lyrics.Artist == NULL)	//If there was no Artist defined in the ID3v2 tag
		Lyrics.Artist=DuplicateString(tag.id3v1artist);	//Use one defined in the ID3v1 tag if it exists
	if(Lyrics.Album == NULL)	//If there was no Album defined in the ID3v2 tag
		Lyrics.Album=DuplicateString(tag.id3v1album);	//Use one defined in the ID3v1 tag if it exists
	if(Lyrics.Year == NULL)		//If there was no Year defined in the ID3v2 tag
		Lyrics.Year=DuplicateString(tag.id3v1year);		//Use one defined in the ID3v1 tag if it exists

	ForceEndLyricLine();
	DestroyID3(&tag);	//Release the ID3 structure's memory

	if(Lyrics.verbose)	printf("ID3 import complete.  %lu lyrics loaded\n\n",Lyrics.piececount);
}
Beispiel #17
0
int VL_PreLoad(FILE *inf,char validate)
{
    long ftell_result=0;				//Used to store return value from ftell()
    char buffer[5]= {0};					//Used to read word/doubleword integers from file
    unsigned long ctr=0;				//Generic counter
    char *temp=NULL;					//Temporary pointer for allocated strings
    struct VL_Sync_entry se= {0,0,0,0,0,NULL};		//Used to store sync entries from file during parsing
    struct VL_Text_entry *ptr1=NULL;	//Used for allocating links for the text chunk list
    struct VL_Sync_entry *ptr2=NULL;	//Used for allocation links for the sync chunk list
    struct VL_Text_entry *curtext;		//Conductor for text chunk linked list
    struct VL_Sync_entry *cursync;		//Conductor for sync chunk linked list

    assert_wrapper(inf != NULL);

//Initialize variables
    VL.numlines=0;
    VL.numsyncs=0;
    VL.filesize=0;
    VL.textsize=0;
    VL.syncsize=0;
    VL.Lyrics=NULL;
    VL.Syncs=NULL;
    curtext=NULL;	//list starts out empty
    cursync=NULL;	//list starts out empty
    se.next=NULL;	//In terms of linked lists, this always represents the last link

    if(Lyrics.verbose)	(void) puts("Reading VL file header");

//Read file header
    fread_err(buffer,4,1,inf);			//Read 4 bytes, which should be 'V','L','2',0
    ReadDWORDLE(inf,&(VL.filesize));	//Read doubleword from file in little endian format

    if(strncmp(buffer,"VL2",4) != 0)
    {
        if(validate)
            return 1;
        else
        {
            (void) puts("Error: Invalid file header string\nAborting");
            exit_wrapper(1);
        }
    }

//Read text header
    fread_err(buffer,4,1,inf);			//Read 4 bytes, which should be 'T','E','X','T'
    buffer[4]='\0';						//Add a NULL character to make buffer into a proper string
    ReadDWORDLE(inf,&(VL.textsize));	//Read doubleword from file in little endian format

    if(VL.textsize % 4 != 0)
    {
        (void) puts("Error: VL spec. violation: Sync chunk is not padded to a double-word alignment");
        if(validate)
            return 2;
        else
        {
            (void) puts("Aborting");
            exit_wrapper(2);
        }
    }

    if(strncmp(buffer,"TEXT",5) != 0)
    {
        (void) puts("Error: Invalid text header string");
        if(validate)
            return 3;
        else
        {
            (void) puts("Aborting");
            exit_wrapper(3);
        }
    }

//Read sync header, which should be th.textsize+16 bytes into the file
    fseek_err(inf,VL.textsize+16,SEEK_SET);
    fread_err(buffer,4,1,inf);			//Read 4 bytes, which should be 'S','Y','N','C'
    buffer[4]='\0';						//Add a NULL character to make buffer into a proper string
    ReadDWORDLE(inf,&(VL.syncsize));	//Read doubleword from file in little endian format

    if(strncmp(buffer,"SYNC",5) != 0)
    {
        (void) puts("Error: Invalid sync header string");
        if(validate)
            return 4;
        else
        {
            (void) puts("Aborting");
            exit_wrapper(4);
        }
    }

//Validate chunk sizes given in headers (textsize was already validated if the Sync header was correctly read)
    fseek_err(inf,0,SEEK_END);	//Seek to end of file
    ftell_result = ftell(inf);	//Get position of end of file
    if(ftell_result < 0)
    {
        printf("Error determining file length during file size validation: %s\n",strerror(errno));
        if(validate)
            return 5;
        else
        {
            (void) puts("Aborting");
            exit_wrapper(5);
        }
    }
    if((unsigned long)ftell_result != VL.filesize+8)	//Validate filesize
    {
        (void) puts("Error: Filesize does not match size given in file header");
        if(validate)
            return 6;
        else
        {
            (void) puts("Aborting");
            exit_wrapper(6);
        }
    }
    if(VL.filesize != VL.textsize + VL.syncsize + 16)	//Validate syncsize
    {
        (void) puts("Error: Incorrect size given in sync chunk");
        if(validate)
            return 7;
        else
        {
            (void) puts("Aborting");
            exit_wrapper(7);
        }
    }

//Load tags
    if(Lyrics.verbose)	(void) puts("Loading VL tag strings");
    fseek_err(inf,16,SEEK_SET);		//Seek to the Title tag

    for(ctr=0; ctr<5; ctr++)	//Expecting 5 null terminated unicode strings
    {
        temp=ReadUnicodeString(inf);	//Allocate array and read Unicode string

        if(temp[0] != '\0')	//If the string wasn't empty, save the tag
            switch(ctr)
            {   //Look at ctr to determine which tag we have read, and assign to the appropriate tag string:
            case 0:
                SetTag(temp,'n',0);
                break;

            case 1:
                SetTag(temp,'s',0);
                break;

            case 2:
                SetTag(temp,'a',0);
                break;

            case 3:
                SetTag(temp,'e',0);
                break;

            case 4:	//Offset
                SetTag(temp,'o',0);	//Do not multiply the offset by -1
                break;

            default:
                (void) puts("Unexpected error");
                if(validate)
                    return 8;
                else
                {
                    (void) puts("Aborting");
                    exit_wrapper(8);
                }
                break;
            }//end switch(ctr)

        free(temp);	//Free string, a copy of which would have been stored in the Lyrics structure as necessary
    }//end for(ctr=0;ctr<5;)

//Validate the existence of three empty unicode strings
    for(ctr=0; ctr<3; ctr++)	//Expecting 3 empty, null terminated unicode strings
        if(ParseUnicodeString(inf) != 0)
        {
            (void) puts("Error: Reserved string is not empty during load");
            if(validate)
                return 9;
            else
            {
                (void) puts("Aborting");
                exit_wrapper(9);
            }
        }

//Load the lyric strings
    if(Lyrics.verbose)	(void) puts("Loading text chunk entries");

    while(1)	//Read lyric lines
    {
        //Check to see if the Sync Chunk has been reached
        if((unsigned long)ftell_err(inf) >= VL.textsize + 16)	//The Text chunk size + the file and text header sizes is the position of the Sync chunk
            break;

        //Read lyric string
        temp=ReadUnicodeString(inf);

        ftell_result=ftell_err(inf);	//If this string is empty
        if(temp[0] == '\0')
        {
            if(VL.textsize+16 - ftell_result <= 3)	//This 0 word value ends within 3 bytes of the sync header (is padding)
            {
                free(temp);	//Release string as it won't be used
                break;		//text chunk has been read
            }
            else
            {
                printf("Error: Empty lyric string detected before file position 0x%lX\n",ftell_result);
                free(temp);
                if(validate)
                    return 10;
                else
                {
                    (void) puts("Aborting");
                    exit_wrapper(10);
                }
            }
        }
        if((unsigned long)ftell_result > VL.textsize + 16)	//If reading this string caused the file position to cross into Sync chunk
        {
            (void) puts("Error: Lyric string overlapped into Sync chunk");
            free(temp);
            if(validate)
                return 11;
            else
            {
                (void) puts("Aborting");
                exit_wrapper(11);
            }
        }

        if(Lyrics.verbose)	printf("\tLoading text piece #%lu: \"%s\"\n",VL.numlines,temp);

        //Allocate link
        ptr1=malloc_err(sizeof(struct VL_Text_entry));	//Allocate memory for new link

        //Build link and insert into list
        ptr1->text=temp;
        ptr1->next=NULL;

        if(VL.Lyrics == NULL)	//This is the first lyric piece read from the input file
            VL.Lyrics=ptr1;
        else
        {
            assert_wrapper(curtext != NULL);
            curtext->next=ptr1;	//The end of the list points forward to this new link
        }

        curtext=ptr1;	//This link is now at the end of the list

        VL.numlines++;	//one more lyric string has been parsed
    }//end while(1)

    if(Lyrics.verbose)	printf("%lu text chunk entries loaded\n",VL.numlines);

//Load the Sync points
    if(Lyrics.verbose)	(void) puts("Loading sync chunk entries");

    fseek_err(inf,VL.textsize+16+8,SEEK_SET);	//Seek to first sync entry (8 bytes past start of sync header)

    while(ReadSyncEntry(&se,inf) == 0)	//Read all entries
    {
        if(se.start_char > se.end_char)
        {
            (void) puts("Error: Invalid sync point offset is specified to start after end offset");
            if(validate)
                return 12;
            else
            {
                (void) puts("Aborting");
                exit_wrapper(12);
            }
        }

        if(se.lyric_number!=0xFFFF && se.start_char!=0xFFFF && se.start_time!=0xFFFFFFFF)
        {   //This is a valid sync entry
            //Allocate link
            ptr2=malloc_err(sizeof(struct VL_Sync_entry));

            //Build link and insert into list
            memcpy(ptr2,&se,sizeof(struct VL_Sync_entry));	//copy structure into newly allocated link
            if(VL.Syncs == NULL)	//This is the first sync piece read from the input file
                VL.Syncs=ptr2;
            else					//The end of the list points forward to this new link
            {
                assert_wrapper(cursync != NULL);
                cursync->next=ptr2;
            }

            cursync=ptr2;		//This link is now at the end of the list
            VL.numsyncs++;		//one more sync entry has been parsed
        }
    }

    if(Lyrics.verbose)	(void) puts("VL_PreLoad completed");
    return 0;
}
Beispiel #18
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
		}
	}
}
Beispiel #19
0
unsigned long ID3FrameProcessor(struct ID3Tag *ptr)
{
	unsigned char header[10]={0};	//Used to store the ID3 frame header
	unsigned long filepos=0;
	int ctr2=0;						//Used to validate frame ID
	struct ID3Frame *temp=NULL;		//Used to allocate an ID3Frame link
	struct ID3Frame *head=NULL;		//Used as the head pointer
	struct ID3Frame *cond=NULL;		//Used as the conductor
	unsigned long ctr=0;			//Count the number of frames processed
	unsigned long framesize=0;		//Used to validate frame size
	char buffer[31]={0};			//Used to read an ID3v1 tag in the absence of an ID3v2 tag
	static struct ID3Frame emptyID3Frame;	//Auto-initialize all members to 0/NULL

//Validate input parameter
	if((ptr == NULL) || (ptr->fp == NULL))
		return 0;	//Return failure

//Find and parse the ID3 header
//ID3v1
	fseek_err(ptr->fp,-128,SEEK_END);	//Seek 128 back from the end of the file, where this tag would exist
	fread_err(buffer,3,1,ptr->fp);		//Read 3 bytes for the "TAG" header
	buffer[3] = '\0';	//Ensure NULL termination
	if(strcasecmp(buffer,"TAG") == 0)	//If this is an ID3v1 header
	{
		if(Lyrics.verbose)	(void) puts("Loading ID3v1 tag");
		ptr->id3v1present=1;	//Track that this tag exists
		fread_err(buffer,30,1,ptr->fp);		//Read the first field in the tag (song title)
		buffer[30] = '\0';	//Ensure NULL termination
		if(buffer[0] != '\0')	//If the string isn't empty
		{
			ptr->id3v1present=2;	//Track that this tag is populated
			ptr->id3v1title=DuplicateString(buffer);
			if(Lyrics.verbose)	(void) puts("\tTitle loaded");
		}
		fread_err(buffer,30,1,ptr->fp);		//Read the second field in the tag (artist)
		buffer[30] = '\0';	//Ensure NULL termination
		if(buffer[0] != '\0')	//If the string isn't empty
		{
			ptr->id3v1present=2;	//Track that this tag is populated
			ptr->id3v1artist=DuplicateString(buffer);
			if(Lyrics.verbose)	(void) puts("\tArtist loaded");
		}
		fread_err(buffer,30,1,ptr->fp);		//Read the third field in the tag (album)
		buffer[30] = '\0';	//Ensure NULL termination
		if(buffer[0] != '\0')	//If the string isn't empty
		{
			ptr->id3v1present=2;	//Track that this tag is populated
			ptr->id3v1album=DuplicateString(buffer);
			if(Lyrics.verbose)	(void) puts("\tAlbum loaded");
		}
		fread_err(buffer,4,1,ptr->fp);		//Read the fourth field in the tag (year)
		buffer[4]='\0';	//Terminate the buffer to make it a string
		if(buffer[0] != '\0')	//If the string isn't empty
		{
			ptr->id3v1present=2;	//Track that this tag is populated
			ptr->id3v1year=DuplicateString(buffer);
			if(Lyrics.verbose)	(void) puts("\tYear loaded");
		}
	}

//ID3v2
//Load ID3 frames into linked list
	rewind_err(ptr->fp);		//Rewind file so that the ID3v2 header can be searched for
	if(FindID3Tag(ptr) == 0)	//Locate the ID3 tag
		return 0;				//Return if there was none

	if(Lyrics.verbose)	(void) puts("Loading ID3v2 tag");
	fseek_err(ptr->fp,ptr->framestart,SEEK_SET);	//Seek to first ID3 frame
	filepos=ftell_err(ptr->fp);						//Record file position of first expected ID3 frame
	while((filepos >= ptr->framestart) && (filepos < ptr->tagend))
	{	//While file position is at or after the end of the ID3 header, before or at end of the ID3 tag
	//Read frame header into buffer
		if(fread(header,10,1,ptr->fp) != 1)
		{
			ptr->frames=head;	//Store the linked list into the ID3 tag structure
			return 0;	//Return failure on I/O error
		}

	//Validate frame ID
		for(ctr2=0;ctr2<4;ctr2++)
		{
			if(!isupper(header[ctr2]) && !isdigit(header[ctr2]))	//If the character is NOT valid for an ID3 frame ID
			{
				ptr->frames=head;	//Store the linked list into the ID3 tag structure
				return ctr;	//Return the number of frames that were already processed
			}
		}

	//Validate frame end
		framesize=((unsigned long)header[4]<<24) | ((unsigned long)header[5]<<16) | ((unsigned long)header[6]<<8) | ((unsigned long)header[7]);	//Convert to 4 byte integer
		if(filepos + framesize + 10 > ptr->tagend)	//If the defined frame size would cause the frame to extend beyond the end of the ID3 tag
		{
			ptr->frames=head;	//Store the linked list into the ID3 tag structure
			return 0;	//Return failure
		}

	//Initialize an ID3Frame structure
		temp=malloc_err(sizeof(struct ID3Frame));	//Allocate memory
		*temp=emptyID3Frame;						//Reliably initialize all values to 0/NULL
		temp->frameid=malloc_err(5);				//Allocate 5 bytes for the ID3 frame ID
		memcpy(temp->frameid,header,4);				//Copy the 4 byte ID3 frame ID into the pre-terminated string
		temp->frameid[4]='\0';						//Terminate the string
		temp->pos=filepos;	//Record the file position of the ID3 frame
		temp->length=((unsigned long)header[4]<<24) | ((unsigned long)header[5]<<16) | ((unsigned long)header[6]<<8) | ((unsigned long)header[7]);
			//Record the frame length (defined in header as 4 byte Big Endian integer)

		//Append ID3Frame link to the list
		if(head == NULL)	//Empty list
		{
			head=temp;	//Point head to new link
			cond=temp;	//Point conductor to new link
		}
		else
		{
			assert_wrapper(cond != NULL);	//The linked list is expected to not be corrupted
			cond->next=temp;	//Conductor points forward to new link
			temp->prev=cond;	//New link points back to conductor
			cond=temp;		//Conductor points to new link
		}

		if(Lyrics.verbose >= 2)	printf("\tFrame ID %s loaded\n",temp->frameid);
		ctr++;	//Iterate counter

		(void) fseek(ptr->fp,framesize,SEEK_CUR);	//Seek ahead to the beginning of the next ID3 frame
		filepos+=framesize + 10;	//Update file position
	}//While file position is at or after the end of the ID3 header, before or at end of the ID3 tag

	if(Lyrics.verbose)	printf("%lu ID3 frames loaded\n\n",ctr);

	ptr->frames=head;	//Store the linked list into the ID3 tag structure
	return ctr;			//Return the number of frames loaded
}