? scrobbler.patch
Index: apps/Makefile
===================================================================
RCS file: /cvsroot/rockbox/apps/Makefile,v
retrieving revision 1.105
diff -u -r1.105 Makefile
--- apps/Makefile	3 Apr 2006 21:11:10 -0000	1.105
+++ apps/Makefile	13 May 2006 00:43:32 -0000
@@ -55,7 +55,7 @@
 endif
 
 CFLAGS = $(INCLUDES) $(GCCOPTS) $(TARGET) $(DEFINES) -DTARGET_ID=$(TARGET_ID)	\
- -DAPPSVERSION=\"$(VERSION)\" $(EXTRA_DEFINES) -DMEM=${MEMORYSIZE}
+ -DTARGET_NAME=\"$(ARCHOS)\" -DAPPSVERSION=\"$(VERSION)\" $(EXTRA_DEFINES) -DMEM=${MEMORYSIZE}
 
 OBJS2 := $(OBJDIR)/lang.o $(patsubst %.c, $(OBJDIR)/%.o, $(SRC))
 OBJS = $(patsubst %.S, $(OBJDIR)/%.o, $(OBJS2))
Index: apps/SOURCES
===================================================================
RCS file: /cvsroot/rockbox/apps/SOURCES,v
retrieving revision 1.44
diff -u -r1.44 SOURCES
--- apps/SOURCES	26 Mar 2006 11:33:41 -0000	1.44
+++ apps/SOURCES	13 May 2006 00:43:32 -0000
@@ -27,6 +27,9 @@
 tree.c
 tagtree.c
 filetree.c
+#ifdef CONFIG_RTC
+scrobbler.c
+#endif
 
 screen_access.c
 gui/buttonbar.c
Index: apps/main.c
===================================================================
RCS file: /cvsroot/rockbox/apps/main.c,v
retrieving revision 1.170
diff -u -r1.170 main.c
--- apps/main.c	1 May 2006 12:54:21 -0000	1.170
+++ apps/main.c	13 May 2006 00:43:32 -0000
@@ -88,6 +88,10 @@
 #include "lcd-remote.h"
 #endif
 
+#ifdef CONFIG_RTC
+#include "scrobbler.h"
+#endif
+
 /*#define AUTOROCK*/ /* define this to check for "autostart.rock" on boot */
 
 const char appsversion[]=APPSVERSION;
@@ -431,6 +435,9 @@
 #if (defined(IRIVER_H100_SERIES) || defined(IRIVER_H300_SERIES)) && !defined(SIMULATOR)
     pcm_rec_init();
 #endif
+#ifdef CONFIG_RTC
+    scrobbler_init();
+#endif
 
     /* runtime database has to be initialized after audio_init() */
     cpu_boost(false);
Index: apps/playback.c
===================================================================
RCS file: /cvsroot/rockbox/apps/playback.c,v
retrieving revision 1.313
diff -u -r1.313 playback.c
--- apps/playback.c	9 May 2006 00:14:51 -0000	1.313
+++ apps/playback.c	13 May 2006 00:43:32 -0000
@@ -267,6 +267,9 @@
 static void audio_fill_file_buffer(
         bool start_play, bool rebuffer, size_t offset);
 
+/* Play time of the previous track */
+unsigned long prev_track_elapsed;
+
 static void swap_codec(void)
 {
     int my_codec = current_codec;
@@ -1991,6 +1994,8 @@
 static bool load_next_track(void) {
     struct event ev;
 
+    prev_track_elapsed = cur_ti->id3.elapsed;
+
     if (ci.seek_time)
         codec_seek_complete_callback();
 
@@ -2035,6 +2040,11 @@
     }
 }
 
