forked from len0rd/rockbox
		
	git-svn-id: svn://svn.rockbox.org/rockbox/trunk@15897 a1c6a512-1295-4272-9138-f99709370657
		
			
				
	
	
		
			302 lines
		
	
	
	
		
			6.9 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			302 lines
		
	
	
	
		
			6.9 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /***************************************************************************
 | |
|  *             __________               __   ___.
 | |
|  *   Open      \______   \ ____   ____ |  | _\_ |__   _______  ___
 | |
|  *   Source     |       _//  _ \_/ ___\|  |/ /| __ \ /  _ \  \/  /
 | |
|  *   Jukebox    |    |   (  <_> )  \___|    < | \_\ (  <_> > <  <
 | |
|  *   Firmware   |____|_  /\____/ \___  >__|_ \|___  /\____/__/\_ \
 | |
|  *                     \/            \/     \/    \/            \/
 | |
|  * $Id$
 | |
|  *
 | |
|  * Copyright (C) 2005 Dave Chapman
 | |
|  *
 | |
|  * All files in this archive are subject to the GNU General Public License.
 | |
|  * See the file COPYING in the source tree root for full license agreement.
 | |
|  *
 | |
|  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
 | |
|  * KIND, either express or implied.
 | |
|  *
 | |
|  ****************************************************************************/
 | |
| #include <stdio.h>
 | |
| #include <string.h>
 | |
| #include <stdlib.h>
 | |
| #include <ctype.h>
 | |
| #include <inttypes.h>
 | |
| 
 | |
| #include "system.h"
 | |
| #include "playback.h"
 | |
| #include "debug.h"
 | |
| #include "logf.h"
 | |
| #include "cuesheet.h"
 | |
| 
 | |
| #if CONFIG_CODEC == SWCODEC
 | |
| 
 | |
| /* For trailing tag stripping */
 | |
| #include "buffering.h"
 | |
| 
 | |
| #include "metadata/metadata_common.h"
 | |
| #include "metadata/metadata_parsers.h"
 | |
| 
 | |
| #endif /* CONFIG_CODEC == SWCODEC */
 | |
| 
 | |
| 
 | |
| /* Simple file type probing by looking at the filename extension. */
 | |
| unsigned int probe_file_format(const char *filename)
 | |
| {
 | |
|     char *suffix;
 | |
|     unsigned int i;
 | |
|     
 | |
|     suffix = strrchr(filename, '.');
 | |
| 
 | |
|     if (suffix == NULL)
 | |
|     {
 | |
|         return AFMT_UNKNOWN;
 | |
|     }
 | |
|     
 | |
|     /* skip '.' */
 | |
|     suffix++;
 | |
|     
 | |
|     for (i = 1; i < AFMT_NUM_CODECS; i++)
 | |
|     {
 | |
|         /* search extension list for type */
 | |
|         const char *ext = audio_formats[i].ext_list;
 | |
| 
 | |
|         do
 | |
|         {
 | |
|             if (strcasecmp(suffix, ext) == 0)
 | |
|             {
 | |
|                 return i;
 | |
|             }
 | |
| 
 | |
|             ext += strlen(ext) + 1;
 | |
|         }
 | |
|         while (*ext != '\0');
 | |
|     }
 | |
|     
 | |
|     return AFMT_UNKNOWN;
 | |
| }
 | |
| 
 | |
| /* Get metadata for track - return false if parsing showed problems with the
 | |
|  * file that would prevent playback.
 | |
|  */
 | |
| bool get_metadata(struct mp3entry* id3, int fd, const char* trackname)
 | |
