diff options
-rw-r--r-- | apps/Makefile | 3 | ||||
-rw-r--r-- | apps/SOURCES | 1 | ||||
-rw-r--r-- | apps/lang/english.lang | 34 | ||||
-rw-r--r-- | apps/main.c | 6 | ||||
-rw-r--r-- | apps/misc.c | 2 | ||||
-rw-r--r-- | apps/playback.c | 10 | ||||
-rwxr-xr-x | apps/scrobbler.c | 263 | ||||
-rw-r--r-- | apps/scrobbler.h | 24 | ||||
-rw-r--r-- | apps/settings.c | 2 | ||||
-rw-r--r-- | apps/settings.h | 1 | ||||
-rw-r--r-- | apps/settings_menu.c | 23 | ||||
-rw-r--r-- | apps/tree.c | 3 | ||||
-rw-r--r-- | firmware/common/timefuncs.c | 35 | ||||
-rw-r--r-- | firmware/export/audio.h | 1 | ||||
-rw-r--r-- | firmware/include/time.h | 3 | ||||
-rw-r--r-- | firmware/include/timefuncs.h | 1 | ||||
-rw-r--r-- | firmware/mpeg.c | 11 |
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 | |||
55 | endif | 55 | endif |
56 | 56 | ||
57 | CFLAGS = $(INCLUDES) $(GCCOPTS) $(TARGET) $(DEFINES) -DTARGET_ID=$(TARGET_ID) \ | 57 | CFLAGS = $(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 | ||
60 | OBJS2 := $(OBJDIR)/lang.o $(patsubst %.c, $(OBJDIR)/%.o, $(SRC)) | 61 | OBJS2 := $(OBJDIR)/lang.o $(patsubst %.c, $(OBJDIR)/%.o, $(SRC)) |
61 | OBJS = $(patsubst %.S, $(OBJDIR)/%.o, $(OBJS2)) | 62 | OBJS = $(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 | |||
29 | tree.c | 29 | tree.c |
30 | tagtree.c | 30 | tagtree.c |
31 | filetree.c | 31 | filetree.c |
32 | scrobbler.c | ||
32 | 33 | ||
33 | screen_access.c | 34 | screen_access.c |
34 | gui/buttonbar.c | 35 | gui/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 |
273 | extern struct codec_api ci_voice; | 273 | extern struct codec_api ci_voice; |
274 | 274 | ||
275 | /* Play time of the previous track */ | ||
276 | unsigned long prev_track_elapsed; | ||
277 | |||
275 | static volatile bool voice_thread_start; | 278 | static volatile bool voice_thread_start; |
276 | static volatile bool voice_is_playing; | 279 | static volatile bool voice_is_playing; |
277 | static volatile bool voice_codec_loaded; | 280 | static 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 | ||
2920 | unsigned long audio_prev_elapsed(void) | ||
2921 | { | ||
2922 | return prev_track_elapsed; | ||
2923 | } | ||
2924 | |||
2915 | static void audio_stop_codec_flush(void) | 2925 | static 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 | /* | ||
20 | Audioscrobbler spec at: | ||
21 | http://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 | |||
61 | static char* scrobbler_cache; | ||
62 | |||
63 | static int scrobbler_fd = -1; | ||
64 | static int cache_pos; | ||
65 | static struct mp3entry scrobbler_entry; | ||
66 | static bool pending = false; | ||
67 | static bool scrobbler_initialised = false; | ||
68 | #ifdef CONFIG_RTC | ||
69 | static time_t timestamp; | ||
70 | #else | ||
71 | static unsigned long timestamp; | ||
72 | #endif | ||
73 | |||
74 | /* Crude work-around for Archos Sims - return a set amount */ | ||
75 | #if (CONFIG_CODEC != SWCODEC) && defined(SIMULATOR) | ||
76 | unsigned long audio_prev_elapsed(void) | ||
77 | { | ||
78 | return 120000; | ||
79 | } | ||
80 | #endif | ||
81 | |||
82 | static 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 | |||
136 | static 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 | |||
189 | void 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 | |||
216 | int 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 | |||
233 | void 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 | |||
249 | void 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 | |||
260 | bool 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 | |||
20 | void scrobbler_change_event(struct mp3entry *id); | ||
21 | int scrobbler_init(void); | ||
22 | void scrobbler_flush_cache(void); | ||
23 | void scrobbler_shutdown(void); | ||
24 | bool 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 | ||
1391 | static 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 | |||
1390 | static bool codepage_setting(void) | 1408 | static 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 */ |
1379 | void tree_flush(void) | 1380 | void 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> */ | ||
136 | time_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 |
143 | unsigned long audio_get_spdif_sample_rate(void); | 143 | unsigned long audio_get_spdif_sample_rate(void); |
144 | unsigned 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) */ |
146 | int audio_get_encoder_id(void); | 147 | int 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 */ | ||
25 | typedef long time_t; | 24 | typedef 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 @@ | |||
27 | struct tm *get_time(void); | 27 | struct tm *get_time(void); |
28 | int set_time(const struct tm *tm); | 28 | int set_time(const struct tm *tm); |
29 | bool valid_time(const struct tm *tm); | 29 | bool valid_time(const struct tm *tm); |
30 | time_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]; | |||
108 | static unsigned int current_track_counter = 0; | 108 | static unsigned int current_track_counter = 0; |
109 | static unsigned int last_track_counter = 0; | 109 | static unsigned int last_track_counter = 0; |
110 | 110 | ||
111 | /* Play time of the previous track */ | ||
112 | unsigned long prev_track_elapsed; | ||
113 | |||
111 | #ifndef SIMULATOR | 114 | #ifndef SIMULATOR |
112 | static int track_read_idx = 0; | 115 | static int track_read_idx = 0; |
113 | static int track_write_idx = 0; | 116 | static 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 | ||
1081 | unsigned long audio_prev_elapsed(void) | ||
1082 | { | ||
1083 | return prev_track_elapsed; | ||
1084 | } | ||
1085 | |||
1075 | #ifdef DEBUG | 1086 | #ifdef DEBUG |
1076 | void hexdump(const unsigned char *buf, int len) | 1087 | void hexdump(const unsigned char *buf, int len) |
1077 | { | 1088 | { |