summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHardeep Sidhu <dyp@pobox.com>2004-01-26 17:05:21 +0000
committerHardeep Sidhu <dyp@pobox.com>2004-01-26 17:05:21 +0000
commit107ebc5ba950acee1108a5cb52f25be371b2ec8f (patch)
tree08a833f8f7a1242612cb2ef8cf97299048f46743
parent1c7a88de787c612598ac47a59b99e00e9a2e3106 (diff)
downloadrockbox-107ebc5ba950acee1108a5cb52f25be371b2ec8f.tar.gz
rockbox-107ebc5ba950acee1108a5cb52f25be371b2ec8f.zip
Modified playlist handling to allow for multiple playlists to be edited at the same time. Added support in playlist viewer for viewing/editing playlists on disk (accessed via ON+PLAY->Playlist->View on a playlist). Added menu in playlist viewer for changing a few settings and saving playlist. Added File Options menu in playlist viewer ON+PLAY menu.
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@4276 a1c6a512-1295-4272-9138-f99709370657
-rw-r--r--apps/bookmark.c11
-rw-r--r--apps/lang/english.lang35
-rw-r--r--apps/menu.c2
-rw-r--r--apps/onplay.c51
-rw-r--r--apps/playlist.c1349
-rw-r--r--apps/playlist.h48
-rw-r--r--apps/playlist_menu.c2
-rw-r--r--apps/playlist_viewer.c400
-rw-r--r--apps/playlist_viewer.h1
-rw-r--r--apps/screens.c4
-rw-r--r--apps/settings.c40
-rw-r--r--apps/settings.h5
-rw-r--r--apps/settings_menu.c4
-rw-r--r--apps/wps-display.c2
14 files changed, 1308 insertions, 646 deletions
diff --git a/apps/bookmark.c b/apps/bookmark.c
index 893c128a15..9e093c0d1f 100644
--- a/apps/bookmark.c
+++ b/apps/bookmark.c
@@ -140,7 +140,7 @@ static bool bookmark_load_menu(void)
140 return false; 140 return false;
141 else 141 else
142 { 142 {
143 char* name = playlist_get_name(global_temp_buffer, 143 char* name = playlist_get_name(NULL, global_temp_buffer,
144 sizeof(global_temp_buffer)); 144 sizeof(global_temp_buffer));
145 if (generate_bookmark_file_name(name, 145 if (generate_bookmark_file_name(name,
146 global_bookmark_file_name, 146 global_bookmark_file_name,
@@ -306,7 +306,7 @@ static bool write_bookmark(bool create_bookmark_file)
306 /* writing the bookmark */ 306 /* writing the bookmark */
307 if (create_bookmark_file) 307 if (create_bookmark_file)
308 { 308 {
309 char* name = playlist_get_name(global_temp_buffer, 309 char* name = playlist_get_name(NULL, global_temp_buffer,
310 sizeof(global_temp_buffer)); 310 sizeof(global_temp_buffer));
311 if (generate_bookmark_file_name(name, 311 if (generate_bookmark_file_name(name,
312 global_bookmark_file_name, 312 global_bookmark_file_name,
@@ -428,12 +428,13 @@ static char* create_bookmark()
428 "%d;%d;%d;%d;%d;%d;%d;%s;%s", 428 "%d;%d;%d;%d;%d;%d;%d;%s;%s",
429 resume_index, 429 resume_index,
430 id3->offset, 430 id3->offset,
431 playlist_get_seed(), 431 playlist_get_seed(NULL),
432 0, 432 0,
433 id3->elapsed, 433 id3->elapsed,
434 global_settings.repeat_mode, 434 global_settings.repeat_mode,
435 global_settings.playlist_shuffle, 435 global_settings.playlist_shuffle,
436 playlist_get_name(global_temp_buffer,sizeof(global_temp_buffer)), 436 playlist_get_name(NULL, global_temp_buffer,
437 sizeof(global_temp_buffer)),
437 file+1); 438 file+1);
438 439
439 /* checking to see if the bookmark is valid */ 440 /* checking to see if the bookmark is valid */
@@ -1090,7 +1091,7 @@ static bool system_check(void)
1090 /* something bad happened while getting the queue information */ 1091 /* something bad happened while getting the queue information */
1091 return false; 1092 return false;
1092 } 1093 }
1093 else if (playlist_modified()) 1094 else if (playlist_modified(NULL))
1094 { 1095 {
1095 /* can't bookmark while in the queue */ 1096 /* can't bookmark while in the queue */
1096 return false; 1097 return false;
diff --git a/apps/lang/english.lang b/apps/lang/english.lang
index 9995ac277b..f88e211192 100644
--- a/apps/lang/english.lang
+++ b/apps/lang/english.lang
@@ -1666,3 +1666,38 @@ id: LANG_CREATE_DIR
1666desc: in main menu 1666desc: in main menu
1667eng: "Create directory" 1667eng: "Create directory"
1668new: 1668new:
1669
1670id: LANG_VIEW
1671desc: in on+play menu
1672eng: "View"
1673new:
1674
1675id: LANG_SHOW_INDICES
1676desc: in playlist viewer menu
1677eng: "Show Indices"
1678new:
1679
1680id: LANG_TRACK_DISPLAY
1681desc: in playlist viewer on+play menu
1682eng: "Track Display"
1683new:
1684
1685id: LANG_DISPLAY_TRACK_NAME_ONLY
1686desc: track display options
1687eng: "Track name only"
1688new:
1689
1690id: LANG_DISPLAY_FULL_PATH
1691desc: track display options
1692eng: "Full path"
1693new:
1694
1695id: LANG_REMOVE
1696desc: in playlist viewer on+play menu
1697eng: "Remove"
1698new:
1699
1700id: LANG_FILE_OPTIONS
1701desc: in playlist viewer on+play menu
1702eng: "File Options"
1703new:
diff --git a/apps/menu.c b/apps/menu.c
index f317403f0d..0bd21b9108 100644
--- a/apps/menu.c
+++ b/apps/menu.c
@@ -44,7 +44,7 @@ struct menu {
44 int itemcount; 44 int itemcount;
45}; 45};
46 46
47#define MAX_MENUS 4 47#define MAX_MENUS 5
48 48
49#ifdef HAVE_LCD_BITMAP 49#ifdef HAVE_LCD_BITMAP
50 50
diff --git a/apps/onplay.c b/apps/onplay.c
index 3e085cea59..2f0ee3b861 100644
--- a/apps/onplay.c
+++ b/apps/onplay.c
@@ -40,6 +40,7 @@
40#include "buffer.h" 40#include "buffer.h"
41#include "settings.h" 41#include "settings.h"
42#include "status.h" 42#include "status.h"
43#include "playlist_viewer.h"
43#include "onplay.h" 44#include "onplay.h"
44 45
45static char* selected_file = NULL; 46static char* selected_file = NULL;
@@ -60,7 +61,7 @@ static bool add_to_playlist(int position, bool queue)
60 playlist_create(NULL, NULL); 61 playlist_create(NULL, NULL);
61 62
62 if ((selected_file_attr & TREE_ATTR_MASK) == TREE_ATTR_MPA) 63 if ((selected_file_attr & TREE_ATTR_MASK) == TREE_ATTR_MPA)
63 playlist_insert_track(selected_file, position, queue); 64 playlist_insert_track(NULL, selected_file, position, queue);
64 else if (selected_file_attr & ATTR_DIRECTORY) 65 else if (selected_file_attr & ATTR_DIRECTORY)
65 { 66 {
66 bool recurse = false; 67 bool recurse = false;
@@ -99,10 +100,11 @@ static bool add_to_playlist(int position, bool queue)
99 } 100 }
100 } 101 }
101 102
102 playlist_insert_directory(selected_file, position, queue, recurse); 103 playlist_insert_directory(NULL, selected_file, position, queue,
104 recurse);
103 } 105 }
104 else if ((selected_file_attr & TREE_ATTR_MASK) == TREE_ATTR_M3U) 106 else if ((selected_file_attr & TREE_ATTR_MASK) == TREE_ATTR_M3U)
105 playlist_insert_playlist(selected_file, position, queue); 107 playlist_insert_playlist(NULL, selected_file, position, queue);
106 108
107 if (new_playlist && (playlist_amount() > 0)) 109 if (new_playlist && (playlist_amount() > 0))
108 { 110 {
@@ -119,12 +121,36 @@ static bool add_to_playlist(int position, bool queue)
119 return false; 121 return false;
120} 122}
121 123
124static bool view_playlist(void)
125{
126 bool was_playing = mpeg_status() & MPEG_STATUS_PLAY;
127 bool result;
128
129 result = playlist_viewer_ex(selected_file);
130
131 if (!was_playing && (mpeg_status() & MPEG_STATUS_PLAY) &&
132 onplay_result == ONPLAY_OK)
133 /* playlist was started from viewer */
134 onplay_result = ONPLAY_START_PLAY;
135
136 return result;
137}
138
122/* Sub-menu for playlist options */ 139/* Sub-menu for playlist options */
123static bool playlist_options(void) 140static bool playlist_options(void)
124{ 141{
125 struct menu_items menu[6]; 142 struct menu_items menu[7];
126 struct playlist_args args[6]; /* increase these 2 if you add entries! */ 143 struct playlist_args args[7]; /* increase these 2 if you add entries! */
127 int m, i=0, result; 144 int m, i=0, pstart=0, result;
145 bool ret = false;
146
147 if ((selected_file_attr & TREE_ATTR_MASK) == TREE_ATTR_M3U)
148 {
149 menu[i].desc = str(LANG_VIEW);
150 menu[i].function = view_playlist;
151 i++;
152 pstart++;
153 }
128 154
129 if (mpeg_status() & MPEG_STATUS_PLAY) 155 if (mpeg_status() & MPEG_STATUS_PLAY)
130 { 156 {
@@ -169,11 +195,13 @@ static bool playlist_options(void)
169 195
170 m = menu_init( menu, i ); 196 m = menu_init( menu, i );
171 result = menu_show(m); 197 result = menu_show(m);
172 if (result >= 0) 198 if (result >= 0 && result < pstart)
173 add_to_playlist(args[result].position, args[result].queue); 199 ret = menu[result].function();
200 else if (result >= pstart)
201 ret = add_to_playlist(args[result].position, args[result].queue);
174 menu_exit(m); 202 menu_exit(m);
175 203
176 return false; 204 return ret;
177} 205}
178 206
179static bool delete_file(void) 207static bool delete_file(void)
@@ -489,8 +517,9 @@ int onplay(char* file, int attr)
489 selected_file = file; 517 selected_file = file;
490 selected_file_attr = attr; 518 selected_file_attr = attr;
491 519
492 if (((attr & TREE_ATTR_MASK) == TREE_ATTR_MPA) || (attr & ATTR_DIRECTORY) || 520 if (((attr & TREE_ATTR_MASK) == TREE_ATTR_MPA) ||
493 (((attr & TREE_ATTR_MASK) == TREE_ATTR_M3U) && (mpeg_status() & MPEG_STATUS_PLAY))) 521 (attr & ATTR_DIRECTORY) ||
522 ((attr & TREE_ATTR_MASK) == TREE_ATTR_M3U))
494 { 523 {
495 menu[i].desc = str(LANG_PLAYINDICES_PLAYLIST); 524 menu[i].desc = str(LANG_PLAYINDICES_PLAYLIST);
496 menu[i].function = playlist_options; 525 menu[i].function = playlist_options;
diff --git a/apps/playlist.c b/apps/playlist.c
index 13919b2f02..8dede37d38 100644
--- a/apps/playlist.c
+++ b/apps/playlist.c
@@ -60,7 +60,9 @@
60 The only resume info that needs to be saved is the current index in the 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 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 62 in the control file will be reapplied so that the playlist indices are
63 exactly the same as before shutdown. 63 exactly the same as before shutdown. To avoid unnecessary disk
64 accesses, the shuffle mode settings are also saved in settings and only
65 flushed to disk when required.
64 */ 66 */
65 67
66#include <stdio.h> 68#include <stdio.h>
@@ -90,8 +92,6 @@
90 92
91#include "lang.h" 93#include "lang.h"
92 94
93static struct playlist_info playlist;
94
95#define PLAYLIST_CONTROL_FILE ROCKBOX_DIR "/.playlist_control" 95#define PLAYLIST_CONTROL_FILE ROCKBOX_DIR "/.playlist_control"
96#define PLAYLIST_CONTROL_FILE_VERSION 2 96#define PLAYLIST_CONTROL_FILE_VERSION 2
97 97
@@ -123,70 +123,83 @@ static struct playlist_info playlist;
123 123
124#define PLAYLIST_DISPLAY_COUNT 10 124#define PLAYLIST_DISPLAY_COUNT 10
125 125
126static struct playlist_info current_playlist;
126static char now_playing[MAX_PATH+1]; 127static char now_playing[MAX_PATH+1];
127 128
128static void empty_playlist(bool resume); 129static void empty_playlist(struct playlist_info* playlist, bool resume);
129static void update_playlist_filename(char *dir, char *file); 130static void new_playlist(struct playlist_info* playlist, char *dir,
130static int add_indices_to_playlist(void); 131 char *file);
131static int add_track_to_playlist(char *filename, int position, bool queue, 132static void create_control(struct playlist_info* playlist);
133static int check_control(struct playlist_info* playlist);
134static void update_playlist_filename(struct playlist_info* playlist,
135 char *dir, char *file);
136static int add_indices_to_playlist(struct playlist_info* playlist,
137 char* buffer, int buflen);
138static int add_track_to_playlist(struct playlist_info* playlist,
139 char *filename, int position, bool queue,
132 int seek_pos); 140 int seek_pos);
133static int add_directory_to_playlist(char *dirname, int *position, bool queue, 141static int add_directory_to_playlist(struct playlist_info* playlist,
142 char *dirname, int *position, bool queue,
134 int *count, bool recurse); 143 int *count, bool recurse);
135static int remove_track_from_playlist(int position, bool write); 144static int remove_track_from_playlist(struct playlist_info* playlist,
136static int randomise_playlist(unsigned int seed, bool start_current, 145 int position, bool write);
146static int randomise_playlist(struct playlist_info* playlist,
147 unsigned int seed, bool start_current,
137 bool write); 148 bool write);
138static int sort_playlist(bool start_current, bool write); 149static int sort_playlist(struct playlist_info* playlist, bool start_current,
139static int get_next_index(int steps); 150 bool write);
140static void find_and_set_playlist_index(unsigned int seek); 151static int get_next_index(struct playlist_info* playlist, int steps);
152static void find_and_set_playlist_index(struct playlist_info* playlist,
153 unsigned int seek);
141static int compare(const void* p1, const void* p2); 154static int compare(const void* p1, const void* p2);
142static int get_filename(int seek, bool control_file, char *buf, 155static int get_filename(struct playlist_info* playlist, int seek,
143 int buf_length); 156 bool control_file, char *buf, int buf_length);
144static int format_track_path(char *dest, char *src, int buf_length, int max, 157static int format_track_path(char *dest, char *src, int buf_length, int max,
145 char *dir); 158 char *dir);
146static void display_playlist_count(int count, char *fmt); 159static void display_playlist_count(int count, char *fmt);
147static void display_buffer_full(void); 160static void display_buffer_full(void);
148static int flush_pending_control(void); 161static int flush_pending_control(struct playlist_info* playlist);
149static int rotate_index(int index); 162static int rotate_index(struct playlist_info* playlist, int index);
150 163
151/* 164/*
152 * remove any files and indices associated with the playlist 165 * remove any files and indices associated with the playlist
153 */ 166 */
154static void empty_playlist(bool resume) 167static void empty_playlist(struct playlist_info* playlist, bool resume)
155{ 168{
156 playlist.filename[0] = '\0'; 169 playlist->filename[0] = '\0';
157 170
158 if(playlist.fd >= 0) 171 if(playlist->fd >= 0)
159 /* If there is an already open playlist, close it. */ 172 /* If there is an already open playlist, close it. */
160 close(playlist.fd); 173 close(playlist->fd);
161 playlist.fd = -1; 174 playlist->fd = -1;
162 175
163 if(playlist.control_fd >= 0) 176 if(playlist->control_fd >= 0)
164 close(playlist.control_fd); 177 close(playlist->control_fd);
165 playlist.control_fd = -1; 178 playlist->control_fd = -1;
166 179 playlist->control_created = false;
167 playlist.in_ram = false; 180
168 playlist.buffer[0] = 0; 181 playlist->in_ram = false;
169 playlist.buffer_end_pos = 0;
170
171 playlist.index = 0;
172 playlist.first_index = 0;
173 playlist.amount = 0;
174 playlist.last_insert_pos = -1;
175 playlist.seed = 0;
176 playlist.shuffle_modified = false;
177 playlist.deleted = false;
178 playlist.num_inserted_tracks = 0;
179 playlist.shuffle_flush = false;
180
181 if (!resume)
182 {
183 int fd;
184 182
183 if (playlist->buffer)
184 playlist->buffer[0] = 0;
185
186 playlist->buffer_end_pos = 0;
187
188 playlist->index = 0;
189 playlist->first_index = 0;
190 playlist->amount = 0;
191 playlist->last_insert_pos = -1;
192 playlist->seed = 0;
193 playlist->shuffle_modified = false;
194 playlist->deleted = false;
195 playlist->num_inserted_tracks = 0;
196 playlist->shuffle_flush = false;
197
198 if (!resume && playlist->current)
199 {
185 /* start with fresh playlist control file when starting new 200 /* start with fresh playlist control file when starting new
186 playlist */ 201 playlist */
187 fd = creat(PLAYLIST_CONTROL_FILE, 0000200); 202 create_control(playlist);
188 if (fd >= 0)
189 close(fd);
190 203
191 /* Reset resume settings */ 204 /* Reset resume settings */
192 global_settings.resume_first_index = 0; 205 global_settings.resume_first_index = 0;
@@ -195,9 +208,86 @@ static void empty_playlist(bool resume)
195} 208}
196 209
197/* 210/*
211 * Initialize a new playlist for viewing/editing/playing. dir is the
212 * directory where the playlist is located and file is the filename.
213 */
214static void new_playlist(struct playlist_info* playlist, char *dir,
215 char *file)
216{
217 empty_playlist(playlist, false);
218
219 if (!file)
220 {
221 file = "";
222
223 if (dir && playlist->current) /* !current cannot be in_ram */
224 playlist->in_ram = true;
225 else
226 dir = ""; /* empty playlist */
227 }
228
229 update_playlist_filename(playlist, dir, file);
230
231 if (playlist->control_fd >= 0)
232 {
233 if (fprintf(playlist->control_fd, "P:%d:%s:%s\n",
234 PLAYLIST_CONTROL_FILE_VERSION, dir, file) > 0)
235 fsync(playlist->control_fd);
236 else
237 splash(HZ*2, true, str(LANG_PLAYLIST_CONTROL_UPDATE_ERROR));
238 }
239}
240
241/*
242 * create control file for playlist
243 */
244static void create_control(struct playlist_info* playlist)
245{
246 playlist->control_fd = creat(playlist->control_filename, 0000200);
247 if (playlist->control_fd < 0)
248 splash(HZ*2, true, str(LANG_PLAYLIST_CONTROL_ACCESS_ERROR));
249 playlist->control_created = true;
250}
251
252/*
253 * validate the control file. This may include creating/initializing it if
254 * necessary;
255 */
256static int check_control(struct playlist_info* playlist)
257{
258 if (!playlist->control_created)
259 {
260 create_control(playlist);
261
262 if (playlist->control_fd >= 0)
263 {
264 char* dir = playlist->filename;
265 char* file = playlist->filename+playlist->dirlen;
266 char c = playlist->filename[playlist->dirlen-1];
267
268 playlist->filename[playlist->dirlen-1] = '\0';
269
270 if (fprintf(playlist->control_fd, "P:%d:%s:%s\n",
271 PLAYLIST_CONTROL_FILE_VERSION, dir, file) > 0)
272 fsync(playlist->control_fd);
273 else
274 splash(HZ*2, true, str(LANG_PLAYLIST_CONTROL_UPDATE_ERROR));
275
276 playlist->filename[playlist->dirlen-1] = c;
277 }
278 }
279
280 if (playlist->control_fd < 0)
281 return -1;
282
283 return 0;
284}
285
286/*
198 * store directory and name of playlist file 287 * store directory and name of playlist file
199 */ 288 */
200static void update_playlist_filename(char *dir, char *file) 289static void update_playlist_filename(struct playlist_info* playlist,
290 char *dir, char *file)
201{ 291{
202 char *sep=""; 292 char *sep="";
203 int dirlen = strlen(dir); 293 int dirlen = strlen(dir);
@@ -210,9 +300,9 @@ static void update_playlist_filename(char *dir, char *file)
210 dirlen++; 300 dirlen++;
211 } 301 }
212 302
213 playlist.dirlen = dirlen; 303 playlist->dirlen = dirlen;
214 304
215 snprintf(playlist.filename, sizeof(playlist.filename), 305 snprintf(playlist->filename, sizeof(playlist->filename),
216 "%s%s%s", 306 "%s%s%s",
217 dir, sep, file); 307 dir, sep, file);
218} 308}
@@ -220,19 +310,18 @@ static void update_playlist_filename(char *dir, char *file)
220/* 310/*
221 * calculate track offsets within a playlist file 311 * calculate track offsets within a playlist file
222 */ 312 */
223static int add_indices_to_playlist(void) 313static int add_indices_to_playlist(struct playlist_info* playlist,
314 char* buffer, int buflen)
224{ 315{
225 unsigned int nread; 316 unsigned int nread;
226 unsigned int i = 0; 317 unsigned int i = 0;
227 unsigned int count = 0; 318 unsigned int count = 0;
228 int buflen;
229 bool store_index; 319 bool store_index;
230 char *buffer;
231 unsigned char *p; 320 unsigned char *p;
232 321
233 if(-1 == playlist.fd) 322 if(-1 == playlist->fd)
234 playlist.fd = open(playlist.filename, O_RDONLY); 323 playlist->fd = open(playlist->filename, O_RDONLY);
235 if(playlist.fd < 0) 324 if(playlist->fd < 0)
236 return -1; /* failure */ 325 return -1; /* failure */
237 326
238#ifdef HAVE_LCD_BITMAP 327#ifdef HAVE_LCD_BITMAP
@@ -244,17 +333,20 @@ static int add_indices_to_playlist(void)
244 333
245 splash(0, true, str(LANG_PLAYLIST_LOAD)); 334 splash(0, true, str(LANG_PLAYLIST_LOAD));
246 335
247 /* use mp3 buffer for maximum load speed */ 336 if (!buffer)
248 buflen = (mp3end - mp3buf); 337 {
249 buffer = mp3buf; 338 /* use mp3 buffer for maximum load speed */
339 mpeg_stop();
250 340
341 buffer = mp3buf;
342 buflen = (mp3end - mp3buf);
343 }
344
251 store_index = true; 345 store_index = true;
252 346
253 mpeg_stop();
254
255 while(1) 347 while(1)
256 { 348 {
257 nread = read(playlist.fd, buffer, buflen); 349 nread = read(playlist->fd, buffer, buflen);
258 /* Terminate on EOF */ 350 /* Terminate on EOF */
259 if(nread <= 0) 351 if(nread <= 0)
260 break; 352 break;
@@ -275,9 +367,9 @@ static int add_indices_to_playlist(void)
275 if(*p != '#') 367 if(*p != '#')
276 { 368 {
277 /* Store a new entry */ 369 /* Store a new entry */
278 playlist.indices[ playlist.amount ] = i+count; 370 playlist->indices[ playlist->amount ] = i+count;
279 playlist.amount++; 371 playlist->amount++;
280 if ( playlist.amount >= playlist.max_playlist_size ) { 372 if ( playlist->amount >= playlist->max_playlist_size ) {
281 display_buffer_full(); 373 display_buffer_full();
282 return -1; 374 return -1;
283 } 375 }
@@ -302,14 +394,15 @@ static int add_indices_to_playlist(void)
302 * matter what other tracks have been inserted. 394 * matter what other tracks have been inserted.
303 * PLAYLIST_INSERT_LAST - Add track to end of playlist 395 * PLAYLIST_INSERT_LAST - Add track to end of playlist
304 */ 396 */
305static int add_track_to_playlist(char *filename, int position, bool queue, 397static int add_track_to_playlist(struct playlist_info* playlist,
398 char *filename, int position, bool queue,
306 int seek_pos) 399 int seek_pos)
307{ 400{
308 int insert_position = position; 401 int insert_position = position;
309 unsigned int flags = PLAYLIST_INSERT_TYPE_INSERT; 402 unsigned int flags = PLAYLIST_INSERT_TYPE_INSERT;
310 int i; 403 int i;
311 404
312 if (playlist.amount >= playlist.max_playlist_size) 405 if (playlist->amount >= playlist->max_playlist_size)
313 { 406 {
314 display_buffer_full(); 407 display_buffer_full();
315 return -1; 408 return -1;
@@ -318,38 +411,38 @@ static int add_track_to_playlist(char *filename, int position, bool queue,
318 switch (position) 411 switch (position)
319 { 412 {
320 case PLAYLIST_PREPEND: 413 case PLAYLIST_PREPEND:
321 insert_position = playlist.first_index; 414 insert_position = playlist->first_index;
322 flags = PLAYLIST_INSERT_TYPE_PREPEND; 415 flags = PLAYLIST_INSERT_TYPE_PREPEND;
323 break; 416 break;
324 case PLAYLIST_INSERT: 417 case PLAYLIST_INSERT:
325 /* if there are already inserted tracks then add track to end of 418 /* if there are already inserted tracks then add track to end of
326 insertion list else add after current playing track */ 419 insertion list else add after current playing track */
327 if (playlist.last_insert_pos >= 0 && 420 if (playlist->last_insert_pos >= 0 &&
328 playlist.last_insert_pos < playlist.amount && 421 playlist->last_insert_pos < playlist->amount &&
329 (playlist.indices[playlist.last_insert_pos]& 422 (playlist->indices[playlist->last_insert_pos]&
330 PLAYLIST_INSERT_TYPE_MASK) == PLAYLIST_INSERT_TYPE_INSERT) 423 PLAYLIST_INSERT_TYPE_MASK) == PLAYLIST_INSERT_TYPE_INSERT)
331 position = insert_position = playlist.last_insert_pos+1; 424 position = insert_position = playlist->last_insert_pos+1;
332 else if (playlist.amount > 0) 425 else if (playlist->amount > 0)
333 position = insert_position = playlist.index + 1; 426 position = insert_position = playlist->index + 1;
334 else 427 else
335 position = insert_position = 0; 428 position = insert_position = 0;
336 429
337 playlist.last_insert_pos = position; 430 playlist->last_insert_pos = position;
338 break; 431 break;
339 case PLAYLIST_INSERT_FIRST: 432 case PLAYLIST_INSERT_FIRST:
340 if (playlist.amount > 0) 433 if (playlist->amount > 0)
341 position = insert_position = playlist.index + 1; 434 position = insert_position = playlist->index + 1;
342 else 435 else
343 position = insert_position = 0; 436 position = insert_position = 0;
344 437
345 if (playlist.last_insert_pos < 0) 438 if (playlist->last_insert_pos < 0)
346 playlist.last_insert_pos = position; 439 playlist->last_insert_pos = position;
347 break; 440 break;
348 case PLAYLIST_INSERT_LAST: 441 case PLAYLIST_INSERT_LAST:
349 if (playlist.first_index > 0) 442 if (playlist->first_index > 0)
350 insert_position = playlist.first_index; 443 insert_position = playlist->first_index;
351 else 444 else
352 insert_position = playlist.amount; 445 insert_position = playlist->amount;
353 446
354 flags = PLAYLIST_INSERT_TYPE_APPEND; 447 flags = PLAYLIST_INSERT_TYPE_APPEND;
355 break; 448 break;
@@ -359,52 +452,52 @@ static int add_track_to_playlist(char *filename, int position, bool queue,
359 flags |= PLAYLIST_QUEUED; 452 flags |= PLAYLIST_QUEUED;
360 453
361 /* shift indices so that track can be added */ 454 /* shift indices so that track can be added */
362 for (i=playlist.amount; i>insert_position; i--) 455 for (i=playlist->amount; i>insert_position; i--)
363 playlist.indices[i] = playlist.indices[i-1]; 456 playlist->indices[i] = playlist->indices[i-1];
364 457
365 /* update stored indices if needed */ 458 /* update stored indices if needed */
366 if (playlist.amount > 0 && insert_position <= playlist.index) 459 if (playlist->amount > 0 && insert_position <= playlist->index)
367 playlist.index++; 460 playlist->index++;
368 461
369 if (playlist.amount > 0 && insert_position <= playlist.first_index && 462 if (playlist->amount > 0 && insert_position <= playlist->first_index &&
370 position != PLAYLIST_PREPEND) 463 position != PLAYLIST_PREPEND)
371 { 464 {
372 playlist.first_index++; 465 playlist->first_index++;
373 466
374 if (seek_pos < 0) 467 if (seek_pos < 0 && playlist->current)
375 { 468 {
376 global_settings.resume_first_index = playlist.first_index; 469 global_settings.resume_first_index = playlist->first_index;
377 settings_save(); 470 settings_save();
378 } 471 }
379 } 472 }
380 473
381 if (insert_position < playlist.last_insert_pos || 474 if (insert_position < playlist->last_insert_pos ||
382 (insert_position == playlist.last_insert_pos && position < 0)) 475 (insert_position == playlist->last_insert_pos && position < 0))
383 playlist.last_insert_pos++; 476 playlist->last_insert_pos++;
384 477
385 if (seek_pos < 0 && playlist.control_fd >= 0) 478 if (seek_pos < 0 && playlist->control_fd >= 0)
386 { 479 {
387 int result = -1; 480 int result = -1;
388 481
389 if (flush_pending_control() < 0) 482 if (flush_pending_control(playlist) < 0)
390 return -1; 483 return -1;
391 484
392 mutex_lock(&playlist.control_mutex); 485 mutex_lock(&playlist->control_mutex);
393 486
394 if (lseek(playlist.control_fd, 0, SEEK_END) >= 0) 487 if (lseek(playlist->control_fd, 0, SEEK_END) >= 0)
395 { 488 {
396 if (fprintf(playlist.control_fd, "%c:%d:%d:", (queue?'Q':'A'), 489 if (fprintf(playlist->control_fd, "%c:%d:%d:", (queue?'Q':'A'),
397 position, playlist.last_insert_pos) > 0) 490 position, playlist->last_insert_pos) > 0)
398 { 491 {
399 /* save the position in file where track name is written */ 492 /* save the position in file where track name is written */
400 seek_pos = lseek(playlist.control_fd, 0, SEEK_CUR); 493 seek_pos = lseek(playlist->control_fd, 0, SEEK_CUR);
401 494
402 if (fprintf(playlist.control_fd, "%s\n", filename) > 0) 495 if (fprintf(playlist->control_fd, "%s\n", filename) > 0)
403 result = 0; 496 result = 0;
404 } 497 }
405 } 498 }
406 499
407 mutex_unlock(&playlist.control_mutex); 500 mutex_unlock(&playlist->control_mutex);
408 501
409 if (result < 0) 502 if (result < 0)
410 { 503 {
@@ -413,10 +506,10 @@ static int add_track_to_playlist(char *filename, int position, bool queue,
413 } 506 }
414 } 507 }
415 508
416 playlist.indices[insert_position] = flags | seek_pos; 509 playlist->indices[insert_position] = flags | seek_pos;
417 510
418 playlist.amount++; 511 playlist->amount++;
419 playlist.num_inserted_tracks++; 512 playlist->num_inserted_tracks++;
420 513
421 return insert_position; 514 return insert_position;
422} 515}
@@ -424,7 +517,8 @@ static int add_track_to_playlist(char *filename, int position, bool queue,
424/* 517/*
425 * Insert directory into playlist. May be called recursively. 518 * Insert directory into playlist. May be called recursively.
426 */ 519 */
427static int add_directory_to_playlist(char *dirname, int *position, bool queue, 520static int add_directory_to_playlist(struct playlist_info* playlist,
521 char *dirname, int *position, bool queue,
428 int *count, bool recurse) 522 int *count, bool recurse)
429{ 523{
430 char buf[MAX_PATH+1]; 524 char buf[MAX_PATH+1];
@@ -474,8 +568,8 @@ static int add_directory_to_playlist(char *dirname, int *position, bool queue,
474 { 568 {
475 /* recursively add directories */ 569 /* recursively add directories */
476 snprintf(buf, sizeof(buf), "%s/%s", dirname, files[i].name); 570 snprintf(buf, sizeof(buf), "%s/%s", dirname, files[i].name);
477 result = add_directory_to_playlist(buf, position, queue, 571 result = add_directory_to_playlist(playlist, buf, position,
478 count, recurse); 572 queue, count, recurse);
479 if (result < 0) 573 if (result < 0)
480 break; 574 break;
481 575
@@ -497,7 +591,8 @@ static int add_directory_to_playlist(char *dirname, int *position, bool queue,
497 591
498 snprintf(buf, sizeof(buf), "%s/%s", dirname, files[i].name); 592 snprintf(buf, sizeof(buf), "%s/%s", dirname, files[i].name);
499 593
500 insert_pos = add_track_to_playlist(buf, *position, queue, -1); 594 insert_pos = add_track_to_playlist(playlist, buf, *position,
595 queue, -1);
501 if (insert_pos < 0) 596 if (insert_pos < 0)
502 { 597 {
503 result = -1; 598 result = -1;
@@ -530,64 +625,65 @@ static int add_directory_to_playlist(char *dirname, int *position, bool queue,
530/* 625/*
531 * remove track at specified position 626 * remove track at specified position
532 */ 627 */
533static int remove_track_from_playlist(int position, bool write) 628static int remove_track_from_playlist(struct playlist_info* playlist,
629 int position, bool write)
534{ 630{
535 int i; 631 int i;
536 bool inserted; 632 bool inserted;
537 633
538 if (playlist.amount <= 0) 634 if (playlist->amount <= 0)
539 return -1; 635 return -1;
540 636
541 inserted = playlist.indices[position] & PLAYLIST_INSERT_TYPE_MASK; 637 inserted = playlist->indices[position] & PLAYLIST_INSERT_TYPE_MASK;
542 638
543 /* shift indices now that track has been removed */ 639 /* shift indices now that track has been removed */
544 for (i=position; i<playlist.amount; i++) 640 for (i=position; i<playlist->amount; i++)
545 playlist.indices[i] = playlist.indices[i+1]; 641 playlist->indices[i] = playlist->indices[i+1];
546 642
547 playlist.amount--; 643 playlist->amount--;
548 644
549 if (inserted) 645 if (inserted)
550 playlist.num_inserted_tracks--; 646 playlist->num_inserted_tracks--;
551 else 647 else
552 playlist.deleted = true; 648 playlist->deleted = true;
553 649
554 /* update stored indices if needed */ 650 /* update stored indices if needed */
555 if (position < playlist.index) 651 if (position < playlist->index)
556 playlist.index--; 652 playlist->index--;
557 653
558 if (position < playlist.first_index) 654 if (position < playlist->first_index)
559 { 655 {
560 playlist.first_index--; 656 playlist->first_index--;
561 657
562 if (write) 658 if (write)
563 { 659 {
564 global_settings.resume_first_index = playlist.first_index; 660 global_settings.resume_first_index = playlist->first_index;
565 settings_save(); 661 settings_save();
566 } 662 }
567 } 663 }
568 664
569 if (position <= playlist.last_insert_pos) 665 if (position <= playlist->last_insert_pos)
570 playlist.last_insert_pos--; 666 playlist->last_insert_pos--;
571 667
572 if (write && playlist.control_fd >= 0) 668 if (write && playlist->control_fd >= 0)
573 { 669 {
574 int result = -1; 670 int result = -1;
575 671
576 if (flush_pending_control() < 0) 672 if (flush_pending_control(playlist) < 0)
577 return -1; 673 return -1;
578 674
579 mutex_lock(&playlist.control_mutex); 675 mutex_lock(&playlist->control_mutex);
580 676
581 if (lseek(playlist.control_fd, 0, SEEK_END) >= 0) 677 if (lseek(playlist->control_fd, 0, SEEK_END) >= 0)
582 { 678 {
583 if (fprintf(playlist.control_fd, "D:%d\n", position) > 0) 679 if (fprintf(playlist->control_fd, "D:%d\n", position) > 0)
584 { 680 {
585 fsync(playlist.control_fd); 681 fsync(playlist->control_fd);
586 result = 0; 682 result = 0;
587 } 683 }
588 } 684 }
589 685
590 mutex_unlock(&playlist.control_mutex); 686 mutex_unlock(&playlist->control_mutex);
591 687
592 if (result < 0) 688 if (result < 0)
593 { 689 {
@@ -603,12 +699,14 @@ static int remove_track_from_playlist(int position, bool write)
603 * randomly rearrange the array of indices for the playlist. If start_current 699 * randomly rearrange the array of indices for the playlist. If start_current
604 * is true then update the index to the new index of the current playing track 700 * is true then update the index to the new index of the current playing track
605 */ 701 */
606static int randomise_playlist(unsigned int seed, bool start_current, bool write) 702static int randomise_playlist(struct playlist_info* playlist,
703 unsigned int seed, bool start_current,
704 bool write)
607{ 705{
608 int count; 706 int count;
609 int candidate; 707 int candidate;
610 int store; 708 int store;
611 unsigned int current = playlist.indices[playlist.index]; 709 unsigned int current = playlist->indices[playlist->index];
612 710
613 /* seed 0 is used to identify sorted playlist for resume purposes */ 711 /* seed 0 is used to identify sorted playlist for resume purposes */
614 if (seed == 0) 712 if (seed == 0)
@@ -618,32 +716,32 @@ static int randomise_playlist(unsigned int seed, bool start_current, bool write)
618 srand(seed); 716 srand(seed);
619 717
620 /* randomise entire indices list */ 718 /* randomise entire indices list */
621 for(count = playlist.amount - 1; count >= 0; count--) 719 for(count = playlist->amount - 1; count >= 0; count--)
622 { 720 {
623 /* the rand is from 0 to RAND_MAX, so adjust to our value range */ 721 /* the rand is from 0 to RAND_MAX, so adjust to our value range */
624 candidate = rand() % (count + 1); 722 candidate = rand() % (count + 1);
625 723
626 /* now swap the values at the 'count' and 'candidate' positions */ 724 /* now swap the values at the 'count' and 'candidate' positions */
627 store = playlist.indices[candidate]; 725 store = playlist->indices[candidate];
628 playlist.indices[candidate] = playlist.indices[count]; 726 playlist->indices[candidate] = playlist->indices[count];
629 playlist.indices[count] = store; 727 playlist->indices[count] = store;
630 } 728 }
631 729
632 if (start_current) 730 if (start_current)
633 find_and_set_playlist_index(current); 731 find_and_set_playlist_index(playlist, current);
634 732
635 /* indices have been moved so last insert position is no longer valid */ 733 /* indices have been moved so last insert position is no longer valid */
636 playlist.last_insert_pos = -1; 734 playlist->last_insert_pos = -1;
637 735
638 playlist.seed = seed; 736 playlist->seed = seed;
639 if (playlist.num_inserted_tracks > 0 || playlist.deleted) 737 if (playlist->num_inserted_tracks > 0 || playlist->deleted)
640 playlist.shuffle_modified = true; 738 playlist->shuffle_modified = true;
641 739
642 if (write) 740 if (write)
643 { 741 {
644 /* Don't write to disk immediately. Instead, save in settings and 742 /* Don't write to disk immediately. Instead, save in settings and
645 only flush if playlist is modified (insertion/deletion) */ 743 only flush if playlist is modified (insertion/deletion) */
646 playlist.shuffle_flush = true; 744 playlist->shuffle_flush = true;
647 global_settings.resume_seed = seed; 745 global_settings.resume_seed = seed;
648 settings_save(); 746 settings_save();
649 } 747 }
@@ -655,27 +753,28 @@ static int randomise_playlist(unsigned int seed, bool start_current, bool write)
655 * Sort the array of indices for the playlist. If start_current is true then 753 * Sort the array of indices for the playlist. If start_current is true then
656 * set the index to the new index of the current song. 754 * set the index to the new index of the current song.
657 */ 755 */
658static int sort_playlist(bool start_current, bool write) 756static int sort_playlist(struct playlist_info* playlist, bool start_current,
757 bool write)
659{ 758{
660 unsigned int current = playlist.indices[playlist.index]; 759 unsigned int current = playlist->indices[playlist->index];
661 760
662 if (playlist.amount > 0) 761 if (playlist->amount > 0)
663 qsort(playlist.indices, playlist.amount, sizeof(playlist.indices[0]), 762 qsort(playlist->indices, playlist->amount, sizeof(playlist->indices[0]),
664 compare); 763 compare);
665 764
666 if (start_current) 765 if (start_current)
667 find_and_set_playlist_index(current); 766 find_and_set_playlist_index(playlist, current);
668 767
669 /* indices have been moved so last insert position is no longer valid */ 768 /* indices have been moved so last insert position is no longer valid */
670 playlist.last_insert_pos = -1; 769 playlist->last_insert_pos = -1;
671 770
672 if (!playlist.num_inserted_tracks && !playlist.deleted) 771 if (!playlist->num_inserted_tracks && !playlist->deleted)
673 playlist.shuffle_modified = false; 772 playlist->shuffle_modified = false;
674 if (write && playlist.control_fd >= 0) 773 if (write && playlist->control_fd >= 0)
675 { 774 {
676 /* Don't write to disk immediately. Instead, save in settings and 775 /* Don't write to disk immediately. Instead, save in settings and
677 only flush if playlist is modified (insertion/deletion) */ 776 only flush if playlist is modified (insertion/deletion) */
678 playlist.shuffle_flush = true; 777 playlist->shuffle_flush = true;
679 global_settings.resume_seed = 0; 778 global_settings.resume_seed = 0;
680 settings_save(); 779 settings_save();
681 } 780 }
@@ -687,26 +786,26 @@ static int sort_playlist(bool start_current, bool write)
687 * returns the index of the track that is "steps" away from current playing 786 * returns the index of the track that is "steps" away from current playing
688 * track. 787 * track.
689 */ 788 */
690static int get_next_index(int steps) 789static int get_next_index(struct playlist_info* playlist, int steps)
691{ 790{
692 int current_index = playlist.index; 791 int current_index = playlist->index;
693 int next_index = -1; 792 int next_index = -1;
694 793
695 if (playlist.amount <= 0) 794 if (playlist->amount <= 0)
696 return -1; 795 return -1;
697 796
698 switch (global_settings.repeat_mode) 797 switch (global_settings.repeat_mode)
699 { 798 {
700 case REPEAT_OFF: 799 case REPEAT_OFF:
701 { 800 {
702 current_index = rotate_index(current_index); 801 current_index = rotate_index(playlist, current_index);
703 802
704 next_index = current_index+steps; 803 next_index = current_index+steps;
705 if ((next_index < 0) || (next_index >= playlist.amount)) 804 if ((next_index < 0) || (next_index >= playlist->amount))
706 next_index = -1; 805 next_index = -1;
707 else 806 else
708 next_index = (next_index+playlist.first_index) % 807 next_index = (next_index+playlist->first_index) %
709 playlist.amount; 808 playlist->amount;
710 809
711 break; 810 break;
712 } 811 }
@@ -718,11 +817,11 @@ static int get_next_index(int steps)
718 case REPEAT_ALL: 817 case REPEAT_ALL:
719 default: 818 default:
720 { 819 {
721 next_index = (current_index+steps) % playlist.amount; 820 next_index = (current_index+steps) % playlist->amount;
722 while (next_index < 0) 821 while (next_index < 0)
723 next_index += playlist.amount; 822 next_index += playlist->amount;
724 823
725 if (steps >= playlist.amount) 824 if (steps >= playlist->amount)
726 { 825 {
727 int i, index; 826 int i, index;
728 827
@@ -730,10 +829,10 @@ static int get_next_index(int steps)
730 next_index = -1; 829 next_index = -1;
731 830
732 /* second time around so skip the queued files */ 831 /* second time around so skip the queued files */
733 for (i=0; i<playlist.amount; i++) 832 for (i=0; i<playlist->amount; i++)
734 { 833 {
735 if (playlist.indices[index] & PLAYLIST_QUEUE_MASK) 834 if (playlist->indices[index] & PLAYLIST_QUEUE_MASK)
736 index = (index+1) % playlist.amount; 835 index = (index+1) % playlist->amount;
737 else 836 else
738 { 837 {
739 next_index = index; 838 next_index = index;
@@ -752,18 +851,24 @@ static int get_next_index(int steps)
752 * Search for the seek track and set appropriate indices. Used after shuffle 851 * Search for the seek track and set appropriate indices. Used after shuffle
753 * to make sure the current index is still pointing to correct track. 852 * to make sure the current index is still pointing to correct track.
754 */ 853 */
755static void find_and_set_playlist_index(unsigned int seek) 854static void find_and_set_playlist_index(struct playlist_info* playlist,
855 unsigned int seek)
756{ 856{
757 int i; 857 int i;
758 858
759 /* Set the index to the current song */ 859 /* Set the index to the current song */
760 for (i=0; i<playlist.amount; i++) 860 for (i=0; i<playlist->amount; i++)
761 { 861 {
762 if (playlist.indices[i] == seek) 862 if (playlist->indices[i] == seek)
763 { 863 {
764 playlist.index = global_settings.resume_first_index = 864 playlist->index = playlist->first_index = i;
765 playlist.first_index = i; 865
766 settings_save(); 866 if (playlist->current)
867 {
868 global_settings.resume_first_index = i;
869 settings_save();
870 }
871
767 break; 872 break;
768 } 873 }
769 } 874 }
@@ -799,8 +904,8 @@ static int compare(const void* p1, const void* p2)
799/* 904/*
800 * gets pathname for track at seek index 905 * gets pathname for track at seek index
801 */ 906 */
802static int get_filename(int seek, bool control_file, char *buf, 907static int get_filename(struct playlist_info* playlist, int seek,
803 int buf_length) 908 bool control_file, char *buf, int buf_length)
804{ 909{
805 int fd; 910 int fd;
806 int max = -1; 911 int max = -1;
@@ -810,34 +915,34 @@ static int get_filename(int seek, bool control_file, char *buf,
810 if (buf_length > MAX_PATH+1) 915 if (buf_length > MAX_PATH+1)
811 buf_length = MAX_PATH+1; 916 buf_length = MAX_PATH+1;
812 917
813 if (playlist.in_ram && !control_file) 918 if (playlist->in_ram && !control_file)
814 { 919 {
815 strncpy(tmp_buf, &playlist.buffer[seek], sizeof(tmp_buf)); 920 strncpy(tmp_buf, &playlist->buffer[seek], sizeof(tmp_buf));
816 tmp_buf[MAX_PATH] = '\0'; 921 tmp_buf[MAX_PATH] = '\0';
817 max = strlen(tmp_buf) + 1; 922 max = strlen(tmp_buf) + 1;
818 } 923 }
819 else 924 else
820 { 925 {
821 if (control_file) 926 if (control_file)
822 fd = playlist.control_fd; 927 fd = playlist->control_fd;
823 else 928 else
824 { 929 {
825 if(-1 == playlist.fd) 930 if(-1 == playlist->fd)
826 playlist.fd = open(playlist.filename, O_RDONLY); 931 playlist->fd = open(playlist->filename, O_RDONLY);
827 932
828 fd = playlist.fd; 933 fd = playlist->fd;
829 } 934 }
830 935
831 if(-1 != fd) 936 if(-1 != fd)
832 { 937 {
833 if (control_file) 938 if (control_file)
834 mutex_lock(&playlist.control_mutex); 939 mutex_lock(&playlist->control_mutex);
835 940
836 lseek(fd, seek, SEEK_SET); 941 lseek(fd, seek, SEEK_SET);
837 max = read(fd, tmp_buf, buf_length); 942 max = read(fd, tmp_buf, buf_length);
838 943
839 if (control_file) 944 if (control_file)
840 mutex_unlock(&playlist.control_mutex); 945 mutex_unlock(&playlist->control_mutex);
841 } 946 }
842 947
843 if (max < 0) 948 if (max < 0)
@@ -852,8 +957,8 @@ static int get_filename(int seek, bool control_file, char *buf,
852 } 957 }
853 } 958 }
854 959
855 strncpy(dir_buf, playlist.filename, playlist.dirlen-1); 960 strncpy(dir_buf, playlist->filename, playlist->dirlen-1);
856 dir_buf[playlist.dirlen-1] = 0; 961 dir_buf[playlist->dirlen-1] = 0;
857 962
858 return (format_track_path(buf, tmp_buf, buf_length, max, dir_buf)); 963 return (format_track_path(buf, tmp_buf, buf_length, max, dir_buf));
859} 964}
@@ -964,29 +1069,29 @@ static void display_buffer_full(void)
964 * Flush any pending control commands to disk. Called when playlist is being 1069 * Flush any pending control commands to disk. Called when playlist is being
965 * modified. Returns 0 on success and -1 on failure. 1070 * modified. Returns 0 on success and -1 on failure.
966 */ 1071 */
967static int flush_pending_control(void) 1072static int flush_pending_control(struct playlist_info* playlist)
968{ 1073{
969 int result = 0; 1074 int result = 0;
970 1075
971 if (playlist.shuffle_flush && global_settings.resume_seed >= 0) 1076 if (playlist->shuffle_flush && global_settings.resume_seed >= 0)
972 { 1077 {
973 /* pending shuffle */ 1078 /* pending shuffle */
974 mutex_lock(&playlist.control_mutex); 1079 mutex_lock(&playlist->control_mutex);
975 1080
976 if (lseek(playlist.control_fd, 0, SEEK_END) >= 0) 1081 if (lseek(playlist->control_fd, 0, SEEK_END) >= 0)
977 { 1082 {
978 if (global_settings.resume_seed == 0) 1083 if (global_settings.resume_seed == 0)
979 result = fprintf(playlist.control_fd, "U:%d\n", 1084 result = fprintf(playlist->control_fd, "U:%d\n",
980 playlist.first_index); 1085 playlist->first_index);
981 else 1086 else
982 result = fprintf(playlist.control_fd, "S:%d:%d\n", 1087 result = fprintf(playlist->control_fd, "S:%d:%d\n",
983 global_settings.resume_seed, playlist.first_index); 1088 global_settings.resume_seed, playlist->first_index);
984 1089
985 if (result > 0) 1090 if (result > 0)
986 { 1091 {
987 fsync(playlist.control_fd); 1092 fsync(playlist->control_fd);
988 1093
989 playlist.shuffle_flush = false; 1094 playlist->shuffle_flush = false;
990 global_settings.resume_seed = -1; 1095 global_settings.resume_seed = -1;
991 settings_save(); 1096 settings_save();
992 1097
@@ -998,7 +1103,7 @@ static int flush_pending_control(void)
998 else 1103 else
999 result = -1; 1104 result = -1;
1000 1105
1001 mutex_unlock(&playlist.control_mutex); 1106 mutex_unlock(&playlist->control_mutex);
1002 1107
1003 if (result < 0) 1108 if (result < 0)
1004 { 1109 {
@@ -1013,11 +1118,11 @@ static int flush_pending_control(void)
1013/* 1118/*
1014 * Rotate indices such that first_index is index 0 1119 * Rotate indices such that first_index is index 0
1015 */ 1120 */
1016static int rotate_index(int index) 1121static int rotate_index(struct playlist_info* playlist, int index)
1017{ 1122{
1018 index -= playlist.first_index; 1123 index -= playlist->first_index;
1019 if (index < 0) 1124 if (index < 0)
1020 index += playlist.amount; 1125 index += playlist->amount;
1021 1126
1022 return index; 1127 return index;
1023} 1128}
@@ -1027,15 +1132,20 @@ static int rotate_index(int index)
1027 */ 1132 */
1028void playlist_init(void) 1133void playlist_init(void)
1029{ 1134{
1030 playlist.fd = -1; 1135 struct playlist_info* playlist = &current_playlist;
1031 playlist.control_fd = -1; 1136
1032 playlist.max_playlist_size = global_settings.max_files_in_playlist; 1137 playlist->current = true;
1033 playlist.indices = buffer_alloc(playlist.max_playlist_size * sizeof(int)); 1138 snprintf(playlist->control_filename, sizeof(playlist->control_filename),
1034 playlist.buffer_size = 1139 "%s", PLAYLIST_CONTROL_FILE);
1140 playlist->fd = -1;
1141 playlist->control_fd = -1;
1142 playlist->max_playlist_size = global_settings.max_files_in_playlist;
1143 playlist->indices = buffer_alloc(playlist->max_playlist_size * sizeof(int));
1144 playlist->buffer_size =
1035 AVERAGE_FILENAME_LENGTH * global_settings.max_files_in_dir; 1145 AVERAGE_FILENAME_LENGTH * global_settings.max_files_in_dir;
1036 playlist.buffer = buffer_alloc(playlist.buffer_size); 1146 playlist->buffer = buffer_alloc(playlist->buffer_size);
1037 mutex_init(&playlist.control_mutex); 1147 mutex_init(&playlist->control_mutex);
1038 empty_playlist(true); 1148 empty_playlist(playlist, true);
1039} 1149}
1040 1150
1041/* 1151/*
@@ -1043,36 +1153,13 @@ void playlist_init(void)
1043 */ 1153 */
1044int playlist_create(char *dir, char *file) 1154int playlist_create(char *dir, char *file)
1045{ 1155{
1046 empty_playlist(false); 1156 struct playlist_info* playlist = &current_playlist;
1047
1048 playlist.control_fd = open(PLAYLIST_CONTROL_FILE, O_RDWR);
1049 if (playlist.control_fd < 0)
1050 splash(HZ*2, true, str(LANG_PLAYLIST_CONTROL_ACCESS_ERROR));
1051
1052 if (!file)
1053 {
1054 file = "";
1055 1157
1056 if (dir) 1158 new_playlist(playlist, dir, file);
1057 playlist.in_ram = true;
1058 else
1059 dir = ""; /* empty playlist */
1060 }
1061
1062 update_playlist_filename(dir, file);
1063 1159
1064 if (playlist.control_fd >= 0) 1160 if (file)
1065 { 1161 /* load the playlist file */
1066 if (fprintf(playlist.control_fd, "P:%d:%s:%s\n", 1162 add_indices_to_playlist(playlist, NULL, 0);
1067 PLAYLIST_CONTROL_FILE_VERSION, dir, file) > 0)
1068 fsync(playlist.control_fd);
1069 else
1070 splash(HZ*2, true, str(LANG_PLAYLIST_CONTROL_UPDATE_ERROR));
1071 }
1072
1073 /* load the playlist file */
1074 if (file[0] != '\0')
1075 add_indices_to_playlist();
1076 1163
1077 return 0; 1164 return 0;
1078} 1165}
@@ -1085,6 +1172,7 @@ int playlist_create(char *dir, char *file)
1085 */ 1172 */
1086int playlist_resume(void) 1173int playlist_resume(void)
1087{ 1174{
1175 struct playlist_info* playlist = &current_playlist;
1088 char *buffer; 1176 char *buffer;
1089 int buflen; 1177 int buflen;
1090 int nread; 1178 int nread;
@@ -1107,17 +1195,18 @@ int playlist_resume(void)
1107 buflen = (mp3end - mp3buf); 1195 buflen = (mp3end - mp3buf);
1108 buffer = mp3buf; 1196 buffer = mp3buf;
1109 1197
1110 empty_playlist(true); 1198 empty_playlist(playlist, true);
1111 1199
1112 playlist.control_fd = open(PLAYLIST_CONTROL_FILE, O_RDWR); 1200 playlist->control_fd = open(playlist->control_filename, O_RDWR);
1113 if (playlist.control_fd < 0) 1201 if (playlist->control_fd < 0)
1114 { 1202 {
1115 splash(HZ*2, true, str(LANG_PLAYLIST_CONTROL_ACCESS_ERROR)); 1203 splash(HZ*2, true, str(LANG_PLAYLIST_CONTROL_ACCESS_ERROR));
1116 return -1; 1204 return -1;
1117 } 1205 }
1206 playlist->control_created = true;
1118 1207
1119 /* read a small amount first to get the header */ 1208 /* read a small amount first to get the header */
1120 nread = read(playlist.control_fd, buffer, 1209 nread = read(playlist->control_fd, buffer,
1121 PLAYLIST_COMMAND_SIZE<buflen?PLAYLIST_COMMAND_SIZE:buflen); 1210 PLAYLIST_COMMAND_SIZE<buflen?PLAYLIST_COMMAND_SIZE:buflen);
1122 if(nread <= 0) 1211 if(nread <= 0)
1123 { 1212 {
@@ -1174,18 +1263,18 @@ int playlist_resume(void)
1174 if (version != PLAYLIST_CONTROL_FILE_VERSION) 1263 if (version != PLAYLIST_CONTROL_FILE_VERSION)
1175 return -1; 1264 return -1;
1176 1265
1177 update_playlist_filename(str2, str3); 1266 update_playlist_filename(playlist, str2, str3);
1178 1267
1179 if (str3[0] != '\0') 1268 if (str3[0] != '\0')
1180 { 1269 {
1181 /* NOTE: add_indices_to_playlist() overwrites the 1270 /* NOTE: add_indices_to_playlist() overwrites the
1182 mp3buf so we need to reload control file 1271 mp3buf so we need to reload control file
1183 data */ 1272 data */
1184 add_indices_to_playlist(); 1273 add_indices_to_playlist(playlist, NULL, 0);
1185 } 1274 }
1186 else if (str2[0] != '\0') 1275 else if (str2[0] != '\0')
1187 { 1276 {
1188 playlist.in_ram = true; 1277 playlist->in_ram = true;
1189 resume_directory(str2); 1278 resume_directory(str2);
1190 } 1279 }
1191 1280
@@ -1216,11 +1305,11 @@ int playlist_resume(void)
1216 1305
1217 /* seek position is based on str3's position in 1306 /* seek position is based on str3's position in
1218 buffer */ 1307 buffer */
1219 if (add_track_to_playlist(str3, position, queue, 1308 if (add_track_to_playlist(playlist, str3, position,
1220 total_read+(str3-buffer)) < 0) 1309 queue, total_read+(str3-buffer)) < 0)
1221 return -1; 1310 return -1;
1222 1311
1223 playlist.last_insert_pos = last_position; 1312 playlist->last_insert_pos = last_position;
1224 1313
1225 break; 1314 break;
1226 } 1315 }
@@ -1238,8 +1327,8 @@ int playlist_resume(void)
1238 1327
1239 position = atoi(str1); 1328 position = atoi(str1);
1240 1329
1241 if (remove_track_from_playlist(position, 1330 if (remove_track_from_playlist(playlist, position,
1242 false) < 0) 1331 false) < 0)
1243 return -1; 1332 return -1;
1244 1333
1245 break; 1334 break;
@@ -1259,13 +1348,14 @@ int playlist_resume(void)
1259 if (!sorted) 1348 if (!sorted)
1260 { 1349 {
1261 /* Always sort list before shuffling */ 1350 /* Always sort list before shuffling */
1262 sort_playlist(false, false); 1351 sort_playlist(playlist, false, false);
1263 } 1352 }
1264 1353
1265 seed = atoi(str1); 1354 seed = atoi(str1);
1266 playlist.first_index = atoi(str2); 1355 playlist->first_index = atoi(str2);
1267 1356
1268 if (randomise_playlist(seed, false, false) < 0) 1357 if (randomise_playlist(playlist, seed, false,
1358 false) < 0)
1269 return -1; 1359 return -1;
1270 1360
1271 sorted = false; 1361 sorted = false;
@@ -1281,9 +1371,9 @@ int playlist_resume(void)
1281 break; 1371 break;
1282 } 1372 }
1283 1373
1284 playlist.first_index = atoi(str1); 1374 playlist->first_index = atoi(str1);
1285 1375
1286 if (sort_playlist(false, false) < 0) 1376 if (sort_playlist(playlist, false, false) < 0)
1287 return -1; 1377 return -1;
1288 1378
1289 sorted = true; 1379 sorted = true;
@@ -1291,7 +1381,7 @@ int playlist_resume(void)
1291 } 1381 }
1292 case resume_reset: 1382 case resume_reset:
1293 { 1383 {
1294 playlist.last_insert_pos = -1; 1384 playlist->last_insert_pos = -1;
1295 break; 1385 break;
1296 } 1386 }
1297 case resume_comment: 1387 case resume_comment:
@@ -1407,17 +1497,17 @@ int playlist_resume(void)
1407 NOTE: because of this, control file must always end with a 1497 NOTE: because of this, control file must always end with a
1408 newline */ 1498 newline */
1409 count = last_newline; 1499 count = last_newline;
1410 lseek(playlist.control_fd, total_read+count, SEEK_SET); 1500 lseek(playlist->control_fd, total_read+count, SEEK_SET);
1411 } 1501 }
1412 1502
1413 total_read += count; 1503 total_read += count;
1414 1504
1415 if (first) 1505 if (first)
1416 /* still looking for header */ 1506 /* still looking for header */
1417 nread = read(playlist.control_fd, buffer, 1507 nread = read(playlist->control_fd, buffer,
1418 PLAYLIST_COMMAND_SIZE<buflen?PLAYLIST_COMMAND_SIZE:buflen); 1508 PLAYLIST_COMMAND_SIZE<buflen?PLAYLIST_COMMAND_SIZE:buflen);
1419 else 1509 else
1420 nread = read(playlist.control_fd, buffer, buflen); 1510 nread = read(playlist->control_fd, buffer, buflen);
1421 1511
1422 /* Terminate on EOF */ 1512 /* Terminate on EOF */
1423 if(nread <= 0) 1513 if(nread <= 0)
@@ -1426,17 +1516,17 @@ int playlist_resume(void)
1426 { 1516 {
1427 /* Apply shuffle command saved in settings */ 1517 /* Apply shuffle command saved in settings */
1428 if (global_settings.resume_seed == 0) 1518 if (global_settings.resume_seed == 0)
1429 sort_playlist(false, true); 1519 sort_playlist(playlist, false, true);
1430 else 1520 else
1431 { 1521 {
1432 if (!sorted) 1522 if (!sorted)
1433 sort_playlist(false, false); 1523 sort_playlist(playlist, false, false);
1434 1524
1435 randomise_playlist(global_settings.resume_seed, false, 1525 randomise_playlist(playlist, global_settings.resume_seed,
1436 true); 1526 false, true);
1437 } 1527 }
1438 1528
1439 playlist.first_index = global_settings.resume_first_index; 1529 playlist->first_index = global_settings.resume_first_index;
1440 } 1530 }
1441 1531
1442 break; 1532 break;
@@ -1451,43 +1541,364 @@ int playlist_resume(void)
1451 */ 1541 */
1452int playlist_add(char *filename) 1542int playlist_add(char *filename)
1453{ 1543{
1544 struct playlist_info* playlist = &current_playlist;
1454 int len = strlen(filename); 1545 int len = strlen(filename);
1455 1546
1456 if((len+1 > playlist.buffer_size - playlist.buffer_end_pos) || 1547 if((len+1 > playlist->buffer_size - playlist->buffer_end_pos) ||
1457 (playlist.amount >= playlist.max_playlist_size)) 1548 (playlist->amount >= playlist->max_playlist_size))
1458 { 1549 {
1459 display_buffer_full(); 1550 display_buffer_full();
1460 return -1; 1551 return -1;
1461 } 1552 }
1462 1553
1463 playlist.indices[playlist.amount++] = playlist.buffer_end_pos; 1554 playlist->indices[playlist->amount++] = playlist->buffer_end_pos;
1555
1556 strcpy(&playlist->buffer[playlist->buffer_end_pos], filename);
1557 playlist->buffer_end_pos += len;
1558 playlist->buffer[playlist->buffer_end_pos++] = '\0';
1559
1560 return 0;
1561}
1562
1563/* shuffle newly created playlist using random seed. */
1564int playlist_shuffle(int random_seed, int start_index)
1565{
1566 struct playlist_info* playlist = &current_playlist;
1567
1568 unsigned int seek_pos = 0;
1569 bool start_current = false;
1570
1571 if (start_index >= 0 && global_settings.play_selected)
1572 {
1573 /* store the seek position before the shuffle */
1574 seek_pos = playlist->indices[start_index];
1575 playlist->index = global_settings.resume_first_index =
1576 playlist->first_index = start_index;
1577 start_current = true;
1578 }
1579
1580 splash(0, true, str(LANG_PLAYLIST_SHUFFLE));
1581
1582 randomise_playlist(playlist, random_seed, start_current, true);
1583
1584 /* Flush shuffle command to disk */
1585 flush_pending_control(playlist);
1586
1587 return playlist->index;
1588}
1589
1590/* start playing current playlist at specified index/offset */
1591int playlist_start(int start_index, int offset)
1592{
1593 struct playlist_info* playlist = &current_playlist;
1594
1595 playlist->index = start_index;
1596 mpeg_play(offset);
1597
1598 return 0;
1599}
1600
1601/* Returns false if 'steps' is out of bounds, else true */
1602bool playlist_check(int steps)
1603{
1604 struct playlist_info* playlist = &current_playlist;
1605 int index = get_next_index(playlist, steps);
1606 return (index >= 0);
1607}
1608
1609/* get trackname of track that is "steps" away from current playing track.
1610 NULL is used to identify end of playlist */
1611char* playlist_peek(int steps)
1612{
1613 struct playlist_info* playlist = &current_playlist;
1614 int seek;
1615 int fd;
1616 char *temp_ptr;
1617 int index;
1618 bool control_file;
1619
1620 index = get_next_index(playlist, steps);
1621 if (index < 0)
1622 return NULL;
1623
1624 control_file = playlist->indices[index] & PLAYLIST_INSERT_TYPE_MASK;
1625 seek = playlist->indices[index] & PLAYLIST_SEEK_MASK;
1626
1627 if (get_filename(playlist, seek, control_file, now_playing,
1628 MAX_PATH+1) < 0)
1629 return NULL;
1630
1631 temp_ptr = now_playing;
1632
1633 if (!playlist->in_ram || control_file)
1634 {
1635 /* remove bogus dirs from beginning of path
1636 (workaround for buggy playlist creation tools) */
1637 while (temp_ptr)
1638 {
1639 fd = open(temp_ptr, O_RDONLY);
1640 if (fd >= 0)
1641 {
1642 close(fd);
1643 break;
1644 }
1645
1646 temp_ptr = strchr(temp_ptr+1, '/');
1647 }
1648
1649 if (!temp_ptr)
1650 {
1651 /* Even though this is an invalid file, we still need to pass a
1652 file name to the caller because NULL is used to indicate end
1653 of playlist */
1654 return now_playing;
1655 }
1656 }
1657
1658 return temp_ptr;
1659}
1660
1661/*
1662 * Update indices as track has changed
1663 */
1664int playlist_next(int steps)
1665{
1666 struct playlist_info* playlist = &current_playlist;
1667 int index;
1668
1669 if (steps > 0 && global_settings.repeat_mode != REPEAT_ONE)
1670 {
1671 int i, j;
1672
1673 /* We need to delete all the queued songs */
1674 for (i=0, j=steps; i<j; i++)
1675 {
1676 index = get_next_index(playlist, i);
1677
1678 if (playlist->indices[index] & PLAYLIST_QUEUE_MASK)
1679 {
1680 remove_track_from_playlist(playlist, index, true);
1681 steps--; /* one less track */
1682 }
1683 }
1684 }
1685
1686 index = get_next_index(playlist, steps);
1687 playlist->index = index;
1688
1689 if (playlist->last_insert_pos >= 0 && steps > 0)
1690 {
1691 /* check to see if we've gone beyond the last inserted track */
1692 int cur = rotate_index(playlist, index);
1693 int last_pos = rotate_index(playlist, playlist->last_insert_pos);
1694
1695 if (cur > last_pos)
1696 {
1697 /* reset last inserted track */
1698 playlist->last_insert_pos = -1;
1699
1700 if (playlist->control_fd >= 0)
1701 {
1702 int result = -1;
1703
1704 mutex_lock(&playlist->control_mutex);
1705
1706 if (lseek(playlist->control_fd, 0, SEEK_END) >= 0)
1707 {
1708 if (fprintf(playlist->control_fd, "R\n") > 0)
1709 {
1710 fsync(playlist->control_fd);
1711 result = 0;
1712 }
1713 }
1714
1715 mutex_unlock(&playlist->control_mutex);
1716
1717 if (result < 0)
1718 {
1719 splash(HZ*2, true,
1720 str(LANG_PLAYLIST_CONTROL_UPDATE_ERROR));
1721 return result;
1722 }
1723 }
1724 }
1725 }
1726
1727 return index;
1728}
1729
1730/* Get resume info for current playing song. If return value is -1 then
1731 settings shouldn't be saved. */
1732int playlist_get_resume_info(int *resume_index)
1733{
1734 struct playlist_info* playlist = &current_playlist;
1735
1736 *resume_index = playlist->index;
1737
1738 return 0;
1739}
1740
1741/* Returns index of current playing track for display purposes. This value
1742 should not be used for resume purposes as it doesn't represent the actual
1743 index into the playlist */
1744int playlist_get_display_index(void)
1745{
1746 struct playlist_info* playlist = &current_playlist;
1747
1748 /* first_index should always be index 0 for display purposes */
1749 int index = rotate_index(playlist, playlist->index);
1750
1751 return (index+1);
1752}
1753
1754/* returns number of tracks in current playlist */
1755int playlist_amount(void)
1756{
1757 return playlist_amount_ex(NULL);
1758}
1759
1760/*
1761 * Create a new playlist If playlist is not NULL then we're loading a
1762 * playlist off disk for viewing/editing. The index_buffer is used to store
1763 * playlist indices (required for and only used if !current playlist). The
1764 * temp_buffer (if not NULL) is used as a scratchpad when loading indices.
1765 */
1766int playlist_create_ex(struct playlist_info* playlist, char* dir, char* file,
1767 void* index_buffer, int index_buffer_size,
1768 void* temp_buffer, int temp_buffer_size)
1769{
1770 if (!playlist)
1771 playlist = &current_playlist;
1772 else
1773 {
1774 /* Initialize playlist structure */
1775 int r = rand() % 10;
1776 playlist->current = false;
1777
1778 /* Use random name for control file */
1779 snprintf(playlist->control_filename, sizeof(playlist->control_filename),
1780 "%s.%d", PLAYLIST_CONTROL_FILE, r);
1781 playlist->fd = -1;
1782 playlist->control_fd = -1;
1783
1784 if (index_buffer)
1785 {
1786 int num_indices = index_buffer_size / sizeof(int);
1787
1788 if (num_indices > global_settings.max_files_in_playlist)
1789 num_indices = global_settings.max_files_in_playlist;
1790
1791 playlist->max_playlist_size = num_indices;
1792 playlist->indices = index_buffer;
1793 }
1794 else
1795 {
1796 playlist->max_playlist_size = current_playlist.max_playlist_size;
1797 playlist->indices = current_playlist.indices;
1798 }
1799
1800 playlist->buffer_size = 0;
1801 playlist->buffer = NULL;
1802 mutex_init(&playlist->control_mutex);
1803 }
1804
1805 new_playlist(playlist, dir, file);
1464 1806
1465 strcpy(&playlist.buffer[playlist.buffer_end_pos], filename); 1807 if (file)
1466 playlist.buffer_end_pos += len; 1808 /* load the playlist file */
1467 playlist.buffer[playlist.buffer_end_pos++] = '\0'; 1809 add_indices_to_playlist(playlist, temp_buffer, temp_buffer_size);
1468 1810
1469 return 0; 1811 return 0;
1470} 1812}
1471 1813
1472/* 1814/*
1815 * Set the specified playlist as the current.
1816 * NOTE: You will get undefined behaviour if something is already playing so
1817 * remember to stop before calling this. Also, this call will
1818 * effectively close your playlist, making it unusable.
1819 */
1820int playlist_set_current(struct playlist_info* playlist)
1821{
1822 if (!playlist || (check_control(playlist) < 0))
1823 return -1;
1824
1825 empty_playlist(&current_playlist, false);
1826
1827 strncpy(current_playlist.filename, playlist->filename,
1828 sizeof(current_playlist.filename));
1829
1830 current_playlist.fd = playlist->fd;
1831
1832 close(playlist->control_fd);
1833 remove(current_playlist.control_filename);
1834 if (rename(playlist->control_filename,
1835 current_playlist.control_filename) < 0)
1836 return -1;
1837 current_playlist.control_fd = open(current_playlist.control_filename,
1838 O_RDWR);
1839 if (current_playlist.control_fd < 0)
1840 return -1;
1841 current_playlist.control_created = true;
1842
1843 current_playlist.dirlen = playlist->dirlen;
1844
1845 if (playlist->indices && playlist->indices != current_playlist.indices)
1846 memcpy(current_playlist.indices, playlist->indices,
1847 playlist->max_playlist_size*sizeof(int));
1848
1849 current_playlist.first_index = playlist->first_index;
1850 current_playlist.amount = playlist->amount;
1851 current_playlist.last_insert_pos = playlist->last_insert_pos;
1852 current_playlist.seed = playlist->seed;
1853 current_playlist.shuffle_modified = playlist->shuffle_modified;
1854 current_playlist.deleted = playlist->deleted;
1855 current_playlist.num_inserted_tracks = playlist->num_inserted_tracks;
1856 current_playlist.shuffle_flush = playlist->shuffle_flush;
1857
1858 return 0;
1859}
1860
1861/*
1862 * Close files and delete control file for non-current playlist.
1863 */
1864void playlist_close(struct playlist_info* playlist)
1865{
1866 if (!playlist)
1867 return;
1868
1869 if (playlist->fd >= 0)
1870 close(playlist->fd);
1871
1872 if (playlist->control_fd >= 0)
1873 close(playlist->control_fd);
1874
1875 if (playlist->control_created)
1876 remove(playlist->control_filename);
1877}
1878
1879/*
1473 * Insert track into playlist at specified position (or one of the special 1880 * Insert track into playlist at specified position (or one of the special
1474 * positions). Returns position where track was inserted or -1 if error. 1881 * positions). Returns position where track was inserted or -1 if error.
1475 */ 1882 */
1476int playlist_insert_track(char *filename, int position, bool queue) 1883int playlist_insert_track(struct playlist_info* playlist, char *filename,
1884 int position, bool queue)
1477{ 1885{
1478 int result; 1886 int result;
1479 1887
1480 if (playlist.control_fd < 0) 1888 if (!playlist)
1889 playlist = &current_playlist;
1890
1891 if (check_control(playlist) < 0)
1481 { 1892 {
1482 splash(HZ*2, true, str(LANG_PLAYLIST_CONTROL_ACCESS_ERROR)); 1893 splash(HZ*2, true, str(LANG_PLAYLIST_CONTROL_ACCESS_ERROR));
1483 return -1; 1894 return -1;
1484 } 1895 }
1485 1896
1486 result = add_track_to_playlist(filename, position, queue, -1); 1897 result = add_track_to_playlist(playlist, filename, position, queue, -1);
1487 1898
1488 if (result != -1) 1899 if (result != -1)
1489 { 1900 {
1490 fsync(playlist.control_fd); 1901 fsync(playlist->control_fd);
1491 mpeg_flush_and_reload_tracks(); 1902 mpeg_flush_and_reload_tracks();
1492 } 1903 }
1493 1904
@@ -1497,14 +1908,17 @@ int playlist_insert_track(char *filename, int position, bool queue)
1497/* 1908/*
1498 * Insert all tracks from specified directory into playlist. 1909 * Insert all tracks from specified directory into playlist.
1499 */ 1910 */
1500int playlist_insert_directory(char *dirname, int position, bool queue, 1911int playlist_insert_directory(struct playlist_info* playlist, char *dirname,
1501 bool recurse) 1912 int position, bool queue, bool recurse)
1502{ 1913{
1503 int count = 0; 1914 int count = 0;
1504 int result; 1915 int result;
1505 char *count_str; 1916 char *count_str;
1506 1917
1507 if (playlist.control_fd < 0) 1918 if (!playlist)
1919 playlist = &current_playlist;
1920
1921 if (check_control(playlist) < 0)
1508 { 1922 {
1509 splash(HZ*2, true, str(LANG_PLAYLIST_CONTROL_ACCESS_ERROR)); 1923 splash(HZ*2, true, str(LANG_PLAYLIST_CONTROL_ACCESS_ERROR));
1510 return -1; 1924 return -1;
@@ -1517,9 +1931,9 @@ int playlist_insert_directory(char *dirname, int position, bool queue,
1517 1931
1518 display_playlist_count(count, count_str); 1932 display_playlist_count(count, count_str);
1519 1933
1520 result = add_directory_to_playlist(dirname, &position, queue, &count, 1934 result = add_directory_to_playlist(playlist, dirname, &position, queue,
1521 recurse); 1935 &count, recurse);
1522 fsync(playlist.control_fd); 1936 fsync(playlist->control_fd);
1523 1937
1524 display_playlist_count(count, count_str); 1938 display_playlist_count(count, count_str);
1525 mpeg_flush_and_reload_tracks(); 1939 mpeg_flush_and_reload_tracks();
@@ -1528,9 +1942,10 @@ int playlist_insert_directory(char *dirname, int position, bool queue,
1528} 1942}
1529 1943
1530/* 1944/*
1531 * Insert all tracks from specified playlist into dynamic playlist 1945 * Insert all tracks from specified playlist into dynamic playlist.
1532 */ 1946 */
1533int playlist_insert_playlist(char *filename, int position, bool queue) 1947int playlist_insert_playlist(struct playlist_info* playlist, char *filename,
1948 int position, bool queue)
1534{ 1949{
1535 int fd; 1950 int fd;
1536 int max; 1951 int max;
@@ -1542,7 +1957,10 @@ int playlist_insert_playlist(char *filename, int position, bool queue)
1542 int count = 0; 1957 int count = 0;
1543 int result = 0; 1958 int result = 0;
1544 1959
1545 if (playlist.control_fd < 0) 1960 if (!playlist)
1961 playlist = &current_playlist;
1962
1963 if (check_control(playlist) < 0)
1546 { 1964 {
1547 splash(HZ*2, true, str(LANG_PLAYLIST_CONTROL_ACCESS_ERROR)); 1965 splash(HZ*2, true, str(LANG_PLAYLIST_CONTROL_ACCESS_ERROR));
1548 return -1; 1966 return -1;
@@ -1594,8 +2012,8 @@ int playlist_insert_playlist(char *filename, int position, bool queue)
1594 break; 2012 break;
1595 } 2013 }
1596 2014
1597 insert_pos = add_track_to_playlist(trackname, position, queue, 2015 insert_pos = add_track_to_playlist(playlist, trackname, position,
1598 -1); 2016 queue, -1);
1599 2017
1600 if (insert_pos < 0) 2018 if (insert_pos < 0)
1601 { 2019 {
@@ -1624,9 +2042,10 @@ int playlist_insert_playlist(char *filename, int position, bool queue)
1624 } 2042 }
1625 2043
1626 close(fd); 2044 close(fd);
1627 fsync(playlist.control_fd); 2045 fsync(playlist->control_fd);
1628 2046
1629 *temp_ptr = '/'; 2047 if (temp_ptr)
2048 *temp_ptr = '/';
1630 2049
1631 display_playlist_count(count, count_str); 2050 display_playlist_count(count, count_str);
1632 mpeg_flush_and_reload_tracks(); 2051 mpeg_flush_and_reload_tracks();
@@ -1638,20 +2057,23 @@ int playlist_insert_playlist(char *filename, int position, bool queue)
1638 * Delete track at specified index. If index is PLAYLIST_DELETE_CURRENT then 2057 * Delete track at specified index. If index is PLAYLIST_DELETE_CURRENT then
1639 * we want to delete the current playing track. 2058 * we want to delete the current playing track.
1640 */ 2059 */
1641int playlist_delete(int index) 2060int playlist_delete(struct playlist_info* playlist, int index)
1642{ 2061{
1643 int result = 0; 2062 int result = 0;
1644 2063
1645 if (playlist.control_fd < 0) 2064 if (!playlist)
2065 playlist = &current_playlist;
2066
2067 if (check_control(playlist) < 0)
1646 { 2068 {
1647 splash(HZ*2, true, str(LANG_PLAYLIST_CONTROL_ACCESS_ERROR)); 2069 splash(HZ*2, true, str(LANG_PLAYLIST_CONTROL_ACCESS_ERROR));
1648 return -1; 2070 return -1;
1649 } 2071 }
1650 2072
1651 if (index == PLAYLIST_DELETE_CURRENT) 2073 if (index == PLAYLIST_DELETE_CURRENT)
1652 index = playlist.index; 2074 index = playlist->index;
1653 2075
1654 result = remove_track_from_playlist(index, true); 2076 result = remove_track_from_playlist(playlist, index, true);
1655 2077
1656 if (result != -1) 2078 if (result != -1)
1657 mpeg_flush_and_reload_tracks(); 2079 mpeg_flush_and_reload_tracks();
@@ -1663,49 +2085,62 @@ int playlist_delete(int index)
1663 * Move track at index to new_index. Tracks between the two are shifted 2085 * Move track at index to new_index. Tracks between the two are shifted
1664 * appropriately. Returns 0 on success and -1 on failure. 2086 * appropriately. Returns 0 on success and -1 on failure.
1665 */ 2087 */
1666int playlist_move(int index, int new_index) 2088int playlist_move(struct playlist_info* playlist, int index, int new_index)
1667{ 2089{
1668 int result; 2090 int result;
1669 int seek; 2091 int seek;
1670 bool control_file; 2092 bool control_file;
1671 bool queue; 2093 bool queue;
1672 bool current = false; 2094 bool current = false;
1673 int r = rotate_index(new_index); 2095 int r;
1674 char filename[MAX_PATH]; 2096 char filename[MAX_PATH];
1675 2097
2098 if (!playlist)
2099 playlist = &current_playlist;
2100
2101 if (check_control(playlist) < 0)
2102 {
2103 splash(HZ*2, true, str(LANG_PLAYLIST_CONTROL_ACCESS_ERROR));
2104 return -1;
2105 }
2106
1676 if (index == new_index) 2107 if (index == new_index)
1677 return -1; 2108 return -1;
1678 2109
1679 if (index == playlist.index) 2110 if (index == playlist->index)
1680 /* Moving the current track */ 2111 /* Moving the current track */
1681 current = true; 2112 current = true;
1682 2113
1683 control_file = playlist.indices[index] & PLAYLIST_INSERT_TYPE_MASK; 2114 control_file = playlist->indices[index] & PLAYLIST_INSERT_TYPE_MASK;
1684 queue = playlist.indices[index] & PLAYLIST_QUEUE_MASK; 2115 queue = playlist->indices[index] & PLAYLIST_QUEUE_MASK;
1685 seek = playlist.indices[index] & PLAYLIST_SEEK_MASK; 2116 seek = playlist->indices[index] & PLAYLIST_SEEK_MASK;
1686 2117
1687 if (get_filename(seek, control_file, filename, sizeof(filename)) < 0) 2118 if (get_filename(playlist, seek, control_file, filename,
2119 sizeof(filename)) < 0)
1688 return -1; 2120 return -1;
1689 2121
1690 /* Delete track from original position */ 2122 /* Delete track from original position */
1691 result = remove_track_from_playlist(index, true); 2123 result = remove_track_from_playlist(playlist, index, true);
1692 2124
1693 if (result != -1) 2125 if (result != -1)
1694 { 2126 {
1695 /* We want to insert the track at the position that was specified by 2127 /* We want to insert the track at the position that was specified by
1696 new_index. This may be different then new_index because of the 2128 new_index. This may be different then new_index because of the
1697 shifting that occurred after the delete */ 2129 shifting that occurred after the delete */
2130 r = rotate_index(playlist, new_index);
2131
1698 if (r == 0) 2132 if (r == 0)
1699 /* First index */ 2133 /* First index */
1700 new_index = PLAYLIST_PREPEND; 2134 new_index = PLAYLIST_PREPEND;
1701 else if (r == playlist.amount) 2135 else if (r == playlist->amount)
1702 /* Append */ 2136 /* Append */
1703 new_index = PLAYLIST_INSERT_LAST; 2137 new_index = PLAYLIST_INSERT_LAST;
1704 else 2138 else
1705 /* Calculate index of desired position */ 2139 /* Calculate index of desired position */
1706 new_index = (r+playlist.first_index)%playlist.amount; 2140 new_index = (r+playlist->first_index)%playlist->amount;
1707 2141
1708 result = add_track_to_playlist(filename, new_index, queue, -1); 2142 result = add_track_to_playlist(playlist, filename, new_index, queue,
2143 -1);
1709 2144
1710 if (result != -1) 2145 if (result != -1)
1711 { 2146 {
@@ -1715,20 +2150,20 @@ int playlist_move(int index, int new_index)
1715 switch (new_index) 2150 switch (new_index)
1716 { 2151 {
1717 case PLAYLIST_PREPEND: 2152 case PLAYLIST_PREPEND:
1718 playlist.index = playlist.first_index; 2153 playlist->index = playlist->first_index;
1719 break; 2154 break;
1720 case PLAYLIST_INSERT_LAST: 2155 case PLAYLIST_INSERT_LAST:
1721 playlist.index = playlist.first_index - 1; 2156 playlist->index = playlist->first_index - 1;
1722 if (playlist.index < 0) 2157 if (playlist->index < 0)
1723 playlist.index += playlist.amount; 2158 playlist->index += playlist->amount;
1724 break; 2159 break;
1725 default: 2160 default:
1726 playlist.index = new_index; 2161 playlist->index = new_index;
1727 break; 2162 break;
1728 } 2163 }
1729 } 2164 }
1730 2165
1731 fsync(playlist.control_fd); 2166 fsync(playlist->control_fd);
1732 mpeg_flush_and_reload_tracks(); 2167 mpeg_flush_and_reload_tracks();
1733 } 2168 }
1734 } 2169 }
@@ -1736,46 +2171,18 @@ int playlist_move(int index, int new_index)
1736 return result; 2171 return result;
1737} 2172}
1738 2173
1739/* shuffle newly created playlist using random seed. */
1740int playlist_shuffle(int random_seed, int start_index)
1741{
1742 unsigned int seek_pos = 0;
1743 bool start_current = false;
1744
1745 if (start_index >= 0 && global_settings.play_selected)
1746 {
1747 /* store the seek position before the shuffle */
1748 seek_pos = playlist.indices[start_index];
1749 playlist.index = global_settings.resume_first_index =
1750 playlist.first_index = start_index;
1751 start_current = true;
1752 }
1753
1754 splash(0, true, str(LANG_PLAYLIST_SHUFFLE));
1755
1756 randomise_playlist(random_seed, start_current, true);
1757
1758 /* Flush shuffle command to disk */
1759 flush_pending_control();
1760
1761 return playlist.index;
1762}
1763
1764/* shuffle currently playing playlist */ 2174/* shuffle currently playing playlist */
1765int playlist_randomise(unsigned int seed, bool start_current) 2175int playlist_randomise(struct playlist_info* playlist, unsigned int seed,
2176 bool start_current)
1766{ 2177{
1767 int result = randomise_playlist(seed, start_current, true); 2178 int result;
1768 2179
1769 if (result != -1) 2180 if (!playlist)
1770 mpeg_flush_and_reload_tracks(); 2181 playlist = &current_playlist;
1771 2182
1772 return result; 2183 check_control(playlist);
1773}
1774 2184
1775/* sort currently playing playlist */ 2185 result = randomise_playlist(playlist, seed, start_current, true);
1776int playlist_sort(bool start_current)
1777{
1778 int result = sort_playlist(start_current, true);
1779 2186
1780 if (result != -1) 2187 if (result != -1)
1781 mpeg_flush_and_reload_tracks(); 2188 mpeg_flush_and_reload_tracks();
@@ -1783,231 +2190,119 @@ int playlist_sort(bool start_current)
1783 return result; 2190 return result;
1784} 2191}
1785 2192
1786/* start playing current playlist at specified index/offset */ 2193/* sort currently playing playlist */
1787int playlist_start(int start_index, int offset) 2194int playlist_sort(struct playlist_info* playlist, bool start_current)
1788{
1789 playlist.index = start_index;
1790 mpeg_play(offset);
1791
1792 return 0;
1793}
1794
1795/* Returns false if 'steps' is out of bounds, else true */
1796bool playlist_check(int steps)
1797{
1798 int index = get_next_index(steps);
1799 return (index >= 0);
1800}
1801
1802/* get trackname of track that is "steps" away from current playing track.
1803 NULL is used to identify end of playlist */
1804char* playlist_peek(int steps)
1805{ 2195{
1806 int seek; 2196 int result;
1807 int fd;
1808 char *temp_ptr;
1809 int index;
1810 bool control_file;
1811 2197
1812 index = get_next_index(steps); 2198 if (!playlist)
1813 if (index < 0) 2199 playlist = &current_playlist;
1814 return NULL;
1815 2200
1816 control_file = playlist.indices[index] & PLAYLIST_INSERT_TYPE_MASK; 2201 check_control(playlist);
1817 seek = playlist.indices[index] & PLAYLIST_SEEK_MASK;
1818 2202
1819 if (get_filename(seek, control_file, now_playing, MAX_PATH+1) < 0) 2203 result = sort_playlist(playlist, start_current, true);
1820 return NULL;
1821 2204
1822 temp_ptr = now_playing; 2205 if (result != -1)
1823 2206 mpeg_flush_and_reload_tracks();
1824 if (!playlist.in_ram || control_file)
1825 {
1826 /* remove bogus dirs from beginning of path
1827 (workaround for buggy playlist creation tools) */
1828 while (temp_ptr)
1829 {
1830 fd = open(temp_ptr, O_RDONLY);
1831 if (fd >= 0)
1832 {
1833 close(fd);
1834 break;
1835 }
1836
1837 temp_ptr = strchr(temp_ptr+1, '/');
1838 }
1839
1840 if (!temp_ptr)
1841 {
1842 /* Even though this is an invalid file, we still need to pass a
1843 file name to the caller because NULL is used to indicate end
1844 of playlist */
1845 return now_playing;
1846 }
1847 }
1848 2207
1849 return temp_ptr; 2208 return result;
1850} 2209}
1851 2210
1852/* 2211/* returns true if playlist has been modified */
1853 * Update indices as track has changed 2212bool playlist_modified(struct playlist_info* playlist)
1854 */
1855int playlist_next(int steps)
1856{ 2213{
1857 int index; 2214 if (!playlist)
1858 2215 playlist = &current_playlist;
1859 if (steps > 0 && global_settings.repeat_mode != REPEAT_ONE)
1860 {
1861 int i, j;
1862
1863 /* We need to delete all the queued songs */
1864 for (i=0, j=steps; i<j; i++)
1865 {
1866 index = get_next_index(i);
1867
1868 if (playlist.indices[index] & PLAYLIST_QUEUE_MASK)
1869 {
1870 remove_track_from_playlist(index, true);
1871 steps--; /* one less track */
1872 }
1873 }
1874 }
1875 2216
1876 index = get_next_index(steps); 2217 if (playlist->shuffle_modified ||
1877 playlist.index = index; 2218 playlist->deleted ||
2219 playlist->num_inserted_tracks > 0)
2220 return true;
1878 2221
1879 if (playlist.last_insert_pos >= 0 && steps > 0)
1880 {
1881 /* check to see if we've gone beyond the last inserted track */
1882 int cur = rotate_index(index);
1883 int last_pos = rotate_index(playlist.last_insert_pos);
1884
1885 if (cur > last_pos)
1886 {
1887 /* reset last inserted track */
1888 playlist.last_insert_pos = -1;
1889
1890 if (playlist.control_fd >= 0)
1891 {
1892 int result = -1;
1893
1894 mutex_lock(&playlist.control_mutex);
1895
1896 if (lseek(playlist.control_fd, 0, SEEK_END) >= 0)
1897 {
1898 if (fprintf(playlist.control_fd, "R\n") > 0)
1899 {
1900 fsync(playlist.control_fd);
1901 result = 0;
1902 }
1903 }
1904
1905 mutex_unlock(&playlist.control_mutex);
1906
1907 if (result < 0)
1908 {
1909 splash(HZ*2, true,
1910 str(LANG_PLAYLIST_CONTROL_UPDATE_ERROR));
1911 return result;
1912 }
1913 }
1914 }
1915 }
1916
1917 return index;
1918}
1919
1920bool playlist_modified(void)
1921{
1922 if ((mpeg_status() & MPEG_STATUS_PLAY))
1923 {
1924 if (playlist.shuffle_modified ||
1925 playlist.deleted ||
1926 playlist.num_inserted_tracks > 0)
1927 return true;
1928 }
1929 return false; 2222 return false;
1930} 2223}
1931 2224
1932int playlist_get_seed(void) 2225/* returns index of first track in playlist */
2226int playlist_get_first_index(struct playlist_info* playlist)
1933{ 2227{
1934 return playlist.seed; 2228 if (!playlist)
2229 playlist = &current_playlist;
2230
2231 return playlist->first_index;
1935} 2232}
1936 2233
1937/* Get resume info for current playing song. If return value is -1 then 2234/* returns shuffle seed of playlist */
1938 settings shouldn't be saved. */ 2235int playlist_get_seed(struct playlist_info* playlist)
1939int playlist_get_resume_info(int *resume_index)
1940{ 2236{
1941 *resume_index = playlist.index; 2237 if (!playlist)
2238 playlist = &current_playlist;
1942 2239
1943 return 0; 2240 return playlist->seed;
1944} 2241}
1945 2242
1946/* Returns index of current playing track for display purposes. This value 2243/* returns number of tracks in playlist (includes queued/inserted tracks) */
1947 should not be used for resume purposes as it doesn't represent the actual 2244int playlist_amount_ex(struct playlist_info* playlist)
1948 index into the playlist */
1949int playlist_get_display_index(void)
1950{ 2245{
1951 /* first_index should always be index 0 for display purposes */ 2246 if (!playlist)
1952 int index = rotate_index(playlist.index); 2247 playlist = &current_playlist;
1953 2248
1954 return (index+1); 2249 return playlist->amount;
1955} 2250}
1956 2251
1957/* returns index of first track in playlist */ 2252/* returns full path of playlist (minus extension) */
1958int playlist_get_first_index(void) 2253char *playlist_name(struct playlist_info* playlist, char *buf, int buf_size)
1959{ 2254{
1960 return playlist.first_index; 2255 char *sep;
1961}
1962 2256
1963char *playlist_get_name(char *buf, int buf_size) 2257 if (!playlist)
1964{ 2258 playlist = &current_playlist;
1965 snprintf(buf, buf_size, "%s", playlist.filename);
1966 2259
2260 snprintf(buf, buf_size, "%s", playlist->filename+playlist->dirlen);
2261
1967 if (!buf[0]) 2262 if (!buf[0])
1968 return NULL; 2263 return NULL;
1969 2264
1970 return buf; 2265 /* Remove extension */
1971} 2266 sep = strrchr(buf, '.');
2267 if (sep)
2268 *sep = 0;
1972 2269
1973/* returns number of tracks in playlist (includes queued/inserted tracks) */ 2270 return buf;
1974int playlist_amount(void)
1975{
1976 return playlist.amount;
1977} 2271}
1978 2272
1979/* returns playlist name */ 2273/* returns the playlist filename */
1980char *playlist_name(char *buf, int buf_size) 2274char *playlist_get_name(struct playlist_info* playlist, char *buf,
2275 int buf_size)
1981{ 2276{
1982 char *sep; 2277 if (!playlist)
2278 playlist = &current_playlist;
1983 2279
1984 snprintf(buf, buf_size, "%s", playlist.filename+playlist.dirlen); 2280 snprintf(buf, buf_size, "%s", playlist->filename);
1985 2281
1986 if (!buf[0]) 2282 if (!buf[0])
1987 return NULL; 2283 return NULL;
1988 2284
1989 /* Remove extension */
1990 sep = strrchr(buf, '.');
1991 if (sep)
1992 *sep = 0;
1993
1994 return buf; 2285 return buf;
1995} 2286}
1996 2287
1997/* Fills info structure with information about track at specified index. 2288/* Fills info structure with information about track at specified index.
1998 Returns 0 on success and -1 on failure */ 2289 Returns 0 on success and -1 on failure */
1999int playlist_get_track_info(int index, struct playlist_track_info* info) 2290int playlist_get_track_info(struct playlist_info* playlist, int index,
2291 struct playlist_track_info* info)
2000{ 2292{
2001 int seek; 2293 int seek;
2002 bool control_file; 2294 bool control_file;
2003 2295
2004 if (index < 0 || index >= playlist.amount) 2296 if (!playlist)
2297 playlist = &current_playlist;
2298
2299 if (index < 0 || index >= playlist->amount)
2005 return -1; 2300 return -1;
2006 2301
2007 control_file = playlist.indices[index] & PLAYLIST_INSERT_TYPE_MASK; 2302 control_file = playlist->indices[index] & PLAYLIST_INSERT_TYPE_MASK;
2008 seek = playlist.indices[index] & PLAYLIST_SEEK_MASK; 2303 seek = playlist->indices[index] & PLAYLIST_SEEK_MASK;
2009 2304
2010 if (get_filename(seek, control_file, info->filename, 2305 if (get_filename(playlist, seek, control_file, info->filename,
2011 sizeof(info->filename)) < 0) 2306 sizeof(info->filename)) < 0)
2012 return -1; 2307 return -1;
2013 2308
@@ -2015,20 +2310,20 @@ int playlist_get_track_info(int index, struct playlist_track_info* info)
2015 2310
2016 if (control_file) 2311 if (control_file)
2017 { 2312 {
2018 if (playlist.indices[index] & PLAYLIST_QUEUE_MASK) 2313 if (playlist->indices[index] & PLAYLIST_QUEUE_MASK)
2019 info->attr |= PLAYLIST_ATTR_QUEUED; 2314 info->attr |= PLAYLIST_ATTR_QUEUED;
2020 else 2315 else
2021 info->attr |= PLAYLIST_ATTR_INSERTED; 2316 info->attr |= PLAYLIST_ATTR_INSERTED;
2022 } 2317 }
2023 2318
2024 info->index = index; 2319 info->index = index;
2025 info->display_index = rotate_index(index) + 1; 2320 info->display_index = rotate_index(playlist, index) + 1;
2026 2321
2027 return 0; 2322 return 0;
2028} 2323}
2029 2324
2030/* save the current dynamic playlist to specified file */ 2325/* save the current dynamic playlist to specified file */
2031int playlist_save(char *filename) 2326int playlist_save(struct playlist_info* playlist, char *filename)
2032{ 2327{
2033 int fd; 2328 int fd;
2034 int i, index; 2329 int i, index;
@@ -2036,13 +2331,10 @@ int playlist_save(char *filename)
2036 char tmp_buf[MAX_PATH+1]; 2331 char tmp_buf[MAX_PATH+1];
2037 int result = 0; 2332 int result = 0;
2038 2333
2039 if (playlist.control_fd < 0) 2334 if (!playlist)
2040 { 2335 playlist = &current_playlist;
2041 splash(HZ*2, true, str(LANG_PLAYLIST_CONTROL_ACCESS_ERROR));
2042 return -1;
2043 }
2044 2336
2045 if (playlist.amount <= 0) 2337 if (playlist->amount <= 0)
2046 return -1; 2338 return -1;
2047 2339
2048 /* use current working directory as base for pathname */ 2340 /* use current working directory as base for pathname */
@@ -2059,8 +2351,8 @@ int playlist_save(char *filename)
2059 2351
2060 display_playlist_count(count, str(LANG_PLAYLIST_SAVE_COUNT)); 2352 display_playlist_count(count, str(LANG_PLAYLIST_SAVE_COUNT));
2061 2353
2062 index = playlist.first_index; 2354 index = playlist->first_index;
2063 for (i=0; i<playlist.amount; i++) 2355 for (i=0; i<playlist->amount; i++)
2064 { 2356 {
2065 bool control_file; 2357 bool control_file;
2066 bool queue; 2358 bool queue;
@@ -2074,14 +2366,15 @@ int playlist_save(char *filename)
2074#endif 2366#endif
2075 break; 2367 break;
2076 2368
2077 control_file = playlist.indices[index] & PLAYLIST_INSERT_TYPE_MASK; 2369 control_file = playlist->indices[index] & PLAYLIST_INSERT_TYPE_MASK;
2078 queue = playlist.indices[index] & PLAYLIST_QUEUE_MASK; 2370 queue = playlist->indices[index] & PLAYLIST_QUEUE_MASK;
2079 seek = playlist.indices[index] & PLAYLIST_SEEK_MASK; 2371 seek = playlist->indices[index] & PLAYLIST_SEEK_MASK;
2080 2372
2081 /* Don't save queued files */ 2373 /* Don't save queued files */
2082 if (!queue) 2374 if (!queue)
2083 { 2375 {
2084 if (get_filename(seek, control_file, tmp_buf, MAX_PATH+1) < 0) 2376 if (get_filename(playlist, seek, control_file, tmp_buf,
2377 MAX_PATH+1) < 0)
2085 { 2378 {
2086 result = -1; 2379 result = -1;
2087 break; 2380 break;
@@ -2102,7 +2395,7 @@ int playlist_save(char *filename)
2102 yield(); 2395 yield();
2103 } 2396 }
2104 2397
2105 index = (index+1)%playlist.amount; 2398 index = (index+1)%playlist->amount;
2106 } 2399 }
2107 2400
2108 display_playlist_count(count, str(LANG_PLAYLIST_SAVE_COUNT)); 2401 display_playlist_count(count, str(LANG_PLAYLIST_SAVE_COUNT));
diff --git a/apps/playlist.h b/apps/playlist.h
index 020d3332cb..482cecd128 100644
--- a/apps/playlist.h
+++ b/apps/playlist.h
@@ -28,9 +28,12 @@
28 28
29struct playlist_info 29struct playlist_info
30{ 30{
31 bool current; /* current playing playlist */
31 char filename[MAX_PATH]; /* path name of m3u playlist on disk */ 32 char filename[MAX_PATH]; /* path name of m3u playlist on disk */
33 char control_filename[MAX_PATH]; /* full path of control file */
32 int fd; /* descriptor of the open playlist file */ 34 int fd; /* descriptor of the open playlist file */
33 int control_fd; /* descriptor of the open control file */ 35 int control_fd; /* descriptor of the open control file */
36 bool control_created; /* has control file been created? */
34 int dirlen; /* Length of the path to the playlist file */ 37 int dirlen; /* Length of the path to the playlist file */
35 unsigned int *indices; /* array of indices */ 38 unsigned int *indices; /* array of indices */
36 int max_playlist_size; /* Max number of files in playlist. Mirror of 39 int max_playlist_size; /* Max number of files in playlist. Mirror of
@@ -63,33 +66,48 @@ struct playlist_track_info
63 int display_index; /* index of track for display */ 66 int display_index; /* index of track for display */
64}; 67};
65 68
69/* Exported functions only for current playlist. */
66void playlist_init(void); 70void playlist_init(void);
67int playlist_create(char *dir, char *file); 71int playlist_create(char *dir, char *file);
68int playlist_resume(void); 72int playlist_resume(void);
69int playlist_add(char *filename); 73int playlist_add(char *filename);
70int playlist_insert_track(char *filename, int position, bool queue);
71int playlist_insert_directory(char *dirname, int position, bool queue,
72 bool recurse);
73int playlist_insert_playlist(char *filename, int position, bool queue);
74int playlist_delete(int index);
75int playlist_move(int index, int new_index);
76int playlist_shuffle(int random_seed, int start_index); 74int playlist_shuffle(int random_seed, int start_index);
77int playlist_randomise(unsigned int seed, bool start_current);
78int playlist_sort(bool start_current);
79int playlist_start(int start_index, int offset); 75int playlist_start(int start_index, int offset);
80bool playlist_check(int steps); 76bool playlist_check(int steps);
81char *playlist_peek(int steps); 77char *playlist_peek(int steps);
82int playlist_next(int steps); 78int playlist_next(int steps);
83int playlist_get_resume_info(int *resume_index); 79int playlist_get_resume_info(int *resume_index);
84int playlist_get_display_index(void); 80int playlist_get_display_index(void);
85int playlist_get_first_index(void);
86int playlist_amount(void); 81int playlist_amount(void);
87char *playlist_name(char *buf, int buf_size); 82
88int playlist_get_track_info(int index, struct playlist_track_info* info); 83/* Exported functions for all playlists. Pass NULL for playlist_info
89int playlist_save(char *filename); 84 structure to work with current playlist. */
90int playlist_get_seed(void); 85int playlist_create_ex(struct playlist_info* playlist, char* dir, char* file,
91char *playlist_get_name(char *buf, int buf_size); 86 void* index_buffer, int index_buffer_size,
92bool playlist_modified(void); 87 void* temp_buffer, int temp_buffer_size);
88int playlist_set_current(struct playlist_info* playlist);
89void playlist_close(struct playlist_info* playlist);
90int playlist_insert_track(struct playlist_info* playlist, char *filename,
91 int position, bool queue);
92int playlist_insert_directory(struct playlist_info* playlist, char *dirname,
93 int position, bool queue, bool recurse);
94int playlist_insert_playlist(struct playlist_info* playlist, char *filename,
95 int position, bool queue);
96int playlist_delete(struct playlist_info* playlist, int index);
97int playlist_move(struct playlist_info* playlist, int index, int new_index);
98int playlist_randomise(struct playlist_info* playlist, unsigned int seed,
99 bool start_current);
100int playlist_sort(struct playlist_info* playlist, bool start_current);
101bool playlist_modified(struct playlist_info* playlist);
102int playlist_get_first_index(struct playlist_info* playlist);
103int playlist_get_seed(struct playlist_info* playlist);
104int playlist_amount_ex(struct playlist_info* playlist);
105char *playlist_name(struct playlist_info* playlist, char *buf, int buf_size);
106char *playlist_get_name(struct playlist_info* playlist, char *buf,
107 int buf_size);
108int playlist_get_track_info(struct playlist_info* playlist, int index,
109 struct playlist_track_info* info);
110int playlist_save(struct playlist_info* playlist, char *filename);
93 111
94enum { 112enum {
95 PLAYLIST_PREPEND = -1, 113 PLAYLIST_PREPEND = -1,
diff --git a/apps/playlist_menu.c b/apps/playlist_menu.c
index f4e4867a92..b2742781a1 100644
--- a/apps/playlist_menu.c
+++ b/apps/playlist_menu.c
@@ -39,7 +39,7 @@ static bool save_playlist(void)
39 39
40 if (!kbd_input(filename, sizeof(filename))) 40 if (!kbd_input(filename, sizeof(filename)))
41 { 41 {
42 playlist_save(filename); 42 playlist_save(NULL, filename);
43 43
44 /* reload in case playlist was saved to cwd */ 44 /* reload in case playlist was saved to cwd */
45 reload_directory(); 45 reload_directory();
diff --git a/apps/playlist_viewer.c b/apps/playlist_viewer.c
index 00da973732..20ac6c05a2 100644
--- a/apps/playlist_viewer.c
+++ b/apps/playlist_viewer.c
@@ -28,6 +28,9 @@
28#include "icons.h" 28#include "icons.h"
29#include "menu.h" 29#include "menu.h"
30#include "plugin.h" 30#include "plugin.h"
31#include "keyboard.h"
32#include "tree.h"
33#include "onplay.h"
31 34
32#ifdef HAVE_LCD_BITMAP 35#ifdef HAVE_LCD_BITMAP
33#include "widgets.h" 36#include "widgets.h"
@@ -35,6 +38,8 @@
35 38
36#include "lang.h" 39#include "lang.h"
37 40
41#include "playlist_viewer.h"
42
38/* Defines for LCD display purposes. Taken from tree.c */ 43/* Defines for LCD display purposes. Taken from tree.c */
39#ifdef HAVE_LCD_BITMAP 44#ifdef HAVE_LCD_BITMAP
40 #define CURSOR_X (global_settings.scrollbar && \ 45 #define CURSOR_X (global_settings.scrollbar && \
@@ -46,7 +51,9 @@
46 51
47 #define MARGIN_X ((global_settings.scrollbar && \ 52 #define MARGIN_X ((global_settings.scrollbar && \
48 viewer.num_tracks > viewer.num_display_lines ? \ 53 viewer.num_tracks > viewer.num_display_lines ? \
49 SCROLLBAR_WIDTH : 0) + CURSOR_WIDTH + ICON_WIDTH) 54 SCROLLBAR_WIDTH : 0) + CURSOR_WIDTH + \
55 (global_settings.playlist_viewer_icons ? \
56 ICON_WIDTH : 0))
50 #define MARGIN_Y (global_settings.statusbar ? STATUSBAR_HEIGHT : 0) 57 #define MARGIN_Y (global_settings.statusbar ? STATUSBAR_HEIGHT : 0)
51 58
52 #define LINE_X 0 59 #define LINE_X 0
@@ -67,11 +74,15 @@
67/* Maximum number of tracks we can have loaded at one time */ 74/* Maximum number of tracks we can have loaded at one time */
68#define MAX_PLAYLIST_ENTRIES 200 75#define MAX_PLAYLIST_ENTRIES 200
69 76
77/* Default playlist name for saving */
78#define DEFAULT_PLAYLIST_NAME "/viewer.m3u"
79
70/* Index of track on display line _pos */ 80/* Index of track on display line _pos */
71#define INDEX(_pos) (viewer.first_display_index - viewer.first_index + (_pos)) 81#define INDEX(_pos) (viewer.first_display_index - viewer.first_index + (_pos))
72 82
73/* Global playlist viewer settings */ 83/* Global playlist viewer settings */
74struct playlist_viewer_info { 84struct playlist_viewer_info {
85 struct playlist_info* playlist; /* playlist being viewed */
75 char *name_buffer; /* Buffer used to store track names */ 86 char *name_buffer; /* Buffer used to store track names */
76 int buffer_size; /* Size of name buffer */ 87 int buffer_size; /* Size of name buffer */
77 88
@@ -103,15 +114,19 @@ struct playlist_entry {
103static struct playlist_viewer_info viewer; 114static struct playlist_viewer_info viewer;
104static struct playlist_entry tracks[MAX_PLAYLIST_ENTRIES]; 115static struct playlist_entry tracks[MAX_PLAYLIST_ENTRIES];
105 116
117/* Used when viewing playlists on disk */
118static struct playlist_info temp_playlist;
119
106#ifdef HAVE_LCD_BITMAP 120#ifdef HAVE_LCD_BITMAP
107extern unsigned char bitmap_icons_6x8[LastIcon][6]; 121extern unsigned char bitmap_icons_6x8[LastIcon][6];
108#endif 122#endif
109 123
110static bool initialize(void); 124static bool initialize(char* filename, bool reload);
111static void load_playlist_entries(int start_index); 125static void load_playlist_entries(int start_index);
112static void load_playlist_entries_r(int end_index); 126static void load_playlist_entries_r(int end_index);
113static int load_entry(int index, int pos, char* p, int size); 127static int load_entry(int index, int pos, char* p, int size);
114static void format_name(char* dest, char* src); 128static void format_name(char* dest, char* src);
129static void format_line(struct playlist_entry* track, char* str, int len);
115static void display_playlist(void); 130static void display_playlist(void);
116static void update_display_line(int line, bool scroll); 131static void update_display_line(int line, bool scroll);
117static void scroll_display(int lines); 132static void scroll_display(int lines);
@@ -120,18 +135,74 @@ static bool update_playlist(bool force);
120#ifdef BUTTON_ON 135#ifdef BUTTON_ON
121static int onplay_menu(int index); 136static int onplay_menu(int index);
122#endif 137#endif
123 138static bool viewer_menu(void);
124/* Initialize the playlist viewer */ 139static bool show_icons(void);
125static bool initialize(void) 140static bool show_indices(void);
141static bool track_display(void);
142static bool save_playlist(void);
143
144/* Initialize the playlist viewer. */
145static bool initialize(char* filename, bool reload)
126{ 146{
127 if (!(mpeg_status() & MPEG_STATUS_PLAY)) 147 char* buffer;
148 int buffer_size;
149 bool is_playing = mpeg_status() & MPEG_STATUS_PLAY;
150
151 if (!filename && !is_playing)
128 /* Nothing is playing, exit */ 152 /* Nothing is playing, exit */
129 return false; 153 return false;
130 154
131 viewer.name_buffer = plugin_get_buffer(&viewer.buffer_size); 155 buffer = plugin_get_buffer(&buffer_size);
132 if (!viewer.name_buffer) 156 if (!buffer)
133 return false; 157 return false;
134 158
159 if (!filename)
160 viewer.playlist = NULL;
161 else
162 {
163 /* Viewing playlist on disk */
164 char *dir, *file, *temp_ptr;
165 char *index_buffer = NULL;
166 int index_buffer_size = 0;
167
168 viewer.playlist = &temp_playlist;
169
170 /* Separate directory from filename */
171 temp_ptr = strrchr(filename+1,'/');
172 if (temp_ptr)
173 {
174 *temp_ptr = 0;
175 dir = filename;
176 file = temp_ptr + 1;
177 }
178 else
179 {
180 dir = "/";
181 file = filename+1;
182 }
183
184 if (is_playing)
185 {
186 /* Something is playing, use half the plugin buffer for playlist
187 indices */
188 index_buffer_size = buffer_size / 2;
189 index_buffer = buffer;
190 }
191
192 playlist_create_ex(viewer.playlist, dir, file, index_buffer,
193 index_buffer_size, buffer+index_buffer_size,
194 buffer_size-index_buffer_size);
195
196 if (temp_ptr)
197 *temp_ptr = '/';
198
199 buffer += index_buffer_size;
200 buffer_size -= index_buffer_size;
201 }
202
203 viewer.name_buffer = buffer;
204 viewer.buffer_size = buffer_size;
205
135#ifdef HAVE_LCD_BITMAP 206#ifdef HAVE_LCD_BITMAP
136 { 207 {
137 char icon_chars[] = "MQ"; /* characters used as icons */ 208 char icon_chars[] = "MQ"; /* characters used as icons */
@@ -166,12 +237,20 @@ static bool initialize(void)
166 viewer.line_height = 1; 237 viewer.line_height = 1;
167#endif 238#endif
168 239
169 viewer.cursor_pos = 0;
170 viewer.move_track = -1; 240 viewer.move_track = -1;
171 241
172 /* Start displaying at current playing track */ 242 if (!reload)
173 viewer.first_display_index = playlist_get_display_index() - 1; 243 {
174 update_first_index(); 244 viewer.cursor_pos = 0;
245
246 if (!viewer.playlist)
247 /* Start displaying at current playing track */
248 viewer.first_display_index = playlist_get_display_index() - 1;
249 else
250 viewer.first_display_index = 0;
251
252 update_first_index();
253 }
175 254
176 if (!update_playlist(true)) 255 if (!update_playlist(true))
177 return false; 256 return false;
@@ -287,21 +366,19 @@ static int load_entry(int index, int pos, char* p, int size)
287 struct playlist_track_info info; 366 struct playlist_track_info info;
288 int len; 367 int len;
289 int result = 0; 368 int result = 0;
290 char name[MAX_PATH];
291 369
292 /* Playlist viewer orders songs based on display index. We need to 370 /* Playlist viewer orders songs based on display index. We need to
293 convert to real playlist index to access track */ 371 convert to real playlist index to access track */
294 index = (index + playlist_get_first_index()) % viewer.num_tracks; 372 index = (index + playlist_get_first_index(viewer.playlist)) %
295 if (playlist_get_track_info(index, &info) < 0) 373 viewer.num_tracks;
374 if (playlist_get_track_info(viewer.playlist, index, &info) < 0)
296 return -1; 375 return -1;
297 376
298 format_name(name, info.filename); 377 len = strlen(info.filename) + 1;
299
300 len = strlen(name) + 1;
301 378
302 if (len <= size) 379 if (len <= size)
303 { 380 {
304 strcpy(p, name); 381 strcpy(p, info.filename);
305 382
306 tracks[pos].name = p; 383 tracks[pos].name = p;
307 tracks[pos].index = info.index; 384 tracks[pos].index = info.index;
@@ -319,18 +396,46 @@ static int load_entry(int index, int pos, char* p, int size)
319/* Format trackname for display purposes */ 396/* Format trackname for display purposes */
320static void format_name(char* dest, char* src) 397static void format_name(char* dest, char* src)
321{ 398{
322 char* p = strrchr(src, '/'); 399 switch (global_settings.playlist_viewer_track_display)
323 int len; 400 {
401 case 0:
402 default:
403 {
404 /* Only display the mp3 filename */
405 char* p = strrchr(src, '/');
406 int len;
407
408 strcpy(dest, p+1);
409 len = strlen(dest);
410
411 /* Remove the extension */
412 if (!strcasecmp(&dest[len-4], ".mp3") ||
413 !strcasecmp(&dest[len-4], ".mp2") ||
414 !strcasecmp(&dest[len-4], ".mpa"))
415 dest[len-4] = '\0';
416
417 break;
418 }
419 case 1:
420 /* Full path */
421 strcpy(dest, src);
422 break;
423 }
424}
425
426/* Format display line */
427static void format_line(struct playlist_entry* track, char* str, int len)
428{
429 char name[MAX_PATH];
324 430
325 /* Only display the mp3 filename */ 431 format_name(name, track->name);
326 strcpy(dest, p+1); 432
327 len = strlen(dest); 433 if (global_settings.playlist_viewer_indices)
434 /* Display playlist index */
435 snprintf(str, len, "%d. %s", track->display_index, name);
436 else
437 snprintf(str, len, "%s", name);
328 438
329 /* Remove the extension */
330 if (!strcasecmp(&dest[len-4], ".mp3") ||
331 !strcasecmp(&dest[len-4], ".mp2") ||
332 !strcasecmp(&dest[len-4], ".mpa"))
333 dest[len-4] = '\0';
334} 439}
335 440
336/* Display tracks on screen */ 441/* Display tracks on screen */
@@ -349,41 +454,44 @@ static void display_playlist(void)
349 454
350 for (i=0; i<=num_display_tracks; i++) 455 for (i=0; i<=num_display_tracks; i++)
351 { 456 {
352 /* Icons */ 457 if (global_settings.playlist_viewer_icons)
353 if (tracks[INDEX(i)].index == viewer.current_playing_track)
354 { 458 {
355 /* Current playing track */ 459 /* Icons */
460 if (tracks[INDEX(i)].index == viewer.current_playing_track)
461 {
462 /* Current playing track */
356#ifdef HAVE_LCD_BITMAP 463#ifdef HAVE_LCD_BITMAP
357 int offset=0; 464 int offset=0;
358 if ( viewer.line_height > 8 ) 465 if ( viewer.line_height > 8 )
359 offset = (viewer.line_height - 8) / 2; 466 offset = (viewer.line_height - 8) / 2;
360 lcd_bitmap(bitmap_icons_6x8[File], 467 lcd_bitmap(bitmap_icons_6x8[File],
361 CURSOR_X * 6 + CURSOR_WIDTH, 468 CURSOR_X * 6 + CURSOR_WIDTH,
362 MARGIN_Y+(i*viewer.line_height) + offset, 469 MARGIN_Y+(i*viewer.line_height) + offset,
363 6, 8, true); 470 6, 8, true);
364#else 471#else
365 lcd_putc(LINE_X-1, i, File); 472 lcd_putc(LINE_X-1, i, File);
366#endif 473#endif
367 } 474 }
368 else if (tracks[INDEX(i)].index == viewer.move_track) 475 else if (tracks[INDEX(i)].index == viewer.move_track)
369 { 476 {
370 /* Track we are moving */ 477 /* Track we are moving */
371#ifdef HAVE_LCD_BITMAP 478#ifdef HAVE_LCD_BITMAP
372 lcd_putsxy(CURSOR_X * 6 + CURSOR_WIDTH, 479 lcd_putsxy(CURSOR_X * 6 + CURSOR_WIDTH,
373 MARGIN_Y+(i*viewer.line_height), "M"); 480 MARGIN_Y+(i*viewer.line_height), "M");
374#else 481#else
375 lcd_putc(LINE_X-1, i, 'M'); 482 lcd_putc(LINE_X-1, i, 'M');
376#endif 483#endif
377 } 484 }
378 else if (tracks[INDEX(i)].queued) 485 else if (tracks[INDEX(i)].queued)
379 { 486 {
380 /* Queued track */ 487 /* Queued track */
381#ifdef HAVE_LCD_BITMAP 488#ifdef HAVE_LCD_BITMAP
382 lcd_putsxy(CURSOR_X * 6 + CURSOR_WIDTH, 489 lcd_putsxy(CURSOR_X * 6 + CURSOR_WIDTH,
383 MARGIN_Y+(i*viewer.line_height), "Q"); 490 MARGIN_Y+(i*viewer.line_height), "Q");
384#else 491#else
385 lcd_putc(LINE_X-1, i, 'Q'); 492 lcd_putc(LINE_X-1, i, 'Q');
386#endif 493#endif
494 }
387 } 495 }
388 496
389 update_display_line(i, false); 497 update_display_line(i, false);
@@ -494,10 +602,8 @@ static void update_display_line(int line, bool scroll)
494{ 602{
495 char str[MAX_PATH + 16]; 603 char str[MAX_PATH + 16];
496 604
497 snprintf(str, sizeof(str), "%d. %s", 605 format_line(&tracks[INDEX(line)], str, sizeof(str));
498 tracks[INDEX(line)].display_index, 606
499 tracks[INDEX(line)].name);
500
501 if (scroll) 607 if (scroll)
502 { 608 {
503#ifdef HAVE_LCD_BITMAP 609#ifdef HAVE_LCD_BITMAP
@@ -516,7 +622,7 @@ static void update_display_line(int line, bool scroll)
516static void update_first_index(void) 622static void update_first_index(void)
517{ 623{
518 /* viewer.num_tracks may be invalid at this point */ 624 /* viewer.num_tracks may be invalid at this point */
519 int num_tracks = playlist_amount(); 625 int num_tracks = playlist_amount_ex(viewer.playlist);
520 626
521 if ((num_tracks - viewer.first_display_index) < viewer.num_display_lines) 627 if ((num_tracks - viewer.first_display_index) < viewer.num_display_lines)
522 { 628 {
@@ -535,14 +641,17 @@ static void update_first_index(void)
535/* Update playlist in case something has changed or forced */ 641/* Update playlist in case something has changed or forced */
536static bool update_playlist(bool force) 642static bool update_playlist(bool force)
537{ 643{
538 playlist_get_resume_info(&viewer.current_playing_track); 644 if (!viewer.playlist)
645 playlist_get_resume_info(&viewer.current_playing_track);
646 else
647 viewer.current_playing_track = -1;
539 648
540 if (force || playlist_amount() != viewer.num_tracks) 649 if (force || playlist_amount_ex(viewer.playlist) != viewer.num_tracks)
541 { 650 {
542 int index; 651 int index;
543 652
544 /* Reload tracks */ 653 /* Reload tracks */
545 viewer.num_tracks = playlist_amount(); 654 viewer.num_tracks = playlist_amount_ex(viewer.playlist);
546 if (viewer.num_tracks < 0) 655 if (viewer.num_tracks < 0)
547 return false; 656 return false;
548 657
@@ -571,16 +680,19 @@ static bool update_playlist(bool force)
571 changed. */ 680 changed. */
572static int onplay_menu(int index) 681static int onplay_menu(int index)
573{ 682{
574 struct menu_items menu[2]; /* increase this if you add entries! */ 683 struct menu_items menu[3]; /* increase this if you add entries! */
575 int m, i=0, result, ret = 0; 684 int m, i=0, result, ret = 0;
576 bool current = (tracks[index].index == viewer.current_playing_track); 685 bool current = (tracks[index].index == viewer.current_playing_track);
577 686
578 menu[i].desc = str(LANG_DELETE); 687 menu[i].desc = str(LANG_REMOVE);
579 i++; 688 i++;
580 689
581 menu[i].desc = str(LANG_MOVE); 690 menu[i].desc = str(LANG_MOVE);
582 i++; 691 i++;
583 692
693 menu[i].desc = str(LANG_FILE_OPTIONS);
694 i++;
695
584 m = menu_init(menu, i); 696 m = menu_init(menu, i);
585 result = menu_show(m); 697 result = menu_show(m);
586 if (result == MENU_ATTACHED_USB) 698 if (result == MENU_ATTACHED_USB)
@@ -597,7 +709,7 @@ static int onplay_menu(int index)
597 if (current) 709 if (current)
598 mpeg_stop(); 710 mpeg_stop();
599 711
600 playlist_delete(tracks[index].index); 712 playlist_delete(viewer.playlist, tracks[index].index);
601 713
602 if (current) 714 if (current)
603 { 715 {
@@ -618,6 +730,17 @@ static int onplay_menu(int index)
618 viewer.move_track = tracks[index].index; 730 viewer.move_track = tracks[index].index;
619 ret = 0; 731 ret = 0;
620 break; 732 break;
733 case 2:
734 {
735 onplay(tracks[index].name, TREE_ATTR_MPA);
736
737 if (!viewer.playlist)
738 ret = 1;
739 else
740 ret = 0;
741
742 break;
743 }
621 } 744 }
622 } 745 }
623 746
@@ -627,17 +750,91 @@ static int onplay_menu(int index)
627} 750}
628#endif 751#endif
629 752
630/* Main viewer function */ 753/* Menu of viewer options. Invoked via F1(r) or Menu(p). */
754static bool viewer_menu(void)
755{
756 int m;
757 bool result;
758
759 struct menu_items items[] = {
760 { str(LANG_SHOW_ICONS), show_icons },
761 { str(LANG_SHOW_INDICES), show_indices },
762 { str(LANG_TRACK_DISPLAY), track_display },
763 { str(LANG_SAVE_DYNAMIC_PLAYLIST), save_playlist },
764 };
765
766 m=menu_init( items, sizeof(items) / sizeof(*items) );
767 result = menu_run(m);
768 menu_exit(m);
769
770 settings_save();
771
772 return result;
773}
774
775/* Show icons in viewer? */
776static bool show_icons(void)
777{
778 return set_bool(str(LANG_SHOW_ICONS),
779 &global_settings.playlist_viewer_icons);
780}
781
782/* Show indices of tracks? */
783static bool show_indices(void)
784{
785 return set_bool(str(LANG_SHOW_INDICES),
786 &global_settings.playlist_viewer_indices);
787}
788
789/* How to display a track */
790static bool track_display(void)
791{
792 char* names[] = {
793 str(LANG_DISPLAY_TRACK_NAME_ONLY),
794 str(LANG_DISPLAY_FULL_PATH)
795 };
796
797 return set_option(str(LANG_TRACK_DISPLAY),
798 &global_settings.playlist_viewer_track_display, INT, names, 2, NULL);
799}
800
801/* Save playlist to disk */
802static bool save_playlist(void)
803{
804 char filename[MAX_PATH+1];
805
806 strncpy(filename, DEFAULT_PLAYLIST_NAME, sizeof(filename));
807
808 if (!kbd_input(filename, sizeof(filename)))
809 {
810 playlist_save(viewer.playlist, filename);
811
812 /* reload in case playlist was saved to cwd */
813 reload_directory();
814 }
815
816 return false;
817}
818
819/* View current playlist */
631bool playlist_viewer(void) 820bool playlist_viewer(void)
632{ 821{
822 return playlist_viewer_ex(NULL);
823}
824
825/* Main viewer function. Filename identifies playlist to be viewed. If NULL,
826 view current playlist. */
827bool playlist_viewer_ex(char* filename)
828{
829 bool ret = false; /* return value */
633 bool exit=false; /* exit viewer */ 830 bool exit=false; /* exit viewer */
634 bool update=true; /* update display */ 831 bool update=true; /* update display */
635 bool cursor_on=true; /* used for flashing cursor */ 832 bool cursor_on=true; /* used for flashing cursor */
636 int old_cursor_pos; /* last cursor position */ 833 int old_cursor_pos; /* last cursor position */
637 int button; 834 int button;
638 835
639 if (!initialize()) 836 if (!initialize(filename, false))
640 return false; 837 goto exit;
641 838
642 old_cursor_pos = viewer.cursor_pos; 839 old_cursor_pos = viewer.cursor_pos;
643 840
@@ -648,7 +845,7 @@ bool playlist_viewer(void)
648 /* Timeout so we can determine if play status has changed */ 845 /* Timeout so we can determine if play status has changed */
649 button = button_get_w_tmo(HZ/2); 846 button = button_get_w_tmo(HZ/2);
650 847
651 if (!(mpeg_status() & MPEG_STATUS_PLAY)) 848 if (!viewer.playlist && !(mpeg_status() & MPEG_STATUS_PLAY))
652 { 849 {
653 /* Play has stopped */ 850 /* Play has stopped */
654#ifdef HAVE_LCD_CHARCELLS 851#ifdef HAVE_LCD_CHARCELLS
@@ -657,7 +854,7 @@ bool playlist_viewer(void)
657 splash(HZ, true, str(LANG_END_PLAYLIST_RECORDER)); 854 splash(HZ, true, str(LANG_END_PLAYLIST_RECORDER));
658#endif 855#endif
659 status_set_playmode(STATUS_STOP); 856 status_set_playmode(STATUS_STOP);
660 return false;; 857 goto exit;
661 } 858 }
662 859
663 if (viewer.move_track != -1 || !cursor_on) 860 if (viewer.move_track != -1 || !cursor_on)
@@ -685,10 +882,13 @@ bool playlist_viewer(void)
685#endif 882#endif
686 } 883 }
687 884
688 playlist_get_resume_info(&track); 885 if (!viewer.playlist)
886 playlist_get_resume_info(&track);
887 else
888 track = -1;
689 889
690 if (track != viewer.current_playing_track || 890 if (track != viewer.current_playing_track ||
691 playlist_amount() != viewer.num_tracks) 891 playlist_amount_ex(viewer.playlist) != viewer.num_tracks)
692 { 892 {
693 /* Playlist has changed (new track started?) */ 893 /* Playlist has changed (new track started?) */
694 update_first_index(); 894 update_first_index();
@@ -769,7 +969,7 @@ bool playlist_viewer(void)
769 /* Move track */ 969 /* Move track */
770 int ret; 970 int ret;
771 971
772 ret = playlist_move(viewer.move_track, 972 ret = playlist_move(viewer.playlist, viewer.move_track,
773 tracks[INDEX(viewer.cursor_pos)].index); 973 tracks[INDEX(viewer.cursor_pos)].index);
774 if (ret < 0) 974 if (ret < 0)
775 splash(HZ, true, str(LANG_MOVE_FAILED)); 975 splash(HZ, true, str(LANG_MOVE_FAILED));
@@ -777,7 +977,7 @@ bool playlist_viewer(void)
777 update_playlist(true); 977 update_playlist(true);
778 viewer.move_track = -1; 978 viewer.move_track = -1;
779 } 979 }
780 else 980 else if (!viewer.playlist)
781 { 981 {
782 /* Stop current track and play new track */ 982 /* Stop current track and play new track */
783 mpeg_stop(); 983 mpeg_stop();
@@ -785,6 +985,22 @@ bool playlist_viewer(void)
785 status_set_playmode(STATUS_PLAY); 985 status_set_playmode(STATUS_PLAY);
786 update_playlist(false); 986 update_playlist(false);
787 } 987 }
988 else
989 {
990 /* Play track from playlist on disk */
991 mpeg_stop();
992
993 /* New playlist */
994 if (playlist_set_current(viewer.playlist) < 0)
995 goto exit;
996
997 playlist_start(tracks[INDEX(viewer.cursor_pos)].index, 0);
998 status_set_playmode(STATUS_PLAY);
999
1000 /* Our playlist is now the current list */
1001 if (!initialize(NULL, true))
1002 goto exit;
1003 }
788 1004
789 display_playlist(); 1005 display_playlist();
790 update = true; 1006 update = true;
@@ -799,13 +1015,15 @@ bool playlist_viewer(void)
799 ret = onplay_menu(INDEX(viewer.cursor_pos)); 1015 ret = onplay_menu(INDEX(viewer.cursor_pos));
800 1016
801 if (ret < 0) 1017 if (ret < 0)
802 /* USB attached */ 1018 {
803 return true; 1019 ret = true;
1020 goto exit;
1021 }
804 else if (ret > 0) 1022 else if (ret > 0)
805 { 1023 {
806 /* Playlist changed */ 1024 /* Playlist changed */
807 update_first_index(); 1025 update_first_index();
808 update_playlist(true); 1026 update_playlist(false);
809 if (viewer.num_tracks <= 0) 1027 if (viewer.num_tracks <= 0)
810 exit = true; 1028 exit = true;
811 } 1029 }
@@ -816,9 +1034,30 @@ bool playlist_viewer(void)
816 break; 1034 break;
817 } 1035 }
818#endif /* BUTTON_ON */ 1036#endif /* BUTTON_ON */
1037#ifdef HAVE_RECORDER_KEYPAD
1038 case BUTTON_F1:
1039#else
1040 case BUTTON_MENU:
1041#endif
1042 if (viewer_menu())
1043 {
1044 ret = true;
1045 goto exit;
1046 }
1047
1048 display_playlist();
1049 update = true;
1050 break;
1051
819 case SYS_USB_CONNECTED: 1052 case SYS_USB_CONNECTED:
820 usb_screen(); 1053 usb_screen();
821 return true; 1054 ret = true;
1055 goto exit;
1056 break;
1057
1058 case BUTTON_NONE:
1059 status_draw(false);
1060 break;
822 } 1061 }
823 1062
824 if (update && !exit) 1063 if (update && !exit)
@@ -852,5 +1091,8 @@ bool playlist_viewer(void)
852 } 1091 }
853 } 1092 }
854 1093
855 return false; 1094exit:
1095 if (viewer.playlist)
1096 playlist_close(viewer.playlist);
1097 return ret;
856} 1098}
diff --git a/apps/playlist_viewer.h b/apps/playlist_viewer.h
index ecc5197566..69f9d03559 100644
--- a/apps/playlist_viewer.h
+++ b/apps/playlist_viewer.h
@@ -22,5 +22,6 @@
22#define _PLAYLIST_VIEWER_H_ 22#define _PLAYLIST_VIEWER_H_
23 23
24bool playlist_viewer(void); 24bool playlist_viewer(void);
25bool playlist_viewer_ex(char* filename);
25 26
26#endif 27#endif
diff --git a/apps/screens.c b/apps/screens.c
index c2ffb44f3c..6987fc92e3 100644
--- a/apps/screens.c
+++ b/apps/screens.c
@@ -512,9 +512,9 @@ bool f2_screen(void)
512 if(mpeg_status() & MPEG_STATUS_PLAY) 512 if(mpeg_status() & MPEG_STATUS_PLAY)
513 { 513 {
514 if (global_settings.playlist_shuffle) 514 if (global_settings.playlist_shuffle)
515 playlist_randomise(current_tick, true); 515 playlist_randomise(NULL, current_tick, true);
516 else 516 else
517 playlist_sort(true); 517 playlist_sort(NULL, true);
518 } 518 }
519 used = true; 519 used = true;
520 break; 520 break;
diff --git a/apps/settings.c b/apps/settings.c
index bfc943bef0..7b9c1feb60 100644
--- a/apps/settings.c
+++ b/apps/settings.c
@@ -146,6 +146,9 @@ Rest of config block, only saved to disk:
146 caption backlight (bit 1) 146 caption backlight (bit 1)
147 car adapter mode (bit 2) 147 car adapter mode (bit 2)
148 line_in (Player only) (bit 3) 148 line_in (Player only) (bit 3)
149 playlist viewer icons (bit 4)
150 playlist viewer indices (bit 5)
151 playlist viewer track display (bit 6)
1490xAF <most-recent-bookmarks, auto-bookmark, autoload> 1520xAF <most-recent-bookmarks, auto-bookmark, autoload>
1500xB0 peak meter clip hold timeout (bit 0-4), peak meter performance (bit 7) 1530xB0 peak meter clip hold timeout (bit 0-4), peak meter performance (bit 7)
1510xB1 peak meter release step size, peak_meter_dbfs (bit 7) 1540xB1 peak meter release step size, peak_meter_dbfs (bit 7)
@@ -421,7 +424,10 @@ int settings_save( void )
421 ((global_settings.fade_on_stop & 1) | 424 ((global_settings.fade_on_stop & 1) |
422 ((global_settings.caption_backlight & 1) << 1) | 425 ((global_settings.caption_backlight & 1) << 1) |
423 ((global_settings.car_adapter_mode & 1) << 2) | 426 ((global_settings.car_adapter_mode & 1) << 2) |
424 ((global_settings.line_in & 1) << 3)); 427 ((global_settings.line_in & 1) << 3) |
428 ((global_settings.playlist_viewer_icons & 1) << 4) |
429 ((global_settings.playlist_viewer_indices & 1) << 5) |
430 ((global_settings.playlist_viewer_track_display & 1) << 6));
425 config_block[0xaf] = ((global_settings.usemrb << 5) | 431 config_block[0xaf] = ((global_settings.usemrb << 5) |
426 (global_settings.autocreatebookmark << 2) | 432 (global_settings.autocreatebookmark << 2) |
427 (global_settings.autoloadbookmark)); 433 (global_settings.autoloadbookmark));
@@ -726,6 +732,12 @@ void settings_load(void)
726 global_settings.caption_backlight = (config_block[0xae] >> 1) & 1; 732 global_settings.caption_backlight = (config_block[0xae] >> 1) & 1;
727 global_settings.car_adapter_mode = (config_block[0xae] >> 2) & 1; 733 global_settings.car_adapter_mode = (config_block[0xae] >> 2) & 1;
728 global_settings.line_in = (config_block[0xae] >> 3) & 1; 734 global_settings.line_in = (config_block[0xae] >> 3) & 1;
735 global_settings.playlist_viewer_icons =
736 (config_block[0xae] >> 4) & 1;
737 global_settings.playlist_viewer_indices =
738 (config_block[0xae] >> 5) & 1;
739 global_settings.playlist_viewer_track_display =
740 (config_block[0xae] >> 6) & 1;
729 } 741 }
730 742
731 if(config_block[0xb0] != 0xff) { 743 if(config_block[0xb0] != 0xff) {
@@ -1161,6 +1173,16 @@ bool settings_load_config(char* file)
1161 static char* options[] = {"off", "on", "unique only"}; 1173 static char* options[] = {"off", "on", "unique only"};
1162 set_cfg_option(&global_settings.usemrb, value, options, 3); 1174 set_cfg_option(&global_settings.usemrb, value, options, 3);
1163 } 1175 }
1176 else if (!strcasecmp(name, "playlist viewer icons"))
1177 set_cfg_bool(&global_settings.playlist_viewer_icons, value);
1178 else if (!strcasecmp(name, "playlist viewer indices"))
1179 set_cfg_bool(&global_settings.playlist_viewer_indices, value);
1180 else if (!strcasecmp(name, "playlist viewer track display"))
1181 {
1182 static char* options[] = {"track name", "full path"};
1183 set_cfg_option(&global_settings.playlist_viewer_track_display,
1184 value, options, 2);
1185 }
1164 } 1186 }
1165 1187
1166 close(fd); 1188 close(fd);
@@ -1494,6 +1516,19 @@ bool settings_save_config(void)
1494 triopt[global_settings.recursive_dir_insert]); 1516 triopt[global_settings.recursive_dir_insert]);
1495 } 1517 }
1496 1518
1519 fprintf(fd, "#\r\n# Playlist viewer\r\n#\r\n");
1520 {
1521 fprintf(fd, "playlist viewer icons: %s\r\n",
1522 boolopt[global_settings.playlist_viewer_icons]);
1523 fprintf(fd, "playlist viewer indices: %s\r\n",
1524 boolopt[global_settings.playlist_viewer_indices]);
1525 {
1526 static char* options[] = {"track name", "full path"};
1527 fprintf(fd, "playlist viewer track display: %s\r\n",
1528 options[global_settings.playlist_viewer_track_display]);
1529 }
1530 }
1531
1497 close(fd); 1532 close(fd);
1498 1533
1499 lcd_clear_display(); 1534 lcd_clear_display();
@@ -1596,6 +1631,9 @@ void settings_reset(void) {
1596 global_settings.show_icons = true; 1631 global_settings.show_icons = true;
1597 global_settings.recursive_dir_insert = RECURSE_OFF; 1632 global_settings.recursive_dir_insert = RECURSE_OFF;
1598 global_settings.line_in = false; 1633 global_settings.line_in = false;
1634 global_settings.playlist_viewer_icons = true;
1635 global_settings.playlist_viewer_indices = true;
1636 global_settings.playlist_viewer_track_display = 0;
1599} 1637}
1600 1638
1601bool set_bool(char* string, bool* variable ) 1639bool set_bool(char* string, bool* variable )
diff --git a/apps/settings.h b/apps/settings.h
index 436b5204d5..3634087cab 100644
--- a/apps/settings.h
+++ b/apps/settings.h
@@ -194,6 +194,11 @@ struct user_settings
194 int recursive_dir_insert; /* should directories be inserted recursively */ 194 int recursive_dir_insert; /* should directories be inserted recursively */
195 195
196 bool line_in; /* false=off, true=active */ 196 bool line_in; /* false=off, true=active */
197
198 /* playlist viewer settings */
199 bool playlist_viewer_icons; /* display icons on viewer */
200 bool playlist_viewer_indices; /* display playlist indices on viewer */
201 int playlist_viewer_track_display; /* how to display tracks in viewer */
197}; 202};
198 203
199enum optiontype { INT, BOOL }; 204enum optiontype { INT, BOOL };
diff --git a/apps/settings_menu.c b/apps/settings_menu.c
index cf7e55ae5b..24a67adc47 100644
--- a/apps/settings_menu.c
+++ b/apps/settings_menu.c
@@ -827,11 +827,11 @@ static bool playback_settings_menu(void)
827 { 827 {
828 if (global_settings.playlist_shuffle) 828 if (global_settings.playlist_shuffle)
829 { 829 {
830 playlist_randomise(current_tick, true); 830 playlist_randomise(NULL, current_tick, true);
831 } 831 }
832 else 832 else
833 { 833 {
834 playlist_sort(true); 834 playlist_sort(NULL, true);
835 } 835 }
836 } 836 }
837 return result; 837 return result;
diff --git a/apps/wps-display.c b/apps/wps-display.c
index 6553e9b542..a24cae9717 100644
--- a/apps/wps-display.c
+++ b/apps/wps-display.c
@@ -499,7 +499,7 @@ static char* get_tag(struct mp3entry* id3,
499 499
500 case 'n': /* Playlist Name (without path) */ 500 case 'n': /* Playlist Name (without path) */
501 *flags |= WPS_REFRESH_STATIC; 501 *flags |= WPS_REFRESH_STATIC;
502 return playlist_name(buf, buf_size); 502 return playlist_name(NULL, buf, buf_size);
503 503
504 case 'e': /* Playlist Total Entries */ 504 case 'e': /* Playlist Total Entries */
505 *flags |= WPS_REFRESH_STATIC; 505 *flags |= WPS_REFRESH_STATIC;