From fc0cea1d4920255d51abaef73c67b860fffc64ab Mon Sep 17 00:00:00 2001 From: Ron Pedde Date: Mon, 12 Jun 2006 05:41:36 +0000 Subject: [PATCH] first pass at aif support. Doesn't honor the ID3 block, but should get merged by the iTunes XML file. Closes #141 --- src/Makefile.am | 2 +- src/mp3-scanner.c | 3 + src/scan-aif.c | 184 +++++++++++++++++++++++++++++++++++++++++++ src/scanner-driver.c | 3 + src/scanner.mk | 2 +- 5 files changed, 192 insertions(+), 2 deletions(-) create mode 100644 src/scan-aif.c diff --git a/src/Makefile.am b/src/Makefile.am index cb3dea71..c6f9cfcf 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -53,7 +53,7 @@ mt_daapd_SOURCES = main.c daapd.h rend.h uici.c uici.h webserver.c \ mp3-scanner.h mp3-scanner.c rend-unix.h \ dynamic-art.c dynamic-art.h \ db-generic.c db-generic.h dispatch.c dispatch.h \ - rxml.c rxml.h redblack.c redblack.h scan-mp3.c scan-mp4.c \ + rxml.c rxml.h redblack.c redblack.h scan-mp3.c scan-mp4.c scan-aif.c \ scan-xml.c scan-wma.c scan-aac.c scan-aac.h scan-wav.c scan-url.c \ smart-parser.c smart-parser.h xml-rpc.c xml-rpc.h \ os.h ll.c ll.h conf.c conf.h compat.c compat.h \ diff --git a/src/mp3-scanner.c b/src/mp3-scanner.c index 2ce1ad7a..9d51b179 100644 --- a/src/mp3-scanner.c +++ b/src/mp3-scanner.c @@ -100,6 +100,7 @@ extern int scan_get_wavinfo(char *filename, MP3FILE *pmp3); extern int scan_get_urlinfo(char *filename, MP3FILE *pmp3); extern int scan_get_mp3info(char *filename, MP3FILE *pmp3); extern int scan_get_mp4info(char *filename, MP3FILE *pmp3); +extern int scan_get_aifinfo(char *filename, MP3FILE *pmp3); /* playlist scanners */ extern int scan_xml_playlist(char *filename); @@ -133,6 +134,8 @@ static TAGHANDLER taghandlers[] = { { "m4p", scan_get_aacinfo, "m4p", "mp4a", "AAC audio file" }, { "mp3", scan_get_mp3info, "mp3", "mpeg", "MPEG audio file" }, { "wav", scan_get_wavinfo, "wav", "wav", "WAV audio file" }, + { "aif", scan_get_aifinfo, "aif", "aif", "AIFF audio file" }, + { "aiff",scan_get_aifinfo, "aif", "aif", "AIFF audio file" }, { "wma", scan_get_wmainfo, "wma", "wma", "WMA audio file" }, { "url", scan_get_urlinfo, "pls", NULL, "Playlist URL" }, { "pls", scan_get_urlinfo, "pls", NULL, "Playlist URL" }, diff --git a/src/scan-aif.c b/src/scan-aif.c new file mode 100644 index 00000000..eaa4f99d --- /dev/null +++ b/src/scan-aif.c @@ -0,0 +1,184 @@ +/* + * $Id: $ + * + * Copyright (C) 2006 Ron Pedde (ron@pedde.com) + * + * 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 program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include +#include +#include + +#include "err.h" +#include "mp3-scanner.h" + +#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ > 4) +# define _PACKED __attribute((packed)) +#else +# define _PACKED +#endif + +#pragma pack(1) +typedef struct tag_aif_chunk_header { + char id[4]; + uint32_t len; +} _PACKED AIF_CHUNK_HEADER; + +typedef struct tag_iff_header { + char id[4]; + uint32_t length; + char type[4]; +} _PACKED AIF_IFF_HEADER; + +typedef struct tag_aif_comm { + int16_t channels; + uint32_t sample_frames; + int16_t sample_size; + uint8_t sample_rate[10]; +} _PACKED AIF_COMM; +#pragma pack() + +uint32_t aif_from_be32(uint32_t *native) { + uint32_t result; + uint8_t *data = (uint8_t *)native; + + result = data[0] << 24 | data[1] << 16 | data[2] << 8 | data[3]; + return result; +} + +uint16_t aif_from_be16(uint16_t *native) { + uint16_t result; + uint8_t *data = (uint8_t *)native; + + result = data[0] << 8 | data[1]; + return result; +} + + +/** + * parse a COMM block -- should be sitting at the bottom half of + * a comm block + * + * @param infile file to read + * @return TRUE on success, FALSE otherwise + */ +int scan_aif_parse_comm(FILE *infile, MP3FILE *pmp3) { + AIF_COMM comm; + int sec; + int ms; + + if(fread((void*)&comm,sizeof(AIF_COMM),1,infile) != 1) { + DPRINTF(E_WARN,L_SCAN,"Error reading aiff file -- bad COMM block\n"); + return FALSE; + } + + /* we'll brute the sample rate */ + + pmp3->samplerate = aif_from_be32((uint32_t*)&comm.sample_rate[2]) >> 16; + if(!pmp3->samplerate) + return TRUE; + + pmp3->bitrate = pmp3->samplerate * comm.channels * + ((comm.sample_size + 7)/8)*8; + + sec = pmp3->file_size / (pmp3->bitrate / 8); + ms = ((pmp3->file_size % (pmp3->bitrate / 8)) * 1000) / (pmp3->bitrate/8); + pmp3->song_length = (sec * 1000) + ms; + + pmp3->bitrate /= 1000; + + return TRUE; +} + +/** + * Get info from the actual aiff headers. Since there is no + * metainfo in .wav files (or at least know standard I + * know about), this merely gets duration, bitrate, etc. + * + * @param filename file to scan + * @param pmp3 MP3FILE struct to be filled + * @returns TRUE if song should be added to database, FALSE otherwise + */ +int scan_get_aifinfo(char *filename, MP3FILE *pmp3) { + FILE *infile; + int done=0; + AIF_CHUNK_HEADER chunk; + AIF_IFF_HEADER iff_header; + size_t current_pos = 0; + + DPRINTF(E_DBG,L_SCAN,"Getting AIFF file info\n"); + + if(!(infile=fopen(filename,"rb"))) { + DPRINTF(E_WARN,L_SCAN,"Could not open %s for reading\n",filename); + return FALSE; + } + + fseek(infile,0,SEEK_END); + pmp3->file_size = ftell(infile); + fseek(infile,0,SEEK_SET); + + + /* first, verify we have a valid iff header */ + if(fread((void*)&iff_header,sizeof(AIF_IFF_HEADER),1,infile) != 1) { + DPRINTF(E_WARN,L_SCAN,"Error reading %s -- bad iff header\n",filename); + fclose(infile); + return FALSE; + } + + if((strncmp(iff_header.id,"FORM",4) != 0) || + (strncmp(iff_header.type,"AIFF",4) != 0)) { + DPRINTF(E_WARN,L_SCAN,"File %s is not an AIFF file\n",filename); + fclose(infile); + return FALSE; + } + + /* loop around, processing chunks */ + while(!done) { + if(fread((void*)&chunk,sizeof(AIF_CHUNK_HEADER),1,infile) != 1) { + done=1; + break; + } + + /* fixup */ + chunk.len = aif_from_be32(&chunk.len); + + DPRINTF(E_DBG,L_SCAN,"Got chunk %c%c%c%c\n",chunk.id[0], + chunk.id[1],chunk.id[2],chunk.id[3]); + + current_pos = ftell(infile); + + /* process the chunk */ + if(strncmp(chunk.id,"COMM",4)==0) { + if(!scan_aif_parse_comm(infile,pmp3)) { + DPRINTF(E_INF,L_SCAN,"Error reading COMM block: %s\n",filename); + fclose(infile); + return FALSE; + } + } + + fseek(infile,current_pos,SEEK_SET); + fseek(infile,chunk.len,SEEK_CUR); + } + + fclose(infile); + return TRUE; +} + diff --git a/src/scanner-driver.c b/src/scanner-driver.c index 0fec2f2a..378db8de 100644 --- a/src/scanner-driver.c +++ b/src/scanner-driver.c @@ -45,6 +45,7 @@ extern int scan_get_wavinfo(char *filename, MP3FILE *pmp3); extern int scan_get_aacinfo(char *filename, MP3FILE *pmp3); extern int scan_get_mp3info(char *filename, MP3FILE *pmp3); extern int scan_get_urlinfo(char *filename, MP3FILE *pmp3); +extern int scan_get_aifinfo(char *filename, MP3FILE *pmp3); /* * Typedefs @@ -71,6 +72,8 @@ SCANNERLIST scanner_list[] = { { "wav",scan_get_wavinfo }, { "url",scan_get_urlinfo }, { "mp3",scan_get_mp3info }, + { "aif",scan_get_aifinfo }, + { "aiff",scan_get_aifinfo }, { NULL, NULL } }; char *av0; diff --git a/src/scanner.mk b/src/scanner.mk index 01cf39e4..0f539605 100644 --- a/src/scanner.mk +++ b/src/scanner.mk @@ -2,7 +2,7 @@ CC=gcc CFLAGS := $(CFLAGS) -g -I/opt/local/include -DHAVE_CONFIG_H -I. -I.. -DHOST='"foo"' -DHAVE_SQL -DHAVE_CONFIG_H LDFLAGS := $(LDFLAGS) -L/opt/local/lib -lid3tag -logg -lvorbisfile -lFLAC -lvorbis -ltag_c -lsqlite -lsqlite3 -lm TARGET = scanner -OBJECTS=scanner-driver.o restart.o err.o scan-wma.o scan-aac.o scan-wav.o scan-flac.o scan-ogg.o scan-mp3.o scan-url.o scan-mpc.o os-unix.o conf.o ll.o xml-rpc.o webserver.o uici.o rend-win32.o configfile.o db-generic.o db-sql-sqlite3.o db-sql-sqlite2.o db-sql.o smart-parser.o plugin.o dispatch.o dynamic-art.o +OBJECTS=scanner-driver.o restart.o err.o scan-aif.o scan-wma.o scan-aac.o scan-wav.o scan-flac.o scan-ogg.o scan-mp3.o scan-url.o scan-mpc.o os-unix.o conf.o ll.o xml-rpc.o webserver.o uici.o rend-win32.o configfile.o db-generic.o db-sql-sqlite3.o db-sql-sqlite2.o db-sql.o smart-parser.o plugin.o dispatch.o dynamic-art.o $(TARGET): $(OBJECTS) $(CC) -o $(TARGET) $(LDFLAGS) $(OBJECTS)