diff options
Diffstat (limited to 'apps')
-rw-r--r-- | apps/applimits.h | 1 | ||||
-rw-r--r-- | apps/lang/english.lang | 90 | ||||
-rw-r--r-- | apps/main_menu.c | 3 | ||||
-rw-r--r-- | apps/onplay.c | 116 | ||||
-rw-r--r-- | apps/onplay.h | 6 | ||||
-rw-r--r-- | apps/playlist.c | 2039 | ||||
-rw-r--r-- | apps/playlist.h | 69 | ||||
-rw-r--r-- | apps/playlist_menu.c | 71 | ||||
-rw-r--r-- | apps/playlist_menu.h | 24 | ||||
-rw-r--r-- | apps/screens.c | 4 | ||||
-rw-r--r-- | apps/settings.c | 51 | ||||
-rw-r--r-- | apps/settings.h | 10 | ||||
-rw-r--r-- | apps/settings_menu.c | 4 | ||||
-rw-r--r-- | apps/tree.c | 477 | ||||
-rw-r--r-- | apps/tree.h | 11 | ||||
-rw-r--r-- | apps/wps-display.c | 7 | ||||
-rw-r--r-- | apps/wps.c | 13 |
17 files changed, 2138 insertions, 858 deletions
diff --git a/apps/applimits.h b/apps/applimits.h index 6372dac924..a18544e87d 100644 --- a/apps/applimits.h +++ b/apps/applimits.h | |||
@@ -23,6 +23,5 @@ | |||
23 | #define AVERAGE_FILENAME_LENGTH 40 | 23 | #define AVERAGE_FILENAME_LENGTH 40 |
24 | #define MAX_DIR_LEVELS 10 | 24 | #define MAX_DIR_LEVELS 10 |
25 | #define MAX_PLAYLIST_SIZE 10000 | 25 | #define MAX_PLAYLIST_SIZE 10000 |
26 | #define MAX_QUEUED_FILES 100 | ||
27 | 26 | ||
28 | #endif | 27 | #endif |
diff --git a/apps/lang/english.lang b/apps/lang/english.lang index 57bec0c656..dab8fa6bd4 100644 --- a/apps/lang/english.lang +++ b/apps/lang/english.lang | |||
@@ -1632,3 +1632,93 @@ id: LANG_STAR | |||
1632 | desc: in the games menu | 1632 | desc: in the games menu |
1633 | eng: "Star" | 1633 | eng: "Star" |
1634 | new: | 1634 | new: |
1635 | |||
1636 | id: LANG_QUEUE_LAST | ||
1637 | desc: in onplay menu. queue a track/playlist at end of playlist. | ||
1638 | eng: "Queue last" | ||
1639 | new: | ||
1640 | |||
1641 | id: LANG_INSERT | ||
1642 | desc: in onplay menu. insert a track/playlist into dynamic playlist. | ||
1643 | eng: "Insert" | ||
1644 | new: | ||
1645 | |||
1646 | id: LANG_INSERT_LAST | ||
1647 | desc: in onplay menu. append a track/playlist into dynamic playlist. | ||
1648 | eng: "Insert last" | ||
1649 | new: | ||
1650 | |||
1651 | id: LANG_QUEUE_FIRST | ||
1652 | desc: in onplay menu. queue a track/playlist into dynamic playlist. | ||
1653 | eng: "Queue first" | ||
1654 | new: | ||
1655 | |||
1656 | id: LANG_INSERT_FIRST | ||
1657 | desc: in onplay menu. insert a track/playlist into dynamic playlist. | ||
1658 | eng: "Insert first" | ||
1659 | new: | ||
1660 | |||
1661 | id: LANG_SAVE_DYNAMIC_PLAYLIST | ||
1662 | desc: in playlist menu. | ||
1663 | eng: "Save Dynamic Playlist" | ||
1664 | new: | ||
1665 | |||
1666 | id: LANG_PLAYLIST_MENU | ||
1667 | desc: in main menu. | ||
1668 | eng: "Playlist Options" | ||
1669 | new: | ||
1670 | |||
1671 | id: LANG_PLAYLIST_INSERT_COUNT | ||
1672 | desc: splash number of tracks inserted | ||
1673 | eng: "Inserted %d tracks (%s)" | ||
1674 | new: | ||
1675 | |||
1676 | id: LANG_PLAYLIST_QUEUE_COUNT | ||
1677 | desc: splash number of tracks queued | ||
1678 | eng: "Queued %d tracks (%s)" | ||
1679 | new: | ||
1680 | |||
1681 | id: LANG_PLAYLIST_SAVE_COUNT | ||
1682 | desc: splash number of tracks saved | ||
1683 | eng: "Saved %d tracks (%s)" | ||
1684 | new: | ||
1685 | |||
1686 | id: LANG_OFF_ABORT | ||
1687 | desc: Used on recorder models | ||
1688 | eng: "OFF to abort" | ||
1689 | new: | ||
1690 | |||
1691 | id: LANG_STOP_ABORT | ||
1692 | desc: Used on player models | ||
1693 | eng: "STOP to abort" | ||
1694 | new: | ||
1695 | |||
1696 | id: LANG_PLAYLIST_CONTROL_UPDATE_ERROR | ||
1697 | desc: Playlist error | ||
1698 | eng: "Error updating playlist control file" | ||
1699 | new: | ||
1700 | |||
1701 | id: LANG_PLAYLIST_ACCESS_ERROR | ||
1702 | desc: Playlist error | ||
1703 | eng: "Error accessing playlist file" | ||
1704 | new: | ||
1705 | |||
1706 | id: LANG_PLAYLIST_CONTROL_ACCESS_ERROR | ||
1707 | desc: Playlist error | ||
1708 | eng: "Error accessing playlist control file" | ||
1709 | new: | ||
1710 | |||
1711 | id: LANG_PLAYLIST_DIRECTORY_ACCESS_ERROR | ||
1712 | desc: Playlist error | ||
1713 | eng: "Error accessing directory" | ||
1714 | new: | ||
1715 | |||
1716 | id: LANG_PLAYLIST_CONTROL_INVALID | ||
1717 | desc: Playlist resume error | ||
1718 | eng: "Playlist control file is invalid" | ||
1719 | new: | ||
1720 | |||
1721 | id: LANG_RECURSE_DIRECTORY | ||
1722 | desc: In playlist menu | ||
1723 | eng: "Recursively Insert Directories" | ||
1724 | new: | ||
diff --git a/apps/main_menu.c b/apps/main_menu.c index b4d952ea5a..e356603653 100644 --- a/apps/main_menu.c +++ b/apps/main_menu.c | |||
@@ -43,6 +43,7 @@ | |||
43 | #include "wps.h" | 43 | #include "wps.h" |
44 | #include "buffer.h" | 44 | #include "buffer.h" |
45 | #include "screens.h" | 45 | #include "screens.h" |
46 | #include "playlist_menu.h" | ||
46 | #ifdef HAVE_FMRADIO | 47 | #ifdef HAVE_FMRADIO |
47 | #include "radio.h" | 48 | #include "radio.h" |
48 | #endif | 49 | #endif |
@@ -266,7 +267,7 @@ bool main_menu(void) | |||
266 | { str(LANG_RECORDING), recording_screen }, | 267 | { str(LANG_RECORDING), recording_screen }, |
267 | { str(LANG_RECORDING_SETTINGS), recording_menu }, | 268 | { str(LANG_RECORDING_SETTINGS), recording_menu }, |
268 | #endif | 269 | #endif |
269 | { str(LANG_CREATE_PLAYLIST), create_playlist }, | 270 | { str(LANG_PLAYLIST_MENU), playlist_menu }, |
270 | { str(LANG_MENU_SHOW_ID3_INFO), browse_id3 }, | 271 | { str(LANG_MENU_SHOW_ID3_INFO), browse_id3 }, |
271 | { str(LANG_SLEEP_TIMER), sleeptimer_screen }, | 272 | { str(LANG_SLEEP_TIMER), sleeptimer_screen }, |
272 | #ifdef HAVE_ALARM_MOD | 273 | #ifdef HAVE_ALARM_MOD |
diff --git a/apps/onplay.c b/apps/onplay.c index 3e0f5a365e..6c538f8d8a 100644 --- a/apps/onplay.c +++ b/apps/onplay.c | |||
@@ -38,13 +38,103 @@ | |||
38 | #include "screens.h" | 38 | #include "screens.h" |
39 | #include "tree.h" | 39 | #include "tree.h" |
40 | #include "buffer.h" | 40 | #include "buffer.h" |
41 | #include "settings.h" | ||
42 | #include "status.h" | ||
43 | #include "onplay.h" | ||
41 | 44 | ||
42 | static char* selected_file = NULL; | 45 | static char* selected_file = NULL; |
43 | static bool reload_dir = false; | 46 | static int selected_file_attr = 0; |
47 | static int onplay_result = ONPLAY_OK; | ||
44 | 48 | ||
45 | static bool queue_file(void) | 49 | /* For playlist options */ |
50 | struct playlist_args { | ||
51 | int position; | ||
52 | bool queue; | ||
53 | }; | ||
54 | |||
55 | static bool add_to_playlist(int position, bool queue) | ||
46 | { | 56 | { |
47 | queue_add(selected_file); | 57 | bool new_playlist = !(mpeg_status() & MPEG_STATUS_PLAY); |
58 | |||
59 | if (new_playlist) | ||
60 | playlist_create(NULL, NULL); | ||
61 | |||
62 | if (selected_file_attr & TREE_ATTR_MPA) | ||
63 | playlist_insert_track(selected_file, position, queue); | ||
64 | else if (selected_file_attr & ATTR_DIRECTORY) | ||
65 | playlist_insert_directory(selected_file, position, queue); | ||
66 | else if (selected_file_attr & TREE_ATTR_M3U) | ||
67 | playlist_insert_playlist(selected_file, position, queue); | ||
68 | |||
69 | if (new_playlist && (playlist_amount() > 0)) | ||
70 | { | ||
71 | /* nothing is currently playing so begin playing what we just | ||
72 | inserted */ | ||
73 | if (global_settings.playlist_shuffle) | ||
74 | playlist_shuffle(current_tick, -1); | ||
75 | playlist_start(0,0); | ||
76 | status_set_playmode(STATUS_PLAY); | ||
77 | status_draw(false); | ||
78 | onplay_result = ONPLAY_START_PLAY; | ||
79 | } | ||
80 | |||
81 | return false; | ||
82 | } | ||
83 | |||
84 | /* Sub-menu for playlist options */ | ||
85 | static bool playlist_options(void) | ||
86 | { | ||
87 | struct menu_items menu[6]; | ||
88 | struct playlist_args args[6]; /* increase these 2 if you add entries! */ | ||
89 | int m, i=0, result; | ||
90 | |||
91 | if (mpeg_status() & MPEG_STATUS_PLAY) | ||
92 | { | ||
93 | menu[i].desc = str(LANG_INSERT); | ||
94 | args[i].position = PLAYLIST_INSERT; | ||
95 | args[i].queue = false; | ||
96 | i++; | ||
97 | |||
98 | menu[i].desc = str(LANG_INSERT_FIRST); | ||
99 | args[i].position = PLAYLIST_INSERT_FIRST; | ||
100 | args[i].queue = false; | ||
101 | i++; | ||
102 | |||
103 | menu[i].desc = str(LANG_INSERT_LAST); | ||
104 | args[i].position = PLAYLIST_INSERT_LAST; | ||
105 | args[i].queue = false; | ||
106 | i++; | ||
107 | |||
108 | menu[i].desc = str(LANG_QUEUE); | ||
109 | args[i].position = PLAYLIST_INSERT; | ||
110 | args[i].queue = true; | ||
111 | i++; | ||
112 | |||
113 | menu[i].desc = str(LANG_QUEUE_FIRST); | ||
114 | args[i].position = PLAYLIST_INSERT_FIRST; | ||
115 | args[i].queue = true; | ||
116 | i++; | ||
117 | |||
118 | menu[i].desc = str(LANG_QUEUE_LAST); | ||
119 | args[i].position = PLAYLIST_INSERT_LAST; | ||
120 | args[i].queue = true; | ||
121 | i++; | ||
122 | } | ||
123 | else if ((selected_file_attr & TREE_ATTR_MPA) || | ||
124 | (selected_file_attr & ATTR_DIRECTORY)) | ||
125 | { | ||
126 | menu[i].desc = str(LANG_INSERT); | ||
127 | args[i].position = PLAYLIST_INSERT; | ||
128 | args[i].queue = false; | ||
129 | i++; | ||
130 | } | ||
131 | |||
132 | m = menu_init( menu, i ); | ||
133 | result = menu_show(m); | ||
134 | if (result >= 0) | ||
135 | add_to_playlist(args[result].position, args[result].queue); | ||
136 | menu_exit(m); | ||
137 | |||
48 | return false; | 138 | return false; |
49 | } | 139 | } |
50 | 140 | ||
@@ -68,7 +158,7 @@ static bool delete_file(void) | |||
68 | switch (btn) { | 158 | switch (btn) { |
69 | case BUTTON_PLAY: | 159 | case BUTTON_PLAY: |
70 | if (!remove(selected_file)) { | 160 | if (!remove(selected_file)) { |
71 | reload_dir = true; | 161 | onplay_result = ONPLAY_RELOAD_DIR; |
72 | lcd_clear_display(); | 162 | lcd_clear_display(); |
73 | lcd_puts(0,0,str(LANG_DELETED)); | 163 | lcd_puts(0,0,str(LANG_DELETED)); |
74 | lcd_puts_scroll(0,1,selected_file); | 164 | lcd_puts_scroll(0,1,selected_file); |
@@ -104,7 +194,7 @@ static bool rename_file(void) | |||
104 | sleep(HZ*2); | 194 | sleep(HZ*2); |
105 | } | 195 | } |
106 | else | 196 | else |
107 | reload_dir = true; | 197 | onplay_result = ONPLAY_RELOAD_DIR; |
108 | } | 198 | } |
109 | 199 | ||
110 | return false; | 200 | return false; |
@@ -225,7 +315,7 @@ static bool vbr_fix(void) | |||
225 | 315 | ||
226 | if(mpeg_status()) { | 316 | if(mpeg_status()) { |
227 | splash(HZ*2, 0, true, str(LANG_VBRFIX_STOP_PLAY)); | 317 | splash(HZ*2, 0, true, str(LANG_VBRFIX_STOP_PLAY)); |
228 | return reload_dir; | 318 | return onplay_result; |
229 | } | 319 | } |
230 | 320 | ||
231 | lcd_clear_display(); | 321 | lcd_clear_display(); |
@@ -356,12 +446,16 @@ int onplay(char* file, int attr) | |||
356 | struct menu_items menu[5]; /* increase this if you add entries! */ | 446 | struct menu_items menu[5]; /* increase this if you add entries! */ |
357 | int m, i=0, result; | 447 | int m, i=0, result; |
358 | 448 | ||
449 | onplay_result = ONPLAY_OK; | ||
450 | |||
359 | selected_file = file; | 451 | selected_file = file; |
360 | 452 | selected_file_attr = attr; | |
361 | if ((mpeg_status() & MPEG_STATUS_PLAY) && (attr & TREE_ATTR_MPA)) | 453 | |
454 | if ((attr & TREE_ATTR_MPA) || (attr & ATTR_DIRECTORY) || | ||
455 | (attr & TREE_ATTR_M3U)) | ||
362 | { | 456 | { |
363 | menu[i].desc = str(LANG_QUEUE); | 457 | menu[i].desc = str(LANG_PLAYINDICES_PLAYLIST); |
364 | menu[i].function = queue_file; | 458 | menu[i].function = playlist_options; |
365 | i++; | 459 | i++; |
366 | } | 460 | } |
367 | 461 | ||
@@ -390,5 +484,5 @@ int onplay(char* file, int attr) | |||
390 | menu[result].function(); | 484 | menu[result].function(); |
391 | menu_exit(m); | 485 | menu_exit(m); |
392 | 486 | ||
393 | return reload_dir; | 487 | return onplay_result; |
394 | } | 488 | } |
diff --git a/apps/onplay.h b/apps/onplay.h index 8540aaf2c7..7b47479e4b 100644 --- a/apps/onplay.h +++ b/apps/onplay.h | |||
@@ -21,4 +21,10 @@ | |||
21 | 21 | ||
22 | int onplay(char* file, int attr); | 22 | int onplay(char* file, int attr); |
23 | 23 | ||
24 | enum { | ||
25 | ONPLAY_OK, | ||
26 | ONPLAY_RELOAD_DIR, | ||
27 | ONPLAY_START_PLAY | ||
28 | }; | ||
29 | |||
24 | #endif | 30 | #endif |
diff --git a/apps/playlist.c b/apps/playlist.c index 726e8a9ecb..05149d164f 100644 --- a/apps/playlist.c +++ b/apps/playlist.c | |||
@@ -17,11 +17,58 @@ | |||
17 | * | 17 | * |
18 | ****************************************************************************/ | 18 | ****************************************************************************/ |
19 | 19 | ||
20 | /* | ||
21 | Dynamic playlist design (based on design originally proposed by ricII) | ||
22 | |||
23 | There are two files associated with a dynamic playlist: | ||
24 | 1. Playlist file : This file contains the initial songs in the playlist. | ||
25 | The file is created by the user and stored on the hard | ||
26 | drive. NOTE: If we are playing the contents of a | ||
27 | directory, there will be no playlist file. | ||
28 | 2. Control file : This file is automatically created when a playlist is | ||
29 | started and contains all the commands done to it. | ||
30 | |||
31 | The first non-comment line in a control file must begin with | ||
32 | "P:VERSION:DIR:FILE" where VERSION is the playlist control file version, | ||
33 | DIR is the directory where the playlist is located and FILE is the | ||
34 | playlist filename. For dirplay, FILE will be empty. An empty playlist | ||
35 | will have both entries as null. | ||
36 | |||
37 | Control file commands: | ||
38 | a. Add track (A:<position>:<last position>:<path to track>) | ||
39 | - Insert a track at the specified position in the current | ||
40 | playlist. Last position is used to specify where last insertion | ||
41 | occurred. | ||
42 | b. Queue track (Q:<position>:<last position>:<path to track>) | ||
43 | - Queue a track at the specified position in the current | ||
44 | playlist. Queued tracks differ from added tracks in that they | ||
45 | are deleted from the playlist as soon as they are played and | ||
46 | they are not saved to disk as part of the playlist. | ||
47 | c. Delete track (D:<position>) | ||
48 | - Delete track from specified position in the current playlist. | ||
49 | d. Shuffle playlist (S:<seed>:<index>) | ||
50 | - Shuffle entire playlist with specified seed. The index | ||
51 | identifies the first index in the newly shuffled playlist | ||
52 | (needed for repeat mode). | ||
53 | e. Unshuffle playlist (U:<index>) | ||
54 | - Unshuffle entire playlist. The index identifies the first index | ||
55 | in the newly unshuffled playlist. | ||
56 | f. Reset last insert position (R) | ||
57 | - Needed so that insertions work properly after resume | ||
58 | |||
59 | Resume: | ||
60 | The only resume info that needs to be saved is the current index in the | ||
61 | playlist and the position in the track. When resuming, all the commands | ||
62 | in the control file will be reapplied so that the playlist indices are | ||
63 | exactly the same as before shutdown. | ||
64 | */ | ||
65 | |||
20 | #include <stdio.h> | 66 | #include <stdio.h> |
21 | #include <stdlib.h> | 67 | #include <stdlib.h> |
22 | #include <string.h> | 68 | #include <string.h> |
23 | #include "playlist.h" | 69 | #include "playlist.h" |
24 | #include "file.h" | 70 | #include "file.h" |
71 | #include "dir.h" | ||
25 | #include "sprintf.h" | 72 | #include "sprintf.h" |
26 | #include "debug.h" | 73 | #include "debug.h" |
27 | #include "mpeg.h" | 74 | #include "mpeg.h" |
@@ -32,6 +79,10 @@ | |||
32 | #include "applimits.h" | 79 | #include "applimits.h" |
33 | #include "screens.h" | 80 | #include "screens.h" |
34 | #include "buffer.h" | 81 | #include "buffer.h" |
82 | #include "atoi.h" | ||
83 | #include "misc.h" | ||
84 | #include "button.h" | ||
85 | #include "tree.h" | ||
35 | #ifdef HAVE_LCD_BITMAP | 86 | #ifdef HAVE_LCD_BITMAP |
36 | #include "icons.h" | 87 | #include "icons.h" |
37 | #include "widgets.h" | 88 | #include "widgets.h" |
@@ -41,526 +92,825 @@ | |||
41 | 92 | ||
42 | static struct playlist_info playlist; | 93 | static struct playlist_info playlist; |
43 | 94 | ||
44 | #define QUEUE_FILE ROCKBOX_DIR "/.queue_file" | 95 | #define PLAYLIST_CONTROL_FILE ROCKBOX_DIR "/.playlist_control" |
96 | #define PLAYLIST_CONTROL_FILE_VERSION 1 | ||
97 | |||
98 | /* | ||
99 | Each playlist index has a flag associated with it which identifies what | ||
100 | type of track it is. These flags are stored in the 3 high order bits of | ||
101 | the index. | ||
102 | |||
103 | NOTE: This limits the playlist file size to a max of 512K. | ||
104 | |||
105 | Bits 31-30: | ||
106 | 00 = Playlist track | ||
107 | 01 = Track was prepended into playlist | ||
108 | 10 = Track was inserted into playlist | ||
109 | 11 = Track was appended into playlist | ||
110 | Bit 29: | ||
111 | 0 = Added track | ||
112 | 1 = Queued track | ||
113 | */ | ||
114 | #define PLAYLIST_SEEK_MASK 0x1FFFFFFF | ||
115 | #define PLAYLIST_INSERT_TYPE_MASK 0xC0000000 | ||
116 | #define PLAYLIST_QUEUE_MASK 0x20000000 | ||
117 | |||
118 | #define PLAYLIST_INSERT_TYPE_PREPEND 0x40000000 | ||
119 | #define PLAYLIST_INSERT_TYPE_INSERT 0x80000000 | ||
120 | #define PLAYLIST_INSERT_TYPE_APPEND 0xC0000000 | ||
45 | 121 | ||
46 | static unsigned char *playlist_buffer; | 122 | #define PLAYLIST_QUEUED 0x20000000 |
47 | 123 | ||
48 | static int playlist_end_pos = 0; | 124 | #define PLAYLIST_DISPLAY_COUNT 10 |
49 | 125 | ||
50 | static char now_playing[MAX_PATH+1]; | 126 | static char now_playing[MAX_PATH+1]; |
51 | 127 | ||
52 | void playlist_init(void) | 128 | static void empty_playlist(bool resume); |
53 | { | 129 | static void update_playlist_filename(char *dir, char *file); |
54 | playlist.fd = -1; | 130 | static int add_indices_to_playlist(void); |
55 | playlist.max_playlist_size = global_settings.max_files_in_playlist; | 131 | static int add_track_to_playlist(char *filename, int position, bool queue, |
56 | playlist.indices = buffer_alloc(playlist.max_playlist_size * sizeof(int)); | 132 | int seek_pos); |
57 | playlist.buffer_size = | 133 | static int add_directory_to_playlist(char *dirname, int *position, bool queue, |
58 | AVERAGE_FILENAME_LENGTH * global_settings.max_files_in_dir; | 134 | int *count); |
59 | playlist_buffer = buffer_alloc(playlist.buffer_size); | 135 | static int remove_track_from_playlist(int position, bool write); |
60 | } | 136 | static int randomise_playlist(unsigned int seed, bool start_current, |
137 | bool write); | ||
138 | static int sort_playlist(bool start_current, bool write); | ||
139 | static int get_next_index(int steps); | ||
140 | static void find_and_set_playlist_index(unsigned int seek); | ||
141 | static int compare(const void* p1, const void* p2); | ||
142 | static int get_filename(int seek, bool control_file, char *buf, | ||
143 | int buf_length); | ||
144 | static int format_track_path(char *dest, char *src, int buf_length, int max, | ||
145 | char *dir); | ||
146 | static void display_playlist_count(int count, char *fmt); | ||
147 | static void display_buffer_full(void); | ||
61 | 148 | ||
62 | /* | 149 | /* |
63 | * remove any files and indices associated with the playlist | 150 | * remove any files and indices associated with the playlist |
64 | */ | 151 | */ |
65 | static void empty_playlist(bool queue_resume) | 152 | static void empty_playlist(bool resume) |
66 | { | 153 | { |
67 | int fd; | ||
68 | |||
69 | playlist.filename[0] = '\0'; | 154 | playlist.filename[0] = '\0'; |
155 | |||
70 | if(-1 != playlist.fd) | 156 | if(-1 != playlist.fd) |
71 | /* If there is an already open playlist, close it. */ | 157 | /* If there is an already open playlist, close it. */ |
72 | close(playlist.fd); | 158 | close(playlist.fd); |
73 | playlist.fd = -1; | 159 | playlist.fd = -1; |
160 | |||
161 | if(-1 != playlist.control_fd) | ||
162 | close(playlist.control_fd); | ||
163 | playlist.control_fd = -1; | ||
164 | |||
165 | playlist.in_ram = false; | ||
166 | playlist.buffer[0] = 0; | ||
167 | playlist.buffer_end_pos = 0; | ||
168 | |||
74 | playlist.index = 0; | 169 | playlist.index = 0; |
75 | playlist.queue_index = 0; | 170 | playlist.first_index = 0; |
76 | playlist.last_queue_index = 0; | ||
77 | playlist.amount = 0; | 171 | playlist.amount = 0; |
78 | playlist.num_queued = 0; | 172 | playlist.last_insert_pos = -1; |
79 | playlist.start_queue = 0; | ||
80 | 173 | ||
81 | if (!queue_resume) | 174 | if (!resume) |
82 | { | 175 | { |
83 | /* start with fresh queue file when starting new playlist */ | 176 | int fd; |
84 | remove(QUEUE_FILE); | 177 | |
85 | fd = creat(QUEUE_FILE, 0); | 178 | /* start with fresh playlist control file when starting new |
86 | if (fd > 0) | 179 | playlist */ |
180 | fd = creat(PLAYLIST_CONTROL_FILE, 0000200); | ||
181 | if (fd >= 0) | ||
87 | close(fd); | 182 | close(fd); |
88 | } | 183 | } |
89 | } | 184 | } |
90 | 185 | ||
91 | /* update queue list after resume */ | 186 | /* |
92 | static void add_indices_to_queuelist(int seek) | 187 | * store directory and name of playlist file |
188 | */ | ||
189 | static void update_playlist_filename(char *dir, char *file) | ||
93 | { | 190 | { |
94 | int nread; | 191 | char *sep=""; |
95 | int fd = -1; | 192 | int dirlen = strlen(dir); |
96 | int i = seek; | 193 | |
97 | int count = 0; | 194 | /* If the dir does not end in trailing slash, we use a separator. |
195 | Otherwise we don't. */ | ||
196 | if('/' != dir[dirlen-1]) | ||
197 | { | ||
198 | sep="/"; | ||
199 | dirlen++; | ||
200 | } | ||
201 | |||
202 | playlist.dirlen = dirlen; | ||
203 | |||
204 | snprintf(playlist.filename, sizeof(playlist.filename), | ||
205 | "%s%s%s", | ||
206 | dir, sep, file); | ||
207 | } | ||
208 | |||
209 | /* | ||
210 | * calculate track offsets within a playlist file | ||
211 | */ | ||
212 | static int add_indices_to_playlist(void) | ||
213 | { | ||
214 | unsigned int nread; | ||
215 | unsigned int i = 0; | ||
216 | unsigned int count = 0; | ||
217 | int buflen; | ||
98 | bool store_index; | 218 | bool store_index; |
99 | char buf[MAX_PATH]; | 219 | char *buffer; |
220 | unsigned char *p; | ||
100 | 221 | ||
101 | unsigned char *p = buf; | 222 | if(-1 == playlist.fd) |
223 | playlist.fd = open(playlist.filename, O_RDONLY); | ||
224 | if(-1 == playlist.fd) | ||
225 | return -1; /* failure */ | ||
226 | |||
227 | #ifdef HAVE_LCD_BITMAP | ||
228 | if(global_settings.statusbar) | ||
229 | lcd_setmargins(0, STATUSBAR_HEIGHT); | ||
230 | else | ||
231 | lcd_setmargins(0, 0); | ||
232 | #endif | ||
102 | 233 | ||
103 | fd = open(QUEUE_FILE, O_RDONLY); | 234 | splash(0, 0, true, str(LANG_PLAYLIST_LOAD)); |
104 | if(fd < 0) | ||
105 | return; | ||
106 | 235 | ||
107 | nread = lseek(fd, seek, SEEK_SET); | 236 | /* use mp3 buffer for maximum load speed */ |
108 | if (nread < 0) | 237 | buflen = (mp3end - mp3buf); |
109 | return; | 238 | buffer = mp3buf; |
110 | 239 | ||
111 | store_index = true; | 240 | store_index = true; |
112 | 241 | ||
242 | mpeg_stop(); | ||
243 | |||
113 | while(1) | 244 | while(1) |
114 | { | 245 | { |
115 | nread = read(fd, buf, MAX_PATH); | 246 | nread = read(playlist.fd, buffer, buflen); |
247 | /* Terminate on EOF */ | ||
116 | if(nread <= 0) | 248 | if(nread <= 0) |
117 | break; | 249 | break; |
118 | |||
119 | p = buf; | ||
120 | 250 | ||
121 | for(count=0; count < nread; count++,p++) { | 251 | p = buffer; |
122 | if(*p == '\n') | 252 | |
253 | for(count=0; count < nread; count++,p++) { | ||
254 | |||
255 | /* Are we on a new line? */ | ||
256 | if((*p == '\n') || (*p == '\r')) | ||
257 | { | ||
123 | store_index = true; | 258 | store_index = true; |
259 | } | ||
124 | else if(store_index) | 260 | else if(store_index) |
125 | { | 261 | { |
126 | store_index = false; | 262 | store_index = false; |
127 | 263 | ||
128 | playlist.queue_indices[playlist.last_queue_index] = i+count; | 264 | if(*p != '#') |
129 | playlist.last_queue_index = | 265 | { |
130 | (playlist.last_queue_index + 1) % MAX_QUEUED_FILES; | 266 | /* Store a new entry */ |
131 | playlist.num_queued++; | 267 | playlist.indices[ playlist.amount ] = i+count; |
268 | playlist.amount++; | ||
269 | if ( playlist.amount >= playlist.max_playlist_size ) { | ||
270 | display_buffer_full(); | ||
271 | return -1; | ||
272 | } | ||
273 | } | ||
132 | } | 274 | } |
133 | } | 275 | } |
134 | 276 | ||
135 | i += count; | 277 | i+= count; |
136 | } | 278 | } |
279 | |||
280 | return 0; | ||
137 | } | 281 | } |
138 | 282 | ||
139 | static int get_next_index(int steps, bool *queue) | 283 | /* |
284 | * Add track to playlist at specified position. There are three special | ||
285 | * positions that can be specified: | ||
286 | * PLAYLIST_PREPEND - Add track at beginning of playlist | ||
287 | * PLAYLIST_INSERT - Add track after current song. NOTE: If there | ||
288 | * are already inserted tracks then track is added | ||
289 | * to the end of the insertion list. | ||
290 | * PLAYLIST_INSERT_FIRST - Add track immediately after current song, no | ||
291 | * matter what other tracks have been inserted. | ||
292 | * PLAYLIST_INSERT_LAST - Add track to end of playlist | ||
293 | */ | ||
294 | static int add_track_to_playlist(char *filename, int position, bool queue, | ||
295 | int seek_pos) | ||
140 | { | 296 | { |
141 | int current_index = playlist.index; | 297 | int insert_position = position; |
142 | int next_index = -1; | 298 | unsigned int flags = PLAYLIST_INSERT_TYPE_INSERT; |
299 | int i; | ||
143 | 300 | ||
144 | if (global_settings.repeat_mode == REPEAT_ONE) | 301 | if (playlist.amount >= playlist.max_playlist_size) |
145 | { | 302 | { |
146 | /* this is needed for repeat one to work with queue mode */ | 303 | display_buffer_full(); |
147 | steps = 0; | 304 | return -1; |
148 | } | 305 | } |
149 | else if (steps >= 0) | 306 | |
307 | switch (position) | ||
150 | { | 308 | { |
151 | /* Queue index starts from 0 which needs to be accounted for. Also, | 309 | case PLAYLIST_PREPEND: |
152 | after resume, this handles case where we want to begin with | 310 | insert_position = playlist.first_index; |
153 | playlist */ | 311 | flags = PLAYLIST_INSERT_TYPE_PREPEND; |
154 | steps -= playlist.start_queue; | 312 | break; |
313 | case PLAYLIST_INSERT: | ||
314 | /* if there are already inserted tracks then add track to end of | ||
315 | insertion list else add after current playing track */ | ||
316 | if (playlist.last_insert_pos >= 0 && | ||
317 | playlist.last_insert_pos < playlist.amount && | ||
318 | (playlist.indices[playlist.last_insert_pos]& | ||
319 | PLAYLIST_INSERT_TYPE_MASK) == PLAYLIST_INSERT_TYPE_INSERT) | ||
320 | position = insert_position = playlist.last_insert_pos+1; | ||
321 | else if (playlist.amount > 0) | ||
322 | position = insert_position = playlist.index + 1; | ||
323 | else | ||
324 | position = insert_position = 0; | ||
325 | |||
326 | playlist.last_insert_pos = position; | ||
327 | break; | ||
328 | case PLAYLIST_INSERT_FIRST: | ||
329 | if (playlist.amount > 0) | ||
330 | position = insert_position = playlist.index + 1; | ||
331 | else | ||
332 | position = insert_position = 0; | ||
333 | |||
334 | if (playlist.last_insert_pos < 0) | ||
335 | playlist.last_insert_pos = position; | ||
336 | break; | ||
337 | case PLAYLIST_INSERT_LAST: | ||
338 | if (playlist.first_index > 0) | ||
339 | insert_position = playlist.first_index; | ||
340 | else | ||
341 | insert_position = playlist.amount; | ||
342 | |||
343 | flags = PLAYLIST_INSERT_TYPE_APPEND; | ||
344 | break; | ||
155 | } | 345 | } |
346 | |||
347 | if (queue) | ||
348 | flags |= PLAYLIST_QUEUED; | ||
156 | 349 | ||
157 | if (steps >= 0 && playlist.num_queued > 0 && | 350 | /* shift indices so that track can be added */ |
158 | playlist.num_queued - steps > 0) | 351 | for (i=playlist.amount; i>insert_position; i--) |
159 | *queue = true; | 352 | playlist.indices[i] = playlist.indices[i-1]; |
160 | else | 353 | |
354 | /* update stored indices if needed */ | ||
355 | if (playlist.amount > 0 && insert_position <= playlist.index) | ||
356 | playlist.index++; | ||
357 | |||
358 | if (playlist.amount > 0 && insert_position <= playlist.first_index && | ||
359 | position != PLAYLIST_PREPEND) | ||
360 | playlist.first_index++; | ||
361 | |||
362 | if (insert_position < playlist.last_insert_pos || | ||
363 | (insert_position == playlist.last_insert_pos && position < 0)) | ||
364 | playlist.last_insert_pos++; | ||
365 | |||
366 | if (seek_pos < 0 && playlist.control_fd >= 0) | ||
161 | { | 367 | { |
162 | *queue = false; | 368 | int result = -1; |
163 | if (playlist.num_queued) | 369 | |
370 | mutex_lock(&playlist.control_mutex); | ||
371 | |||
372 | if (lseek(playlist.control_fd, 0, SEEK_END) >= 0) | ||
164 | { | 373 | { |
165 | if (steps >= 0) | 374 | if (fprintf(playlist.control_fd, "%c:%d:%d:", (queue?'Q':'A'), |
166 | { | 375 | position, playlist.last_insert_pos) > 0) |
167 | /* skip the queued tracks */ | ||
168 | steps -= (playlist.num_queued - 1); | ||
169 | } | ||
170 | else if (!playlist.start_queue) | ||
171 | { | 376 | { |
172 | /* previous from queue list needs to go to current track in | 377 | /* save the position in file where track name is written */ |
173 | playlist */ | 378 | seek_pos = lseek(playlist.control_fd, 0, SEEK_CUR); |
174 | steps += 1; | 379 | |
380 | if (fprintf(playlist.control_fd, "%s\n", filename) > 0) | ||
381 | result = 0; | ||
175 | } | 382 | } |
176 | } | 383 | } |
384 | |||
385 | mutex_unlock(&playlist.control_mutex); | ||
386 | |||
387 | if (result < 0) | ||
388 | { | ||
389 | splash(HZ*2, 0, true, str(LANG_PLAYLIST_CONTROL_UPDATE_ERROR)); | ||
390 | return result; | ||
391 | } | ||
177 | } | 392 | } |
178 | 393 | ||
179 | switch (global_settings.repeat_mode) | 394 | playlist.indices[insert_position] = flags | seek_pos; |
395 | |||
396 | playlist.amount++; | ||
397 | |||
398 | return insert_position; | ||
399 | } | ||
400 | |||
401 | /* | ||
402 | * Insert directory into playlist. May be called recursively. | ||
403 | */ | ||
404 | static int add_directory_to_playlist(char *dirname, int *position, bool queue, | ||
405 | int *count) | ||
406 | { | ||
407 | char buf[MAX_PATH+1]; | ||
408 | char *count_str; | ||
409 | int result = 0; | ||
410 | int num_files = 0; | ||
411 | bool buffer_full = false; | ||
412 | int i; | ||
413 | struct entry *files; | ||
414 | |||
415 | /* use the tree browser dircache to load files */ | ||
416 | files = load_and_sort_directory(dirname, SHOW_ALL, &num_files, | ||
417 | &buffer_full); | ||
418 | |||
419 | if(!files) | ||
180 | { | 420 | { |
181 | case REPEAT_OFF: | 421 | splash(HZ*2, 0, true, str(LANG_PLAYLIST_DIRECTORY_ACCESS_ERROR)); |
182 | if (*queue) | 422 | return 0; |
183 | next_index = (playlist.queue_index+steps) % MAX_QUEUED_FILES; | 423 | } |
184 | else | 424 | |
185 | { | 425 | /* we've overwritten the dircache so tree browser will need to be |
186 | if (current_index < playlist.first_index) | 426 | reloaded */ |
187 | current_index += playlist.amount; | 427 | reload_directory(); |
188 | current_index -= playlist.first_index; | 428 | |
189 | 429 | if (queue) | |
190 | next_index = current_index+steps; | 430 | count_str = str(LANG_PLAYLIST_QUEUE_COUNT); |
191 | if ((next_index < 0) || (next_index >= playlist.amount)) | 431 | else |
192 | next_index = -1; | 432 | count_str = str(LANG_PLAYLIST_INSERT_COUNT); |
193 | else | 433 | |
194 | next_index = (next_index+playlist.first_index) % | 434 | for (i=0; i<num_files; i++) |
195 | playlist.amount; | 435 | { |
196 | } | 436 | /* user abort */ |
437 | #ifdef HAVE_PLAYER_KEYPAD | ||
438 | if (button_get(false) == BUTTON_STOP) | ||
439 | #else | ||
440 | if (button_get(false) == BUTTON_OFF) | ||
441 | #endif | ||
442 | { | ||
443 | result = -1; | ||
197 | break; | 444 | break; |
445 | } | ||
198 | 446 | ||
199 | case REPEAT_ONE: | 447 | if (files[i].attr & ATTR_DIRECTORY) |
200 | /* if we are still in playlist when repeat one is set, don't go to | 448 | { |
201 | queue list */ | 449 | if (global_settings.recursive_dir_insert) |
202 | if (*queue && !playlist.start_queue) | 450 | { |
203 | next_index = playlist.queue_index; | 451 | /* recursively add directories */ |
452 | snprintf(buf, sizeof(buf), "%s/%s", dirname, files[i].name); | ||
453 | result = add_directory_to_playlist(buf, position, queue, | ||
454 | count); | ||
455 | if (result < 0) | ||
456 | break; | ||
457 | |||
458 | /* we now need to reload our current directory */ | ||
459 | files = load_and_sort_directory(dirname, SHOW_ALL, &num_files, | ||
460 | &buffer_full); | ||
461 | if (!files) | ||
462 | { | ||
463 | result = -1; | ||
464 | break; | ||
465 | } | ||
466 | } | ||
204 | else | 467 | else |
468 | continue; | ||
469 | } | ||
470 | else if (files[i].attr & TREE_ATTR_MPA) | ||
471 | { | ||
472 | int insert_pos; | ||
473 | |||
474 | snprintf(buf, sizeof(buf), "%s/%s", dirname, files[i].name); | ||
475 | |||
476 | insert_pos = add_track_to_playlist(buf, *position, queue, -1); | ||
477 | if (insert_pos < 0) | ||
205 | { | 478 | { |
206 | next_index = current_index; | 479 | result = -1; |
207 | *queue = false; | 480 | break; |
208 | } | 481 | } |
209 | break; | ||
210 | 482 | ||
211 | case REPEAT_ALL: | 483 | (*count)++; |
212 | default: | 484 | |
213 | if (*queue) | 485 | /* Make sure tracks are inserted in correct order if user requests |
214 | next_index = (playlist.queue_index+steps) % MAX_QUEUED_FILES; | 486 | INSERT_FIRST */ |
215 | else | 487 | if (*position == PLAYLIST_INSERT_FIRST || *position >= 0) |
488 | *position = insert_pos + 1; | ||
489 | |||
490 | if ((*count%PLAYLIST_DISPLAY_COUNT) == 0) | ||
216 | { | 491 | { |
217 | next_index = (current_index+steps) % playlist.amount; | 492 | display_playlist_count(*count, count_str); |
218 | while (next_index < 0) | 493 | |
219 | next_index += playlist.amount; | 494 | if (*count == PLAYLIST_DISPLAY_COUNT) |
495 | mpeg_flush_and_reload_tracks(); | ||
220 | } | 496 | } |
221 | break; | 497 | |
498 | /* let the other threads work */ | ||
499 | yield(); | ||
500 | } | ||
222 | } | 501 | } |
223 | 502 | ||
224 | return next_index; | 503 | return result; |
225 | } | 504 | } |
226 | 505 | ||
227 | int playlist_amount(void) | 506 | /* |
507 | * remove track at specified position | ||
508 | */ | ||
509 | static int remove_track_from_playlist(int position, bool write) | ||
228 | { | 510 | { |
229 | return playlist.amount + playlist.num_queued; | 511 | int i; |
230 | } | ||
231 | 512 | ||
232 | int playlist_first_index(void) | 513 | if (playlist.amount <= 0) |
233 | { | 514 | return -1; |
234 | return playlist.first_index; | ||
235 | } | ||
236 | 515 | ||
237 | /* Get resume info for current playing song. If return value is -1 then | 516 | /* shift indices now that track has been removed */ |
238 | settings shouldn't be saved (eg. when playing queued song and save queue | 517 | for (i=position; i<playlist.amount; i++) |
239 | disabled) */ | 518 | playlist.indices[i] = playlist.indices[i+1]; |
240 | int playlist_get_resume_info(int *resume_index, int *queue_resume, | ||
241 | int *queue_resume_index) | ||
242 | { | ||
243 | int result = 0; | ||
244 | 519 | ||
245 | *resume_index = playlist.index; | 520 | playlist.amount--; |
521 | |||
522 | /* update stored indices if needed */ | ||
523 | if (position < playlist.index) | ||
524 | playlist.index--; | ||
525 | |||
526 | if (position < playlist.first_index) | ||
527 | playlist.first_index--; | ||
246 | 528 | ||
247 | if (playlist.num_queued > 0) | 529 | if (position <= playlist.last_insert_pos) |
530 | playlist.last_insert_pos--; | ||
531 | |||
532 | if (write && playlist.control_fd >= 0) | ||
248 | { | 533 | { |
249 | if (global_settings.save_queue_resume) | 534 | int result = -1; |
535 | |||
536 | mutex_lock(&playlist.control_mutex); | ||
537 | |||
538 | if (lseek(playlist.control_fd, 0, SEEK_END) >= 0) | ||
250 | { | 539 | { |
251 | *queue_resume_index = | 540 | if (fprintf(playlist.control_fd, "D:%d\n", position) > 0) |
252 | playlist.queue_indices[playlist.queue_index]; | 541 | { |
253 | /* have we started playing the queue list yet? */ | 542 | fsync(playlist.control_fd); |
254 | if (playlist.start_queue) | 543 | result = 0; |
255 | *queue_resume = QUEUE_BEGIN_PLAYLIST; | 544 | } |
256 | else | ||
257 | *queue_resume = QUEUE_BEGIN_QUEUE; | ||
258 | } | 545 | } |
259 | else if (!playlist.start_queue) | 546 | |
547 | mutex_unlock(&playlist.control_mutex); | ||
548 | |||
549 | if (result < 0) | ||
260 | { | 550 | { |
261 | *queue_resume = QUEUE_OFF; | 551 | splash(HZ*2, 0, true, str(LANG_PLAYLIST_CONTROL_UPDATE_ERROR)); |
262 | result = -1; | 552 | return result; |
263 | } | 553 | } |
264 | } | 554 | } |
265 | else | ||
266 | *queue_resume = QUEUE_OFF; | ||
267 | 555 | ||
268 | return result; | 556 | return 0; |
269 | } | 557 | } |
270 | 558 | ||
271 | char *playlist_name(char *buf, int buf_size) | 559 | /* |
560 | * randomly rearrange the array of indices for the playlist. If start_current | ||
561 | * is true then update the index to the new index of the current playing track | ||
562 | */ | ||
563 | static int randomise_playlist(unsigned int seed, bool start_current, bool write) | ||
272 | { | 564 | { |
273 | char *sep; | 565 | int count; |
566 | int candidate; | ||
567 | int store; | ||
568 | unsigned int current = playlist.indices[playlist.index]; | ||
569 | |||
570 | /* seed with the given seed */ | ||
571 | srand(seed); | ||
274 | 572 | ||
275 | snprintf(buf, buf_size, "%s", playlist.filename+playlist.dirlen); | 573 | /* randomise entire indices list */ |
574 | for(count = playlist.amount - 1; count >= 0; count--) | ||
575 | { | ||
576 | /* the rand is from 0 to RAND_MAX, so adjust to our value range */ | ||
577 | candidate = rand() % (count + 1); | ||
276 | 578 | ||
277 | if (0 == buf[0]) | 579 | /* now swap the values at the 'count' and 'candidate' positions */ |
278 | return NULL; | 580 | store = playlist.indices[candidate]; |
581 | playlist.indices[candidate] = playlist.indices[count]; | ||
582 | playlist.indices[count] = store; | ||
583 | } | ||
279 | 584 | ||
280 | /* Remove extension */ | 585 | if (start_current) |
281 | sep = strrchr(buf, '.'); | 586 | find_and_set_playlist_index(current); |
282 | if (NULL != sep) | ||
283 | *sep = 0; | ||
284 | |||
285 | return buf; | ||
286 | } | ||
287 | 587 | ||
288 | void playlist_clear(void) | 588 | /* indices have been moved so last insert position is no longer valid */ |
289 | { | 589 | playlist.last_insert_pos = -1; |
290 | playlist_end_pos = 0; | ||
291 | playlist_buffer[0] = 0; | ||
292 | } | ||
293 | 590 | ||
294 | int playlist_add(char *filename) | 591 | if (write && playlist.control_fd >= 0) |
295 | { | 592 | { |
296 | int len = strlen(filename); | 593 | int result = -1; |
297 | 594 | ||
298 | if(len+2 > playlist.buffer_size - playlist_end_pos) | 595 | mutex_lock(&playlist.control_mutex); |
299 | return -1; | 596 | |
597 | if (lseek(playlist.control_fd, 0, SEEK_END) >= 0) | ||
598 | { | ||
599 | if (fprintf(playlist.control_fd, "S:%d:%d\n", seed, | ||
600 | playlist.first_index) > 0) | ||
601 | { | ||
602 | fsync(playlist.control_fd); | ||
603 | result = 0; | ||
604 | } | ||
605 | } | ||
606 | |||
607 | mutex_unlock(&playlist.control_mutex); | ||
608 | |||
609 | if (result < 0) | ||
610 | { | ||
611 | splash(HZ*2, 0, true, str(LANG_PLAYLIST_CONTROL_UPDATE_ERROR)); | ||
612 | return result; | ||
613 | } | ||
614 | } | ||
300 | 615 | ||
301 | strcpy(&playlist_buffer[playlist_end_pos], filename); | ||
302 | playlist_end_pos += len; | ||
303 | playlist_buffer[playlist_end_pos++] = '\n'; | ||
304 | playlist_buffer[playlist_end_pos] = '\0'; | ||
305 | return 0; | 616 | return 0; |
306 | } | 617 | } |
307 | 618 | ||
308 | /* Add track to queue file */ | 619 | /* |
309 | int queue_add(char *filename) | 620 | * Sort the array of indices for the playlist. If start_current is true then |
621 | * set the index to the new index of the current song. | ||
622 | */ | ||
623 | static int sort_playlist(bool start_current, bool write) | ||
310 | { | 624 | { |
311 | int fd, seek, result; | 625 | unsigned int current = playlist.indices[playlist.index]; |
312 | int len = strlen(filename); | ||
313 | 626 | ||
314 | if(playlist.num_queued >= MAX_QUEUED_FILES) | 627 | if (playlist.amount > 0) |
315 | return -1; | 628 | qsort(playlist.indices, playlist.amount, sizeof(playlist.indices[0]), |
629 | compare); | ||
316 | 630 | ||
317 | fd = open(QUEUE_FILE, O_WRONLY); | 631 | if (start_current) |
318 | if (fd < 0) | 632 | find_and_set_playlist_index(current); |
319 | return -1; | ||
320 | 633 | ||
321 | seek = lseek(fd, 0, SEEK_END); | 634 | /* indices have been moved so last insert position is no longer valid */ |
322 | if (seek < 0) | 635 | playlist.last_insert_pos = -1; |
323 | { | ||
324 | close(fd); | ||
325 | return -1; | ||
326 | } | ||
327 | 636 | ||
328 | /* save the file name with a trailing \n. QUEUE_FILE can be used as a | 637 | if (write && playlist.control_fd >= 0) |
329 | playlist if desired */ | ||
330 | filename[len] = '\n'; | ||
331 | result = write(fd, filename, len+1); | ||
332 | if (result < 0) | ||
333 | { | 638 | { |
334 | close(fd); | 639 | int result = -1; |
335 | return -1; | ||
336 | } | ||
337 | filename[len] = '\0'; | ||
338 | 640 | ||
339 | close(fd); | 641 | mutex_lock(&playlist.control_mutex); |
340 | 642 | ||
341 | if (playlist.num_queued <= 0) | 643 | if (lseek(playlist.control_fd, 0, SEEK_END) >= 0) |
342 | playlist.start_queue = 1; | 644 | { |
645 | if (fprintf(playlist.control_fd, "U:%d\n", playlist.first_index) > | ||
646 | 0) | ||
647 | { | ||
648 | fsync(playlist.control_fd); | ||
649 | result = 0; | ||
650 | } | ||
651 | } | ||
343 | 652 | ||
344 | playlist.queue_indices[playlist.last_queue_index] = seek; | 653 | mutex_unlock(&playlist.control_mutex); |
345 | playlist.last_queue_index = | ||
346 | (playlist.last_queue_index + 1) % MAX_QUEUED_FILES; | ||
347 | playlist.num_queued++; | ||
348 | 654 | ||
349 | /* the play order has changed */ | 655 | if (result < 0) |
350 | mpeg_flush_and_reload_tracks(); | 656 | { |
657 | splash(HZ*2, 0, true, str(LANG_PLAYLIST_CONTROL_UPDATE_ERROR)); | ||
658 | return result; | ||
659 | } | ||
660 | } | ||
351 | 661 | ||
352 | return playlist.num_queued; | 662 | return 0; |
353 | } | 663 | } |
354 | 664 | ||
355 | int playlist_next(int steps) | 665 | /* |
666 | * returns the index of the track that is "steps" away from current playing | ||
667 | * track. | ||
668 | */ | ||
669 | static int get_next_index(int steps) | ||
356 | { | 670 | { |
357 | bool queue; | 671 | int current_index = playlist.index; |
358 | int index = get_next_index(steps, &queue); | 672 | int next_index = -1; |
359 | 673 | ||
360 | if (queue) | 674 | if (playlist.amount <= 0) |
361 | { | 675 | return -1; |
362 | /* queue_diff accounts for bad songs in queue list */ | ||
363 | int queue_diff = index - playlist.queue_index; | ||
364 | if (queue_diff < 0) | ||
365 | queue_diff += MAX_QUEUED_FILES; | ||
366 | 676 | ||
367 | playlist.num_queued -= queue_diff; | 677 | switch (global_settings.repeat_mode) |
368 | playlist.queue_index = index; | ||
369 | playlist.start_queue = 0; | ||
370 | } | ||
371 | else | ||
372 | { | 678 | { |
373 | playlist.index = index; | 679 | case REPEAT_OFF: |
374 | if (playlist.num_queued > 0 && !playlist.start_queue) | ||
375 | { | 680 | { |
376 | if (steps >= 0) | 681 | /* Rotate indices such that first_index is considered index 0 to |
377 | { | 682 | simplify next calculation */ |
378 | /* done with queue list */ | 683 | current_index -= playlist.first_index; |
379 | playlist.queue_index = playlist.last_queue_index; | 684 | if (current_index < 0) |
380 | playlist.num_queued = 0; | 685 | current_index += playlist.amount; |
381 | } | 686 | |
687 | next_index = current_index+steps; | ||
688 | if ((next_index < 0) || (next_index >= playlist.amount)) | ||
689 | next_index = -1; | ||
382 | else | 690 | else |
691 | next_index = (next_index+playlist.first_index) % | ||
692 | playlist.amount; | ||
693 | |||
694 | break; | ||
695 | } | ||
696 | |||
697 | case REPEAT_ONE: | ||
698 | next_index = current_index; | ||
699 | break; | ||
700 | |||
701 | case REPEAT_ALL: | ||
702 | default: | ||
703 | { | ||
704 | next_index = (current_index+steps) % playlist.amount; | ||
705 | while (next_index < 0) | ||
706 | next_index += playlist.amount; | ||
707 | |||
708 | if (steps >= playlist.amount) | ||
383 | { | 709 | { |
384 | /* user requested previous. however, don't forget about queue | 710 | int i, index; |
385 | list */ | 711 | |
386 | playlist.start_queue = 1; | 712 | index = next_index; |
713 | next_index = -1; | ||
714 | |||
715 | /* second time around so skip the queued files */ | ||
716 | for (i=0; i<playlist.amount; i++) | ||
717 | { | ||
718 | if (playlist.indices[index] & PLAYLIST_QUEUE_MASK) | ||
719 | index = (index+1) % playlist.amount; | ||
720 | else | ||
721 | { | ||
722 | next_index = index; | ||
723 | break; | ||
724 | } | ||
725 | } | ||
387 | } | 726 | } |
727 | break; | ||
388 | } | 728 | } |
389 | } | 729 | } |
390 | 730 | ||
391 | return index; | 731 | return next_index; |
392 | } | 732 | } |
393 | 733 | ||
394 | /* Returns false if 'steps' is out of bounds, else true */ | 734 | /* |
395 | bool playlist_check(int steps) | 735 | * Search for the seek track and set appropriate indices. Used after shuffle |
736 | * to make sure the current index is still pointing to correct track. | ||
737 | */ | ||
738 | static void find_and_set_playlist_index(unsigned int seek) | ||
396 | { | 739 | { |
397 | bool queue; | 740 | int i; |
398 | int index = get_next_index(steps, &queue); | 741 | |
399 | return (index >= 0); | 742 | /* Set the index to the current song */ |
743 | for (i=0; i<playlist.amount; i++) | ||
744 | { | ||
745 | if (playlist.indices[i] == seek) | ||
746 | { | ||
747 | playlist.index = playlist.first_index = i; | ||
748 | break; | ||
749 | } | ||
750 | } | ||
400 | } | 751 | } |
401 | 752 | ||
402 | char* playlist_peek(int steps) | 753 | /* |
754 | * used to sort track indices. Sort order is as follows: | ||
755 | * 1. Prepended tracks (in prepend order) | ||
756 | * 2. Playlist/directory tracks (in playlist order) | ||
757 | * 3. Inserted/Appended tracks (in insert order) | ||
758 | */ | ||
759 | static int compare(const void* p1, const void* p2) | ||
760 | { | ||
761 | unsigned int* e1 = (unsigned int*) p1; | ||
762 | unsigned int* e2 = (unsigned int*) p2; | ||
763 | unsigned int flags1 = *e1 & PLAYLIST_INSERT_TYPE_MASK; | ||
764 | unsigned int flags2 = *e2 & PLAYLIST_INSERT_TYPE_MASK; | ||
765 | |||
766 | if (flags1 == flags2) | ||
767 | return (*e1 & PLAYLIST_SEEK_MASK) - (*e2 & PLAYLIST_SEEK_MASK); | ||
768 | else if (flags1 == PLAYLIST_INSERT_TYPE_PREPEND || | ||
769 | flags2 == PLAYLIST_INSERT_TYPE_APPEND) | ||
770 | return -1; | ||
771 | else if (flags1 == PLAYLIST_INSERT_TYPE_APPEND || | ||
772 | flags2 == PLAYLIST_INSERT_TYPE_PREPEND) | ||
773 | return 1; | ||
774 | else if (flags1 && flags2) | ||
775 | return (*e1 & PLAYLIST_SEEK_MASK) - (*e2 & PLAYLIST_SEEK_MASK); | ||
776 | else | ||
777 | return *e1 - *e2; | ||
778 | } | ||
779 | |||
780 | /* | ||
781 | * gets pathname for track at seek index | ||
782 | */ | ||
783 | static int get_filename(int seek, bool control_file, char *buf, | ||
784 | int buf_length) | ||
403 | { | 785 | { |
404 | int seek; | ||
405 | int max; | ||
406 | int i; | ||
407 | int fd; | 786 | int fd; |
408 | char *buf; | 787 | int max = -1; |
788 | char tmp_buf[MAX_PATH+1]; | ||
409 | char dir_buf[MAX_PATH+1]; | 789 | char dir_buf[MAX_PATH+1]; |
410 | char *dir_end; | ||
411 | int index; | ||
412 | bool queue; | ||
413 | 790 | ||
414 | index = get_next_index(steps, &queue); | 791 | if (buf_length > MAX_PATH+1) |
415 | if (index < 0) | 792 | buf_length = MAX_PATH+1; |
416 | return NULL; | ||
417 | 793 | ||
418 | if (queue) | 794 | if (playlist.in_ram && !control_file) |
419 | { | 795 | { |
420 | seek = playlist.queue_indices[index]; | 796 | strncpy(tmp_buf, &playlist.buffer[seek], sizeof(tmp_buf)); |
421 | fd = open(QUEUE_FILE, O_RDONLY); | 797 | tmp_buf[MAX_PATH] = '\0'; |
422 | if(-1 != fd) | 798 | max = strlen(tmp_buf) + 1; |
423 | { | ||
424 | buf = dir_buf; | ||
425 | lseek(fd, seek, SEEK_SET); | ||
426 | max = read(fd, buf, MAX_PATH); | ||
427 | close(fd); | ||
428 | } | ||
429 | else | ||
430 | return NULL; | ||
431 | } | 799 | } |
432 | else | 800 | else |
433 | { | 801 | { |
434 | seek = playlist.indices[index]; | 802 | if (control_file) |
435 | 803 | fd = playlist.control_fd; | |
436 | if(playlist.in_ram) | ||
437 | { | ||
438 | buf = playlist_buffer + seek; | ||
439 | max = playlist_end_pos - seek; | ||
440 | } | ||
441 | else | 804 | else |
442 | { | 805 | { |
443 | if(-1 == playlist.fd) | 806 | if(-1 == playlist.fd) |
444 | playlist.fd = open(playlist.filename, O_RDONLY); | 807 | playlist.fd = open(playlist.filename, O_RDONLY); |
808 | |||
809 | fd = playlist.fd; | ||
810 | } | ||
811 | |||
812 | if(-1 != fd) | ||
813 | { | ||
814 | if (control_file) | ||
815 | mutex_lock(&playlist.control_mutex); | ||
816 | |||
817 | lseek(fd, seek, SEEK_SET); | ||
818 | max = read(fd, tmp_buf, buf_length); | ||
819 | |||
820 | if (control_file) | ||
821 | mutex_unlock(&playlist.control_mutex); | ||
822 | } | ||
445 | 823 | ||
446 | if(-1 != playlist.fd) | 824 | if (max < 0) |
447 | { | 825 | { |
448 | buf = playlist_buffer; | 826 | if (control_file) |
449 | lseek(playlist.fd, seek, SEEK_SET); | 827 | splash(HZ*2, 0, true, |
450 | max = read(playlist.fd, buf, MAX_PATH); | 828 | str(LANG_PLAYLIST_CONTROL_ACCESS_ERROR)); |
451 | } | ||
452 | else | 829 | else |
453 | return NULL; | 830 | splash(HZ*2, 0, true, str(LANG_PLAYLIST_ACCESS_ERROR)); |
831 | |||
832 | return max; | ||
454 | } | 833 | } |
455 | } | 834 | } |
456 | 835 | ||
836 | strncpy(dir_buf, playlist.filename, playlist.dirlen-1); | ||
837 | dir_buf[playlist.dirlen-1] = 0; | ||
838 | |||
839 | return (format_track_path(buf, tmp_buf, buf_length, max, dir_buf)); | ||
840 | } | ||
841 | |||
842 | /* | ||
843 | * Returns absolute path of track | ||
844 | */ | ||
845 | static int format_track_path(char *dest, char *src, int buf_length, int max, | ||
846 | char *dir) | ||
847 | { | ||
848 | int i = 0; | ||
849 | int j; | ||
850 | char *temp_ptr; | ||
851 | |||
457 | /* Zero-terminate the file name */ | 852 | /* Zero-terminate the file name */ |
458 | seek=0; | 853 | while((src[i] != '\n') && |
459 | while((buf[seek] != '\n') && | 854 | (src[i] != '\r') && |
460 | (buf[seek] != '\r') && | 855 | (i < max)) |
461 | (seek < max)) | 856 | i++; |
462 | seek++; | ||
463 | 857 | ||
464 | /* Now work back killing white space */ | 858 | /* Now work back killing white space */ |
465 | while((buf[seek-1] == ' ') || | 859 | while((src[i-1] == ' ') || |
466 | (buf[seek-1] == '\t')) | 860 | (src[i-1] == '\t')) |
467 | seek--; | 861 | i--; |
468 | 862 | ||
469 | buf[seek]=0; | 863 | src[i]=0; |
470 | 864 | ||
471 | /* replace backslashes with forward slashes */ | 865 | /* replace backslashes with forward slashes */ |
472 | for ( i=0; i<seek; i++ ) | 866 | for ( j=0; j<i; j++ ) |
473 | if ( buf[i] == '\\' ) | 867 | if ( src[j] == '\\' ) |
474 | buf[i] = '/'; | 868 | src[j] = '/'; |
475 | 869 | ||
476 | if('/' == buf[0]) { | 870 | if('/' == src[0]) |
477 | strcpy(now_playing, &buf[0]); | 871 | { |
872 | strncpy(dest, src, buf_length); | ||
478 | } | 873 | } |
479 | else { | 874 | else |
480 | strncpy(dir_buf, playlist.filename, playlist.dirlen-1); | 875 | { |
481 | dir_buf[playlist.dirlen-1] = 0; | ||
482 | |||
483 | /* handle dos style drive letter */ | 876 | /* handle dos style drive letter */ |
484 | if ( ':' == buf[1] ) { | 877 | if (':' == src[1]) |
485 | strcpy(now_playing, &buf[2]); | 878 | strcpy(src, &dest[2]); |
486 | } | 879 | else if ('.' == src[0] && '.' == src[1] && '/' == src[2]) |
487 | else if ( '.' == buf[0] && '.' == buf[1] && '/' == buf[2] ) { | 880 | { |
488 | /* handle relative paths */ | 881 | /* handle relative paths */ |
489 | seek=3; | 882 | i=3; |
490 | while(buf[seek] == '.' && | 883 | while(src[i] == '.' && |
491 | buf[seek+1] == '.' && | 884 | src[i] == '.' && |
492 | buf[seek+2] == '/') | 885 | src[i] == '/') |
493 | seek += 3; | 886 | i += 3; |
494 | for (i=0; i<seek/3; i++) { | 887 | for (j=0; j<i/3; j++) { |
495 | dir_end = strrchr(dir_buf, '/'); | 888 | temp_ptr = strrchr(dir, '/'); |
496 | if (dir_end) | 889 | if (temp_ptr) |
497 | *dir_end = '\0'; | 890 | *temp_ptr = '\0'; |
498 | else | 891 | else |
499 | break; | 892 | break; |
500 | } | 893 | } |
501 | snprintf(now_playing, MAX_PATH+1, "%s/%s", dir_buf, &buf[seek]); | 894 | snprintf(dest, buf_length, "%s/%s", dir, &src[i]); |
502 | } | 895 | } |
503 | else if ( '.' == buf[0] && '/' == buf[1] ) { | 896 | else if ( '.' == src[0] && '/' == src[1] ) { |
504 | snprintf(now_playing, MAX_PATH+1, "%s/%s", dir_buf, &buf[2]); | 897 | snprintf(dest, buf_length, "%s/%s", dir, &src[2]); |
505 | } | 898 | } |
506 | else { | 899 | else { |
507 | snprintf(now_playing, MAX_PATH+1, "%s/%s", dir_buf, buf); | 900 | snprintf(dest, buf_length, "%s/%s", dir, src); |
508 | } | ||
509 | } | ||
510 | |||
511 | buf = now_playing; | ||
512 | |||
513 | /* remove bogus dirs from beginning of path | ||
514 | (workaround for buggy playlist creation tools) */ | ||
515 | if(!playlist.in_ram) | ||
516 | { | ||
517 | while (buf) | ||
518 | { | ||
519 | fd = open(buf, O_RDONLY); | ||
520 | if (fd >= 0) | ||
521 | { | ||
522 | close(fd); | ||
523 | break; | ||
524 | } | ||
525 | |||
526 | buf = strchr(buf+1, '/'); | ||
527 | } | 901 | } |
528 | } | 902 | } |
529 | |||
530 | if (!buf) | ||
531 | { | ||
532 | /* Even though this is an invalid file, we still need to pass a file | ||
533 | name to the caller because NULL is used to indicate end of | ||
534 | playlist */ | ||
535 | return now_playing; | ||
536 | } | ||
537 | 903 | ||
538 | return buf; | 904 | return 0; |
539 | } | 905 | } |
540 | 906 | ||
541 | /* | 907 | /* |
542 | * This function is called to start playback of a given playlist. This | 908 | * Display splash message showing progress of playlist/directory insertion or |
543 | * playlist may be stored in RAM (when using full-dir playback). | 909 | * save. |
544 | * | ||
545 | * Return: the new index (possibly different due to shuffle) | ||
546 | */ | 910 | */ |
547 | int play_list(char *dir, /* "current directory" */ | 911 | static void display_playlist_count(int count, char *fmt) |
548 | char *file, /* playlist */ | ||
549 | int start_index, /* index in the playlist */ | ||
550 | bool shuffled_index, /* if TRUE the specified index is for the | ||
551 | playlist AFTER the shuffle */ | ||
552 | int start_offset, /* offset in the file */ | ||
553 | int random_seed, /* used for shuffling */ | ||
554 | int first_index, /* first index of playlist */ | ||
555 | int queue_resume, /* resume queue list? */ | ||
556 | int queue_resume_index ) /* queue list seek pos */ | ||
557 | { | 912 | { |
558 | char *sep=""; | 913 | lcd_clear_display(); |
559 | int dirlen; | ||
560 | empty_playlist(queue_resume); | ||
561 | |||
562 | playlist.index = start_index; | ||
563 | playlist.first_index = first_index; | ||
564 | 914 | ||
565 | #ifdef HAVE_LCD_BITMAP | 915 | #ifdef HAVE_LCD_BITMAP |
566 | if(global_settings.statusbar) | 916 | if(global_settings.statusbar) |
@@ -569,228 +919,915 @@ int play_list(char *dir, /* "current directory" */ | |||
569 | lcd_setmargins(0, 0); | 919 | lcd_setmargins(0, 0); |
570 | #endif | 920 | #endif |
571 | 921 | ||
572 | /* If file is NULL, the list is in RAM */ | 922 | #ifdef HAVE_PLAYER_KEYPAD |
573 | if(file) { | 923 | splash(0, 0, true, fmt, count, str(LANG_STOP_ABORT)); |
574 | splash(0, 0, true, str(LANG_PLAYLIST_LOAD)); | 924 | #else |
575 | playlist.in_ram = false; | 925 | splash(0, 0, true, fmt, count, str(LANG_OFF_ABORT)); |
576 | } else { | 926 | #endif |
577 | /* Assign a dummy filename */ | 927 | } |
578 | file = ""; | 928 | |
579 | playlist.in_ram = true; | 929 | /* |
930 | * Display buffer full message | ||
931 | */ | ||
932 | static void display_buffer_full(void) | ||
933 | { | ||
934 | lcd_clear_display(); | ||
935 | lcd_puts(0,0,str(LANG_PLAYINDICES_PLAYLIST)); | ||
936 | lcd_puts(0,1,str(LANG_PLAYINDICES_BUFFER)); | ||
937 | lcd_update(); | ||
938 | sleep(HZ*2); | ||
939 | lcd_clear_display(); | ||
940 | } | ||
941 | |||
942 | /* | ||
943 | * Initialize playlist entries at startup | ||
944 | */ | ||
945 | void playlist_init(void) | ||
946 | { | ||
947 | playlist.fd = -1; | ||
948 | playlist.control_fd = -1; | ||
949 | playlist.max_playlist_size = global_settings.max_files_in_playlist; | ||
950 | playlist.indices = buffer_alloc(playlist.max_playlist_size * sizeof(int)); | ||
951 | playlist.buffer_size = | ||
952 | AVERAGE_FILENAME_LENGTH * global_settings.max_files_in_dir; | ||
953 | playlist.buffer = buffer_alloc(playlist.buffer_size); | ||
954 | mutex_init(&playlist.control_mutex); | ||
955 | empty_playlist(true); | ||
956 | } | ||
957 | |||
958 | /* | ||
959 | * Create new playlist | ||
960 | */ | ||
961 | int playlist_create(char *dir, char *file) | ||
962 | { | ||
963 | empty_playlist(false); | ||
964 | |||
965 | playlist.control_fd = open(PLAYLIST_CONTROL_FILE, O_RDWR); | ||
966 | if (-1 == playlist.control_fd) | ||
967 | { | ||
968 | splash(HZ*2, 0, true, str(LANG_PLAYLIST_CONTROL_ACCESS_ERROR)); | ||
969 | return -1; | ||
580 | } | 970 | } |
581 | 971 | ||
582 | dirlen = strlen(dir); | 972 | if (!file) |
973 | { | ||
974 | file = ""; | ||
583 | 975 | ||
584 | /* If the dir does not end in trailing slash, we use a separator. | 976 | if (dir) |
585 | Otherwise we don't. */ | 977 | playlist.in_ram = true; |
586 | if('/' != dir[dirlen-1]) { | 978 | else |
587 | sep="/"; | 979 | dir = ""; /* empty playlist */ |
588 | dirlen++; | ||
589 | } | 980 | } |
590 | 981 | ||
591 | playlist.dirlen = dirlen; | 982 | update_playlist_filename(dir, file); |
592 | 983 | ||
593 | snprintf(playlist.filename, sizeof(playlist.filename), | 984 | if (fprintf(playlist.control_fd, "P:%d:%s:%s\n", |
594 | "%s%s%s", | 985 | PLAYLIST_CONTROL_FILE_VERSION, dir, file) > 0) |
595 | dir, sep, file); | 986 | fsync(playlist.control_fd); |
987 | else | ||
988 | { | ||
989 | splash(HZ*2, 0, true, str(LANG_PLAYLIST_CONTROL_UPDATE_ERROR)); | ||
990 | return -1; | ||
991 | } | ||
596 | 992 | ||
597 | /* add track indices to playlist data structure */ | 993 | /* load the playlist file */ |
598 | add_indices_to_playlist(); | 994 | if (file[0] != '\0') |
599 | 995 | add_indices_to_playlist(); | |
600 | if(global_settings.playlist_shuffle) { | 996 | |
601 | if(!playlist.in_ram) { | 997 | return 0; |
602 | splash(0, 0, true, str(LANG_PLAYLIST_SHUFFLE)); | 998 | } |
603 | randomise_playlist( random_seed ); | 999 | |
604 | } | 1000 | #define PLAYLIST_COMMAND_SIZE (MAX_PATH+12) |
605 | else { | 1001 | |
606 | int i; | 1002 | /* |
1003 | * Restore the playlist state based on control file commands. Called to | ||
1004 | * resume playback after shutdown. | ||
1005 | */ | ||
1006 | int playlist_resume(void) | ||
1007 | { | ||
1008 | char *buffer; | ||
1009 | int buflen; | ||
1010 | int nread; | ||
1011 | int total_read = 0; | ||
1012 | bool first = true; | ||
1013 | |||
1014 | enum { | ||
1015 | resume_playlist, | ||
1016 | resume_add, | ||
1017 | resume_queue, | ||
1018 | resume_delete, | ||
1019 | resume_shuffle, | ||
1020 | resume_unshuffle, | ||
1021 | resume_reset, | ||
1022 | resume_comment | ||
1023 | }; | ||
1024 | |||
1025 | /* use mp3 buffer for maximum load speed */ | ||
1026 | buflen = (mp3end - mp3buf); | ||
1027 | buffer = mp3buf; | ||
1028 | |||
1029 | empty_playlist(true); | ||
1030 | |||
1031 | playlist.control_fd = open(PLAYLIST_CONTROL_FILE, O_RDWR); | ||
1032 | if (-1 == playlist.control_fd) | ||
1033 | { | ||
1034 | splash(HZ*2, 0, true, str(LANG_PLAYLIST_CONTROL_ACCESS_ERROR)); | ||
1035 | return -1; | ||
1036 | } | ||
607 | 1037 | ||
608 | /* store the seek position before the shuffle */ | 1038 | /* read a small amount first to get the header */ |
609 | int seek_pos = playlist.indices[start_index]; | 1039 | nread = read(playlist.control_fd, buffer, |
1040 | PLAYLIST_COMMAND_SIZE<buflen?PLAYLIST_COMMAND_SIZE:buflen); | ||
1041 | if(nread <= 0) | ||
1042 | { | ||
1043 | splash(HZ*2, 0, true, str(LANG_PLAYLIST_CONTROL_ACCESS_ERROR)); | ||
1044 | return -1; | ||
1045 | } | ||
610 | 1046 | ||
611 | /* now shuffle around the indices */ | 1047 | while (1) |
612 | randomise_playlist( random_seed ); | 1048 | { |
1049 | int result = 0; | ||
1050 | int count; | ||
1051 | int current_command = resume_comment; | ||
1052 | int last_newline = 0; | ||
1053 | int str_count = -1; | ||
1054 | bool newline = true; | ||
1055 | bool exit_loop = false; | ||
1056 | char *p = buffer; | ||
1057 | char *str1 = NULL; | ||
1058 | char *str2 = NULL; | ||
1059 | char *str3 = NULL; | ||
1060 | |||
1061 | for(count=0; count<nread && !exit_loop; count++,p++) | ||
1062 | { | ||
1063 | /* Are we on a new line? */ | ||
1064 | if((*p == '\n') || (*p == '\r')) | ||
1065 | { | ||
1066 | *p = '\0'; | ||
613 | 1067 | ||
614 | if(!shuffled_index && global_settings.play_selected) { | 1068 | /* save last_newline in case we need to load more data */ |
615 | /* The given index was for the unshuffled list, so we need | 1069 | last_newline = count; |
616 | to figure out the index AFTER the shuffle has been made. | ||
617 | We scan for the seek position we remmber from before. */ | ||
618 | 1070 | ||
619 | for(i=0; i< playlist.amount; i++) { | 1071 | switch (current_command) |
620 | if(seek_pos == playlist.indices[i]) { | 1072 | { |
621 | /* here's the start song! yiepee! */ | 1073 | case resume_playlist: |
622 | playlist.index = i; | 1074 | { |
623 | playlist.first_index = i; | 1075 | /* str1=version str2=dir str3=file */ |
624 | break; /* now stop searching */ | 1076 | int version; |
1077 | |||
1078 | if (!str1) | ||
1079 | { | ||
1080 | result = -1; | ||
1081 | exit_loop = true; | ||
1082 | break; | ||
1083 | } | ||
1084 | |||
1085 | if (!str2) | ||
1086 | str2 = ""; | ||
1087 | |||
1088 | if (!str3) | ||
1089 | str3 = ""; | ||
1090 | |||
1091 | version = atoi(str1); | ||
1092 | |||
1093 | if (version != PLAYLIST_CONTROL_FILE_VERSION) | ||
1094 | { | ||
1095 | result = -1; | ||
1096 | exit_loop = true; | ||
1097 | break; | ||
1098 | } | ||
1099 | |||
1100 | update_playlist_filename(str2, str3); | ||
1101 | |||
1102 | if (str3[0] != '\0') | ||
1103 | { | ||
1104 | /* NOTE: add_indices_to_playlist() overwrites the | ||
1105 | mp3buf so we need to reload control file | ||
1106 | data */ | ||
1107 | add_indices_to_playlist(); | ||
1108 | } | ||
1109 | else if (str2[0] != '\0') | ||
1110 | { | ||
1111 | playlist.in_ram = true; | ||
1112 | resume_directory(str2); | ||
1113 | } | ||
1114 | |||
1115 | /* load the rest of the data */ | ||
1116 | first = false; | ||
1117 | exit_loop = true; | ||
1118 | |||
1119 | break; | ||
1120 | } | ||
1121 | case resume_add: | ||
1122 | case resume_queue: | ||
1123 | { | ||
1124 | /* str1=position str2=last_position str3=file */ | ||
1125 | int position, last_position; | ||
1126 | bool queue; | ||
1127 | |||
1128 | if (!str1 || !str2 || !str3) | ||
1129 | { | ||
1130 | result = -1; | ||
1131 | exit_loop = true; | ||
1132 | break; | ||
1133 | } | ||
1134 | |||
1135 | position = atoi(str1); | ||
1136 | last_position = atoi(str2); | ||
1137 | |||
1138 | queue = (current_command == resume_add)?false:true; | ||
1139 | |||
1140 | /* seek position is based on str3's position in | ||
1141 | buffer */ | ||
1142 | if (add_track_to_playlist(str3, position, queue, | ||
1143 | total_read+(str3-buffer)) < 0) | ||
1144 | return -1; | ||
1145 | |||
1146 | playlist.last_insert_pos = last_position; | ||
1147 | |||
1148 | break; | ||
1149 | } | ||
1150 | case resume_delete: | ||
1151 | { | ||
1152 | /* str1=position */ | ||
1153 | int position; | ||
1154 | |||
1155 | if (!str1) | ||
1156 | { | ||
1157 | result = -1; | ||
1158 | exit_loop = true; | ||
1159 | break; | ||
1160 | } | ||
1161 | |||
1162 | position = atoi(str1); | ||
1163 | |||
1164 | if (remove_track_from_playlist(position, | ||
1165 | false) < 0) | ||
1166 | return -1; | ||
1167 | |||
1168 | break; | ||
1169 | } | ||
1170 | case resume_shuffle: | ||
1171 | { | ||
1172 | /* str1=seed str2=first_index */ | ||
1173 | int seed; | ||
1174 | |||
1175 | if (!str1 || !str2) | ||
1176 | { | ||
1177 | result = -1; | ||
1178 | exit_loop = true; | ||
1179 | break; | ||
1180 | } | ||
1181 | |||
1182 | seed = atoi(str1); | ||
1183 | playlist.first_index = atoi(str2); | ||
1184 | |||
1185 | if (randomise_playlist(seed, false, false) < 0) | ||
1186 | return -1; | ||
1187 | |||
1188 | break; | ||
1189 | } | ||
1190 | case resume_unshuffle: | ||
1191 | { | ||
1192 | /* str1=first_index */ | ||
1193 | if (!str1) | ||
1194 | { | ||
1195 | result = -1; | ||
1196 | exit_loop = true; | ||
1197 | break; | ||
1198 | } | ||
1199 | |||
1200 | playlist.first_index = atoi(str1); | ||
1201 | |||
1202 | if (sort_playlist(false, false) < 0) | ||
1203 | return -1; | ||
1204 | |||
1205 | break; | ||
1206 | } | ||
1207 | case resume_reset: | ||
1208 | { | ||
1209 | playlist.last_insert_pos = -1; | ||
1210 | break; | ||
1211 | } | ||
1212 | case resume_comment: | ||
1213 | default: | ||
1214 | break; | ||
1215 | } | ||
1216 | |||
1217 | newline = true; | ||
1218 | |||
1219 | /* to ignore any extra newlines */ | ||
1220 | current_command = resume_comment; | ||
1221 | } | ||
1222 | else if(newline) | ||
1223 | { | ||
1224 | newline = false; | ||
1225 | |||
1226 | /* first non-comment line must always specify playlist */ | ||
1227 | if (first && *p != 'P' && *p != '#') | ||
1228 | { | ||
1229 | result = -1; | ||
1230 | exit_loop = true; | ||
1231 | break; | ||
1232 | } | ||
1233 | |||
1234 | switch (*p) | ||
1235 | { | ||
1236 | case 'P': | ||
1237 | /* playlist can only be specified once */ | ||
1238 | if (!first) | ||
1239 | { | ||
1240 | result = -1; | ||
1241 | exit_loop = true; | ||
1242 | break; | ||
1243 | } | ||
1244 | |||
1245 | current_command = resume_playlist; | ||
1246 | break; | ||
1247 | case 'A': | ||
1248 | current_command = resume_add; | ||
1249 | break; | ||
1250 | case 'Q': | ||
1251 | current_command = resume_queue; | ||
1252 | break; | ||
1253 | case 'D': | ||
1254 | current_command = resume_delete; | ||
1255 | break; | ||
1256 | case 'S': | ||
1257 | current_command = resume_shuffle; | ||
1258 | break; | ||
1259 | case 'U': | ||
1260 | current_command = resume_unshuffle; | ||
1261 | break; | ||
1262 | case 'R': | ||
1263 | current_command = resume_reset; | ||
1264 | break; | ||
1265 | case '#': | ||
1266 | current_command = resume_comment; | ||
1267 | break; | ||
1268 | default: | ||
1269 | result = -1; | ||
1270 | exit_loop = true; | ||
1271 | break; | ||
1272 | } | ||
1273 | |||
1274 | str_count = -1; | ||
1275 | str1 = NULL; | ||
1276 | str2 = NULL; | ||
1277 | str3 = NULL; | ||
1278 | } | ||
1279 | else if(current_command != resume_comment) | ||
1280 | { | ||
1281 | /* all control file strings are separated with a colon. | ||
1282 | Replace the colon with 0 to get proper strings that can be | ||
1283 | used by commands above */ | ||
1284 | if (*p == ':') | ||
1285 | { | ||
1286 | *p = '\0'; | ||
1287 | str_count++; | ||
1288 | |||
1289 | if ((count+1) < nread) | ||
1290 | { | ||
1291 | switch (str_count) | ||
1292 | { | ||
1293 | case 0: | ||
1294 | str1 = p+1; | ||
1295 | break; | ||
1296 | case 1: | ||
1297 | str2 = p+1; | ||
1298 | break; | ||
1299 | case 2: | ||
1300 | str3 = p+1; | ||
1301 | break; | ||
1302 | default: | ||
1303 | /* allow last string to contain colons */ | ||
1304 | *p = ':'; | ||
1305 | break; | ||
1306 | } | ||
625 | } | 1307 | } |
626 | } | 1308 | } |
627 | /* if we for any reason wouldn't find the index again, it | ||
628 | won't set the index again and we should start at index 0 | ||
629 | instead */ | ||
630 | } | 1309 | } |
631 | } | 1310 | } |
632 | } | ||
633 | 1311 | ||
634 | if (queue_resume) | 1312 | if (result < 0) |
635 | { | 1313 | { |
636 | /* update the queue indices */ | 1314 | splash(HZ*2, 0, true, str(LANG_PLAYLIST_CONTROL_INVALID)); |
637 | add_indices_to_queuelist(queue_resume_index); | 1315 | return result; |
1316 | } | ||
638 | 1317 | ||
639 | if (queue_resume == QUEUE_BEGIN_PLAYLIST) | 1318 | if (!newline || (exit_loop && count<nread)) |
640 | { | 1319 | { |
641 | /* begin with current playlist index */ | 1320 | /* We didn't end on a newline or we exited loop prematurely. |
642 | playlist.start_queue = 1; | 1321 | Either way, re-read the remainder. |
643 | playlist.index++; /* so we begin at the correct track */ | 1322 | NOTE: because of this, control file must always end with a |
1323 | newline */ | ||
1324 | count = last_newline; | ||
1325 | lseek(playlist.control_fd, total_read+count, SEEK_SET); | ||
644 | } | 1326 | } |
1327 | |||
1328 | total_read += count; | ||
1329 | |||
1330 | if (first) | ||
1331 | /* still looking for header */ | ||
1332 | nread = read(playlist.control_fd, buffer, | ||
1333 | PLAYLIST_COMMAND_SIZE<buflen?PLAYLIST_COMMAND_SIZE:buflen); | ||
1334 | else | ||
1335 | nread = read(playlist.control_fd, buffer, buflen); | ||
1336 | |||
1337 | /* Terminate on EOF */ | ||
1338 | if(nread <= 0) | ||
1339 | break; | ||
1340 | } | ||
1341 | |||
1342 | return 0; | ||
1343 | } | ||
1344 | |||
1345 | /* | ||
1346 | * Add track to in_ram playlist. Used when playing directories. | ||
1347 | */ | ||
1348 | int playlist_add(char *filename) | ||
1349 | { | ||
1350 | int len = strlen(filename); | ||
1351 | |||
1352 | if((len+1 > playlist.buffer_size - playlist.buffer_end_pos) || | ||
1353 | (playlist.amount >= playlist.max_playlist_size)) | ||
1354 | { | ||
1355 | display_buffer_full(); | ||
1356 | return -1; | ||
645 | } | 1357 | } |
646 | 1358 | ||
647 | /* also make the first song get playing */ | 1359 | playlist.indices[playlist.amount++] = playlist.buffer_end_pos; |
648 | mpeg_play(start_offset); | ||
649 | 1360 | ||
650 | return playlist.index; | 1361 | strcpy(&playlist.buffer[playlist.buffer_end_pos], filename); |
1362 | playlist.buffer_end_pos += len; | ||
1363 | playlist.buffer[playlist.buffer_end_pos++] = '\0'; | ||
1364 | |||
1365 | return 0; | ||
651 | } | 1366 | } |
652 | 1367 | ||
653 | /* | 1368 | /* |
654 | * calculate track offsets within a playlist file | 1369 | * Insert track into playlist at specified position (or one of the special |
1370 | * positions). Returns position where track was inserted or -1 if error. | ||
655 | */ | 1371 | */ |
656 | void add_indices_to_playlist(void) | 1372 | int playlist_insert_track(char *filename, int position, bool queue) |
1373 | { | ||
1374 | int result = add_track_to_playlist(filename, position, queue, -1); | ||
1375 | |||
1376 | if (result != -1) | ||
1377 | { | ||
1378 | fsync(playlist.control_fd); | ||
1379 | mpeg_flush_and_reload_tracks(); | ||
1380 | } | ||
1381 | |||
1382 | return result; | ||
1383 | } | ||
1384 | |||
1385 | /* | ||
1386 | * Insert all tracks from specified directory into playlist. | ||
1387 | */ | ||
1388 | int playlist_insert_directory(char *dirname, int position, bool queue) | ||
657 | { | 1389 | { |
658 | int nread; | ||
659 | int i = 0; | ||
660 | int count = 0; | 1390 | int count = 0; |
661 | unsigned char* buffer = playlist_buffer; | 1391 | int result; |
662 | int buflen = playlist.buffer_size; | 1392 | char *count_str; |
663 | bool store_index; | ||
664 | unsigned char *p; | ||
665 | 1393 | ||
666 | if(!playlist.in_ram) { | 1394 | if (queue) |
667 | if(-1 == playlist.fd) | 1395 | count_str = str(LANG_PLAYLIST_QUEUE_COUNT); |
668 | playlist.fd = open(playlist.filename, O_RDONLY); | 1396 | else |
669 | if(-1 == playlist.fd) | 1397 | count_str = str(LANG_PLAYLIST_INSERT_COUNT); |
670 | return; /* failure */ | 1398 | |
671 | 1399 | display_playlist_count(count, count_str); | |
672 | #ifndef SIMULATOR | 1400 | |
673 | /* use mp3 buffer for maximum load speed */ | 1401 | result = add_directory_to_playlist(dirname, &position, queue, &count); |
674 | buflen = (mp3end - mp3buf); | 1402 | fsync(playlist.control_fd); |
675 | buffer = mp3buf; | 1403 | |
1404 | display_playlist_count(count, count_str); | ||
1405 | mpeg_flush_and_reload_tracks(); | ||
1406 | |||
1407 | return result; | ||
1408 | } | ||
1409 | |||
1410 | /* | ||
1411 | * Insert all tracks from specified playlist into dynamic playlist | ||
1412 | */ | ||
1413 | int playlist_insert_playlist(char *filename, int position, bool queue) | ||
1414 | { | ||
1415 | int fd; | ||
1416 | int max; | ||
1417 | char *temp_ptr; | ||
1418 | char *dir; | ||
1419 | char *count_str; | ||
1420 | char temp_buf[MAX_PATH+1]; | ||
1421 | char trackname[MAX_PATH+1]; | ||
1422 | int count = 0; | ||
1423 | int result = 0; | ||
1424 | |||
1425 | fd = open(filename, O_RDONLY); | ||
1426 | if (fd < 0) | ||
1427 | { | ||
1428 | splash(HZ*2, 0, true, str(LANG_PLAYLIST_ACCESS_ERROR)); | ||
1429 | return -1; | ||
1430 | } | ||
1431 | |||
1432 | /* we need the directory name for formatting purposes */ | ||
1433 | dir = filename; | ||
1434 | |||
1435 | temp_ptr = strrchr(filename+1,'/'); | ||
1436 | if (temp_ptr) | ||
1437 | *temp_ptr = 0; | ||
1438 | else | ||
1439 | dir = "/"; | ||
1440 | |||
1441 | if (queue) | ||
1442 | count_str = str(LANG_PLAYLIST_QUEUE_COUNT); | ||
1443 | else | ||
1444 | count_str = str(LANG_PLAYLIST_INSERT_COUNT); | ||
1445 | |||
1446 | display_playlist_count(count, count_str); | ||
1447 | |||
1448 | while ((max = read_line(fd, temp_buf, sizeof(temp_buf))) > 0) | ||
1449 | { | ||
1450 | /* user abort */ | ||
1451 | #ifdef HAVE_PLAYER_KEYPAD | ||
1452 | if (button_get(false) == BUTTON_STOP) | ||
1453 | #else | ||
1454 | if (button_get(false) == BUTTON_OFF) | ||
676 | #endif | 1455 | #endif |
1456 | break; | ||
1457 | |||
1458 | if (temp_buf[0] != '#' || temp_buf[0] != '\0') | ||
1459 | { | ||
1460 | int insert_pos; | ||
1461 | |||
1462 | /* we need to format so that relative paths are correctly | ||
1463 | handled */ | ||
1464 | if (format_track_path(trackname, temp_buf, sizeof(trackname), max, | ||
1465 | dir) < 0) | ||
1466 | { | ||
1467 | result = -1; | ||
1468 | break; | ||
1469 | } | ||
1470 | |||
1471 | insert_pos = add_track_to_playlist(trackname, position, queue, | ||
1472 | -1); | ||
1473 | |||
1474 | if (insert_pos < 0) | ||
1475 | { | ||
1476 | result = -1; | ||
1477 | break; | ||
1478 | } | ||
1479 | |||
1480 | /* Make sure tracks are inserted in correct order if user | ||
1481 | requests INSERT_FIRST */ | ||
1482 | if (position == PLAYLIST_INSERT_FIRST || position >= 0) | ||
1483 | position = insert_pos + 1; | ||
1484 | |||
1485 | count++; | ||
1486 | |||
1487 | if ((count%PLAYLIST_DISPLAY_COUNT) == 0) | ||
1488 | { | ||
1489 | display_playlist_count(count, count_str); | ||
1490 | |||
1491 | if (count == PLAYLIST_DISPLAY_COUNT) | ||
1492 | mpeg_flush_and_reload_tracks(); | ||
1493 | } | ||
1494 | } | ||
1495 | |||
1496 | /* let the other threads work */ | ||
1497 | yield(); | ||
677 | } | 1498 | } |
678 | 1499 | ||
679 | store_index = true; | 1500 | close(fd); |
1501 | fsync(playlist.control_fd); | ||
680 | 1502 | ||
681 | mpeg_stop(); | 1503 | *temp_ptr = '/'; |
1504 | |||
1505 | display_playlist_count(count, count_str); | ||
1506 | mpeg_flush_and_reload_tracks(); | ||
1507 | |||
1508 | return result; | ||
1509 | } | ||
1510 | |||
1511 | /* delete track at specified index */ | ||
1512 | int playlist_delete(int index) | ||
1513 | { | ||
1514 | int result = remove_track_from_playlist(index, true); | ||
1515 | |||
1516 | if (result != -1) | ||
1517 | mpeg_flush_and_reload_tracks(); | ||
1518 | |||
1519 | return result; | ||
1520 | } | ||
1521 | |||
1522 | /* shuffle newly created playlist using random seed. */ | ||
1523 | int playlist_shuffle(int random_seed, int start_index) | ||
1524 | { | ||
1525 | unsigned int seek_pos = 0; | ||
1526 | bool start_current = false; | ||
1527 | |||
1528 | if (start_index >= 0 && global_settings.play_selected) | ||
1529 | { | ||
1530 | /* store the seek position before the shuffle */ | ||
1531 | seek_pos = playlist.indices[start_index]; | ||
1532 | playlist.index = playlist.first_index = start_index; | ||
1533 | start_current = true; | ||
1534 | } | ||
1535 | |||
1536 | splash(0, 0, true, str(LANG_PLAYLIST_SHUFFLE)); | ||
682 | 1537 | ||
683 | while(1) | 1538 | randomise_playlist(random_seed, start_current, true); |
1539 | |||
1540 | return playlist.index; | ||
1541 | } | ||
1542 | |||
1543 | /* shuffle currently playing playlist */ | ||
1544 | int playlist_randomise(unsigned int seed, bool start_current) | ||
1545 | { | ||
1546 | int result = randomise_playlist(seed, start_current, true); | ||
1547 | |||
1548 | if (result != -1) | ||
1549 | mpeg_flush_and_reload_tracks(); | ||
1550 | |||
1551 | return result; | ||
1552 | } | ||
1553 | |||
1554 | /* sort currently playing playlist */ | ||
1555 | int playlist_sort(bool start_current) | ||
1556 | { | ||
1557 | int result = sort_playlist(start_current, true); | ||
1558 | |||
1559 | if (result != -1) | ||
1560 | mpeg_flush_and_reload_tracks(); | ||
1561 | |||
1562 | return result; | ||
1563 | } | ||
1564 | |||
1565 | /* start playing current playlist at specified index/offset */ | ||
1566 | int playlist_start(int start_index, int offset) | ||
1567 | { | ||
1568 | playlist.index = start_index; | ||
1569 | mpeg_play(offset); | ||
1570 | |||
1571 | return 0; | ||
1572 | } | ||
1573 | |||
1574 | /* Returns false if 'steps' is out of bounds, else true */ | ||
1575 | bool playlist_check(int steps) | ||
1576 | { | ||
1577 | int index = get_next_index(steps); | ||
1578 | return (index >= 0); | ||
1579 | } | ||
1580 | |||
1581 | /* get trackname of track that is "steps" away from current playing track. | ||
1582 | NULL is used to identify end of playlist */ | ||
1583 | char* playlist_peek(int steps) | ||
1584 | { | ||
1585 | int seek; | ||
1586 | int fd; | ||
1587 | char *temp_ptr; | ||
1588 | int index; | ||
1589 | bool control_file; | ||
1590 | |||
1591 | index = get_next_index(steps); | ||
1592 | if (index < 0) | ||
1593 | return NULL; | ||
1594 | |||
1595 | control_file = playlist.indices[index] & PLAYLIST_INSERT_TYPE_MASK; | ||
1596 | seek = playlist.indices[index] & PLAYLIST_SEEK_MASK; | ||
1597 | |||
1598 | if (get_filename(seek, control_file, now_playing, MAX_PATH+1) < 0) | ||
1599 | return NULL; | ||
1600 | |||
1601 | temp_ptr = now_playing; | ||
1602 | |||
1603 | if (!playlist.in_ram || control_file) | ||
684 | { | 1604 | { |
685 | if(playlist.in_ram) { | 1605 | /* remove bogus dirs from beginning of path |
686 | nread = playlist_end_pos; | 1606 | (workaround for buggy playlist creation tools) */ |
687 | } else { | 1607 | while (temp_ptr) |
688 | nread = read(playlist.fd, buffer, buflen); | 1608 | { |
689 | /* Terminate on EOF */ | 1609 | fd = open(temp_ptr, O_RDONLY); |
690 | if(nread <= 0) | 1610 | if (fd >= 0) |
1611 | { | ||
1612 | close(fd); | ||
691 | break; | 1613 | break; |
1614 | } | ||
1615 | |||
1616 | temp_ptr = strchr(temp_ptr+1, '/'); | ||
692 | } | 1617 | } |
693 | 1618 | ||
694 | p = buffer; | 1619 | if (!temp_ptr) |
1620 | { | ||
1621 | /* Even though this is an invalid file, we still need to pass a | ||
1622 | file name to the caller because NULL is used to indicate end | ||
1623 | of playlist */ | ||
1624 | return now_playing; | ||
1625 | } | ||
1626 | } | ||
695 | 1627 | ||
696 | for(count=0; count < nread; count++,p++) { | 1628 | return temp_ptr; |
1629 | } | ||
697 | 1630 | ||
698 | /* Are we on a new line? */ | 1631 | /* |
699 | if((*p == '\n') || (*p == '\r')) | 1632 | * Update indices as track has changed |
1633 | */ | ||
1634 | int playlist_next(int steps) | ||
1635 | { | ||
1636 | int index; | ||
1637 | |||
1638 | if (steps > 0) | ||
1639 | { | ||
1640 | int i, j; | ||
1641 | |||
1642 | /* We need to delete all the queued songs */ | ||
1643 | for (i=0, j=steps; i<j; i++) | ||
1644 | { | ||
1645 | index = get_next_index(i); | ||
1646 | |||
1647 | if (playlist.indices[index] & PLAYLIST_QUEUE_MASK) | ||
700 | { | 1648 | { |
701 | store_index = true; | 1649 | remove_track_from_playlist(index, true); |
702 | } | 1650 | steps--; /* one less track */ |
703 | else if(store_index) | 1651 | } |
1652 | } | ||
1653 | } | ||
1654 | |||
1655 | index = get_next_index(steps); | ||
1656 | playlist.index = index; | ||
1657 | |||
1658 | if (playlist.last_insert_pos >= 0) | ||
1659 | { | ||
1660 | /* check to see if we've gone beyond the last inserted track */ | ||
1661 | int rot_index = index; | ||
1662 | int rot_last_pos = playlist.last_insert_pos; | ||
1663 | |||
1664 | rot_index -= playlist.first_index; | ||
1665 | if (rot_index < 0) | ||
1666 | rot_index += playlist.amount; | ||
1667 | |||
1668 | rot_last_pos -= playlist.first_index; | ||
1669 | if (rot_last_pos < 0) | ||
1670 | rot_last_pos += playlist.amount; | ||
1671 | |||
1672 | if (rot_index > rot_last_pos) | ||
1673 | { | ||
1674 | /* reset last inserted track */ | ||
1675 | playlist.last_insert_pos = -1; | ||
1676 | |||
1677 | if (playlist.control_fd >= 0) | ||
704 | { | 1678 | { |
705 | store_index = false; | 1679 | int result = -1; |
706 | 1680 | ||
707 | if(playlist.in_ram || (*p != '#')) | 1681 | mutex_lock(&playlist.control_mutex); |
1682 | |||
1683 | if (lseek(playlist.control_fd, 0, SEEK_END) >= 0) | ||
708 | { | 1684 | { |
709 | /* Store a new entry */ | 1685 | if (fprintf(playlist.control_fd, "R\n") > 0) |
710 | playlist.indices[ playlist.amount ] = i+count; | 1686 | { |
711 | playlist.amount++; | 1687 | fsync(playlist.control_fd); |
712 | if ( playlist.amount >= playlist.max_playlist_size ) { | 1688 | result = 0; |
713 | lcd_clear_display(); | ||
714 | lcd_puts(0,0,str(LANG_PLAYINDICES_PLAYLIST)); | ||
715 | lcd_puts(0,1,str(LANG_PLAYINDICES_BUFFER)); | ||
716 | lcd_update(); | ||
717 | sleep(HZ*2); | ||
718 | lcd_clear_display(); | ||
719 | |||
720 | return; | ||
721 | } | 1689 | } |
722 | } | 1690 | } |
1691 | |||
1692 | mutex_unlock(&playlist.control_mutex); | ||
1693 | |||
1694 | if (result < 0) | ||
1695 | { | ||
1696 | splash(HZ*2, 0, true, | ||
1697 | str(LANG_PLAYLIST_CONTROL_UPDATE_ERROR)); | ||
1698 | return result; | ||
1699 | } | ||
723 | } | 1700 | } |
724 | } | 1701 | } |
1702 | } | ||
725 | 1703 | ||
726 | i+= count; | 1704 | return index; |
1705 | } | ||
727 | 1706 | ||
728 | if(playlist.in_ram) | 1707 | /* Get resume info for current playing song. If return value is -1 then |
729 | break; | 1708 | settings shouldn't be saved. */ |
730 | } | 1709 | int playlist_get_resume_info(int *resume_index) |
1710 | { | ||
1711 | *resume_index = playlist.index; | ||
1712 | |||
1713 | return 0; | ||
731 | } | 1714 | } |
732 | 1715 | ||
733 | /* | 1716 | /* Returns index of current playing track for display purposes. This value |
734 | * randomly rearrange the array of indices for the playlist | 1717 | should not be used for resume purposes as it doesn't represent the actual |
735 | */ | 1718 | index into the playlist */ |
736 | void randomise_playlist( unsigned int seed ) | 1719 | int playlist_get_display_index(void) |
737 | { | 1720 | { |
738 | int count; | 1721 | int index = playlist.index; |
739 | int candidate; | ||
740 | int store; | ||
741 | |||
742 | /* seed with the given seed */ | ||
743 | srand( seed ); | ||
744 | 1722 | ||
745 | /* randomise entire indices list */ | 1723 | /* first_index should always be index 0 for display purposes */ |
746 | for(count = playlist.amount - 1; count >= 0; count--) | 1724 | index -= playlist.first_index; |
747 | { | 1725 | if (index < 0) |
748 | /* the rand is from 0 to RAND_MAX, so adjust to our value range */ | 1726 | index += playlist.amount; |
749 | candidate = rand() % (count + 1); | ||
750 | 1727 | ||
751 | /* now swap the values at the 'count' and 'candidate' positions */ | 1728 | return (index+1); |
752 | store = playlist.indices[candidate]; | 1729 | } |
753 | playlist.indices[candidate] = playlist.indices[count]; | ||
754 | playlist.indices[count] = store; | ||
755 | } | ||
756 | 1730 | ||
757 | mpeg_flush_and_reload_tracks(); | 1731 | /* returns number of tracks in playlist (includes queued/inserted tracks) */ |
1732 | int playlist_amount(void) | ||
1733 | { | ||
1734 | return playlist.amount; | ||
758 | } | 1735 | } |
759 | 1736 | ||
760 | static int compare(const void* p1, const void* p2) | 1737 | /* returns playlist name */ |
1738 | char *playlist_name(char *buf, int buf_size) | ||
761 | { | 1739 | { |
762 | int* e1 = (int*) p1; | 1740 | char *sep; |
763 | int* e2 = (int*) p2; | 1741 | |
1742 | snprintf(buf, buf_size, "%s", playlist.filename+playlist.dirlen); | ||
1743 | |||
1744 | if (0 == buf[0]) | ||
1745 | return NULL; | ||
764 | 1746 | ||
765 | return *e1 - *e2; | 1747 | /* Remove extension */ |
1748 | sep = strrchr(buf, '.'); | ||
1749 | if (NULL != sep) | ||
1750 | *sep = 0; | ||
1751 | |||
1752 | return buf; | ||
766 | } | 1753 | } |
767 | 1754 | ||
768 | /* | 1755 | /* save the current dynamic playlist to specified file */ |
769 | * Sort the array of indices for the playlist. If start_current is true then | 1756 | int playlist_save(char *filename) |
770 | * set the index to the new index of the current song. | ||
771 | */ | ||
772 | void sort_playlist(bool start_current) | ||
773 | { | 1757 | { |
774 | int i; | 1758 | int fd; |
775 | int current = playlist.indices[playlist.index]; | 1759 | int i, index; |
1760 | int count = 0; | ||
1761 | char tmp_buf[MAX_PATH+1]; | ||
1762 | int result = 0; | ||
776 | 1763 | ||
777 | if (playlist.amount > 0) | 1764 | if (playlist.amount <= 0) |
1765 | return -1; | ||
1766 | |||
1767 | /* use current working directory as base for pathname */ | ||
1768 | if (format_track_path(tmp_buf, filename, sizeof(tmp_buf), | ||
1769 | strlen(filename)+1, getcwd(NULL, -1)) < 0) | ||
1770 | return -1; | ||
1771 | |||
1772 | fd = open(tmp_buf, O_CREAT|O_WRONLY|O_TRUNC); | ||
1773 | if (fd < 0) | ||
778 | { | 1774 | { |
779 | qsort(playlist.indices, playlist.amount, sizeof(playlist.indices[0]), compare); | 1775 | splash(HZ*2, 0, true, str(LANG_PLAYLIST_ACCESS_ERROR)); |
1776 | return -1; | ||
780 | } | 1777 | } |
781 | 1778 | ||
782 | if (start_current) | 1779 | display_playlist_count(count, str(LANG_PLAYLIST_SAVE_COUNT)); |
1780 | |||
1781 | index = playlist.first_index; | ||
1782 | for (i=0; i<playlist.amount; i++) | ||
783 | { | 1783 | { |
784 | /* Set the index to the current song */ | 1784 | bool control_file; |
785 | for (i=0; i<playlist.amount; i++) | 1785 | bool queue; |
1786 | int seek; | ||
1787 | |||
1788 | /* user abort */ | ||
1789 | #ifdef HAVE_PLAYER_KEYPAD | ||
1790 | if (button_get(false) == BUTTON_STOP) | ||
1791 | #else | ||
1792 | if (button_get(false) == BUTTON_OFF) | ||
1793 | #endif | ||
1794 | break; | ||
1795 | |||
1796 | control_file = playlist.indices[index] & PLAYLIST_INSERT_TYPE_MASK; | ||
1797 | queue = playlist.indices[index] & PLAYLIST_QUEUE_MASK; | ||
1798 | seek = playlist.indices[index] & PLAYLIST_SEEK_MASK; | ||
1799 | |||
1800 | /* Don't save queued files */ | ||
1801 | if (!queue) | ||
786 | { | 1802 | { |
787 | if (playlist.indices[i] == current) | 1803 | if (get_filename(seek, control_file, tmp_buf, MAX_PATH+1) < 0) |
788 | { | 1804 | { |
789 | playlist.index = i; | 1805 | result = -1; |
790 | break; | 1806 | break; |
791 | } | 1807 | } |
1808 | |||
1809 | if (fprintf(fd, "%s\n", tmp_buf) < 0) | ||
1810 | { | ||
1811 | splash(HZ*2, 0, true, | ||
1812 | str(LANG_PLAYLIST_CONTROL_UPDATE_ERROR)); | ||
1813 | result = -1; | ||
1814 | break; | ||
1815 | } | ||
1816 | |||
1817 | count++; | ||
1818 | |||
1819 | if ((count%PLAYLIST_DISPLAY_COUNT) == 0) | ||
1820 | display_playlist_count(count, str(LANG_PLAYLIST_SAVE_COUNT)); | ||
1821 | |||
1822 | yield(); | ||
792 | } | 1823 | } |
1824 | |||
1825 | index = (index+1)%playlist.amount; | ||
793 | } | 1826 | } |
794 | 1827 | ||
795 | mpeg_flush_and_reload_tracks(); | 1828 | display_playlist_count(count, str(LANG_PLAYLIST_SAVE_COUNT)); |
1829 | |||
1830 | close(fd); | ||
1831 | |||
1832 | return result; | ||
796 | } | 1833 | } |
diff --git a/apps/playlist.h b/apps/playlist.h index 9ff5122f44..a5318cd202 100644 --- a/apps/playlist.h +++ b/apps/playlist.h | |||
@@ -22,57 +22,56 @@ | |||
22 | 22 | ||
23 | #include <stdbool.h> | 23 | #include <stdbool.h> |
24 | #include "file.h" | 24 | #include "file.h" |
25 | #include "applimits.h" | 25 | #include "kernel.h" |
26 | 26 | ||
27 | /* playlist data */ | 27 | /* playlist data */ |
28 | 28 | ||
29 | struct playlist_info | 29 | struct playlist_info |
30 | { | 30 | { |
31 | char filename[MAX_PATH]; /* path name of m3u playlist on disk */ | 31 | char filename[MAX_PATH]; /* path name of m3u playlist on disk */ |
32 | int fd; /* file descriptor of the open playlist */ | 32 | int fd; /* descriptor of the open playlist file */ |
33 | int control_fd; /* descriptor of the open control file */ | ||
33 | int dirlen; /* Length of the path to the playlist file */ | 34 | int dirlen; /* Length of the path to the playlist file */ |
34 | int *indices; /* array of indices */ | 35 | unsigned int *indices; /* array of indices */ |
35 | int max_playlist_size; /* Max number of files in playlist. Mirror of | 36 | int max_playlist_size; /* Max number of files in playlist. Mirror of |
36 | global_settings.max_files_in_playlist */ | 37 | global_settings.max_files_in_playlist */ |
37 | int buffer_size; /* Playlist buffer size */ | 38 | bool in_ram; /* playlist stored in ram (dirplay) */ |
39 | char *buffer; /* buffer for in-ram playlists */ | ||
40 | int buffer_size; /* size of buffer */ | ||
41 | int buffer_end_pos; /* last position where buffer was written */ | ||
38 | int index; /* index of current playing track */ | 42 | int index; /* index of current playing track */ |
39 | int first_index; /* index of first song in playlist */ | 43 | int first_index; /* index of first song in playlist */ |
40 | int seed; /* random seed */ | ||
41 | int amount; /* number of tracks in the index */ | 44 | int amount; /* number of tracks in the index */ |
42 | bool in_ram; /* True if the playlist is RAM-based */ | 45 | int last_insert_pos; /* last position we inserted a track */ |
43 | 46 | struct mutex control_mutex; /* mutex for control file access */ | |
44 | /* Queue function */ | ||
45 | int queue_indices[MAX_QUEUED_FILES]; /* array of queue indices */ | ||
46 | int last_queue_index; /* index of last queued track */ | ||
47 | int queue_index; /* index of current playing queued track */ | ||
48 | int num_queued; /* number of songs queued */ | ||
49 | int start_queue; /* the first song was queued */ | ||
50 | }; | 47 | }; |
51 | 48 | ||
52 | extern struct playlist_info playlist; | ||
53 | extern bool playlist_shuffle; | ||
54 | |||
55 | void playlist_init(void); | 49 | void playlist_init(void); |
56 | int play_list(char *dir, char *file, int start_index, | 50 | int playlist_create(char *dir, char *file); |
57 | bool shuffled_index, int start_offset, | 51 | int playlist_resume(void); |
58 | int random_seed, int first_index, int queue_resume, | ||
59 | int queue_resume_index); | ||
60 | char* playlist_peek(int steps); | ||
61 | char* playlist_name(char *name, int name_size); | ||
62 | int playlist_next(int steps); | ||
63 | bool playlist_check(int steps); | ||
64 | void randomise_playlist( unsigned int seed ); | ||
65 | void sort_playlist(bool start_current); | ||
66 | void add_indices_to_playlist(void); | ||
67 | void playlist_clear(void); | ||
68 | int playlist_add(char *filename); | 52 | int playlist_add(char *filename); |
69 | int queue_add(char *filename); | 53 | int playlist_insert_track(char *filename, int position, bool queue); |
54 | int playlist_insert_directory(char *dirname, int position, bool queue); | ||
55 | int playlist_insert_playlist(char *filename, int position, bool queue); | ||
56 | int playlist_delete(int index); | ||
57 | int playlist_shuffle(int random_seed, int start_index); | ||
58 | int playlist_randomise(unsigned int seed, bool start_current); | ||
59 | int playlist_sort(bool start_current); | ||
60 | int playlist_start(int start_index, int offset); | ||
61 | bool playlist_check(int steps); | ||
62 | char *playlist_peek(int steps); | ||
63 | int playlist_next(int steps); | ||
64 | int playlist_get_resume_info(int *resume_index); | ||
65 | int playlist_get_display_index(void); | ||
70 | int playlist_amount(void); | 66 | int playlist_amount(void); |
71 | int playlist_first_index(void); | 67 | char *playlist_name(char *buf, int buf_size); |
72 | int playlist_get_resume_info(int *resume_index, int *queue_resume, | 68 | int playlist_save(char *filename); |
73 | int *queue_resume_index); | ||
74 | 69 | ||
75 | enum { QUEUE_OFF, QUEUE_BEGIN_QUEUE, QUEUE_BEGIN_PLAYLIST, NUM_QUEUE_MODES }; | 70 | enum { |
71 | PLAYLIST_PREPEND = -1, | ||
72 | PLAYLIST_INSERT = -2, | ||
73 | PLAYLIST_INSERT_LAST = -3, | ||
74 | PLAYLIST_INSERT_FIRST = -4 | ||
75 | }; | ||
76 | 76 | ||
77 | #endif /* __PLAYLIST_H__ */ | 77 | #endif /* __PLAYLIST_H__ */ |
78 | |||
diff --git a/apps/playlist_menu.c b/apps/playlist_menu.c new file mode 100644 index 0000000000..3508240efe --- /dev/null +++ b/apps/playlist_menu.c | |||
@@ -0,0 +1,71 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2002 Björn Stenberg | ||
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 | #include <string.h> | ||
21 | |||
22 | #include "menu.h" | ||
23 | #include "file.h" | ||
24 | #include "keyboard.h" | ||
25 | #include "playlist.h" | ||
26 | #include "tree.h" | ||
27 | #include "settings.h" | ||
28 | |||
29 | #include "lang.h" | ||
30 | |||
31 | #define DEFAULT_PLAYLIST_NAME "/dynamic.m3u" | ||
32 | |||
33 | static bool save_playlist(void) | ||
34 | { | ||
35 | char filename[MAX_PATH+1]; | ||
36 | |||
37 | strncpy(filename, DEFAULT_PLAYLIST_NAME, sizeof(filename)); | ||
38 | |||
39 | if (!kbd_input(filename, sizeof(filename))) | ||
40 | { | ||
41 | playlist_save(filename); | ||
42 | |||
43 | /* reload in case playlist was saved to cwd */ | ||
44 | reload_directory(); | ||
45 | } | ||
46 | |||
47 | return false; | ||
48 | } | ||
49 | |||
50 | static bool recurse_directory(void) | ||
51 | { | ||
52 | return (set_bool( str(LANG_RECURSE_DIRECTORY), | ||
53 | &global_settings.recursive_dir_insert)); | ||
54 | } | ||
55 | |||
56 | bool playlist_menu(void) | ||
57 | { | ||
58 | int m; | ||
59 | bool result; | ||
60 | |||
61 | struct menu_items items[] = { | ||
62 | { str(LANG_CREATE_PLAYLIST), create_playlist }, | ||
63 | { str(LANG_SAVE_DYNAMIC_PLAYLIST), save_playlist }, | ||
64 | { str(LANG_RECURSE_DIRECTORY), recurse_directory }, | ||
65 | }; | ||
66 | |||
67 | m = menu_init( items, sizeof items / sizeof(struct menu_items) ); | ||
68 | result = menu_run(m); | ||
69 | menu_exit(m); | ||
70 | return result; | ||
71 | } | ||
diff --git a/apps/playlist_menu.h b/apps/playlist_menu.h new file mode 100644 index 0000000000..e10fc81299 --- /dev/null +++ b/apps/playlist_menu.h | |||
@@ -0,0 +1,24 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2002 Björn Stenberg | ||
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 | #ifndef _PLAYLIST_MENU_H | ||
20 | #define _PLAYLIST_MENU_H | ||
21 | |||
22 | bool playlist_menu(void); | ||
23 | |||
24 | #endif | ||
diff --git a/apps/screens.c b/apps/screens.c index 6055be3a2d..eb31eff5cf 100644 --- a/apps/screens.c +++ b/apps/screens.c | |||
@@ -341,9 +341,9 @@ bool f2_screen(void) | |||
341 | if(mpeg_status() & MPEG_STATUS_PLAY) | 341 | if(mpeg_status() & MPEG_STATUS_PLAY) |
342 | { | 342 | { |
343 | if (global_settings.playlist_shuffle) | 343 | if (global_settings.playlist_shuffle) |
344 | randomise_playlist(current_tick); | 344 | playlist_randomise(current_tick, true); |
345 | else | 345 | else |
346 | sort_playlist(true); | 346 | playlist_sort(true); |
347 | } | 347 | } |
348 | used = true; | 348 | used = true; |
349 | break; | 349 | break; |
diff --git a/apps/settings.c b/apps/settings.c index d6e555f03e..e5fbfb5e06 100644 --- a/apps/settings.c +++ b/apps/settings.c | |||
@@ -62,7 +62,7 @@ | |||
62 | struct user_settings global_settings; | 62 | struct user_settings global_settings; |
63 | char rockboxdir[] = ROCKBOX_DIR; /* config/font/data file directory */ | 63 | char rockboxdir[] = ROCKBOX_DIR; /* config/font/data file directory */ |
64 | 64 | ||
65 | #define CONFIG_BLOCK_VERSION 6 | 65 | #define CONFIG_BLOCK_VERSION 7 |
66 | #define CONFIG_BLOCK_SIZE 512 | 66 | #define CONFIG_BLOCK_SIZE 512 |
67 | #define RTC_BLOCK_SIZE 44 | 67 | #define RTC_BLOCK_SIZE 44 |
68 | 68 | ||
@@ -98,10 +98,10 @@ offset abs | |||
98 | 0x12 0x26 <(int) Resume playlist index, or -1 if no playlist resume> | 98 | 0x12 0x26 <(int) Resume playlist index, or -1 if no playlist resume> |
99 | 0x16 0x2a <(int) Byte offset into resume file> | 99 | 0x16 0x2a <(int) Byte offset into resume file> |
100 | 0x1a 0x2e <time until disk spindown> | 100 | 0x1a 0x2e <time until disk spindown> |
101 | 0x1b 0x2f <browse current, play selected, queue_resume> | 101 | 0x1b 0x2f <browse current, play selected, recursive dir insert> |
102 | 0x1c 0x30 <peak meter hold timeout (bit 0-4), | 102 | 0x1c 0x30 <peak meter hold timeout (bit 0-4), |
103 | rec_editable (bit 7)> | 103 | rec_editable (bit 7)> |
104 | 0x1d 0x31 <(int) queue resume index> | 104 | 0x1d 0x31 <unused> |
105 | 0x21 0x35 <repeat mode (bit 0-1), rec. channels (bit 2), | 105 | 0x21 0x35 <repeat mode (bit 0-1), rec. channels (bit 2), |
106 | mic gain (bit 4-7)> | 106 | mic gain (bit 4-7)> |
107 | 0x22 0x36 <rec. quality (bit 0-2), source (bit 3-4), frequency (bit 5-7)> | 107 | 0x22 0x36 <rec. quality (bit 0-2), source (bit 3-4), frequency (bit 5-7)> |
@@ -144,9 +144,9 @@ Rest of config block, only saved to disk: | |||
144 | 0xB8 (char[20]) WPS file | 144 | 0xB8 (char[20]) WPS file |
145 | 0xCC (char[20]) Lang file | 145 | 0xCC (char[20]) Lang file |
146 | 0xE0 (char[20]) Font file | 146 | 0xE0 (char[20]) Font file |
147 | 0xF4 (int) Playlist first index | 147 | 0xF4 <unused> |
148 | 0xF8 (int) Playlist shuffle seed | 148 | 0xF8 <unused> |
149 | 0xFC-0x1FF (char[260]) Resume playlist (path/to/dir or path/to/playlist.m3u) | 149 | 0xFC <unused> |
150 | 150 | ||
151 | *************************************/ | 151 | *************************************/ |
152 | 152 | ||
@@ -343,18 +343,19 @@ int settings_save( void ) | |||
343 | 343 | ||
344 | memcpy(&config_block[0x12], &global_settings.resume_index, 4); | 344 | memcpy(&config_block[0x12], &global_settings.resume_index, 4); |
345 | memcpy(&config_block[0x16], &global_settings.resume_offset, 4); | 345 | memcpy(&config_block[0x16], &global_settings.resume_offset, 4); |
346 | DEBUGF( "+Resume index %X offset %X\n", | ||
347 | global_settings.resume_index, | ||
348 | global_settings.resume_offset ); | ||
346 | 349 | ||
347 | config_block[0x1a] = (unsigned char)global_settings.disk_spindown; | 350 | config_block[0x1a] = (unsigned char)global_settings.disk_spindown; |
348 | config_block[0x1b] = (unsigned char) | 351 | config_block[0x1b] = (unsigned char) |
349 | (((global_settings.browse_current & 1)) | | 352 | (((global_settings.browse_current & 1)) | |
350 | ((global_settings.play_selected & 1) << 1) | | 353 | ((global_settings.play_selected & 1) << 1) | |
351 | ((global_settings.queue_resume & 3) << 2)); | 354 | ((global_settings.recursive_dir_insert & 1) << 2)); |
352 | 355 | ||
353 | config_block[0x1c] = (unsigned char)global_settings.peak_meter_hold | | 356 | config_block[0x1c] = (unsigned char)global_settings.peak_meter_hold | |
354 | (global_settings.rec_editable?0x80:0); | 357 | (global_settings.rec_editable?0x80:0); |
355 | 358 | ||
356 | memcpy(&config_block[0x1d], &global_settings.queue_resume_index, 4); | ||
357 | |||
358 | config_block[0x21] = (unsigned char) | 359 | config_block[0x21] = (unsigned char) |
359 | ((global_settings.repeat_mode & 3) | | 360 | ((global_settings.repeat_mode & 3) | |
360 | ((global_settings.rec_channels & 1) << 2) | | 361 | ((global_settings.rec_channels & 1) << 2) | |
@@ -415,17 +416,6 @@ int settings_save( void ) | |||
415 | strncpy(&config_block[0xb8], global_settings.wps_file, MAX_FILENAME); | 416 | strncpy(&config_block[0xb8], global_settings.wps_file, MAX_FILENAME); |
416 | strncpy(&config_block[0xcc], global_settings.lang_file, MAX_FILENAME); | 417 | strncpy(&config_block[0xcc], global_settings.lang_file, MAX_FILENAME); |
417 | strncpy(&config_block[0xe0], global_settings.font_file, MAX_FILENAME); | 418 | strncpy(&config_block[0xe0], global_settings.font_file, MAX_FILENAME); |
418 | memcpy(&config_block[0xF4], &global_settings.resume_first_index, 4); | ||
419 | memcpy(&config_block[0xF8], &global_settings.resume_seed, 4); | ||
420 | |||
421 | strncpy(&config_block[0xFC], global_settings.resume_file, MAX_PATH); | ||
422 | DEBUGF( "+Resume file %s\n",global_settings.resume_file ); | ||
423 | DEBUGF( "+Resume index %X offset %X\n", | ||
424 | global_settings.resume_index, | ||
425 | global_settings.resume_offset ); | ||
426 | DEBUGF( "+Resume shuffle %s seed %X\n", | ||
427 | global_settings.playlist_shuffle?"on":"off", | ||
428 | global_settings.resume_seed ); | ||
429 | 419 | ||
430 | if(save_config_buffer()) | 420 | if(save_config_buffer()) |
431 | { | 421 | { |
@@ -655,7 +645,8 @@ void settings_load(void) | |||
655 | if (config_block[0x1b] != 0xFF) { | 645 | if (config_block[0x1b] != 0xFF) { |
656 | global_settings.browse_current = (config_block[0x1b]) & 1; | 646 | global_settings.browse_current = (config_block[0x1b]) & 1; |
657 | global_settings.play_selected = (config_block[0x1b] >> 1) & 1; | 647 | global_settings.play_selected = (config_block[0x1b] >> 1) & 1; |
658 | global_settings.queue_resume = (config_block[0x1b] >> 2) & 3; | 648 | global_settings.recursive_dir_insert = |
649 | (config_block[0x1b] >> 2) & 1; | ||
659 | } | 650 | } |
660 | 651 | ||
661 | if (config_block[0x1c] != 0xFF) { | 652 | if (config_block[0x1c] != 0xFF) { |
@@ -664,10 +655,6 @@ void settings_load(void) | |||
664 | (config_block[0x1c] & 0x80)?true:false; | 655 | (config_block[0x1c] & 0x80)?true:false; |
665 | } | 656 | } |
666 | 657 | ||
667 | if (config_block[0x1d] != 0xFF) | ||
668 | memcpy(&global_settings.queue_resume_index, &config_block[0x1d], | ||
669 | 4); | ||
670 | |||
671 | if (config_block[0x21] != 0xFF) | 658 | if (config_block[0x21] != 0xFF) |
672 | { | 659 | { |
673 | global_settings.repeat_mode = config_block[0x21] & 3; | 660 | global_settings.repeat_mode = config_block[0x21] & 3; |
@@ -745,14 +732,9 @@ void settings_load(void) | |||
745 | global_settings.max_files_in_playlist = | 732 | global_settings.max_files_in_playlist = |
746 | config_block[0xaa] | (config_block[0xab] << 8); | 733 | config_block[0xaa] | (config_block[0xab] << 8); |
747 | 734 | ||
748 | memcpy(&global_settings.resume_first_index, &config_block[0xF4], 4); | ||
749 | memcpy(&global_settings.resume_seed, &config_block[0xF8], 4); | ||
750 | |||
751 | strncpy(global_settings.wps_file, &config_block[0xb8], MAX_FILENAME); | 735 | strncpy(global_settings.wps_file, &config_block[0xb8], MAX_FILENAME); |
752 | strncpy(global_settings.lang_file, &config_block[0xcc], MAX_FILENAME); | 736 | strncpy(global_settings.lang_file, &config_block[0xcc], MAX_FILENAME); |
753 | strncpy(global_settings.font_file, &config_block[0xe0], MAX_FILENAME); | 737 | strncpy(global_settings.font_file, &config_block[0xe0], MAX_FILENAME); |
754 | strncpy(global_settings.resume_file, &config_block[0xFC], MAX_PATH); | ||
755 | global_settings.resume_file[MAX_PATH]=0; | ||
756 | #ifdef HAVE_LCD_CHARCELLS | 738 | #ifdef HAVE_LCD_CHARCELLS |
757 | if (config_block[0xa8] != 0xff) | 739 | if (config_block[0xa8] != 0xff) |
758 | global_settings.jump_scroll = config_block[0xa8]; | 740 | global_settings.jump_scroll = config_block[0xa8]; |
@@ -1097,6 +1079,8 @@ bool settings_load_config(char* file) | |||
1097 | else if (!strcasecmp(name, "max files in playlist")) | 1079 | else if (!strcasecmp(name, "max files in playlist")) |
1098 | set_cfg_int(&global_settings.max_files_in_playlist, value, | 1080 | set_cfg_int(&global_settings.max_files_in_playlist, value, |
1099 | 1000, 20000); | 1081 | 1000, 20000); |
1082 | else if (!strcasecmp(name, "recursive directory insert")) | ||
1083 | set_cfg_bool(&global_settings.recursive_dir_insert, value); | ||
1100 | } | 1084 | } |
1101 | 1085 | ||
1102 | close(fd); | 1086 | close(fd); |
@@ -1385,6 +1369,9 @@ bool settings_save_config(void) | |||
1385 | fprintf(fd, "max files in playlist: %d\r\n", | 1369 | fprintf(fd, "max files in playlist: %d\r\n", |
1386 | global_settings.max_files_in_playlist); | 1370 | global_settings.max_files_in_playlist); |
1387 | 1371 | ||
1372 | fprintf(fd, "recursive directory insert: %s\r\n", | ||
1373 | boolopt[global_settings.recursive_dir_insert]); | ||
1374 | |||
1388 | close(fd); | 1375 | close(fd); |
1389 | 1376 | ||
1390 | lcd_clear_display(); | 1377 | lcd_clear_display(); |
@@ -1450,9 +1437,6 @@ void settings_reset(void) { | |||
1450 | global_settings.ff_rewind_accel = DEFAULT_FF_REWIND_ACCEL_SETTING; | 1437 | global_settings.ff_rewind_accel = DEFAULT_FF_REWIND_ACCEL_SETTING; |
1451 | global_settings.resume_index = -1; | 1438 | global_settings.resume_index = -1; |
1452 | global_settings.resume_offset = -1; | 1439 | global_settings.resume_offset = -1; |
1453 | global_settings.save_queue_resume = true; | ||
1454 | global_settings.queue_resume = 0; | ||
1455 | global_settings.queue_resume_index = -1; | ||
1456 | global_settings.disk_spindown = 5; | 1440 | global_settings.disk_spindown = 5; |
1457 | global_settings.disk_poweroff = false; | 1441 | global_settings.disk_poweroff = false; |
1458 | global_settings.buffer_margin = 0; | 1442 | global_settings.buffer_margin = 0; |
@@ -1475,6 +1459,7 @@ void settings_reset(void) { | |||
1475 | global_settings.max_files_in_dir = 400; | 1459 | global_settings.max_files_in_dir = 400; |
1476 | global_settings.max_files_in_playlist = 10000; | 1460 | global_settings.max_files_in_playlist = 10000; |
1477 | global_settings.show_icons = true; | 1461 | global_settings.show_icons = true; |
1462 | global_settings.recursive_dir_insert = false; | ||
1478 | } | 1463 | } |
1479 | 1464 | ||
1480 | bool set_bool(char* string, bool* variable ) | 1465 | bool set_bool(char* string, bool* variable ) |
diff --git a/apps/settings.h b/apps/settings.h index 624b06e06b..25efd65e9a 100644 --- a/apps/settings.h +++ b/apps/settings.h | |||
@@ -103,16 +103,7 @@ struct user_settings | |||
103 | int resume; /* resume option: 0=off, 1=ask, 2=on */ | 103 | int resume; /* resume option: 0=off, 1=ask, 2=on */ |
104 | int resume_index; /* index in playlist (-1 for no active resume) */ | 104 | int resume_index; /* index in playlist (-1 for no active resume) */ |
105 | int resume_offset; /* byte offset in mp3 file */ | 105 | int resume_offset; /* byte offset in mp3 file */ |
106 | int resume_seed; /* random seed for playlist shuffle */ | ||
107 | int resume_first_index; /* first index of playlist */ | ||
108 | 106 | ||
109 | bool save_queue_resume; /* save queued songs for resume */ | ||
110 | int queue_resume; /* resume queue file?: 0 = no | ||
111 | 1 = resume at queue index | ||
112 | 2 = resume at playlist index */ | ||
113 | int queue_resume_index; /* queue index (seek point in queue file) */ | ||
114 | |||
115 | unsigned char resume_file[MAX_PATH+1]; /* playlist name (or dir) */ | ||
116 | unsigned char font_file[MAX_FILENAME+1]; /* last font */ | 107 | unsigned char font_file[MAX_FILENAME+1]; /* last font */ |
117 | unsigned char wps_file[MAX_FILENAME+1]; /* last wps */ | 108 | unsigned char wps_file[MAX_FILENAME+1]; /* last wps */ |
118 | unsigned char lang_file[MAX_FILENAME+1]; /* last language */ | 109 | unsigned char lang_file[MAX_FILENAME+1]; /* last language */ |
@@ -175,6 +166,7 @@ struct user_settings | |||
175 | int max_files_in_dir; /* Max entries in directory (file browser) */ | 166 | int max_files_in_dir; /* Max entries in directory (file browser) */ |
176 | int max_files_in_playlist; /* Max entries in playlist */ | 167 | int max_files_in_playlist; /* Max entries in playlist */ |
177 | bool show_icons; /* 0=hide 1=show */ | 168 | bool show_icons; /* 0=hide 1=show */ |
169 | bool recursive_dir_insert;/* should directories be inserted recursively */ | ||
178 | }; | 170 | }; |
179 | 171 | ||
180 | enum optiontype { INT, BOOL }; | 172 | enum optiontype { INT, BOOL }; |
diff --git a/apps/settings_menu.c b/apps/settings_menu.c index 57ba18132a..f64ee17ccd 100644 --- a/apps/settings_menu.c +++ b/apps/settings_menu.c | |||
@@ -724,11 +724,11 @@ static bool playback_settings_menu(void) | |||
724 | { | 724 | { |
725 | if (global_settings.playlist_shuffle) | 725 | if (global_settings.playlist_shuffle) |
726 | { | 726 | { |
727 | randomise_playlist(current_tick); | 727 | playlist_randomise(current_tick, true); |
728 | } | 728 | } |
729 | else | 729 | else |
730 | { | 730 | { |
731 | sort_playlist(true); | 731 | playlist_sort(true); |
732 | } | 732 | } |
733 | } | 733 | } |
734 | return result; | 734 | return result; |
diff --git a/apps/tree.c b/apps/tree.c index f7a83de85f..29ca4dfe15 100644 --- a/apps/tree.c +++ b/apps/tree.c | |||
@@ -66,10 +66,6 @@ static int max_files_in_dir; | |||
66 | static char *name_buffer; | 66 | static char *name_buffer; |
67 | static int name_buffer_size; /* Size of allocated buffer */ | 67 | static int name_buffer_size; /* Size of allocated buffer */ |
68 | static int name_buffer_length; /* Currently used amount */ | 68 | static int name_buffer_length; /* Currently used amount */ |
69 | struct entry { | ||
70 | short attr; /* FAT attributes + file type flags */ | ||
71 | char *name; | ||
72 | }; | ||
73 | 69 | ||
74 | static struct entry *dircache; | 70 | static struct entry *dircache; |
75 | 71 | ||
@@ -87,6 +83,8 @@ static int boot_size = 0; | |||
87 | static int boot_cluster; | 83 | static int boot_cluster; |
88 | static bool boot_changed = false; | 84 | static bool boot_changed = false; |
89 | 85 | ||
86 | static bool dirbrowse(char *root); | ||
87 | |||
90 | void browse_root(void) | 88 | void browse_root(void) |
91 | { | 89 | { |
92 | #ifndef SIMULATOR | 90 | #ifndef SIMULATOR |
@@ -158,14 +156,13 @@ static int build_playlist(int start_index) | |||
158 | int i; | 156 | int i; |
159 | int start=start_index; | 157 | int start=start_index; |
160 | 158 | ||
161 | playlist_clear(); | ||
162 | |||
163 | for(i = 0;i < filesindir;i++) | 159 | for(i = 0;i < filesindir;i++) |
164 | { | 160 | { |
165 | if(dircache[i].attr & TREE_ATTR_MPA) | 161 | if(dircache[i].attr & TREE_ATTR_MPA) |
166 | { | 162 | { |
167 | DEBUGF("Adding %s\n", dircache[i].name); | 163 | DEBUGF("Adding %s\n", dircache[i].name); |
168 | playlist_add(dircache[i].name); | 164 | if (playlist_add(dircache[i].name) < 0) |
165 | break; | ||
169 | } | 166 | } |
170 | else | 167 | else |
171 | { | 168 | { |
@@ -237,6 +234,133 @@ static void showfileline(int line, int direntry, bool scroll) | |||
237 | } | 234 | } |
238 | } | 235 | } |
239 | 236 | ||
237 | /* load sorted directory into dircache. returns NULL on failure. */ | ||
238 | struct entry* load_and_sort_directory(char *dirname, int dirfilter, | ||
239 | int *num_files, bool *buffer_full) | ||
240 | { | ||
241 | int i; | ||
242 | |||
243 | DIR *dir = opendir(dirname); | ||
244 | if(!dir) | ||
245 | return NULL; /* not a directory */ | ||
246 | |||
247 | name_buffer_length = 0; | ||
248 | *buffer_full = false; | ||
249 | |||
250 | for ( i=0; i < max_files_in_dir; i++ ) { | ||
251 | int len; | ||
252 | struct dirent *entry = readdir(dir); | ||
253 | struct entry* dptr = &dircache[i]; | ||
254 | if (!entry) | ||
255 | break; | ||
256 | |||
257 | len = strlen(entry->d_name); | ||
258 | |||
259 | /* skip directories . and .. */ | ||
260 | if ((entry->attribute & ATTR_DIRECTORY) && | ||
261 | (((len == 1) && | ||
262 | (!strncmp(entry->d_name, ".", 1))) || | ||
263 | ((len == 2) && | ||
264 | (!strncmp(entry->d_name, "..", 2))))) { | ||
265 | i--; | ||
266 | continue; | ||
267 | } | ||
268 | |||
269 | /* Skip FAT volume ID */ | ||
270 | if (entry->attribute & ATTR_VOLUME_ID) { | ||
271 | i--; | ||
272 | continue; | ||
273 | } | ||
274 | |||
275 | /* filter out dotfiles and hidden files */ | ||
276 | if (dirfilter != SHOW_ALL && | ||
277 | ((entry->d_name[0]=='.') || | ||
278 | (entry->attribute & ATTR_HIDDEN))) { | ||
279 | i--; | ||
280 | continue; | ||
281 | } | ||
282 | |||
283 | dptr->attr = entry->attribute; | ||
284 | |||
285 | /* mark mp? and m3u files as such */ | ||
286 | if ( !(dptr->attr & ATTR_DIRECTORY) && (len > 4) ) { | ||
287 | if (!strcasecmp(&entry->d_name[len-4], ".mp3") || | ||
288 | (!strcasecmp(&entry->d_name[len-4], ".mp2")) || | ||
289 | (!strcasecmp(&entry->d_name[len-4], ".mpa"))) | ||
290 | dptr->attr |= TREE_ATTR_MPA; | ||
291 | else if (!strcasecmp(&entry->d_name[len-4], ".m3u")) | ||
292 | dptr->attr |= TREE_ATTR_M3U; | ||
293 | else if (!strcasecmp(&entry->d_name[len-4], ".cfg")) | ||
294 | dptr->attr |= TREE_ATTR_CFG; | ||
295 | else if (!strcasecmp(&entry->d_name[len-4], ".wps")) | ||
296 | dptr->attr |= TREE_ATTR_WPS; | ||
297 | else if (!strcasecmp(&entry->d_name[len-4], ".txt")) | ||
298 | dptr->attr |= TREE_ATTR_TXT; | ||
299 | else if (!strcasecmp(&entry->d_name[len-4], ".lng")) | ||
300 | dptr->attr |= TREE_ATTR_LNG; | ||
301 | #ifdef HAVE_RECORDER_KEYPAD | ||
302 | else if (!strcasecmp(&entry->d_name[len-4], ".fnt")) | ||
303 | dptr->attr |= TREE_ATTR_FONT; | ||
304 | else if (!strcasecmp(&entry->d_name[len-4], ".ajz")) | ||
305 | #else | ||
306 | else if (!strcasecmp(&entry->d_name[len-4], ".mod")) | ||
307 | #endif | ||
308 | dptr->attr |= TREE_ATTR_MOD; | ||
309 | else if (!strcasecmp(&entry->d_name[len-5], ".rock")) | ||
310 | dptr->attr |= TREE_ATTR_ROCK; | ||
311 | } | ||
312 | |||
313 | /* memorize/compare details about the boot file */ | ||
314 | if ((currdir[1] == 0) && !strcmp(entry->d_name, BOOTFILE)) { | ||
315 | if (boot_size) { | ||
316 | if ((entry->size != boot_size) || | ||
317 | (entry->startcluster != boot_cluster)) | ||
318 | boot_changed = true; | ||
319 | } | ||
320 | boot_size = entry->size; | ||
321 | boot_cluster = entry->startcluster; | ||
322 | } | ||
323 | |||
324 | /* filter out all non-playlist files */ | ||
325 | if ( dirfilter == SHOW_PLAYLIST && | ||
326 | (!(dptr->attr & | ||
327 | (ATTR_DIRECTORY|TREE_ATTR_M3U))) ) { | ||
328 | i--; | ||
329 | continue; | ||
330 | } | ||
331 | |||
332 | /* filter out non-music files */ | ||
333 | if ( dirfilter == SHOW_MUSIC && | ||
334 | (!(dptr->attr & | ||
335 | (ATTR_DIRECTORY|TREE_ATTR_MPA|TREE_ATTR_M3U))) ) { | ||
336 | i--; | ||
337 | continue; | ||
338 | } | ||
339 | |||
340 | /* filter out non-supported files */ | ||
341 | if ( dirfilter == SHOW_SUPPORTED && | ||
342 | (!(dptr->attr & TREE_ATTR_MASK)) ) { | ||
343 | i--; | ||
344 | continue; | ||
345 | } | ||
346 | |||
347 | if (len > name_buffer_size - name_buffer_length - 1) { | ||
348 | /* Tell the world that we ran out of buffer space */ | ||
349 | *buffer_full = true; | ||
350 | break; | ||
351 | } | ||
352 | dptr->name = &name_buffer[name_buffer_length]; | ||
353 | strcpy(dptr->name,entry->d_name); | ||
354 | name_buffer_length += len + 1; | ||
355 | } | ||
356 | *num_files = i; | ||
357 | closedir(dir); | ||
358 | strncpy(lastdir,dirname,sizeof(lastdir)); | ||
359 | lastdir[sizeof(lastdir)-1] = 0; | ||
360 | qsort(dircache,i,sizeof(struct entry),compare); | ||
361 | |||
362 | return dircache; | ||
363 | } | ||
240 | 364 | ||
241 | static int showdir(char *path, int start) | 365 | static int showdir(char *path, int start) |
242 | { | 366 | { |
@@ -258,124 +382,9 @@ static int showdir(char *path, int start) | |||
258 | 382 | ||
259 | /* new dir? cache it */ | 383 | /* new dir? cache it */ |
260 | if (strncmp(path,lastdir,sizeof(lastdir)) || reload_dir) { | 384 | if (strncmp(path,lastdir,sizeof(lastdir)) || reload_dir) { |
261 | DIR *dir = opendir(path); | 385 | if (!load_and_sort_directory(path, global_settings.dirfilter, |
262 | if(!dir) | 386 | &filesindir, &dir_buffer_full)) |
263 | return -1; /* not a directory */ | 387 | return -1; |
264 | |||
265 | name_buffer_length = 0; | ||
266 | dir_buffer_full = false; | ||
267 | |||
268 | for ( i=0; i < max_files_in_dir; i++ ) { | ||
269 | int len; | ||
270 | struct dirent *entry = readdir(dir); | ||
271 | struct entry* dptr = &dircache[i]; | ||
272 | if (!entry) | ||
273 | break; | ||
274 | |||
275 | len = strlen(entry->d_name); | ||
276 | |||
277 | /* skip directories . and .. */ | ||
278 | if ((entry->attribute & ATTR_DIRECTORY) && | ||
279 | (((len == 1) && | ||
280 | (!strncmp(entry->d_name, ".", 1))) || | ||
281 | ((len == 2) && | ||
282 | (!strncmp(entry->d_name, "..", 2))))) { | ||
283 | i--; | ||
284 | continue; | ||
285 | } | ||
286 | |||
287 | /* Skip FAT volume ID */ | ||
288 | if (entry->attribute & ATTR_VOLUME_ID) { | ||
289 | i--; | ||
290 | continue; | ||
291 | } | ||
292 | |||
293 | /* filter out dotfiles and hidden files */ | ||
294 | if (global_settings.dirfilter != SHOW_ALL && | ||
295 | ((entry->d_name[0]=='.') || | ||
296 | (entry->attribute & ATTR_HIDDEN))) { | ||
297 | i--; | ||
298 | continue; | ||
299 | } | ||
300 | |||
301 | dptr->attr = entry->attribute; | ||
302 | |||
303 | /* mark mp? and m3u files as such */ | ||
304 | if ( !(dptr->attr & ATTR_DIRECTORY) && (len > 4) ) { | ||
305 | if (!strcasecmp(&entry->d_name[len-4], ".mp3") || | ||
306 | (!strcasecmp(&entry->d_name[len-4], ".mp2")) || | ||
307 | (!strcasecmp(&entry->d_name[len-4], ".mpa"))) | ||
308 | dptr->attr |= TREE_ATTR_MPA; | ||
309 | else if (!strcasecmp(&entry->d_name[len-4], ".m3u")) | ||
310 | dptr->attr |= TREE_ATTR_M3U; | ||
311 | else if (!strcasecmp(&entry->d_name[len-4], ".cfg")) | ||
312 | dptr->attr |= TREE_ATTR_CFG; | ||
313 | else if (!strcasecmp(&entry->d_name[len-4], ".wps")) | ||
314 | dptr->attr |= TREE_ATTR_WPS; | ||
315 | else if (!strcasecmp(&entry->d_name[len-4], ".txt")) | ||
316 | dptr->attr |= TREE_ATTR_TXT; | ||
317 | else if (!strcasecmp(&entry->d_name[len-4], ".lng")) | ||
318 | dptr->attr |= TREE_ATTR_LNG; | ||
319 | #ifdef HAVE_RECORDER_KEYPAD | ||
320 | else if (!strcasecmp(&entry->d_name[len-4], ".fnt")) | ||
321 | dptr->attr |= TREE_ATTR_FONT; | ||
322 | else if (!strcasecmp(&entry->d_name[len-4], ".ajz")) | ||
323 | #else | ||
324 | else if (!strcasecmp(&entry->d_name[len-4], ".mod")) | ||
325 | #endif | ||
326 | dptr->attr |= TREE_ATTR_MOD; | ||
327 | else if (!strcasecmp(&entry->d_name[len-5], ".rock")) | ||
328 | dptr->attr |= TREE_ATTR_ROCK; | ||
329 | } | ||
330 | |||
331 | /* memorize/compare details about the boot file */ | ||
332 | if ((currdir[1] == 0) && !strcmp(entry->d_name, BOOTFILE)) { | ||
333 | if (boot_size) { | ||
334 | if ((entry->size != boot_size) || | ||
335 | (entry->startcluster != boot_cluster)) | ||
336 | boot_changed = true; | ||
337 | } | ||
338 | boot_size = entry->size; | ||
339 | boot_cluster = entry->startcluster; | ||
340 | } | ||
341 | |||
342 | /* filter out all non-playlist files */ | ||
343 | if ( global_settings.dirfilter == SHOW_PLAYLIST && | ||
344 | (!(dptr->attr & | ||
345 | (ATTR_DIRECTORY|TREE_ATTR_M3U))) ) { | ||
346 | i--; | ||
347 | continue; | ||
348 | } | ||
349 | |||
350 | /* filter out non-music files */ | ||
351 | if ( global_settings.dirfilter == SHOW_MUSIC && | ||
352 | (!(dptr->attr & | ||
353 | (ATTR_DIRECTORY|TREE_ATTR_MPA|TREE_ATTR_M3U))) ) { | ||
354 | i--; | ||
355 | continue; | ||
356 | } | ||
357 | |||
358 | /* filter out non-supported files */ | ||
359 | if ( global_settings.dirfilter == SHOW_SUPPORTED && | ||
360 | (!(dptr->attr & TREE_ATTR_MASK)) ) { | ||
361 | i--; | ||
362 | continue; | ||
363 | } | ||
364 | |||
365 | if (len > name_buffer_size - name_buffer_length - 1) { | ||
366 | /* Tell the world that we ran out of buffer space */ | ||
367 | dir_buffer_full = true; | ||
368 | break; | ||
369 | } | ||
370 | dptr->name = &name_buffer[name_buffer_length]; | ||
371 | strcpy(dptr->name,entry->d_name); | ||
372 | name_buffer_length += len + 1; | ||
373 | } | ||
374 | filesindir = i; | ||
375 | closedir(dir); | ||
376 | strncpy(lastdir,path,sizeof(lastdir)); | ||
377 | lastdir[sizeof(lastdir)-1] = 0; | ||
378 | qsort(dircache,filesindir,sizeof(struct entry),compare); | ||
379 | 388 | ||
380 | if ( dir_buffer_full || filesindir == max_files_in_dir ) { | 389 | if ( dir_buffer_full || filesindir == max_files_in_dir ) { |
381 | #ifdef HAVE_LCD_CHARCELLS | 390 | #ifdef HAVE_LCD_CHARCELLS |
@@ -531,7 +540,7 @@ static int showdir(char *path, int start) | |||
531 | return filesindir; | 540 | return filesindir; |
532 | } | 541 | } |
533 | 542 | ||
534 | bool ask_resume(void) | 543 | static bool ask_resume(void) |
535 | { | 544 | { |
536 | #ifdef HAVE_LCD_CHARCELLS | 545 | #ifdef HAVE_LCD_CHARCELLS |
537 | lcd_double_height(false); | 546 | lcd_double_height(false); |
@@ -570,92 +579,62 @@ bool ask_resume(void) | |||
570 | return false; | 579 | return false; |
571 | } | 580 | } |
572 | 581 | ||
573 | void start_resume(void) | 582 | /* load tracks from specified directory to resume play */ |
583 | void resume_directory(char *dir) | ||
584 | { | ||
585 | bool buffer_full; | ||
586 | |||
587 | if (!load_and_sort_directory(dir, global_settings.dirfilter, &filesindir, | ||
588 | &buffer_full)) | ||
589 | return; | ||
590 | lastdir[0] = 0; | ||
591 | |||
592 | build_playlist(0); | ||
593 | } | ||
594 | |||
595 | /* Returns the current working directory and also writes cwd to buf if | ||
596 | non-NULL. In case of error, returns NULL. */ | ||
597 | char *getcwd(char *buf, int size) | ||
598 | { | ||
599 | if (!buf) | ||
600 | return currdir; | ||
601 | else if (size > 0) | ||
602 | { | ||
603 | strncpy(buf, currdir, size); | ||
604 | return buf; | ||
605 | } | ||
606 | else | ||
607 | return NULL; | ||
608 | } | ||
609 | |||
610 | /* Force a reload of the directory next time directory browser is called */ | ||
611 | void reload_directory(void) | ||
612 | { | ||
613 | reload_dir = true; | ||
614 | } | ||
615 | |||
616 | static void start_resume(void) | ||
574 | { | 617 | { |
575 | if ( global_settings.resume && | 618 | if ( global_settings.resume && |
576 | global_settings.resume_index != -1 ) { | 619 | global_settings.resume_index != -1 ) { |
577 | int len = strlen(global_settings.resume_file); | ||
578 | |||
579 | DEBUGF("Resume file %s\n",global_settings.resume_file); | ||
580 | DEBUGF("Resume index %X offset %X\n", | 620 | DEBUGF("Resume index %X offset %X\n", |
581 | global_settings.resume_index, | 621 | global_settings.resume_index, |
582 | global_settings.resume_offset); | 622 | global_settings.resume_offset); |
583 | DEBUGF("Resume shuffle %s seed %X\n", | ||
584 | global_settings.playlist_shuffle?"on":"off", | ||
585 | global_settings.resume_seed); | ||
586 | |||
587 | /* playlist? */ | ||
588 | if (!strcasecmp(&global_settings.resume_file[len-4], ".m3u")) { | ||
589 | char* slash; | ||
590 | |||
591 | /* check that the file exists */ | ||
592 | int fd = open(global_settings.resume_file, O_RDONLY); | ||
593 | if(fd<0) | ||
594 | return; | ||
595 | close(fd); | ||
596 | |||
597 | if (!ask_resume()) | ||
598 | return; | ||
599 | |||
600 | slash = strrchr(global_settings.resume_file,'/'); | ||
601 | if (slash) { | ||
602 | *slash=0; | ||
603 | play_list(global_settings.resume_file, | ||
604 | slash+1, | ||
605 | global_settings.resume_index, | ||
606 | true, /* the index is AFTER shuffle */ | ||
607 | global_settings.resume_offset, | ||
608 | global_settings.resume_seed, | ||
609 | global_settings.resume_first_index, | ||
610 | global_settings.queue_resume, | ||
611 | global_settings.queue_resume_index); | ||
612 | *slash='/'; | ||
613 | } | ||
614 | else { | ||
615 | /* check that the dir exists */ | ||
616 | DIR* dir = opendir(global_settings.resume_file); | ||
617 | if(!dir) | ||
618 | return; | ||
619 | closedir(dir); | ||
620 | |||
621 | if (!ask_resume()) | ||
622 | return; | ||
623 | |||
624 | play_list("/", | ||
625 | global_settings.resume_file, | ||
626 | global_settings.resume_index, | ||
627 | true, | ||
628 | global_settings.resume_offset, | ||
629 | global_settings.resume_seed, | ||
630 | global_settings.resume_first_index, | ||
631 | global_settings.queue_resume, | ||
632 | global_settings.queue_resume_index); | ||
633 | } | ||
634 | } | ||
635 | else { | ||
636 | if (!ask_resume()) | ||
637 | return; | ||
638 | |||
639 | if (showdir(global_settings.resume_file, 0) < 0 ) | ||
640 | return; | ||
641 | |||
642 | lastdir[0] = '\0'; | ||
643 | |||
644 | build_playlist(global_settings.resume_index); | ||
645 | play_list(global_settings.resume_file, | ||
646 | NULL, | ||
647 | global_settings.resume_index, | ||
648 | true, | ||
649 | global_settings.resume_offset, | ||
650 | global_settings.resume_seed, | ||
651 | global_settings.resume_first_index, | ||
652 | global_settings.queue_resume, | ||
653 | global_settings.queue_resume_index); | ||
654 | } | ||
655 | 623 | ||
656 | status_set_playmode(STATUS_PLAY); | 624 | if (!ask_resume()) |
657 | status_draw(true); | 625 | return; |
658 | wps_show(); | 626 | |
627 | if (playlist_resume() != -1) | ||
628 | { | ||
629 | playlist_start(global_settings.resume_index, | ||
630 | global_settings.resume_offset); | ||
631 | |||
632 | status_set_playmode(STATUS_PLAY); | ||
633 | status_draw(true); | ||
634 | wps_show(); | ||
635 | } | ||
636 | else | ||
637 | return; | ||
659 | } | 638 | } |
660 | } | 639 | } |
661 | 640 | ||
@@ -751,19 +730,33 @@ static bool handle_on(int* ds, int* dc, int numentries, int tree_max_on_screen) | |||
751 | 730 | ||
752 | case BUTTON_PLAY: | 731 | case BUTTON_PLAY: |
753 | case BUTTON_RC_PLAY: | 732 | case BUTTON_RC_PLAY: |
754 | case BUTTON_ON | BUTTON_PLAY: | 733 | case BUTTON_ON | BUTTON_PLAY: { |
734 | int onplay_result; | ||
735 | |||
755 | if (currdir[1]) | 736 | if (currdir[1]) |
756 | snprintf(buf, sizeof buf, "%s/%s", | 737 | snprintf(buf, sizeof buf, "%s/%s", |
757 | currdir, dircache[dircursor+dirstart].name); | 738 | currdir, dircache[dircursor+dirstart].name); |
758 | else | 739 | else |
759 | snprintf(buf, sizeof buf, "/%s", | 740 | snprintf(buf, sizeof buf, "/%s", |
760 | dircache[dircursor+dirstart].name); | 741 | dircache[dircursor+dirstart].name); |
761 | if (onplay(buf, dircache[dircursor+dirstart].attr)) | 742 | onplay_result = onplay(buf, |
762 | reload_dir = 1; | 743 | dircache[dircursor+dirstart].attr); |
744 | switch (onplay_result) | ||
745 | { | ||
746 | case ONPLAY_OK: | ||
747 | used = true; | ||
748 | break; | ||
749 | case ONPLAY_RELOAD_DIR: | ||
750 | reload_dir = 1; | ||
751 | used = true; | ||
752 | break; | ||
753 | case ONPLAY_START_PLAY: | ||
754 | used = false; /* this will enable the wps */ | ||
755 | break; | ||
756 | } | ||
763 | exit = true; | 757 | exit = true; |
764 | used = true; | ||
765 | break; | 758 | break; |
766 | 759 | } | |
767 | case BUTTON_ON | BUTTON_REL: | 760 | case BUTTON_ON | BUTTON_REL: |
768 | case BUTTON_ON | TREE_PREV | BUTTON_REL: | 761 | case BUTTON_ON | TREE_PREV | BUTTON_REL: |
769 | case BUTTON_ON | TREE_NEXT | BUTTON_REL: | 762 | case BUTTON_ON | TREE_NEXT | BUTTON_REL: |
@@ -793,7 +786,7 @@ static bool handle_on(int* ds, int* dc, int numentries, int tree_max_on_screen) | |||
793 | return used; | 786 | return used; |
794 | } | 787 | } |
795 | 788 | ||
796 | bool dirbrowse(char *root) | 789 | static bool dirbrowse(char *root) |
797 | { | 790 | { |
798 | int numentries=0; | 791 | int numentries=0; |
799 | char buf[MAX_PATH]; | 792 | char buf[MAX_PATH]; |
@@ -934,41 +927,36 @@ bool dirbrowse(char *root) | |||
934 | lcd_stop_scroll(); | 927 | lcd_stop_scroll(); |
935 | switch ( file->attr & TREE_ATTR_MASK ) { | 928 | switch ( file->attr & TREE_ATTR_MASK ) { |
936 | case TREE_ATTR_M3U: | 929 | case TREE_ATTR_M3U: |
937 | if ( global_settings.resume ) { | 930 | if (playlist_create(currdir, file->name) != -1) |
938 | if (currdir[1]) | 931 | { |
939 | snprintf(global_settings.resume_file, | 932 | if (global_settings.playlist_shuffle) |
940 | MAX_PATH, "%s/%s", | 933 | playlist_shuffle(seed, -1); |
941 | currdir, file->name); | 934 | start_index = 0; |
942 | else | 935 | playlist_start(start_index,0); |
943 | snprintf(global_settings.resume_file, | 936 | play = true; |
944 | MAX_PATH, "/%s", file->name); | ||
945 | } | 937 | } |
946 | play_list(currdir, file->name, 0, false, 0, | ||
947 | seed, 0, 0, -1); | ||
948 | start_index = 0; | ||
949 | play = true; | ||
950 | break; | 938 | break; |
951 | 939 | ||
952 | case TREE_ATTR_MPA: | 940 | case TREE_ATTR_MPA: |
953 | if ( global_settings.resume ) | 941 | if (playlist_create(currdir, NULL) != -1) |
954 | strncpy(global_settings.resume_file, | 942 | { |
955 | currdir, MAX_PATH); | 943 | start_index = |
956 | 944 | build_playlist(dircursor+dirstart); | |
957 | start_index = | 945 | if (global_settings.playlist_shuffle) |
958 | build_playlist(dircursor+dirstart); | 946 | { |
959 | 947 | start_index = | |
960 | /* when shuffling dir.: play all files even if the | 948 | playlist_shuffle(seed,start_index); |
961 | file selected by user is not the first one */ | 949 | |
962 | if (global_settings.playlist_shuffle | 950 | /* when shuffling dir.: play all files |
963 | && !global_settings.play_selected) | 951 | even if the file selected by user is |
964 | start_index = 0; | 952 | not the first one */ |
965 | 953 | if (!global_settings.play_selected) | |
966 | /* it is important that we get back the index | 954 | start_index = 0; |
967 | in the (shuffled) list and store that */ | 955 | } |
968 | start_index = play_list(currdir, NULL, | 956 | |
969 | start_index, false, | 957 | playlist_start(start_index, 0); |
970 | 0, seed, 0, 0, -1); | 958 | play = true; |
971 | play = true; | 959 | } |
972 | break; | 960 | break; |
973 | 961 | ||
974 | /* wps config file */ | 962 | /* wps config file */ |
@@ -1055,9 +1043,6 @@ bool dirbrowse(char *root) | |||
1055 | shuffled list in case shuffle is enabled */ | 1043 | shuffled list in case shuffle is enabled */ |
1056 | global_settings.resume_index = start_index; | 1044 | global_settings.resume_index = start_index; |
1057 | global_settings.resume_offset = 0; | 1045 | global_settings.resume_offset = 0; |
1058 | global_settings.resume_first_index = | ||
1059 | playlist_first_index(); | ||
1060 | global_settings.resume_seed = seed; | ||
1061 | settings_save(); | 1046 | settings_save(); |
1062 | } | 1047 | } |
1063 | 1048 | ||
diff --git a/apps/tree.h b/apps/tree.h index aa8f2127f3..66e83bc8f0 100644 --- a/apps/tree.h +++ b/apps/tree.h | |||
@@ -21,6 +21,11 @@ | |||
21 | 21 | ||
22 | #include <stdbool.h> | 22 | #include <stdbool.h> |
23 | 23 | ||
24 | struct entry { | ||
25 | short attr; /* FAT attributes + file type flags */ | ||
26 | char *name; | ||
27 | }; | ||
28 | |||
24 | /* using attribute not used by FAT */ | 29 | /* using attribute not used by FAT */ |
25 | #define TREE_ATTR_MPA 0x40 /* mpeg audio file */ | 30 | #define TREE_ATTR_MPA 0x40 /* mpeg audio file */ |
26 | #define TREE_ATTR_M3U 0x80 /* playlist */ | 31 | #define TREE_ATTR_M3U 0x80 /* playlist */ |
@@ -36,7 +41,11 @@ | |||
36 | void tree_init(void); | 41 | void tree_init(void); |
37 | void browse_root(void); | 42 | void browse_root(void); |
38 | void set_current_file(char *path); | 43 | void set_current_file(char *path); |
39 | bool dirbrowse(char *root); | ||
40 | bool create_playlist(void); | 44 | bool create_playlist(void); |
45 | void resume_directory(char *dir); | ||
46 | char *getcwd(char *buf, int size); | ||
47 | void reload_directory(void); | ||
48 | struct entry* load_and_sort_directory(char *dirname, int dirfilter, | ||
49 | int *num_files, bool *buffer_full); | ||
41 | 50 | ||
42 | #endif | 51 | #endif |
diff --git a/apps/wps-display.c b/apps/wps-display.c index 71ba4c5433..d3c2613a1e 100644 --- a/apps/wps-display.c +++ b/apps/wps-display.c | |||
@@ -418,12 +418,7 @@ static char* get_tag(struct mp3entry* id3, | |||
418 | #endif | 418 | #endif |
419 | case 'p': /* Playlist Position */ | 419 | case 'p': /* Playlist Position */ |
420 | *flags |= WPS_REFRESH_STATIC; | 420 | *flags |= WPS_REFRESH_STATIC; |
421 | { | 421 | snprintf(buf, buf_size, "%d", playlist_get_display_index()); |
422 | int index = id3->index - playlist_first_index(); | ||
423 | if (index < 0) | ||
424 | index += playlist_amount(); | ||
425 | snprintf(buf, buf_size, "%d", index + 1); | ||
426 | } | ||
427 | return buf; | 422 | return buf; |
428 | 423 | ||
429 | case 'n': /* Playlist Name (without path) */ | 424 | case 'n': /* Playlist Name (without path) */ |
diff --git a/apps/wps.c b/apps/wps.c index c5a27d9e9d..ca1d80fe28 100644 --- a/apps/wps.c +++ b/apps/wps.c | |||
@@ -223,13 +223,8 @@ bool browse_id3(void) | |||
223 | 223 | ||
224 | case 7: | 224 | case 7: |
225 | lcd_puts(0, 0, str(LANG_ID3_PLAYLIST)); | 225 | lcd_puts(0, 0, str(LANG_ID3_PLAYLIST)); |
226 | { | 226 | snprintf(scroll_text,sizeof(scroll_text), "%d/%d", |
227 | int index = id3->index - playlist_first_index(); | 227 | playlist_get_display_index(), playlist_amount()); |
228 | if (index < 0) | ||
229 | index += playlist_amount(); | ||
230 | snprintf(scroll_text,sizeof(scroll_text), "%d/%d", | ||
231 | index + 1, playlist_amount()); | ||
232 | } | ||
233 | lcd_puts_scroll(0, 1, scroll_text); | 228 | lcd_puts_scroll(0, 1, scroll_text); |
234 | break; | 229 | break; |
235 | 230 | ||
@@ -455,9 +450,7 @@ static bool update(void) | |||
455 | DEBUGF("R%X,%X (%X)\n", global_settings.resume_offset, | 450 | DEBUGF("R%X,%X (%X)\n", global_settings.resume_offset, |
456 | id3->offset,id3); | 451 | id3->offset,id3); |
457 | 452 | ||
458 | if (!playlist_get_resume_info(&global_settings.resume_index, | 453 | if (!playlist_get_resume_info(&global_settings.resume_index)) |
459 | &global_settings.queue_resume, | ||
460 | &global_settings.queue_resume_index)) | ||
461 | { | 454 | { |
462 | global_settings.resume_offset = id3->offset; | 455 | global_settings.resume_offset = id3->offset; |
463 | settings_save(); | 456 | settings_save(); |