/*************************************************************************** * __________ __ ___. * Open \______ \ ____ ____ | | _\_ |__ _______ ___ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ * \/ \/ \/ \/ \/ * $Id$ * * Copyright (C) 2005 Dave Chapman * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY * KIND, either express or implied. * ****************************************************************************/ #include #include #include #include #include #include "system.h" #include "playback.h" #include "debug.h" #include "logf.h" #include "cuesheet.h" #include "metadata.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_MOD: if (!get_mod_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 */