+unsigned long ret_prev_elapsed(void)
+{
+    return prev_track_elapsed;
+}
+
 static bool voice_request_next_track_callback(void)
 {
     ci_voice.new_track = 0;
Index: apps/scrobbler.c
===================================================================
RCS file: apps/scrobbler.c
diff -N apps/scrobbler.c
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ apps/scrobbler.c	13 May 2006 00:43:32 -0000
@@ -0,0 +1,223 @@
+
+/* mktime() code taken from lynx-2.8.5 source, written
+ by Philippe De Muyter <phdm@macqel.be>
+
+Audioscrobbler spec at:
+http://www.audioscrobbler.net/wiki/Portable_Player_Logging
+*/
+
+#include "file.h"
+#include "sprintf.h"
+#include "playback.h"
+#include "logf.h"
+#include "id3.h"
+#include "splash.h"
+#include "kernel.h"
+#include "audio.h"
+#ifdef CONFIG_RTC
+#include "time.h"
+#include "timefuncs.h"
+#endif
+
+#include "scrobbler.h"
+
+/* increment this on any code change that effects output */
+/* replace with CVS Revision keyword? */
+#define SCROBBLER_REVISION "1.0"
+
+#define SCROBBLER_MAX_CACHE 7
+/* longest entry I've had is ~250, doubled it to be safe */
+#define SCROBBLER_CACHE_LEN 511
+
+char scrobbler_cache[SCROBBLER_MAX_CACHE][SCROBBLER_CACHE_LEN];
+
+int scrobbler_fd = -1;
+int cache_pos;
+struct mp3entry scrobbler_entry;
+bool pending = false;
+time_t timestamp;
+
+static time_t mktime(struct tm *t)
+{
+    short month, year;
+    time_t	result;
+    static int	m_to_d[12] =
+        {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334};
+
+    month = t->tm_mon;
+    year = t->tm_year + month / 12 + 1900;
+    month %= 12;
+    if (month < 0)
+    {
+        year -= 1;
+        month += 12;
+    }
+    result = (year - 1970) * 365 + (year - 1969) / 4 + m_to_d[month];
+    result = (year - 1970) * 365 + m_to_d[month];
+    if (month <= 1)
+        year -= 1;
+    result += (year - 1968) / 4;
+    result -= (year - 1900) / 100;
+    result += (year - 1600) / 400;
+    result += t->tm_mday;
+    result -= 1;
+    result *= 24;
+    result += t->tm_hour;
+    result *= 60;
+    result += t->tm_min;
+    result *= 60;
+    result += t->tm_sec;
+    return(result);
+}
+
+void write_cache(void)
+{
+    int i;
+
+    scrobbler_fd = open(SCROBBLER_FILE, O_RDWR | O_APPEND);
+    if(scrobbler_fd >= 0)
+    {
+        logf("SCROBBLER: writing %d entries", cache_pos); 
+        for ( i=0; i < cache_pos; i++ )
+        {
+            logf("SCROBBLER: write %d", i);
+            fdprintf(scrobbler_fd, "%s", scrobbler_cache[i]);
+        }
+        close(scrobbler_fd);
+    }
+    else
+    {
+        logf("SCROBBLER: error writing file");
+    }
+    cache_pos = 0;
+}
+
+void add_to_cache(bool guess_time)
+{
+    /* FIXME  this should just be > but dies a horrible death */
+    if (cache_pos >= SCROBBLER_MAX_CACHE)
+        write_cache();
+
+    int ret;
+    char rating = 0x53; /* "S" */;
+    
+    logf("SCROBBLER: add_to_cache %d", cache_pos);
+
+    if (guess_time)
+    {
+        if ((mktime(get_time()) - timestamp) >
+            (long)(scrobbler_entry.length/2000))
+            rating = 0x4C; /* "L" */
+    } else {
+        if ( ret_prev_elapsed() >
+            (scrobbler_entry.length/2) )
+            rating = 0x4C; /* "L" */
+    }
+
+    if (scrobbler_entry.album[0] == 0)
+        scrobbler_entry.album = "\t";
+
+    if (scrobbler_entry.tracknum > 0)
+    {
+        ret = snprintf(scrobbler_cache[cache_pos], 
+                sizeof(scrobbler_cache[cache_pos]),
+                "%s\t%s\t%s\t%d\t%d\t%c\t%ld\n",
+                scrobbler_entry.artist,
+                scrobbler_entry.album,
+                scrobbler_entry.title,
+                scrobbler_entry.tracknum,
+                (int)scrobbler_entry.length/1000,
+                rating,
+                (long)timestamp);
+    } else {
+        ret = snprintf(scrobbler_cache[cache_pos], 
+                sizeof(scrobbler_cache[cache_pos]),
+                "%s\t%s\t%s\t\t\t%d\t%c\t%ld\n",
+                scrobbler_entry.artist,
+                scrobbler_entry.album,
+                scrobbler_entry.title,
+                (int)scrobbler_entry.length/1000,
+                rating,
+                (long)timestamp);
+    }
+    if ( ret > SCROBBLER_CACHE_LEN )
+    {
+        logf("SCROBBLER: entry too long:");
+        logf("SCROBBLER: %s", scrobbler_entry.path);
+        /* only increment if a valid entry */
+        /* cache_pos--; */
+    } else
+        cache_pos++;
+}
+
+void scrobbler_change_event(struct mp3entry *id)
+{
+    /* change add_to_cache(false) when playback function exists */
+    if (pending)
+        add_to_cache(false);
+
+    /*  check if track was resumed > %50 played
+        check for blank artist or track name */
+    if ((id->elapsed > (id->length/2)) ||
+        (id->artist[0] == 0) ||
+        (id->title[0] == 0) )
+    {
+        pending = false; 
+        logf("SCROBBLER: skipping file %s", id->path);
+    }
+    else
+    {
+        copy_mp3entry(&scrobbler_entry, id);
+        timestamp = mktime(get_time());
+        pending = true;
+        /* cache_pos++; */ 
+    }
+}
+
+int scrobbler_init(void)
+{
+#if 0
+    if(!global_settings.scrobber)
+        return -1;
+#endif
+
+    scrobbler_fd = open(SCROBBLER_FILE, O_RDONLY);
+    if(scrobbler_fd < 0)
+    {
+        scrobbler_fd = open(SCROBBLER_FILE, O_RDWR | O_CREAT);
+        if(scrobbler_fd >= 0)
+        {
+            fdprintf(scrobbler_fd, "#AUDIOSCROBBLER/%s\n", SCROBBLER_VERSION);
+            fdprintf(scrobbler_fd, "#TZ/UNKNOWN\n");
+            fdprintf(scrobbler_fd, "#CLIENT/Rockbox %s %s\n", TARGET_NAME, SCROBBLER_REVISION);
+            close(scrobbler_fd);
+        }
+        else
+        {
+            logf("SCROBBLER: cannnot create log file");
+            return -1;
+        }
+    }
+    close(scrobbler_fd);
+
+    audio_set_track_changed_event(&scrobbler_change_event);
+    cache_pos = 0;
+    pending = false;
+
+    return 1;
+}
+
+void scrobbler_shutdown(void)
+{
+    /* add check for global_settings.scrobbler */
+    if (pending)
+        add_to_cache(true);
+    
+    /* check that the last add_to_cache didn't fail */
+    if (cache_pos > 0)
+        write_cache();
+
+    pending = false;
+
+    audio_set_track_changed_event(NULL);
+}
Index: apps/scrobbler.h
===================================================================
RCS file: apps/scrobbler.h
diff -N apps/scrobbler.h
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ apps/scrobbler.h	13 May 2006 00:43:32 -0000
@@ -0,0 +1,10 @@
+#define SCROBBLER_VERSION "1.0"
+#define SCROBBLER_FILE "/.scrobbler.log"
+
+#if defined(CONFIG_RTC) && !defined(SIMULATOR)
+typedef long time_t;
+#endif
+
+void scrobbler_change_event(struct mp3entry *id);
+int scrobbler_init(void);
+void scrobbler_shutdown(void);
Index: apps/tree.c
===================================================================
RCS file: /cvsroot/rockbox/apps/tree.c,v
retrieving revision 1.402
diff -u -r1.402 tree.c
--- apps/tree.c	7 May 2006 06:16:17 -0000	1.402
+++ apps/tree.c	13 May 2006 00:43:32 -0000
@@ -75,6 +75,10 @@
 #include "widgets.h"
 #endif
 
+#ifdef CONFIG_RTC
+#include "scrobbler.h"
+#endif
+
 /* a table for the know file types */
 const struct filetype filetypes[] = {
     { "mp3", TREE_ATTR_MPA, Icon_Audio, VOICE_EXT_MPA },
@@ -1350,6 +1354,9 @@
 /* These two functions are called by the USB and shutdown handlers */
 void tree_flush(void)
 {
+#ifdef CONFIG_RTC
+    scrobbler_shutdown();
+#endif
     tagcache_stop_scan();
     playlist_shutdown();
 
@@ -1393,6 +1400,9 @@
             gui_textarea_clear(&screens[i]);
         }
     }
+#ifdef CONFIG_RTC
+    scrobbler_init();
+#endif
     tagcache_start_scan();
 #endif
 }