| {
 | |
| #if CONFIG_CODEC == SWCODEC
 | |
|     unsigned char* buf;
 | |
| #endif
 | |
| 
 | |
|     /* Clear the mp3entry to avoid having bogus pointers appear */
 | |
|     memset(id3, 0, sizeof(struct mp3entry));
 | |
| 
 | |
|     /* Take our best guess at the codec type based on file extension */
 | |
|     id3->codectype = probe_file_format(trackname);
 | |
| 
 | |
|     /* Load codec specific track tag information and confirm the codec type. */
 | |
|     switch (id3->codectype)
 | |
|     {
 | |
|     case AFMT_MPA_L1:
 | |
|     case AFMT_MPA_L2:
 | |
|     case AFMT_MPA_L3:
 | |
|         if (!get_mp3_metadata(fd, id3, trackname))
 | |
|         {
 | |
|             return false;
 | |
|         }
 | |
| 
 | |
|         break;
 | |
| 
 | |
| #if CONFIG_CODEC == SWCODEC
 | |
|     case AFMT_FLAC:
 | |
|         if (!get_flac_metadata(fd, id3))
 | |
|         {
 | |
|             return false;
 | |
|         }
 | |
| 
 | |
|         break;
 | |
| 
 | |
|     case AFMT_WMA:
 | |
|         if (!get_asf_metadata(fd, id3))
 | |
|         {
 | |
|             return false;
 | |
|         }
 | |
|         break;
 | |
| 
 | |
|     case AFMT_APE:
 | |
|         if (!get_monkeys_metadata(fd, id3))
 | |
|         {
 | |
|             return false;
 | |
|         }
 | |
|         read_ape_tags(fd, id3);
 | |
|         break;
 | |
| 
 | |
|     case AFMT_MPC:
 | |
|         if (!get_musepack_metadata(fd, id3))
 | |
|             return false;
 | |
|         read_ape_tags(fd, id3);
 | |
|         break;
 | |
|     
 | |
|     case AFMT_OGG_VORBIS:
 | |
|         if (!get_ogg_metadata(fd, id3))/*detects and handles Ogg/Speex files*/
 | |
|         {
 | |
|             return false;
 | |
|         }
 | |
| 
 | |
|         break;
 | |
| 
 | |
|     case AFMT_SPEEX:
 | |
|         if (!get_ogg_metadata(fd, id3))
 | |
|         {
 | |
|             return false;
 | |
|         }
 | |
| 
 | |
|         break;
 | |
| 
 | |
|     case AFMT_PCM_WAV:
 | |
|         if (!get_wave_metadata(fd, id3))
 | |
|         {
 | |
|             return false;
 | |
|         }
 | |
| 
 | |
|         break;
 | |
| 
 | |
|     case AFMT_WAVPACK:
 | |
|         if (!get_wavpack_metadata(fd, id3))
 | |
|         {
 | |
|             return false;
 | |
|         }
 | |
| 
 | |
|         read_ape_tags(fd, id3);     /* use any apetag info we find */
 | |
|         break;
 | |
| 
 | |
|     case AFMT_A52:
 | |
|         if (!get_a52_metadata(fd, id3))
 | |
|         {
 | |
|             return false;
 | |
|         }
 | |
| 
 | |
|         break;
 | |
| 
 | |
|     case AFMT_ALAC:
 | |
|     case AFMT_AAC:
 | |
|         if (!get_mp4_metadata(fd, id3))
 | |
|         {
 | |
|             return false;
 | |
|         }
 | |
| 
 | |
|         break;
 | |
| 
 | |
|     case AFMT_SHN:
 | |
|         id3->vbr = true;
 | |
|         id3->filesize = filesize(fd);
 | |
|         if (!skip_id3v2(fd, id3))
 | |
|         {
 | |
|             return false;
 | |
|         }
 | |
|         /* TODO: read the id3v2 header if it exists */
 | |
|         break;
 | |
| 
 | |
|     case AFMT_SID:
 | |
|         if (!get_sid_metadata(fd, id3))
 | |
|         {
 | |
|             return false;
 | |
|         }
 | |
|         break;
 | |
| 
 | |
|     case AFMT_SPC:
 | |
|         if (!get_spc_metadata(fd, id3))
 | |
|         {
 | |
|             DEBUGF("get_spc_metadata error\n");
 | |
|             return false;
 | |
|         }
 | |
|         id3->filesize = filesize(fd);
 | |
|         id3->genre_string = id3_get_num_genre(36);
 | |
|         break;
 | |
| 
 | |
|     case AFMT_ADX:
 | |
|         if (!get_adx_metadata(fd, id3))
 | |
|         {
 | |
|             DEBUGF("get_adx_metadata error\n");
 | |
|             return false;
 | |
|         }
 | |
|         
 | |
|         break;
 | |
| 
 | |
|     case AFMT_NSF:
 | |
|         buf = (unsigned char *)id3->path;
 | |
|         if ((lseek(fd, 0, SEEK_SET) < 0) || ((read(fd, buf, 8)) < 8))
 | |
|         {
 | |
|             DEBUGF("lseek or read failed\n");
 | |
|             return false;
 | |
|         }
 | |
|         id3->vbr = false;
 | |
|         id3->filesize = filesize(fd);
 | |
|         if (memcmp(buf,"NESM",4) && memcmp(buf,"NSFE",4)) return false;
 | |
|         break;
 | |
| 
 | |
|     case AFMT_AIFF:
 | |
|         if (!get_aiff_metadata(fd, id3))
 | |
|         {
 | |
|             return false;
 | |
|         }
 | |
| 
 | |
|         break;
 | |
| 
 | |
| #endif /* CONFIG_CODEC == SWCODEC */
 | |
|         
 | |
|     default:
 | |
|         /* If we don't know how to read the metadata, assume we can't play 
 | |
|            the file */
 | |
|         return false;
 | |
|         break;
 | |
|     }
 | |
| 
 | |
|     /* We have successfully read the metadata from the file */
 | |
| 
 | |
| #ifndef __PCTOOL__
 | |
|     if (cuesheet_is_enabled() && look_for_cuesheet_file(trackname, NULL))
 | |
|     {
 | |
|         id3->cuesheet_type = 1;
 | |
|     }
 | |
| #endif
 | |
|     
 | |
|     lseek(fd, 0, SEEK_SET);
 | |
|     strncpy(id3->path, trackname, sizeof(id3->path));
 | |
| 
 | |
|     return true;
 | |
| }
 | |
