summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLinus Nielsen Feltzing <linus@haxx.se>2006-10-19 09:42:58 +0000
committerLinus Nielsen Feltzing <linus@haxx.se>2006-10-19 09:42:58 +0000
commitda153da0be485aa4b937d58ee209eda7fb342053 (patch)
tree3506b6bba4935fa252250b892d023203e54f3513
parent1645d32aa38bd61806125c8d6b1c44fa3ac0f3ca (diff)
downloadrockbox-da153da0be485aa4b937d58ee209eda7fb342053.tar.gz
rockbox-da153da0be485aa4b937d58ee209eda7fb342053.zip
Patch #5166 by Robert Keevil - Last.fm logging
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@11269 a1c6a512-1295-4272-9138-f99709370657
-rw-r--r--apps/Makefile3
-rw-r--r--apps/SOURCES1
-rw-r--r--apps/lang/english.lang34
-rw-r--r--apps/main.c6
-rw-r--r--apps/misc.c2
-rw-r--r--apps/playback.c10
-rwxr-xr-xapps/scrobbler.c263
-rw-r--r--apps/scrobbler.h24
-rw-r--r--apps/settings.c2
-rw-r--r--apps/settings.h1
-rw-r--r--apps/settings_menu.c23
-rw-r--r--apps/tree.c3
-rw-r--r--firmware/common/timefuncs.c35
-rw-r--r--firmware/export/audio.h1
-rw-r--r--firmware/include/time.h3
-rw-r--r--firmware/include/timefuncs.h1
-rw-r--r--firmware/mpeg.c11
17 files changed, 414 insertions, 9 deletions
diff --git a/apps/Makefile b/apps/Makefile
index 6214ce138b..815e061d3c 100644
--- a/apps/Makefile
+++ b/apps/Makefile
@@ -55,7 +55,8 @@ ifdef APPEXTRA
55endif 55endif
56 56
57CFLAGS = $(INCLUDES) $(GCCOPTS) $(TARGET) $(DEFINES) -DTARGET_ID=$(TARGET_ID) \ 57CFLAGS = $(INCLUDES) $(GCCOPTS) $(TARGET) $(DEFINES) -DTARGET_ID=$(TARGET_ID) \
58 -DAPPSVERSION=\"$(VERSION)\" $(EXTRA_DEFINES) -DMEM=${MEMORYSIZE} 58 -DAPPSVERSION=\"$(VERSION)\" $(EXTRA_DEFINES) -DMEM=${MEMORYSIZE} \
59 -DTARGET_NAME=\"$(ARCHOS)\"
59 60
60OBJS2 := $(OBJDIR)/lang.o $(patsubst %.c, $(OBJDIR)/%.o, $(SRC)) 61OBJS2 := $(OBJDIR)/lang.o $(patsubst %.c, $(OBJDIR)/%.o, $(SRC))
61OBJS = $(patsubst %.S, $(OBJDIR)/%.o, $(OBJS2)) 62OBJS = $(patsubst %.S, $(OBJDIR)/%.o, $(OBJS2))
diff --git a/apps/SOURCES b/apps/SOURCES
index dccb6f95ac..d759e49a6f 100644
--- a/apps/SOURCES
+++ b/apps/SOURCES
@@ -29,6 +29,7 @@ talk.c
29tree.c 29tree.c
30tagtree.c 30tagtree.c
31filetree.c 31filetree.c
32scrobbler.c
32 33
33screen_access.c 34screen_access.c
34gui/buttonbar.c 35gui/buttonbar.c
diff --git a/apps/lang/english.lang b/apps/lang/english.lang
index 8a2341b204..6cd5b2d533 100644
--- a/apps/lang/english.lang
+++ b/apps/lang/english.lang
@@ -3861,13 +3861,13 @@
3861</phrase> 3861</phrase>
3862<phrase> 3862<phrase>
3863 id: LANG_DIRCACHE_REBOOT 3863 id: LANG_DIRCACHE_REBOOT
3864 desc: when activating directory cache 3864 desc: DEPRECATED
3865 user: 3865 user:
3866 <source> 3866 <source>
3867 *: "Please reboot to enable the cache" 3867 *: ""
3868 </source> 3868 </source>
3869 <dest> 3869 <dest>
3870 *: "Please reboot to enable the cache" 3870 *: ""
3871 </dest> 3871 </dest>
3872 <voice> 3872 <voice>
3873 *: "" 3873 *: ""
@@ -9940,3 +9940,31 @@
9940 *: "Random" 9940 *: "Random"
9941 </voice> 9941 </voice>
9942</phrase> 9942</phrase>
9943<phrase>
9944 id: LANG_AUDIOSCROBBLER
9945 desc: "Last.fm Log" in the playback menu
9946 user:
9947 <source>
9948 *: "Last.fm Log"
9949 </source>
9950 <dest>
9951 *: "Last.fm Log"
9952 </dest>
9953 <voice>
9954 *: "Last.fm Log"
9955 </voice>
9956</phrase>
9957<phrase>
9958 id: LANG_PLEASE_REBOOT
9959 desc: when activating an option that requires a reboot
9960 user:
9961 <source>
9962 *: "Please reboot to enable"
9963 </source>
9964 <dest>
9965 *: "Please reboot to enable"
9966 </dest>
9967 <voice>
9968 *: ""
9969 </voice>
9970</phrase>
diff --git a/apps/main.c b/apps/main.c
index bd1dd7b03c..838a4859d2 100644
--- a/apps/main.c
+++ b/apps/main.c
@@ -66,6 +66,7 @@
66#include "string.h" 66#include "string.h"
67#include "splash.h" 67#include "splash.h"
68#include "eeprom_settings.h" 68#include "eeprom_settings.h"
69#include "scrobbler.h"
69 70
70#if (CONFIG_CODEC == SWCODEC) 71#if (CONFIG_CODEC == SWCODEC)
71#include "playback.h" 72#include "playback.h"
@@ -252,6 +253,8 @@ void init(void)
252 audio_preinit(); 253 audio_preinit();
253#endif 254#endif
254 255
256 scrobbler_init();
257
255 /* audio_init must to know the size of voice buffer so init voice first */ 258 /* audio_init must to know the size of voice buffer so init voice first */
256#if CONFIG_CODEC == SWCODEC 259#if CONFIG_CODEC == SWCODEC
257 talk_init(); 260 talk_init();
@@ -455,7 +458,8 @@ void init(void)
455 status_init(); 458 status_init();
456 playlist_init(); 459 playlist_init();
457 tree_init(); 460 tree_init();
458 461 scrobbler_init();
462
459 /* No buffer allocation (see buffer.c) may take place after the call to 463 /* No buffer allocation (see buffer.c) may take place after the call to
460 audio_init() since the mpeg thread takes the rest of the buffer space */ 464 audio_init() since the mpeg thread takes the rest of the buffer space */
461 mp3_init( global_settings.volume, 465 mp3_init( global_settings.volume,
diff --git a/apps/misc.c b/apps/misc.c
index 87fd93513e..4be9e43fd9 100644
--- a/apps/misc.c
+++ b/apps/misc.c
@@ -45,6 +45,7 @@
45#include "font.h" 45#include "font.h"
46#include "splash.h" 46#include "splash.h"
47#include "tagcache.h" 47#include "tagcache.h"
48#include "scrobbler.h"
48#ifdef HAVE_MMC 49#ifdef HAVE_MMC
49#include "ata_mmc.h" 50#include "ata_mmc.h"
50#endif 51#endif
@@ -625,6 +626,7 @@ long default_event_handler_ex(long event, void (*callback)(void *), void *parame
625 if (!mmc_touched() || (mmc_remove_request() == SYS_MMC_EXTRACTED)) 626 if (!mmc_touched() || (mmc_remove_request() == SYS_MMC_EXTRACTED))
626#endif 627#endif
627 { 628 {
629 scrobbler_flush_cache();
628 system_flush(); 630 system_flush();
629 usb_screen(); 631 usb_screen();
630 system_restore(); 632 system_restore();
diff --git a/apps/playback.c b/apps/playback.c
index 486edf80b2..495629e5ea 100644
--- a/apps/playback.c
+++ b/apps/playback.c
@@ -272,6 +272,9 @@ struct thread_entry *codec_thread_p;
272#ifdef PLAYBACK_VOICE 272#ifdef PLAYBACK_VOICE
273extern struct codec_api ci_voice; 273extern struct codec_api ci_voice;
274 274
275/* Play time of the previous track */
276unsigned long prev_track_elapsed;
277
275static volatile bool voice_thread_start; 278static volatile bool voice_thread_start;
276static volatile bool voice_is_playing; 279static volatile bool voice_is_playing;
277static volatile bool voice_codec_loaded; 280static volatile bool voice_codec_loaded;
@@ -1630,6 +1633,8 @@ static bool codec_load_next_track(void)
1630{ 1633{
1631 struct event ev; 1634 struct event ev;
1632 1635
1636 prev_track_elapsed = CUR_TI->id3.elapsed;
1637
1633 if (ci.seek_time) 1638 if (ci.seek_time)
1634 codec_seek_complete_callback(); 1639 codec_seek_complete_callback();
1635 1640
@@ -2912,6 +2917,11 @@ void audio_set_track_changed_event(void (*handler)(struct mp3entry *id3))
2912 track_changed_callback = handler; 2917 track_changed_callback = handler;
2913} 2918}
2914 2919
2920unsigned long audio_prev_elapsed(void)
2921{
2922 return prev_track_elapsed;
2923}
2924
2915static void audio_stop_codec_flush(void) 2925static void audio_stop_codec_flush(void)
2916{ 2926{
2917 ci.stop_codec = true; 2927 ci.stop_codec = true;
diff --git a/apps/scrobbler.c b/apps/scrobbler.c
new file mode 100755
index 0000000000..68c5e9addf
--- /dev/null
+++ b/apps/scrobbler.c
@@ -0,0 +1,263 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2006 Robert Keevil
11 *
12 * All files in this archive are subject to the GNU General Public License.
13 * See the file COPYING in the source tree root for full license agreement.
14 *
15 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
16 * KIND, either express or implied.
17 *
18 ****************************************************************************/
19/*
20Audioscrobbler spec at:
21http://www.audioscrobbler.net/wiki/Portable_Player_Logging
22*/
23
24#include "file.h"
25#include "sprintf.h"
26#include "playback.h"
27#include "logf.h"
28#include "id3.h"
29#include "kernel.h"
30#include "audio.h"
31#include "buffer.h"
32#include "settings.h"
33
34#ifndef SIMULATOR
35#include "ata.h"
36#endif
37
38#ifdef CONFIG_RTC
39#include "time.h"
40#include "timefuncs.h"
41#endif
42
43#include "scrobbler.h"
44
45#define SCROBBLER_VERSION "1.0"
46
47#ifdef CONFIG_RTC
48#define SCROBBLER_FILE "/.scrobbler.log"
49#else
50#define SCROBBLER_FILE "/.scrobbler-timeless.log"
51#endif
52
53/* increment this on any code change that effects output */
54/* replace with CVS Revision keyword? */
55#define SCROBBLER_REVISION "1.0"
56
57#define SCROBBLER_MAX_CACHE 32
58/* longest entry I've had is 323, add a safety margin */
59#define SCROBBLER_CACHE_LEN 512
60
61static char* scrobbler_cache;
62
63static int scrobbler_fd = -1;
64static int cache_pos;
65static struct mp3entry scrobbler_entry;
66static bool pending = false;
67static bool scrobbler_initialised = false;
68#ifdef CONFIG_RTC
69static time_t timestamp;
70#else
71static unsigned long timestamp;
72#endif
73
74/* Crude work-around for Archos Sims - return a set amount */
75#if (CONFIG_CODEC != SWCODEC) && defined(SIMULATOR)
76unsigned long audio_prev_elapsed(void)
77{
78 return 120000;
79}
80#endif
81
82static void write_cache(void)
83{
84 int i;
85
86 /* If the file doesn't exist, create it.
87 Check at each write since file may be deleted at any time */
88 scrobbler_fd = open(SCROBBLER_FILE, O_RDONLY);
89 if(scrobbler_fd < 0)
90 {
91 scrobbler_fd = open(SCROBBLER_FILE, O_RDWR | O_CREAT);
92 if(scrobbler_fd >= 0)
93 {
94 fdprintf(scrobbler_fd, "#AUDIOSCROBBLER/%s\n", SCROBBLER_VERSION);
95 fdprintf(scrobbler_fd, "#TZ/UNKNOWN\n");
96#ifdef CONFIG_RTC
97 fdprintf(scrobbler_fd, "#CLIENT/Rockbox %s %s\n",
98 TARGET_NAME, SCROBBLER_REVISION);
99#else
100 fdprintf(scrobbler_fd, "#CLIENT/Rockbox %s %s Timeless\n",
101 TARGET_NAME, SCROBBLER_REVISION);
102#endif
103 close(scrobbler_fd);
104 }
105 else
106 {
107 logf("SCROBBLER: cannot create log file");
108 }
109 }
110 close(scrobbler_fd);
111 scrobbler_fd = -1;
112
113 /* write the cache entries */
114 scrobbler_fd = open(SCROBBLER_FILE, O_WRONLY | O_APPEND);
115 if(scrobbler_fd >= 0)
116 {
117 logf("SCROBBLER: writing %d entries", cache_pos);
118
119 for ( i=0; i < cache_pos; i++ )
120 {
121 logf("SCROBBLER: write %d", i);
122 fdprintf(scrobbler_fd, "%s", scrobbler_cache+(SCROBBLER_CACHE_LEN*i));
123 }
124 close(scrobbler_fd);
125 }
126 else
127 {
128 logf("SCROBBLER: error writing file");
129 }
130
131 /* clear even if unsuccessful - don't want to overflow the buffer */
132 cache_pos = 0;
133 scrobbler_fd = -1;
134}
135
136static void add_to_cache(void)
137{
138/* using HAVE_MMC to check for Ondios - anything better to use? */
139#ifndef SIMULATOR
140#if defined(IPOD_NANO) || defined(HAVE_MMC)
141 if ( cache_pos >= SCROBBLER_MAX_CACHE )
142#else
143 if ( ( cache_pos >= SCROBBLER_MAX_CACHE ) || ( ata_disk_is_active() ) )
144#endif
145#endif /* !SIMULATOR */
146 write_cache();
147
148 int ret;
149 char rating = 'S'; /* Skipped */
150
151 logf("SCROBBLER: add_to_cache[%d]", cache_pos);
152
153 if ( audio_prev_elapsed() >
154 (scrobbler_entry.length/2) )
155 rating = 'L'; /* Listened */
156
157 if (scrobbler_entry.tracknum > 0)
158 {
159 ret = snprintf(scrobbler_cache+(SCROBBLER_CACHE_LEN*cache_pos),
160 SCROBBLER_CACHE_LEN,
161 "%s\t%s\t%s\t%d\t%d\t%c\t%ld\n",
162 scrobbler_entry.artist,
163 scrobbler_entry.album?scrobbler_entry.album:"",
164 scrobbler_entry.title,
165 scrobbler_entry.tracknum,
166 (int)scrobbler_entry.length/1000,
167 rating,
168 (long)timestamp);
169 } else {
170 ret = snprintf(scrobbler_cache+(SCROBBLER_CACHE_LEN*cache_pos),
171 SCROBBLER_CACHE_LEN,
172 "%s\t%s\t%s\t\t%d\t%c\t%ld\n",
173 scrobbler_entry.artist,
174 scrobbler_entry.album?scrobbler_entry.album:"",
175 scrobbler_entry.title,
176 (int)scrobbler_entry.length/1000,
177 rating,
178 (long)timestamp);
179 }
180
181 if ( ret >= SCROBBLER_CACHE_LEN )
182 {
183 logf("SCROBBLER: entry too long:");
184 logf("SCROBBLER: %s", scrobbler_entry.path);
185 } else
186 cache_pos++;
187}
188
189void scrobbler_change_event(struct mp3entry *id)
190{
191 /* add entry using the previous scrobbler_entry and timestamp */
192 if (pending)
193 add_to_cache();
194
195 /* check if track was resumed > %50 played
196 check for blank artist or track name */
197 if ((id->elapsed > (id->length/2)) ||
198 (!id->artist ) || (!id->title ) )
199 {
200 pending = false;
201 logf("SCROBBLER: skipping file %s", id->path);
202 }
203 else
204 {
205 logf("SCROBBLER: add pending");
206 copy_mp3entry(&scrobbler_entry, id);
207#ifdef CONFIG_RTC
208 timestamp = mktime(get_time());
209#else
210 timestamp = 0;
211#endif
212 pending = true;
213 }
214}
215
216int scrobbler_init(void)
217{
218 logf("SCROBBLER: init %d", global_settings.audioscrobbler);
219
220 if(!global_settings.audioscrobbler)
221 return -1;
222
223 scrobbler_cache = buffer_alloc(SCROBBLER_MAX_CACHE*SCROBBLER_CACHE_LEN);
224
225 audio_set_track_changed_event(&scrobbler_change_event);
226 cache_pos = 0;
227 pending = false;
228 scrobbler_initialised = true;
229
230 return 1;
231}
232
233void scrobbler_flush_cache(void)
234{
235 if (scrobbler_initialised)
236 {
237 /* Add any pending entries to the cache */
238 if(pending)
239 add_to_cache();
240
241 /* Write the cache to disk if needed */
242 if (cache_pos)
243 write_cache();
244
245 pending = false;
246 }
247}
248
249void scrobbler_shutdown(void)
250{
251 scrobbler_flush_cache();
252
253 if (scrobbler_initialised)
254 {
255 audio_set_track_changed_event(NULL);
256 scrobbler_initialised = false;
257 }
258}
259
260bool scrobbler_is_enabled(void)
261{
262 return scrobbler_initialised;
263}
diff --git a/apps/scrobbler.h b/apps/scrobbler.h
new file mode 100644
index 0000000000..543a30ed13
--- /dev/null
+++ b/apps/scrobbler.h
@@ -0,0 +1,24 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2006 Robert Keevil
11 *
12 * All files in this archive are subject to the GNU General Public License.
13 * See the file COPYING in the source tree root for full license agreement.
14 *
15 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
16 * KIND, either express or implied.
17 *
18 ****************************************************************************/
19
20void scrobbler_change_event(struct mp3entry *id);
21int scrobbler_init(void);
22void scrobbler_flush_cache(void);
23void scrobbler_shutdown(void);
24bool scrobbler_is_enabled(void);
diff --git a/apps/settings.c b/apps/settings.c
index 196c432c8f..97945682df 100644
--- a/apps/settings.c
+++ b/apps/settings.c
@@ -665,6 +665,8 @@ static const struct bit_entry hd_bits[] =
665 {2, S_O(fm_region), 0, "fm_region", "eu,us,jp,kr" }, 665 {2, S_O(fm_region), 0, "fm_region", "eu,us,jp,kr" },
666#endif 666#endif
667 667
668 {1, S_O(audioscrobbler), false, "Last.fm Logging", off_on},
669
668 /* If values are just added to the end, no need to bump the version. */ 670 /* If values are just added to the end, no need to bump the version. */
669 /* new stuff to be added at the end */ 671 /* new stuff to be added at the end */
670 672
diff --git a/apps/settings.h b/apps/settings.h
index 5313fe37b1..5fc8c7e5f5 100644
--- a/apps/settings.h
+++ b/apps/settings.h
@@ -498,6 +498,7 @@ struct user_settings
498#ifdef CONFIG_TUNER 498#ifdef CONFIG_TUNER
499 int fm_region; 499 int fm_region;
500#endif 500#endif
501 bool audioscrobbler; /* Audioscrobbler logging */
501 502
502}; 503};
503 504
diff --git a/apps/settings_menu.c b/apps/settings_menu.c
index eabe153810..0193aa0102 100644
--- a/apps/settings_menu.c
+++ b/apps/settings_menu.c
@@ -56,6 +56,7 @@
56#include "yesno.h" 56#include "yesno.h"
57#include "list.h" 57#include "list.h"
58#include "color_picker.h" 58#include "color_picker.h"
59#include "scrobbler.h"
59 60
60#ifdef HAVE_LCD_BITMAP 61#ifdef HAVE_LCD_BITMAP
61#include "peakmeter.h" 62#include "peakmeter.h"
@@ -1387,6 +1388,23 @@ static bool next_folder(void)
1387 INT, names, 3, NULL ); 1388 INT, names, 3, NULL );
1388} 1389}
1389 1390
1391static bool audioscrobbler(void)
1392{
1393 bool result = set_bool_options(str(LANG_AUDIOSCROBBLER),
1394 &global_settings.audioscrobbler,
1395 STR(LANG_ON),
1396 STR(LANG_OFF),
1397 NULL);
1398
1399 if (!scrobbler_is_enabled() && global_settings.audioscrobbler)
1400 gui_syncsplash(HZ*2, true, str(LANG_PLEASE_REBOOT));
1401
1402 if(!result)
1403 scrobbler_shutdown();
1404
1405 return result;
1406}
1407
1390static bool codepage_setting(void) 1408static bool codepage_setting(void)
1391{ 1409{
1392 static const struct opt_items names[] = { 1410 static const struct opt_items names[] = {
@@ -1605,7 +1623,7 @@ static bool dircache(void)
1605 NULL); 1623 NULL);
1606 1624
1607 if (!dircache_is_enabled() && global_settings.dircache) 1625 if (!dircache_is_enabled() && global_settings.dircache)
1608 gui_syncsplash(HZ*2, true, str(LANG_DIRCACHE_REBOOT)); 1626 gui_syncsplash(HZ*2, true, str(LANG_PLEASE_REBOOT));
1609 1627
1610 if (!result) 1628 if (!result)
1611 dircache_disable(); 1629 dircache_disable();
@@ -1747,8 +1765,9 @@ static bool playback_settings_menu(void)
1747 { ID2P(LANG_ID3_ORDER), id3_order }, 1765 { ID2P(LANG_ID3_ORDER), id3_order },
1748 { ID2P(LANG_NEXT_FOLDER), next_folder }, 1766 { ID2P(LANG_NEXT_FOLDER), next_folder },
1749#ifdef HAVE_HEADPHONE_DETECTION 1767#ifdef HAVE_HEADPHONE_DETECTION
1750 { ID2P(LANG_UNPLUG), unplug_menu } 1768 { ID2P(LANG_UNPLUG), unplug_menu },
1751#endif 1769#endif
1770 { ID2P(LANG_AUDIOSCROBBLER), audioscrobbler}
1752 }; 1771 };
1753 1772
1754 bool old_shuffle = global_settings.playlist_shuffle; 1773 bool old_shuffle = global_settings.playlist_shuffle;
diff --git a/apps/tree.c b/apps/tree.c
index 70b83f8934..bfb6412bfe 100644
--- a/apps/tree.c
+++ b/apps/tree.c
@@ -65,6 +65,7 @@
65#include "yesno.h" 65#include "yesno.h"
66#include "gwps-common.h" 66#include "gwps-common.h"
67#include "eeprom_settings.h" 67#include "eeprom_settings.h"
68#include "scrobbler.h"
68 69
69/* gui api */ 70/* gui api */
70#include "list.h" 71#include "list.h"
@@ -1378,6 +1379,7 @@ void ft_play_filename(char *dir, char *file)
1378/* These two functions are called by the USB and shutdown handlers */ 1379/* These two functions are called by the USB and shutdown handlers */
1379void tree_flush(void) 1380void tree_flush(void)
1380{ 1381{
1382 scrobbler_shutdown();
1381 tagcache_shutdown(); 1383 tagcache_shutdown();
1382 playlist_shutdown(); 1384 playlist_shutdown();
1383 1385
@@ -1439,4 +1441,5 @@ void tree_restore(void)
1439 } 1441 }
1440#endif 1442#endif
1441 tagcache_start_scan(); 1443 tagcache_start_scan();
1444 scrobbler_init();
1442} 1445}
diff --git a/firmware/common/timefuncs.c b/firmware/common/timefuncs.c
index fb16f0c253..e48aadd0a2 100644
--- a/firmware/common/timefuncs.c
+++ b/firmware/common/timefuncs.c
@@ -130,3 +130,38 @@ int set_time(const struct tm *tm)
130 return 0; 130 return 0;
131#endif 131#endif
132} 132}
133
134/* mktime() code taken from lynx-2.8.5 source, written
135 by Philippe De Muyter <phdm@macqel.be> */
136time_t mktime(struct tm *t)
137{
138 short month, year;
139 time_t result;
140 static int m_to_d[12] =
141 {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334};
142
143 month = t->tm_mon;
144 year = t->tm_year + month / 12 + 1900;
145 month %= 12;
146 if (month < 0)
147 {
148 year -= 1;
149 month += 12;
150 }
151 result = (year - 1970) * 365 + (year - 1969) / 4 + m_to_d[month];
152 result = (year - 1970) * 365 + m_to_d[month];
153 if (month <= 1)
154 year -= 1;
155 result += (year - 1968) / 4;
156 result -= (year - 1900) / 100;
157 result += (year - 1600) / 400;
158 result += t->tm_mday;
159 result -= 1;
160 result *= 24;
161 result += t->tm_hour;
162 result *= 60;
163 result += t->tm_min;
164 result *= 60;
165 result += t->tm_sec;
166 return(result);
167}
diff --git a/firmware/export/audio.h b/firmware/export/audio.h
index 58bc1e157d..9099cb3765 100644
--- a/firmware/export/audio.h
+++ b/firmware/export/audio.h
@@ -141,6 +141,7 @@ void audio_set_spdif_power_setting(bool on);
141#endif 141#endif
142#endif 142#endif
143unsigned long audio_get_spdif_sample_rate(void); 143unsigned long audio_get_spdif_sample_rate(void);
144unsigned long audio_prev_elapsed(void);
144#if CONFIG_CODEC == SWCODEC 145#if CONFIG_CODEC == SWCODEC
145/* audio encoder functions (defined in playback.c) */ 146/* audio encoder functions (defined in playback.c) */
146int audio_get_encoder_id(void); 147int audio_get_encoder_id(void);
diff --git a/firmware/include/time.h b/firmware/include/time.h
index cddec1e708..23f72fd93d 100644
--- a/firmware/include/time.h
+++ b/firmware/include/time.h
@@ -20,8 +20,7 @@ struct tm
20 int tm_isdst; 20 int tm_isdst;
21}; 21};
22 22
23#if defined(SIMULATOR) && !defined(_TIME_T_DEFINED) && !defined(_TIME_T_DECLARED) 23#if !defined(_TIME_T_DEFINED) && !defined(_TIME_T_DECLARED)
24/* for non-win32 simulators */
25typedef long time_t; 24typedef long time_t;
26 25
27/* this define below is used by the mingw headers to prevent duplicate 26/* this define below is used by the mingw headers to prevent duplicate
diff --git a/firmware/include/timefuncs.h b/firmware/include/timefuncs.h
index 03bb06fb9c..d2f42eb9ff 100644
--- a/firmware/include/timefuncs.h
+++ b/firmware/include/timefuncs.h
@@ -27,5 +27,6 @@
27struct tm *get_time(void); 27struct tm *get_time(void);
28int set_time(const struct tm *tm); 28int set_time(const struct tm *tm);
29bool valid_time(const struct tm *tm); 29bool valid_time(const struct tm *tm);
30time_t mktime(struct tm *t);
30 31
31#endif /* _TIMEFUNCS_H_ */ 32#endif /* _TIMEFUNCS_H_ */
diff --git a/firmware/mpeg.c b/firmware/mpeg.c
index 61b0a22d87..9fe3df224c 100644
--- a/firmware/mpeg.c
+++ b/firmware/mpeg.c
@@ -108,6 +108,9 @@ static struct trackdata trackdata[MAX_TRACK_ENTRIES];
108static unsigned int current_track_counter = 0; 108static unsigned int current_track_counter = 0;
109static unsigned int last_track_counter = 0; 109static unsigned int last_track_counter = 0;
110 110
111/* Play time of the previous track */
112unsigned long prev_track_elapsed;
113
111#ifndef SIMULATOR 114#ifndef SIMULATOR
112static int track_read_idx = 0; 115static int track_read_idx = 0;
113static int track_write_idx = 0; 116static int track_write_idx = 0;
@@ -1056,6 +1059,9 @@ static void track_change(void)
1056{ 1059{
1057 DEBUGF("Track change\n"); 1060 DEBUGF("Track change\n");
1058 1061
1062 struct trackdata *track = get_trackdata(0);
1063 prev_track_elapsed = track->id3.elapsed;
1064
1059#if (CONFIG_CODEC == MAS3587F) || (CONFIG_CODEC == MAS3539F) 1065#if (CONFIG_CODEC == MAS3587F) || (CONFIG_CODEC == MAS3539F)
1060 /* Reset the AVC */ 1066 /* Reset the AVC */
1061 sound_set_avc(-1); 1067 sound_set_avc(-1);
@@ -1072,6 +1078,11 @@ static void track_change(void)
1072 current_track_counter++; 1078 current_track_counter++;
1073} 1079}
1074 1080
1081unsigned long audio_prev_elapsed(void)
1082{
1083 return prev_track_elapsed;
1084}
1085
1075#ifdef DEBUG 1086#ifdef DEBUG
1076void hexdump(const unsigned char *buf, int len) 1087void hexdump(const unsigned char *buf, int len)
1077{ 1088{