Index: firmware/mpeg.c
===================================================================
RCS file: /cvsroot/rockbox/firmware/mpeg.c,v
retrieving revision 1.359
diff -u -r1.359 mpeg.c
--- firmware/mpeg.c	30 Apr 2006 23:18:21 -0000	1.359
+++ firmware/mpeg.c	13 May 2006 00:43:33 -0000
@@ -108,6 +108,9 @@
 static unsigned int current_track_counter = 0;
 static unsigned int last_track_counter = 0;
 
+/* Play time of the previous track */
+unsigned long prev_track_elapsed;
+
 #ifndef SIMULATOR
 static int track_read_idx = 0;
 static int track_write_idx = 0;
@@ -1060,6 +1063,9 @@
 {
     DEBUGF("Track change\n");
 
+    struct trackdata *track = get_trackdata(0);
+    prev_track_elapsed = track->id3.elapsed;
+
 #if (CONFIG_CODEC == MAS3587F) || (CONFIG_CODEC == MAS3539F)
     /* Reset the AVC */
     sound_set_avc(-1);
@@ -1076,6 +1082,11 @@
     current_track_counter++;
 }
 
+unsigned long ret_prev_elapsed(void)
+{
+    return prev_track_elapsed;
+}
+
 #ifdef DEBUG
 void hexdump(const unsigned char *buf, int len)
 {
Index: firmware/export/audio.h
===================================================================
RCS file: /cvsroot/rockbox/firmware/export/audio.h,v
retrieving revision 1.16
diff -u -r1.16 audio.h
--- firmware/export/audio.h	11 May 2006 22:55:24 -0000	1.16
+++ firmware/export/audio.h	13 May 2006 00:43:33 -0000
@@ -102,6 +102,7 @@
 unsigned long audio_num_recorded_bytes(void);
 void audio_set_spdif_power_setting(bool on);
 unsigned long audio_get_spdif_sample_rate(void);
+unsigned long ret_prev_elapsed(void);
 
 
 
