/* * As the ID3Tag_Link function of id3lib-3.8.0pre2 returns the ID3v1 tags * when a file has both ID3v1 and ID3v2 tags, we first try to explicitely * get the ID3v2 tags with ID3Tag_LinkWithFlags and, if we cannot get them, * fall back to the ID3v1 tags. * (Written by Holger Schemel). */ static size_t local__ID3Tag_Link_wrapper(ID3Tag *id3tag, const char *filename) { size_t offset; # if ( (ID3LIB_MAJOR >= 3) && (ID3LIB_MINOR >= 8) ) /* First, try to get the ID3v2 tags */ offset = ID3Tag_LinkWithFlags(id3tag, filename, ID3TT_ID3V2); if (offset == 0) { /* No ID3v2 tags available => try to get the ID3v1 tags */ offset = ID3Tag_LinkWithFlags(id3tag, filename, ID3TT_ID3V1); } # else /* Function 'ID3Tag_LinkWithFlags' is not defined up to id3lib-.3.7.13 */ offset = ID3Tag_Link(id3tag, filename); # endif return offset; }
// Reads ID3v2.x tag // idea of this come from "tag" by Case <*****@*****.**> int readtag_id3v2 ( apetag *mem_cnt, char* fileName ) { ID3Tag* tag; ID3Frame* frame; ID3TagIterator* iter; char* item = NULL; int itemSize; char* value = NULL; int valueSize; unsigned long flags; // first - check of id3tag v2 and init if ( (tag = ID3Tag_New ()) == NULL ) return 1; // on some casses its weerrryyy slooowwwwlyyy 65k file take 2-5 sec ID3Tag_LinkWithFlags ( tag, fileName, ID3TT_ID3V2 ); if ( tag == NULL ) { ID3Tag_Delete (tag); return 0; } if ( !ID3Tag_HasTagType ( tag, ID3TT_ID3V2 ) ) { ID3Tag_Delete (tag); return 0; } if ( (iter = ID3Tag_CreateIterator (tag)) == NULL ) { ID3Tag_Delete (tag); return 0; } while ( (frame = ID3TagIterator_GetNext (iter)) != NULL ) { libapetag_convertID3v2toAPE ( frame, &item, &itemSize, &value, &valueSize, &flags); if ( !item || !value || item[0] == '\0' || value[0] == '\0' ) continue; if ( !value || value[0] != '\0' ) { PRINT_D4(">id3v2_read>[i%i]%s: [v%i]'%s'\n",itemSize,item,valueSize,value); if ( apefrm_getstr (mem_cnt, item) == NULL ) /* noreplece !!! */ apefrm_add_bin (mem_cnt, flags, itemSize, item, valueSize ,value); } free ( value ); free ( item ); } ID3TagIterator_Delete (iter); ID3Tag_Delete (tag); return 0; }
/* * Read infos into header of first frame */ gboolean Mpeg_Header_Read_File_Info (gchar *filename, ET_File_Info *ETFileInfo) { /* * With id3lib, the header frame couldn't be read if the file contains an ID3v2 tag with an APIC frame */ ID3Tag *id3_tag = NULL; /* Tag defined by the id3lib */ const Mp3_Headerinfo* headerInfo = NULL; g_return_val_if_fail (filename != NULL || ETFileInfo != NULL, FALSE); /* Get size of file */ ETFileInfo->size = Get_File_Size (filename); /* Get data from tag */ if ( (id3_tag = ID3Tag_New()) == NULL ) return FALSE; /* Link the file to the tag (uses ID3TT_ID3V2 to get header if APIC is present in Tag) */ ID3Tag_LinkWithFlags(id3_tag,filename,ID3TT_ID3V2); if ( (headerInfo = ID3Tag_GetMp3HeaderInfo(id3_tag)) ) { switch (headerInfo->version) { case MPEGVERSION_1: ETFileInfo->version = 1; ETFileInfo->mpeg25 = FALSE; break; case MPEGVERSION_2: ETFileInfo->version = 2; ETFileInfo->mpeg25 = FALSE; break; case MPEGVERSION_2_5: ETFileInfo->mpeg25 = TRUE; ETFileInfo->mpeg25 = FALSE; break; default: break; } switch (headerInfo->layer) { case MPEGLAYER_I: ETFileInfo->layer = 1; break; case MPEGLAYER_II: ETFileInfo->layer = 2; break; case MPEGLAYER_III: ETFileInfo->layer = 3; break; default: break; } // Samplerate ETFileInfo->samplerate = headerInfo->frequency; // Mode -> Seems to be detected but incorrect?! switch (headerInfo->modeext) { case MP3CHANNELMODE_STEREO: ETFileInfo->mode = 0; break; case MP3CHANNELMODE_JOINT_STEREO: ETFileInfo->mode = 1; break; case MP3CHANNELMODE_DUAL_CHANNEL: ETFileInfo->mode = 2; break; case MP3CHANNELMODE_SINGLE_CHANNEL: ETFileInfo->mode = 3; break; default: break; } // Bitrate if (headerInfo->vbr_bitrate <= 0) { ETFileInfo->variable_bitrate = FALSE; ETFileInfo->bitrate = headerInfo->bitrate/1000; }else { ETFileInfo->variable_bitrate = TRUE; ETFileInfo->bitrate = headerInfo->vbr_bitrate/1000; } // Duration ETFileInfo->duration = headerInfo->time; } /* Free allocated data */ ID3Tag_Delete(id3_tag); return TRUE; }
/* * Read infos into header of first frame */ gboolean Mpeg_Header_Read_File_Info (gchar *filename, ET_File_Info *ETFileInfo) { #if (!USE_ID3LIB_4_HEADER) FILE *file; gulong filesize; if (!filename || !ETFileInfo) return FALSE; /* Get size of file */ filesize = Get_File_Size(filename); ETFileInfo->size = filesize; /* * This part was taken from XMMS */ if ((file = fopen(filename, "rb")) != NULL) { guint32 head; unsigned char tmp[4]; struct frame frm; gboolean id3_found = FALSE; if (fread(tmp, 1, 4, file) != 4) { fclose(file); return FALSE; } // Skip data of the ID3v2.x tag (It may contain data similar to mpeg frame // as, for example, in the id3 APIC frame) (patch from Artur Polaczynski) if (tmp[0] == 'I' && tmp[1] == 'D' && tmp[2] == '3' && tmp[3] < 0xFF) { // ID3v2 tag skipeer $49 44 33 yy yy xx zz zz zz zz [zz size] long id3v2size; fseek(file, 2, SEEK_CUR); // Size is 6-9 position if (fread(tmp, 1, 4, file) != 4) // Read bytes of tag size { fclose(file); return FALSE; } id3v2size = 10 + ( (long)(tmp[3]) | ((long)(tmp[2]) << 7) | ((long)(tmp[1]) << 14) | ((long)(tmp[0]) << 21) ); fseek(file, id3v2size, SEEK_SET); if (fread(tmp, 1, 4, file) != 4) // Read mpeg header { fclose(file); return FALSE; } } head = ((guint32) tmp[0] << 24) | ((guint32) tmp[1] << 16) | ((guint32) tmp[2] << 8) | (guint32) tmp[3]; while (!mpg123_head_check(head)) { head <<= 8; if (fread(tmp, 1, 1, file) != 1) { fclose(file); return FALSE; } head |= tmp[0]; } if (mpg123_decode_header(&frm, head)) { guchar *buf; gdouble tpf; gint pos; XHEADDATA xing_header; guint32 num_frames; buf = g_malloc(frm.framesize + 4); fseek(file, -4, SEEK_CUR); fread(buf, 1, frm.framesize + 4, file); xing_header.toc = NULL; tpf = mpg123_compute_tpf(&frm); // MPEG and Layer version ETFileInfo->mpeg25 = frm.mpeg25; if (!ETFileInfo->mpeg25) ETFileInfo->version = frm.lsf+1; ETFileInfo->layer = frm.lay; //if (ETFileInfo->mpeg25) g_print("mpeg_level: MPEG 2.5, layer %d\n",ETFileInfo->layer); //else g_print("mpeg_level: MPEG %d, layer %d\n",ETFileInfo->version,ETFileInfo->layer); pos = ftell(file); fseek(file, 0, SEEK_END); // Variable bitrate? + bitrate if ( (ETFileInfo->variable_bitrate=mpg123_get_xing_header(&xing_header,buf)) ) { num_frames = xing_header.frames; ETFileInfo->bitrate = (gint) ((xing_header.bytes * 8) / (tpf * xing_header.frames * 1000)); //g_print("Bitrate: Variable,\navg. bitrate: %d kb/s\n",ETFileInfo->bitrate); } else { num_frames = ((ftell(file) - pos - (id3_found ? 128 : 0)) / mpg123_compute_bpf(&frm)) + 1; ETFileInfo->bitrate = tabsel_123[frm.lsf][frm.lay - 1][frm.bitrate_index]; //g_print("Bitrate: %d kb/s\n",ETFileInfo->bitrate); } // Samplerate ETFileInfo->samplerate = mpg123_freqs[frm.sampling_frequency]; // Mode ETFileInfo->mode = frm.mode; //g_print("Samplerate: %ld Hz\n", mpg123_freqs[frm.sampling_frequency]); //g_print("%s\nError protection: %s\nCopyright: %s\nOriginal: %s\nEmphasis: %s\n", channel_mode_name(frm.mode), bool_label[frm.error_protection], bool_label[frm.copyright], bool_label[frm.original], emphasis[frm.emphasis]); //g_print("%d frames\nFilesize: %lu B\n", num_frames, ftell(file)); g_free(buf); } // Duration ETFileInfo->duration = mpg123_get_song_time(file)/1000; //g_print("time %s\n",Convert_Duration(ETFileInfo->duration)); fclose(file); }else { gchar *filename_utf8 = filename_to_display(filename); Log_Print(LOG_ERROR,_("ERROR while opening file: '%s' (%s)."),filename_utf8,g_strerror(errno)); g_free(filename_utf8); return FALSE; } #else // Needs to uncomment some #include at the beginning /* * With id3lib, the header frame couldn't be read if the file contains an ID3v2 tag with an APIC frame */ gulong filesize; ID3Tag *id3_tag = NULL; /* Tag defined by the id3lib */ const Mp3_Headerinfo* headerInfo = NULL; if (!filename || !ETFileInfo) return FALSE; /* Get size of file */ filesize = Get_File_Size(filename); ETFileInfo->size = filesize; /* Get data from tag */ if ( (id3_tag = ID3Tag_New()) == NULL ) return FALSE; /* Link the file to the tag (uses ID3TT_ID3V2 to get header if APIC is present in Tag) */ ID3Tag_LinkWithFlags(id3_tag,filename,ID3TT_ID3V2); /*ID3_STRUCT(Mp3_Headerinfo) { Mpeg_Layers layer; Mpeg_Version version; MP3_BitRates bitrate; Mp3_ChannelMode channelmode; Mp3_ModeExt modeext; Mp3_Emphasis emphasis; Mp3_Crc crc; uint32 vbr_bitrate; // avg bitrate from xing header uint32 frequency; // samplerate uint32 framesize; uint32 frames; // nr of frames uint32 time; // nr of seconds in song bool privatebit; bool copyrighted; bool original; };*/ if ( (headerInfo = ID3Tag_GetMp3HeaderInfo(id3_tag)) ) { switch (headerInfo->version) { case MPEGVERSION_1: ETFileInfo->version = 1; ETFileInfo->mpeg25 = FALSE; break; case MPEGVERSION_2: ETFileInfo->version = 2; ETFileInfo->mpeg25 = FALSE; break; case MPEGVERSION_2_5: ETFileInfo->mpeg25 = TRUE; ETFileInfo->mpeg25 = FALSE; break; default: break; } switch (headerInfo->layer) { case MPEGLAYER_I: ETFileInfo->layer = 1; break; case MPEGLAYER_II: ETFileInfo->layer = 2; break; case MPEGLAYER_III: ETFileInfo->layer = 3; break; default: break; } // Samplerate ETFileInfo->samplerate = headerInfo->frequency; // Mode -> Seems to be detected but incorrect?! switch (headerInfo->modeext) { case MP3CHANNELMODE_STEREO: ETFileInfo->mode = 0; break; case MP3CHANNELMODE_JOINT_STEREO: ETFileInfo->mode = 1; break; case MP3CHANNELMODE_DUAL_CHANNEL: ETFileInfo->mode = 2; break; case MP3CHANNELMODE_SINGLE_CHANNEL: ETFileInfo->mode = 3; break; default: break; } // Bitrate if (headerInfo->vbr_bitrate <= 0) { ETFileInfo->variable_bitrate = FALSE; ETFileInfo->bitrate = headerInfo->bitrate/1000; }else { ETFileInfo->variable_bitrate = TRUE; ETFileInfo->bitrate = headerInfo->vbr_bitrate/1000; } // Duration ETFileInfo->duration = headerInfo->time; } /* Free allocated data */ ID3Tag_Delete(id3_tag); #endif return TRUE; }
/* read tags from a file */ FileInfo *filetags_read_file(gchar *filename) { /* id3lib has some really weird C API. we have to manipulate this carefully. also there seems to be code for unicode and stuff liek that (esp if you look at libmad). all that is not dealt with here. the strategy is this: for every file we try to get both v1 and v2. we prefer v2 frames over v1 frames (i dont think they are called frames in v1). if a v2 frame doesnt exist then we try to get it from v1. */ FileInfo *filetag = NULL; size_t v1pos = 0, v2pos = 0; ID3Tag *id3v1tag = NULL, *id3v2tag = NULL; int len = 0; char buffer[MAX_STRING_LEN]; FILE *file = NULL; /* ok, make sure the file exists first of all. */ if (!filename) { return NULL; } file = fopen(filename, "r"); if (file == NULL) { return NULL; } fclose(file); /* ok file exists. now get a new tag and link it with filename. */ filetag = (FileInfo*)g_malloc(sizeof(FileInfo)); bzero(filetag, sizeof(FileInfo)); id3v1tag = ID3Tag_New(); id3v2tag = ID3Tag_New(); v1pos = ID3Tag_LinkWithFlags(id3v1tag, filename, ID3TT_ID3V2); v2pos = ID3Tag_LinkWithFlags(id3v2tag, filename, ID3TT_ID3V1); /* I dont know if we really care about position though. id3lib * sources says something about _prepended_bytes. Did I mention I * hate C++? id3lib just made it worse. I wonder how libmad's * libid3tag would be. */ /* lets get the following frames: Title, Artist, Album and Time * mainly and optionally get things like Genre, Year. */ /* title */ bzero(buffer, MAX_STRING_LEN*sizeof(char)); len = get_text_field_from_tag(id3v2tag, ID3FID_TITLE, ID3FN_TEXT, buffer, 0); if (len == 0) { len = get_text_field_from_tag(id3v1tag, ID3FID_TITLE, ID3FN_TEXT, buffer, 0); } if (len != 0) { filetag->title = g_strdup(buffer); } /* artist: can be one of ID3FID_ORIGARTIST (TOPE), ID3FID_LEADARTIST (TPE1), ID3FID_BAND (TPE2), ID3FID_CONDUCTOR (TPE3), ID3FID_MIXARTIST (TPE4). We will get it in the order TPE1, TPE2, TOPE, TPE3, TPE4. */ bzero(buffer, MAX_STRING_LEN*sizeof(char)); len = get_text_field_from_tag(id3v2tag, ID3FID_LEADARTIST, ID3FN_TEXT, buffer, 0); if (len == 0) { len = get_text_field_from_tag(id3v1tag, ID3FID_LEADARTIST, ID3FN_TEXT, buffer, 0); } if (len != 0) { filetag->artist = g_strdup(buffer); } /* album */ bzero(buffer, MAX_STRING_LEN*sizeof(char)); len = get_text_field_from_tag(id3v2tag, ID3FID_ALBUM, ID3FN_TEXT, buffer, 0); if (len == 0) { len = get_text_field_from_tag(id3v1tag, ID3FID_ALBUM, ID3FN_TEXT, buffer, 0); } if (len != 0) { filetag->album = g_strdup(buffer); } ID3Tag_Delete(id3v1tag); ID3Tag_Delete(id3v2tag); return filetag; }