| 
 | |
| #if CONFIG_CODEC == SWCODEC
 | |
| void strip_tags(int handle_id)
 | |
| {
 | |
|     static const unsigned char tag[] = "TAG";
 | |
|     static const unsigned char apetag[] = "APETAGEX";    
 | |
|     size_t len, version;
 | |
|     void *tail;
 | |
| 
 | |
|     if (bufgettail(handle_id, 128, &tail) != 128)
 | |
|         return;
 | |
| 
 | |
|     if (memcmp(tail, tag, 3) == 0)
 | |
|     {
 | |
|         /* Skip id3v1 tag */
 | |
|         logf("Cutting off ID3v1 tag");
 | |
|         bufcuttail(handle_id, 128);
 | |
|     }
 | |
| 
 | |
|     /* Get a new tail, as the old one may have been cut */
 | |
|     if (bufgettail(handle_id, 32, &tail) != 32)
 | |
|         return;
 | |
| 
 | |
|     /* Check for APE tag (look for the APE tag footer) */
 | |
|     if (memcmp(tail, apetag, 8) != 0)
 | |
|         return;
 | |
| 
 | |
|     /* Read the version and length from the footer */
 | |
|     version = get_long_le(&((unsigned char *)tail)[8]);
 | |
|     len = get_long_le(&((unsigned char *)tail)[12]);
 | |
|     if (version == 2000)
 | |
|         len += 32; /* APEv2 has a 32 byte header */
 | |
| 
 | |
|     /* Skip APE tag */
 | |
|     logf("Cutting off APE tag (%ldB)", len);
 | |
|     bufcuttail(handle_id, len);
 | |
| }
 | |
| #endif /* CONFIG_CODEC == SWCODEC */
 |