Added ID3 database support. Still very early.
[kugel-rb.git] / apps / playlist.c
blob1a68a052baf98007e700d372c8f5b8ac403b90e4
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
10 * Copyright (C) 2002 by wavey@wavey.org
12 * All files in this archive are subject to the GNU General Public License.
13 * See the file COPYING in the source tree root for full license agreement.
15 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
16 * KIND, either express or implied.
18 ****************************************************************************/
21 Dynamic playlist design (based on design originally proposed by ricII)
23 There are two files associated with a dynamic playlist:
24 1. Playlist file : This file contains the initial songs in the playlist.
25 The file is created by the user and stored on the hard
26 drive. NOTE: If we are playing the contents of a
27 directory, there will be no playlist file.
28 2. Control file : This file is automatically created when a playlist is
29 started and contains all the commands done to it.
31 The first non-comment line in a control file must begin with
32 "P:VERSION:DIR:FILE" where VERSION is the playlist control file version,
33 DIR is the directory where the playlist is located and FILE is the
34 playlist filename. For dirplay, FILE will be empty. An empty playlist
35 will have both entries as null.
37 Control file commands:
38 a. Add track (A:<position>:<last position>:<path to track>)
39 - Insert a track at the specified position in the current
40 playlist. Last position is used to specify where last insertion
41 occurred.
42 b. Queue track (Q:<position>:<last position>:<path to track>)
43 - Queue a track at the specified position in the current
44 playlist. Queued tracks differ from added tracks in that they
45 are deleted from the playlist as soon as they are played and
46 they are not saved to disk as part of the playlist.
47 c. Delete track (D:<position>)
48 - Delete track from specified position in the current playlist.
49 d. Shuffle playlist (S:<seed>:<index>)
50 - Shuffle entire playlist with specified seed. The index
51 identifies the first index in the newly shuffled playlist
52 (needed for repeat mode).
53 e. Unshuffle playlist (U:<index>)
54 - Unshuffle entire playlist. The index identifies the first index
55 in the newly unshuffled playlist.
56 f. Reset last insert position (R)
57 - Needed so that insertions work properly after resume
59 Resume:
60 The only resume info that needs to be saved is the current index in the
61 playlist and the position in the track. When resuming, all the commands
62 in the control file will be reapplied so that the playlist indices are
63 exactly the same as before shutdown. To avoid unnecessary disk
64 accesses, the shuffle mode settings are also saved in settings and only
65 flushed to disk when required.
68 #include <stdio.h>
69 #include <stdlib.h>
70 #include <string.h>
71 #include "playlist.h"
72 #include "file.h"
73 #include "dir.h"
74 #include "sprintf.h"
75 #include "debug.h"
76 #include "mpeg.h"
77 #include "lcd.h"
78 #include "kernel.h"
79 #include "settings.h"
80 #include "status.h"
81 #include "applimits.h"
82 #include "screens.h"
83 #include "buffer.h"
84 #include "atoi.h"
85 #include "misc.h"
86 #include "button.h"
87 #include "filetree.h"
88 #ifdef HAVE_LCD_BITMAP
89 #include "icons.h"
90 #include "widgets.h"
91 #endif
93 #include "lang.h"
94 #include "talk.h"
96 #define PLAYLIST_CONTROL_FILE ROCKBOX_DIR "/.playlist_control"
97 #define PLAYLIST_CONTROL_FILE_VERSION 2
100 Each playlist index has a flag associated with it which identifies what
101 type of track it is. These flags are stored in the 3 high order bits of
102 the index.
104 NOTE: This limits the playlist file size to a max of 512M.
106 Bits 31-30:
107 00 = Playlist track
108 01 = Track was prepended into playlist
109 10 = Track was inserted into playlist
110 11 = Track was appended into playlist
111 Bit 29:
112 0 = Added track
113 1 = Queued track
115 #define PLAYLIST_SEEK_MASK 0x1FFFFFFF
116 #define PLAYLIST_INSERT_TYPE_MASK 0xC0000000
117 #define PLAYLIST_QUEUE_MASK 0x20000000
119 #define PLAYLIST_INSERT_TYPE_PREPEND 0x40000000
120 #define PLAYLIST_INSERT_TYPE_INSERT 0x80000000
121 #define PLAYLIST_INSERT_TYPE_APPEND 0xC0000000
123 #define PLAYLIST_QUEUED 0x20000000
125 #define PLAYLIST_DISPLAY_COUNT 10
127 static struct playlist_info current_playlist;
128 static char now_playing[MAX_PATH+1];
130 static void empty_playlist(struct playlist_info* playlist, bool resume);
131 static void new_playlist(struct playlist_info* playlist, const char *dir,
132 const char *file);
133 static void create_control(struct playlist_info* playlist);
134 static int check_control(struct playlist_info* playlist);
135 static void update_playlist_filename(struct playlist_info* playlist,
136 const char *dir, const char *file);
137 static int add_indices_to_playlist(struct playlist_info* playlist,
138 char* buffer, int buflen);
139 static int add_track_to_playlist(struct playlist_info* playlist,
140 const char *filename, int position,
141 bool queue, int seek_pos);
142 static int add_directory_to_playlist(struct playlist_info* playlist,
143 const char *dirname, int *position, bool queue,
144 int *count, bool recurse);
145 static int remove_track_from_playlist(struct playlist_info* playlist,
146 int position, bool write);
147 static int randomise_playlist(struct playlist_info* playlist,
148 unsigned int seed, bool start_current,
149 bool write);
150 static int sort_playlist(struct playlist_info* playlist, bool start_current,
151 bool write);
152 static int get_next_index(const struct playlist_info* playlist, int steps);
153 static void find_and_set_playlist_index(struct playlist_info* playlist,
154 unsigned int seek);
155 static int compare(const void* p1, const void* p2);
156 static int get_filename(struct playlist_info* playlist, int seek,
157 bool control_file, char *buf, int buf_length);
158 static int format_track_path(char *dest, char *src, int buf_length, int max,
159 char *dir);
160 static void display_playlist_count(int count, const char *fmt);
161 static void display_buffer_full(void);
162 static int flush_pending_control(struct playlist_info* playlist);
163 static int rotate_index(const struct playlist_info* playlist, int index);
166 * remove any files and indices associated with the playlist
168 static void empty_playlist(struct playlist_info* playlist, bool resume)
170 playlist->filename[0] = '\0';
172 if(playlist->fd >= 0)
173 /* If there is an already open playlist, close it. */
174 close(playlist->fd);
175 playlist->fd = -1;
177 if(playlist->control_fd >= 0)
178 close(playlist->control_fd);
179 playlist->control_fd = -1;
180 playlist->control_created = false;
182 playlist->in_ram = false;
184 if (playlist->buffer)
185 playlist->buffer[0] = 0;
187 playlist->buffer_end_pos = 0;
189 playlist->index = 0;
190 playlist->first_index = 0;
191 playlist->amount = 0;
192 playlist->last_insert_pos = -1;
193 playlist->seed = 0;
194 playlist->shuffle_modified = false;
195 playlist->deleted = false;
196 playlist->num_inserted_tracks = 0;
197 playlist->shuffle_flush = false;
199 if (!resume && playlist->current)
201 /* start with fresh playlist control file when starting new
202 playlist */
203 create_control(playlist);
205 /* Reset resume settings */
206 global_settings.resume_first_index = 0;
207 global_settings.resume_seed = -1;
212 * Initialize a new playlist for viewing/editing/playing. dir is the
213 * directory where the playlist is located and file is the filename.
215 static void new_playlist(struct playlist_info* playlist, const char *dir,
216 const char *file)
218 empty_playlist(playlist, false);
220 if (!file)
222 file = "";
224 if (dir && playlist->current) /* !current cannot be in_ram */
225 playlist->in_ram = true;
226 else
227 dir = ""; /* empty playlist */
230 update_playlist_filename(playlist, dir, file);
232 if (playlist->control_fd >= 0)
234 if (fprintf(playlist->control_fd, "P:%d:%s:%s\n",
235 PLAYLIST_CONTROL_FILE_VERSION, dir, file) > 0)
236 fsync(playlist->control_fd);
237 else
238 splash(HZ*2, true, str(LANG_PLAYLIST_CONTROL_UPDATE_ERROR));
243 * create control file for playlist
245 static void create_control(struct playlist_info* playlist)
247 playlist->control_fd = creat(playlist->control_filename, 0000200);
248 if (playlist->control_fd < 0)
250 if (check_rockboxdir())
252 splash(HZ*2, true, "%s (%d)", str(LANG_PLAYLIST_CONTROL_ACCESS_ERROR),
253 playlist->control_fd);
255 playlist->control_created = false;
257 else
259 playlist->control_created = true;
264 * validate the control file. This may include creating/initializing it if
265 * necessary;
267 static int check_control(struct playlist_info* playlist)
269 if (!playlist->control_created)
271 create_control(playlist);
273 if (playlist->control_fd >= 0)
275 char* dir = playlist->filename;
276 char* file = playlist->filename+playlist->dirlen;
277 char c = playlist->filename[playlist->dirlen-1];
279 playlist->filename[playlist->dirlen-1] = '\0';
281 if (fprintf(playlist->control_fd, "P:%d:%s:%s\n",
282 PLAYLIST_CONTROL_FILE_VERSION, dir, file) > 0)
283 fsync(playlist->control_fd);
284 else
285 splash(HZ*2, true, str(LANG_PLAYLIST_CONTROL_UPDATE_ERROR));
287 playlist->filename[playlist->dirlen-1] = c;
291 if (playlist->control_fd < 0)
292 return -1;
294 return 0;
298 * store directory and name of playlist file
300 static void update_playlist_filename(struct playlist_info* playlist,
301 const char *dir, const char *file)
303 char *sep="";
304 int dirlen = strlen(dir);
306 /* If the dir does not end in trailing slash, we use a separator.
307 Otherwise we don't. */
308 if('/' != dir[dirlen-1])
310 sep="/";
311 dirlen++;
314 playlist->dirlen = dirlen;
316 snprintf(playlist->filename, sizeof(playlist->filename),
317 "%s%s%s",
318 dir, sep, file);
322 * calculate track offsets within a playlist file
324 static int add_indices_to_playlist(struct playlist_info* playlist,
325 char* buffer, int buflen)
327 unsigned int nread;
328 unsigned int i = 0;
329 unsigned int count = 0;
330 bool store_index;
331 unsigned char *p;
333 if(-1 == playlist->fd)
334 playlist->fd = open(playlist->filename, O_RDONLY);
335 if(playlist->fd < 0)
336 return -1; /* failure */
338 #ifdef HAVE_LCD_BITMAP
339 if(global_settings.statusbar)
340 lcd_setmargins(0, STATUSBAR_HEIGHT);
341 else
342 lcd_setmargins(0, 0);
343 #endif
345 splash(0, true, str(LANG_PLAYLIST_LOAD));
347 if (!buffer)
349 /* use mp3 buffer for maximum load speed */
350 mpeg_stop();
351 talk_buffer_steal(); /* we use the mp3 buffer, need to tell */
353 buffer = mp3buf;
354 buflen = (mp3end - mp3buf);
357 store_index = true;
359 while(1)
361 nread = read(playlist->fd, buffer, buflen);
362 /* Terminate on EOF */
363 if(nread <= 0)
364 break;
366 p = buffer;
368 for(count=0; count < nread; count++,p++) {
370 /* Are we on a new line? */
371 if((*p == '\n') || (*p == '\r'))
373 store_index = true;
375 else if(store_index)
377 store_index = false;
379 if(*p != '#')
381 /* Store a new entry */
382 playlist->indices[ playlist->amount ] = i+count;
383 playlist->amount++;
384 if ( playlist->amount >= playlist->max_playlist_size ) {
385 display_buffer_full();
386 return -1;
392 i+= count;
395 return 0;
399 * Add track to playlist at specified position. There are three special
400 * positions that can be specified:
401 * PLAYLIST_PREPEND - Add track at beginning of playlist
402 * PLAYLIST_INSERT - Add track after current song. NOTE: If there
403 * are already inserted tracks then track is added
404 * to the end of the insertion list.
405 * PLAYLIST_INSERT_FIRST - Add track immediately after current song, no
406 * matter what other tracks have been inserted.
407 * PLAYLIST_INSERT_LAST - Add track to end of playlist
409 static int add_track_to_playlist(struct playlist_info* playlist,
410 const char *filename, int position,
411 bool queue, int seek_pos)
413 int insert_position = position;
414 unsigned int flags = PLAYLIST_INSERT_TYPE_INSERT;
415 int i;
417 if (playlist->amount >= playlist->max_playlist_size)
419 display_buffer_full();
420 return -1;
423 switch (position)
425 case PLAYLIST_PREPEND:
426 insert_position = playlist->first_index;
427 flags = PLAYLIST_INSERT_TYPE_PREPEND;
428 break;
429 case PLAYLIST_INSERT:
430 /* if there are already inserted tracks then add track to end of
431 insertion list else add after current playing track */
432 if (playlist->last_insert_pos >= 0 &&
433 playlist->last_insert_pos < playlist->amount &&
434 (playlist->indices[playlist->last_insert_pos]&
435 PLAYLIST_INSERT_TYPE_MASK) == PLAYLIST_INSERT_TYPE_INSERT)
436 position = insert_position = playlist->last_insert_pos+1;
437 else if (playlist->amount > 0)
438 position = insert_position = playlist->index + 1;
439 else
440 position = insert_position = 0;
442 playlist->last_insert_pos = position;
443 break;
444 case PLAYLIST_INSERT_FIRST:
445 if (playlist->amount > 0)
446 position = insert_position = playlist->index + 1;
447 else
448 position = insert_position = 0;
450 if (playlist->last_insert_pos < 0)
451 playlist->last_insert_pos = position;
452 break;
453 case PLAYLIST_INSERT_LAST:
454 if (playlist->first_index > 0)
455 insert_position = playlist->first_index;
456 else
457 insert_position = playlist->amount;
459 flags = PLAYLIST_INSERT_TYPE_APPEND;
460 break;
463 if (queue)
464 flags |= PLAYLIST_QUEUED;
466 /* shift indices so that track can be added */
467 for (i=playlist->amount; i>insert_position; i--)
468 playlist->indices[i] = playlist->indices[i-1];
470 /* update stored indices if needed */
471 if (playlist->amount > 0 && insert_position <= playlist->index)
472 playlist->index++;
474 if (playlist->amount > 0 && insert_position <= playlist->first_index &&
475 position != PLAYLIST_PREPEND)
477 playlist->first_index++;
479 if (seek_pos < 0 && playlist->current)
481 global_settings.resume_first_index = playlist->first_index;
482 settings_save();
486 if (insert_position < playlist->last_insert_pos ||
487 (insert_position == playlist->last_insert_pos && position < 0))
488 playlist->last_insert_pos++;
490 if (seek_pos < 0 && playlist->control_fd >= 0)
492 int result = -1;
494 if (flush_pending_control(playlist) < 0)
495 return -1;
497 mutex_lock(&playlist->control_mutex);
499 if (lseek(playlist->control_fd, 0, SEEK_END) >= 0)
501 if (fprintf(playlist->control_fd, "%c:%d:%d:", (queue?'Q':'A'),
502 position, playlist->last_insert_pos) > 0)
504 /* save the position in file where track name is written */
505 seek_pos = lseek(playlist->control_fd, 0, SEEK_CUR);
507 if (fprintf(playlist->control_fd, "%s\n", filename) > 0)
508 result = 0;
512 mutex_unlock(&playlist->control_mutex);
514 if (result < 0)
516 splash(HZ*2, true, str(LANG_PLAYLIST_CONTROL_UPDATE_ERROR));
517 return result;
521 playlist->indices[insert_position] = flags | seek_pos;
523 playlist->amount++;
524 playlist->num_inserted_tracks++;
526 return insert_position;
530 * Insert directory into playlist. May be called recursively.
532 static int add_directory_to_playlist(struct playlist_info* playlist,
533 const char *dirname, int *position, bool queue,
534 int *count, bool recurse)
536 char buf[MAX_PATH+1];
537 char *count_str;
538 int result = 0;
539 int num_files = 0;
540 int i;
541 int dirfilter = global_settings.dirfilter;
542 struct entry *files;
544 /* use the tree browser dircache to load files */
545 global_settings.dirfilter = SHOW_ALL;
546 struct tree_context* tc = tree_get_context();
547 strncpy(tc->currdir, dirname, sizeof(tc->currdir));
548 num_files = ft_load(tc, NULL);
549 files = (struct entry*) tc->dircache;
551 if(!num_files)
553 splash(HZ*2, true, str(LANG_PLAYLIST_DIRECTORY_ACCESS_ERROR));
554 return 0;
557 /* we've overwritten the dircache so tree browser will need to be
558 reloaded */
559 reload_directory();
561 if (queue)
562 count_str = str(LANG_PLAYLIST_QUEUE_COUNT);
563 else
564 count_str = str(LANG_PLAYLIST_INSERT_COUNT);
566 for (i=0; i<num_files; i++)
568 /* user abort */
569 if (button_get(false) == SETTINGS_CANCEL)
571 result = -1;
572 break;
575 if (files[i].attr & ATTR_DIRECTORY)
577 if (recurse)
579 /* recursively add directories */
580 snprintf(buf, sizeof(buf), "%s/%s", dirname, files[i].name);
581 result = add_directory_to_playlist(playlist, buf, position,
582 queue, count, recurse);
583 if (result < 0)
584 break;
586 /* we now need to reload our current directory */
587 strncpy(tc->currdir, dirname, sizeof(tc->currdir));
588 num_files = ft_load(tc, NULL);
589 files = (struct entry*) tc->dircache;
590 if (!num_files)
592 result = -1;
593 break;
596 else
597 continue;
599 else if ((files[i].attr & TREE_ATTR_MASK) == TREE_ATTR_MPA)
601 int insert_pos;
603 snprintf(buf, sizeof(buf), "%s/%s", dirname, files[i].name);
605 insert_pos = add_track_to_playlist(playlist, buf, *position,
606 queue, -1);
607 if (insert_pos < 0)
609 result = -1;
610 break;
613 (*count)++;
615 /* Make sure tracks are inserted in correct order if user requests
616 INSERT_FIRST */
617 if (*position == PLAYLIST_INSERT_FIRST || *position >= 0)
618 *position = insert_pos + 1;
620 if ((*count%PLAYLIST_DISPLAY_COUNT) == 0)
622 display_playlist_count(*count, count_str);
624 if (*count == PLAYLIST_DISPLAY_COUNT)
625 mpeg_flush_and_reload_tracks();
628 /* let the other threads work */
629 yield();
633 /* restore dirfilter */
634 global_settings.dirfilter = dirfilter;
636 return result;
640 * remove track at specified position
642 static int remove_track_from_playlist(struct playlist_info* playlist,
643 int position, bool write)
645 int i;
646 bool inserted;
648 if (playlist->amount <= 0)
649 return -1;
651 inserted = playlist->indices[position] & PLAYLIST_INSERT_TYPE_MASK;
653 /* shift indices now that track has been removed */
654 for (i=position; i<playlist->amount; i++)
655 playlist->indices[i] = playlist->indices[i+1];
657 playlist->amount--;
659 if (inserted)
660 playlist->num_inserted_tracks--;
661 else
662 playlist->deleted = true;
664 /* update stored indices if needed */
665 if (position < playlist->index)
666 playlist->index--;
668 if (position < playlist->first_index)
670 playlist->first_index--;
672 if (write)
674 global_settings.resume_first_index = playlist->first_index;
675 settings_save();
679 if (position <= playlist->last_insert_pos)
680 playlist->last_insert_pos--;
682 if (write && playlist->control_fd >= 0)
684 int result = -1;
686 if (flush_pending_control(playlist) < 0)
687 return -1;
689 mutex_lock(&playlist->control_mutex);
691 if (lseek(playlist->control_fd, 0, SEEK_END) >= 0)
693 if (fprintf(playlist->control_fd, "D:%d\n", position) > 0)
695 fsync(playlist->control_fd);
696 result = 0;
700 mutex_unlock(&playlist->control_mutex);
702 if (result < 0)
704 splash(HZ*2, true, str(LANG_PLAYLIST_CONTROL_UPDATE_ERROR));
705 return result;
709 return 0;
713 * randomly rearrange the array of indices for the playlist. If start_current
714 * is true then update the index to the new index of the current playing track
716 static int randomise_playlist(struct playlist_info* playlist,
717 unsigned int seed, bool start_current,
718 bool write)
720 int count;
721 int candidate;
722 int store;
723 unsigned int current = playlist->indices[playlist->index];
725 /* seed 0 is used to identify sorted playlist for resume purposes */
726 if (seed == 0)
727 seed = 1;
729 /* seed with the given seed */
730 srand(seed);
732 /* randomise entire indices list */
733 for(count = playlist->amount - 1; count >= 0; count--)
735 /* the rand is from 0 to RAND_MAX, so adjust to our value range */
736 candidate = rand() % (count + 1);
738 /* now swap the values at the 'count' and 'candidate' positions */
739 store = playlist->indices[candidate];
740 playlist->indices[candidate] = playlist->indices[count];
741 playlist->indices[count] = store;
744 if (start_current)
745 find_and_set_playlist_index(playlist, current);
747 /* indices have been moved so last insert position is no longer valid */
748 playlist->last_insert_pos = -1;
750 playlist->seed = seed;
751 if (playlist->num_inserted_tracks > 0 || playlist->deleted)
752 playlist->shuffle_modified = true;
754 if (write)
756 /* Don't write to disk immediately. Instead, save in settings and
757 only flush if playlist is modified (insertion/deletion) */
758 playlist->shuffle_flush = true;
759 global_settings.resume_seed = seed;
760 settings_save();
763 return 0;
767 * Sort the array of indices for the playlist. If start_current is true then
768 * set the index to the new index of the current song.
770 static int sort_playlist(struct playlist_info* playlist, bool start_current,
771 bool write)
773 unsigned int current = playlist->indices[playlist->index];
775 if (playlist->amount > 0)
776 qsort(playlist->indices, playlist->amount, sizeof(playlist->indices[0]),
777 compare);
779 if (start_current)
780 find_and_set_playlist_index(playlist, current);
782 /* indices have been moved so last insert position is no longer valid */
783 playlist->last_insert_pos = -1;
785 if (!playlist->num_inserted_tracks && !playlist->deleted)
786 playlist->shuffle_modified = false;
787 if (write && playlist->control_fd >= 0)
789 /* Don't write to disk immediately. Instead, save in settings and
790 only flush if playlist is modified (insertion/deletion) */
791 playlist->shuffle_flush = true;
792 global_settings.resume_seed = 0;
793 settings_save();
796 return 0;
800 * returns the index of the track that is "steps" away from current playing
801 * track.
803 static int get_next_index(const struct playlist_info* playlist, int steps)
805 int current_index = playlist->index;
806 int next_index = -1;
808 if (playlist->amount <= 0)
809 return -1;
811 switch (global_settings.repeat_mode)
813 case REPEAT_OFF:
815 current_index = rotate_index(playlist, current_index);
817 next_index = current_index+steps;
818 if ((next_index < 0) || (next_index >= playlist->amount))
819 next_index = -1;
820 else
821 next_index = (next_index+playlist->first_index) %
822 playlist->amount;
824 break;
827 case REPEAT_ONE:
828 next_index = current_index;
829 break;
831 case REPEAT_ALL:
832 default:
834 next_index = (current_index+steps) % playlist->amount;
835 while (next_index < 0)
836 next_index += playlist->amount;
838 if (steps >= playlist->amount)
840 int i, index;
842 index = next_index;
843 next_index = -1;
845 /* second time around so skip the queued files */
846 for (i=0; i<playlist->amount; i++)
848 if (playlist->indices[index] & PLAYLIST_QUEUE_MASK)
849 index = (index+1) % playlist->amount;
850 else
852 next_index = index;
853 break;
857 break;
861 return next_index;
865 * Search for the seek track and set appropriate indices. Used after shuffle
866 * to make sure the current index is still pointing to correct track.
868 static void find_and_set_playlist_index(struct playlist_info* playlist,
869 unsigned int seek)
871 int i;
873 /* Set the index to the current song */
874 for (i=0; i<playlist->amount; i++)
876 if (playlist->indices[i] == seek)
878 playlist->index = playlist->first_index = i;
880 if (playlist->current)
882 global_settings.resume_first_index = i;
883 settings_save();
886 break;
892 * used to sort track indices. Sort order is as follows:
893 * 1. Prepended tracks (in prepend order)
894 * 2. Playlist/directory tracks (in playlist order)
895 * 3. Inserted/Appended tracks (in insert order)
897 static int compare(const void* p1, const void* p2)
899 unsigned int* e1 = (unsigned int*) p1;
900 unsigned int* e2 = (unsigned int*) p2;
901 unsigned int flags1 = *e1 & PLAYLIST_INSERT_TYPE_MASK;
902 unsigned int flags2 = *e2 & PLAYLIST_INSERT_TYPE_MASK;
904 if (flags1 == flags2)
905 return (*e1 & PLAYLIST_SEEK_MASK) - (*e2 & PLAYLIST_SEEK_MASK);
906 else if (flags1 == PLAYLIST_INSERT_TYPE_PREPEND ||
907 flags2 == PLAYLIST_INSERT_TYPE_APPEND)
908 return -1;
909 else if (flags1 == PLAYLIST_INSERT_TYPE_APPEND ||
910 flags2 == PLAYLIST_INSERT_TYPE_PREPEND)
911 return 1;
912 else if (flags1 && flags2)
913 return (*e1 & PLAYLIST_SEEK_MASK) - (*e2 & PLAYLIST_SEEK_MASK);
914 else
915 return *e1 - *e2;
919 * gets pathname for track at seek index
921 static int get_filename(struct playlist_info* playlist, int seek,
922 bool control_file, char *buf, int buf_length)
924 int fd;
925 int max = -1;
926 char tmp_buf[MAX_PATH+1];
927 char dir_buf[MAX_PATH+1];
929 if (buf_length > MAX_PATH+1)
930 buf_length = MAX_PATH+1;
932 if (playlist->in_ram && !control_file)
934 strncpy(tmp_buf, &playlist->buffer[seek], sizeof(tmp_buf));
935 tmp_buf[MAX_PATH] = '\0';
936 max = strlen(tmp_buf) + 1;
938 else
940 if (control_file)
941 fd = playlist->control_fd;
942 else
944 if(-1 == playlist->fd)
945 playlist->fd = open(playlist->filename, O_RDONLY);
947 fd = playlist->fd;
950 if(-1 != fd)
952 if (control_file)
953 mutex_lock(&playlist->control_mutex);
955 lseek(fd, seek, SEEK_SET);
956 max = read(fd, tmp_buf, buf_length);
958 if (control_file)
959 mutex_unlock(&playlist->control_mutex);
962 if (max < 0)
964 if (control_file)
965 splash(HZ*2, true,
966 str(LANG_PLAYLIST_CONTROL_ACCESS_ERROR));
967 else
968 splash(HZ*2, true, str(LANG_PLAYLIST_ACCESS_ERROR));
970 return max;
974 strncpy(dir_buf, playlist->filename, playlist->dirlen-1);
975 dir_buf[playlist->dirlen-1] = 0;
977 return (format_track_path(buf, tmp_buf, buf_length, max, dir_buf));
981 * Returns absolute path of track
983 static int format_track_path(char *dest, char *src, int buf_length, int max,
984 char *dir)
986 int i = 0;
987 int j;
988 char *temp_ptr;
990 /* Zero-terminate the file name */
991 while((src[i] != '\n') &&
992 (src[i] != '\r') &&
993 (i < max))
994 i++;
996 /* Now work back killing white space */
997 while((src[i-1] == ' ') ||
998 (src[i-1] == '\t'))
999 i--;
1001 src[i]=0;
1003 /* replace backslashes with forward slashes */
1004 for ( j=0; j<i; j++ )
1005 if ( src[j] == '\\' )
1006 src[j] = '/';
1008 if('/' == src[0])
1010 strncpy(dest, src, buf_length);
1012 else
1014 /* handle dos style drive letter */
1015 if (':' == src[1])
1016 strncpy(dest, &src[2], buf_length);
1017 else if (!strncmp(src, "../", 3))
1019 /* handle relative paths */
1020 i=3;
1021 while(!strncmp(&src[i], "../", 3))
1022 i += 3;
1023 for (j=0; j<i/3; j++) {
1024 temp_ptr = strrchr(dir, '/');
1025 if (temp_ptr)
1026 *temp_ptr = '\0';
1027 else
1028 break;
1030 snprintf(dest, buf_length, "%s/%s", dir, &src[i]);
1032 else if ( '.' == src[0] && '/' == src[1] ) {
1033 snprintf(dest, buf_length, "%s/%s", dir, &src[2]);
1035 else {
1036 snprintf(dest, buf_length, "%s/%s", dir, src);
1040 return 0;
1044 * Display splash message showing progress of playlist/directory insertion or
1045 * save.
1047 static void display_playlist_count(int count, const char *fmt)
1049 lcd_clear_display();
1051 #ifdef HAVE_LCD_BITMAP
1052 if(global_settings.statusbar)
1053 lcd_setmargins(0, STATUSBAR_HEIGHT);
1054 else
1055 lcd_setmargins(0, 0);
1056 #endif
1058 splash(0, true, fmt, count,
1059 #if CONFIG_KEYPAD == PLAYER_PAD
1060 str(LANG_STOP_ABORT)
1061 #else
1062 str(LANG_OFF_ABORT)
1063 #endif
1068 * Display buffer full message
1070 static void display_buffer_full(void)
1072 splash(HZ*2, true, "%s %s",
1073 str(LANG_PLAYINDICES_PLAYLIST),
1074 str(LANG_PLAYINDICES_BUFFER));
1078 * Flush any pending control commands to disk. Called when playlist is being
1079 * modified. Returns 0 on success and -1 on failure.
1081 static int flush_pending_control(struct playlist_info* playlist)
1083 int result = 0;
1085 if (playlist->shuffle_flush && global_settings.resume_seed >= 0)
1087 /* pending shuffle */
1088 mutex_lock(&playlist->control_mutex);
1090 if (lseek(playlist->control_fd, 0, SEEK_END) >= 0)
1092 if (global_settings.resume_seed == 0)
1093 result = fprintf(playlist->control_fd, "U:%d\n",
1094 playlist->first_index);
1095 else
1096 result = fprintf(playlist->control_fd, "S:%d:%d\n",
1097 global_settings.resume_seed, playlist->first_index);
1099 if (result > 0)
1101 fsync(playlist->control_fd);
1103 playlist->shuffle_flush = false;
1104 global_settings.resume_seed = -1;
1105 settings_save();
1107 result = 0;
1109 else
1110 result = -1;
1112 else
1113 result = -1;
1115 mutex_unlock(&playlist->control_mutex);
1117 if (result < 0)
1119 splash(HZ*2, true, str(LANG_PLAYLIST_CONTROL_UPDATE_ERROR));
1120 return result;
1124 return result;
1128 * Rotate indices such that first_index is index 0
1130 static int rotate_index(const struct playlist_info* playlist, int index)
1132 index -= playlist->first_index;
1133 if (index < 0)
1134 index += playlist->amount;
1136 return index;
1140 * Initialize playlist entries at startup
1142 void playlist_init(void)
1144 struct playlist_info* playlist = &current_playlist;
1146 playlist->current = true;
1147 snprintf(playlist->control_filename, sizeof(playlist->control_filename),
1148 "%s", PLAYLIST_CONTROL_FILE);
1149 playlist->fd = -1;
1150 playlist->control_fd = -1;
1151 playlist->max_playlist_size = global_settings.max_files_in_playlist;
1152 playlist->indices = buffer_alloc(playlist->max_playlist_size * sizeof(int));
1153 playlist->buffer_size =
1154 AVERAGE_FILENAME_LENGTH * global_settings.max_files_in_dir;
1155 playlist->buffer = buffer_alloc(playlist->buffer_size);
1156 mutex_init(&playlist->control_mutex);
1157 empty_playlist(playlist, true);
1161 * Create new playlist
1163 int playlist_create(const char *dir, const char *file)
1165 struct playlist_info* playlist = &current_playlist;
1167 new_playlist(playlist, dir, file);
1169 if (file)
1170 /* load the playlist file */
1171 add_indices_to_playlist(playlist, NULL, 0);
1173 return 0;
1176 #define PLAYLIST_COMMAND_SIZE (MAX_PATH+12)
1179 * Restore the playlist state based on control file commands. Called to
1180 * resume playback after shutdown.
1182 int playlist_resume(void)
1184 struct playlist_info* playlist = &current_playlist;
1185 char *buffer;
1186 int buflen;
1187 int nread;
1188 int total_read = 0;
1189 int control_file_size = 0;
1190 bool first = true;
1191 bool sorted = true;
1193 enum {
1194 resume_playlist,
1195 resume_add,
1196 resume_queue,
1197 resume_delete,
1198 resume_shuffle,
1199 resume_unshuffle,
1200 resume_reset,
1201 resume_comment
1204 /* use mp3 buffer for maximum load speed */
1205 talk_buffer_steal(); /* we use the mp3 buffer, need to tell */
1206 buflen = (mp3end - mp3buf);
1207 buffer = mp3buf;
1209 empty_playlist(playlist, true);
1211 playlist->control_fd = open(playlist->control_filename, O_RDWR);
1212 if (playlist->control_fd < 0)
1214 splash(HZ*2, true, str(LANG_PLAYLIST_CONTROL_ACCESS_ERROR));
1215 return -1;
1217 playlist->control_created = true;
1219 control_file_size = filesize(playlist->control_fd);
1220 if (control_file_size <= 0)
1222 splash(HZ*2, true, str(LANG_PLAYLIST_CONTROL_ACCESS_ERROR));
1223 return -1;
1226 /* read a small amount first to get the header */
1227 nread = read(playlist->control_fd, buffer,
1228 PLAYLIST_COMMAND_SIZE<buflen?PLAYLIST_COMMAND_SIZE:buflen);
1229 if(nread <= 0)
1231 splash(HZ*2, true, str(LANG_PLAYLIST_CONTROL_ACCESS_ERROR));
1232 return -1;
1235 while (1)
1237 int result = 0;
1238 int count;
1239 int current_command = resume_comment;
1240 int last_newline = 0;
1241 int str_count = -1;
1242 bool newline = true;
1243 bool exit_loop = false;
1244 char *p = buffer;
1245 char *str1 = NULL;
1246 char *str2 = NULL;
1247 char *str3 = NULL;
1249 for(count=0; count<nread && !exit_loop; count++,p++)
1251 /* Are we on a new line? */
1252 if((*p == '\n') || (*p == '\r'))
1254 *p = '\0';
1256 /* save last_newline in case we need to load more data */
1257 last_newline = count;
1259 switch (current_command)
1261 case resume_playlist:
1263 /* str1=version str2=dir str3=file */
1264 int version;
1266 if (!str1)
1268 result = -1;
1269 exit_loop = true;
1270 break;
1273 if (!str2)
1274 str2 = "";
1276 if (!str3)
1277 str3 = "";
1279 version = atoi(str1);
1281 if (version != PLAYLIST_CONTROL_FILE_VERSION)
1282 return -1;
1284 update_playlist_filename(playlist, str2, str3);
1286 if (str3[0] != '\0')
1288 /* NOTE: add_indices_to_playlist() overwrites the
1289 mp3buf so we need to reload control file
1290 data */
1291 add_indices_to_playlist(playlist, NULL, 0);
1293 else if (str2[0] != '\0')
1295 playlist->in_ram = true;
1296 resume_directory(str2);
1299 /* load the rest of the data */
1300 first = false;
1301 exit_loop = true;
1303 break;
1305 case resume_add:
1306 case resume_queue:
1308 /* str1=position str2=last_position str3=file */
1309 int position, last_position;
1310 bool queue;
1312 if (!str1 || !str2 || !str3)
1314 result = -1;
1315 exit_loop = true;
1316 break;
1319 position = atoi(str1);
1320 last_position = atoi(str2);
1322 queue = (current_command == resume_add)?false:true;
1324 /* seek position is based on str3's position in
1325 buffer */
1326 if (add_track_to_playlist(playlist, str3, position,
1327 queue, total_read+(str3-buffer)) < 0)
1328 return -1;
1330 playlist->last_insert_pos = last_position;
1332 break;
1334 case resume_delete:
1336 /* str1=position */
1337 int position;
1339 if (!str1)
1341 result = -1;
1342 exit_loop = true;
1343 break;
1346 position = atoi(str1);
1348 if (remove_track_from_playlist(playlist, position,
1349 false) < 0)
1350 return -1;
1352 break;
1354 case resume_shuffle:
1356 /* str1=seed str2=first_index */
1357 int seed;
1359 if (!str1 || !str2)
1361 result = -1;
1362 exit_loop = true;
1363 break;
1366 if (!sorted)
1368 /* Always sort list before shuffling */
1369 sort_playlist(playlist, false, false);
1372 seed = atoi(str1);
1373 playlist->first_index = atoi(str2);
1375 if (randomise_playlist(playlist, seed, false,
1376 false) < 0)
1377 return -1;
1379 sorted = false;
1380 break;
1382 case resume_unshuffle:
1384 /* str1=first_index */
1385 if (!str1)
1387 result = -1;
1388 exit_loop = true;
1389 break;
1392 playlist->first_index = atoi(str1);
1394 if (sort_playlist(playlist, false, false) < 0)
1395 return -1;
1397 sorted = true;
1398 break;
1400 case resume_reset:
1402 playlist->last_insert_pos = -1;
1403 break;
1405 case resume_comment:
1406 default:
1407 break;
1410 newline = true;
1412 /* to ignore any extra newlines */
1413 current_command = resume_comment;
1415 else if(newline)
1417 newline = false;
1419 /* first non-comment line must always specify playlist */
1420 if (first && *p != 'P' && *p != '#')
1422 result = -1;
1423 exit_loop = true;
1424 break;
1427 switch (*p)
1429 case 'P':
1430 /* playlist can only be specified once */
1431 if (!first)
1433 result = -1;
1434 exit_loop = true;
1435 break;
1438 current_command = resume_playlist;
1439 break;
1440 case 'A':
1441 current_command = resume_add;
1442 break;
1443 case 'Q':
1444 current_command = resume_queue;
1445 break;
1446 case 'D':
1447 current_command = resume_delete;
1448 break;
1449 case 'S':
1450 current_command = resume_shuffle;
1451 break;
1452 case 'U':
1453 current_command = resume_unshuffle;
1454 break;
1455 case 'R':
1456 current_command = resume_reset;
1457 break;
1458 case '#':
1459 current_command = resume_comment;
1460 break;
1461 default:
1462 result = -1;
1463 exit_loop = true;
1464 break;
1467 str_count = -1;
1468 str1 = NULL;
1469 str2 = NULL;
1470 str3 = NULL;
1472 else if(current_command != resume_comment)
1474 /* all control file strings are separated with a colon.
1475 Replace the colon with 0 to get proper strings that can be
1476 used by commands above */
1477 if (*p == ':')
1479 *p = '\0';
1480 str_count++;
1482 if ((count+1) < nread)
1484 switch (str_count)
1486 case 0:
1487 str1 = p+1;
1488 break;
1489 case 1:
1490 str2 = p+1;
1491 break;
1492 case 2:
1493 str3 = p+1;
1494 break;
1495 default:
1496 /* allow last string to contain colons */
1497 *p = ':';
1498 break;
1505 if (result < 0)
1507 splash(HZ*2, true, str(LANG_PLAYLIST_CONTROL_INVALID));
1508 return result;
1511 if (!newline || (exit_loop && count<nread))
1513 if ((total_read + count) >= control_file_size)
1515 /* no newline at end of control file */
1516 splash(HZ*2, true, str(LANG_PLAYLIST_CONTROL_INVALID));
1517 return -1;
1520 /* We didn't end on a newline or we exited loop prematurely.
1521 Either way, re-read the remainder. */
1522 count = last_newline;
1523 lseek(playlist->control_fd, total_read+count, SEEK_SET);
1526 total_read += count;
1528 if (first)
1529 /* still looking for header */
1530 nread = read(playlist->control_fd, buffer,
1531 PLAYLIST_COMMAND_SIZE<buflen?PLAYLIST_COMMAND_SIZE:buflen);
1532 else
1533 nread = read(playlist->control_fd, buffer, buflen);
1535 /* Terminate on EOF */
1536 if(nread <= 0)
1538 if (global_settings.resume_seed >= 0)
1540 /* Apply shuffle command saved in settings */
1541 if (global_settings.resume_seed == 0)
1542 sort_playlist(playlist, false, true);
1543 else
1545 if (!sorted)
1546 sort_playlist(playlist, false, false);
1548 randomise_playlist(playlist, global_settings.resume_seed,
1549 false, true);
1552 playlist->first_index = global_settings.resume_first_index;
1555 break;
1559 return 0;
1563 * Add track to in_ram playlist. Used when playing directories.
1565 int playlist_add(const char *filename)
1567 struct playlist_info* playlist = &current_playlist;
1568 int len = strlen(filename);
1570 if((len+1 > playlist->buffer_size - playlist->buffer_end_pos) ||
1571 (playlist->amount >= playlist->max_playlist_size))
1573 display_buffer_full();
1574 return -1;
1577 playlist->indices[playlist->amount++] = playlist->buffer_end_pos;
1579 strcpy(&playlist->buffer[playlist->buffer_end_pos], filename);
1580 playlist->buffer_end_pos += len;
1581 playlist->buffer[playlist->buffer_end_pos++] = '\0';
1583 return 0;
1586 /* shuffle newly created playlist using random seed. */
1587 int playlist_shuffle(int random_seed, int start_index)
1589 struct playlist_info* playlist = &current_playlist;
1591 unsigned int seek_pos = 0;
1592 bool start_current = false;
1594 if (start_index >= 0 && global_settings.play_selected)
1596 /* store the seek position before the shuffle */
1597 seek_pos = playlist->indices[start_index];
1598 playlist->index = global_settings.resume_first_index =
1599 playlist->first_index = start_index;
1600 start_current = true;
1603 splash(0, true, str(LANG_PLAYLIST_SHUFFLE));
1605 randomise_playlist(playlist, random_seed, start_current, true);
1607 /* Flush shuffle command to disk */
1608 flush_pending_control(playlist);
1610 return playlist->index;
1613 /* start playing current playlist at specified index/offset */
1614 int playlist_start(int start_index, int offset)
1616 struct playlist_info* playlist = &current_playlist;
1618 playlist->index = start_index;
1619 talk_buffer_steal(); /* will use the mp3 buffer */
1620 mpeg_play(offset);
1622 return 0;
1625 /* Returns false if 'steps' is out of bounds, else true */
1626 bool playlist_check(int steps)
1628 struct playlist_info* playlist = &current_playlist;
1629 int index = get_next_index(playlist, steps);
1630 return (index >= 0);
1633 /* get trackname of track that is "steps" away from current playing track.
1634 NULL is used to identify end of playlist */
1635 char* playlist_peek(int steps)
1637 struct playlist_info* playlist = &current_playlist;
1638 int seek;
1639 int fd;
1640 char *temp_ptr;
1641 int index;
1642 bool control_file;
1644 index = get_next_index(playlist, steps);
1645 if (index < 0)
1646 return NULL;
1648 control_file = playlist->indices[index] & PLAYLIST_INSERT_TYPE_MASK;
1649 seek = playlist->indices[index] & PLAYLIST_SEEK_MASK;
1651 if (get_filename(playlist, seek, control_file, now_playing,
1652 MAX_PATH+1) < 0)
1653 return NULL;
1655 temp_ptr = now_playing;
1657 if (!playlist->in_ram || control_file)
1659 /* remove bogus dirs from beginning of path
1660 (workaround for buggy playlist creation tools) */
1661 while (temp_ptr)
1663 fd = open(temp_ptr, O_RDONLY);
1664 if (fd >= 0)
1666 close(fd);
1667 break;
1670 temp_ptr = strchr(temp_ptr+1, '/');
1673 if (!temp_ptr)
1675 /* Even though this is an invalid file, we still need to pass a
1676 file name to the caller because NULL is used to indicate end
1677 of playlist */
1678 return now_playing;
1682 return temp_ptr;
1686 * Update indices as track has changed
1688 int playlist_next(int steps)
1690 struct playlist_info* playlist = &current_playlist;
1691 int index;
1693 if (steps > 0 && global_settings.repeat_mode != REPEAT_ONE)
1695 int i, j;
1697 /* We need to delete all the queued songs */
1698 for (i=0, j=steps; i<j; i++)
1700 index = get_next_index(playlist, i);
1702 if (playlist->indices[index] & PLAYLIST_QUEUE_MASK)
1704 remove_track_from_playlist(playlist, index, true);
1705 steps--; /* one less track */
1710 index = get_next_index(playlist, steps);
1711 playlist->index = index;
1713 if (playlist->last_insert_pos >= 0 && steps > 0)
1715 /* check to see if we've gone beyond the last inserted track */
1716 int cur = rotate_index(playlist, index);
1717 int last_pos = rotate_index(playlist, playlist->last_insert_pos);
1719 if (cur > last_pos)
1721 /* reset last inserted track */
1722 playlist->last_insert_pos = -1;
1724 if (playlist->control_fd >= 0)
1726 int result = -1;
1728 mutex_lock(&playlist->control_mutex);
1730 if (lseek(playlist->control_fd, 0, SEEK_END) >= 0)
1732 if (fprintf(playlist->control_fd, "R\n") > 0)
1734 fsync(playlist->control_fd);
1735 result = 0;
1739 mutex_unlock(&playlist->control_mutex);
1741 if (result < 0)
1743 splash(HZ*2, true,
1744 str(LANG_PLAYLIST_CONTROL_UPDATE_ERROR));
1745 return result;
1751 return index;
1754 /* Get resume info for current playing song. If return value is -1 then
1755 settings shouldn't be saved. */
1756 int playlist_get_resume_info(int *resume_index)
1758 struct playlist_info* playlist = &current_playlist;
1760 *resume_index = playlist->index;
1762 return 0;
1765 /* Returns index of current playing track for display purposes. This value
1766 should not be used for resume purposes as it doesn't represent the actual
1767 index into the playlist */
1768 int playlist_get_display_index(void)
1770 struct playlist_info* playlist = &current_playlist;
1772 /* first_index should always be index 0 for display purposes */
1773 int index = rotate_index(playlist, playlist->index);
1775 return (index+1);
1778 /* returns number of tracks in current playlist */
1779 int playlist_amount(void)
1781 return playlist_amount_ex(NULL);
1785 * Create a new playlist If playlist is not NULL then we're loading a
1786 * playlist off disk for viewing/editing. The index_buffer is used to store
1787 * playlist indices (required for and only used if !current playlist). The
1788 * temp_buffer (if not NULL) is used as a scratchpad when loading indices.
1790 int playlist_create_ex(struct playlist_info* playlist,
1791 const char* dir, const char* file,
1792 void* index_buffer, int index_buffer_size,
1793 void* temp_buffer, int temp_buffer_size)
1795 if (!playlist)
1796 playlist = &current_playlist;
1797 else
1799 /* Initialize playlist structure */
1800 int r = rand() % 10;
1801 playlist->current = false;
1803 /* Use random name for control file */
1804 snprintf(playlist->control_filename, sizeof(playlist->control_filename),
1805 "%s.%d", PLAYLIST_CONTROL_FILE, r);
1806 playlist->fd = -1;
1807 playlist->control_fd = -1;
1809 if (index_buffer)
1811 int num_indices = index_buffer_size / sizeof(int);
1813 if (num_indices > global_settings.max_files_in_playlist)
1814 num_indices = global_settings.max_files_in_playlist;
1816 playlist->max_playlist_size = num_indices;
1817 playlist->indices = index_buffer;
1819 else
1821 playlist->max_playlist_size = current_playlist.max_playlist_size;
1822 playlist->indices = current_playlist.indices;
1825 playlist->buffer_size = 0;
1826 playlist->buffer = NULL;
1827 mutex_init(&playlist->control_mutex);
1830 new_playlist(playlist, dir, file);
1832 if (file)
1833 /* load the playlist file */
1834 add_indices_to_playlist(playlist, temp_buffer, temp_buffer_size);
1836 return 0;
1840 * Set the specified playlist as the current.
1841 * NOTE: You will get undefined behaviour if something is already playing so
1842 * remember to stop before calling this. Also, this call will
1843 * effectively close your playlist, making it unusable.
1845 int playlist_set_current(struct playlist_info* playlist)
1847 if (!playlist || (check_control(playlist) < 0))
1848 return -1;
1850 empty_playlist(&current_playlist, false);
1852 strncpy(current_playlist.filename, playlist->filename,
1853 sizeof(current_playlist.filename));
1855 current_playlist.fd = playlist->fd;
1857 close(playlist->control_fd);
1858 remove(current_playlist.control_filename);
1859 if (rename(playlist->control_filename,
1860 current_playlist.control_filename) < 0)
1861 return -1;
1862 current_playlist.control_fd = open(current_playlist.control_filename,
1863 O_RDWR);
1864 if (current_playlist.control_fd < 0)
1865 return -1;
1866 current_playlist.control_created = true;
1868 current_playlist.dirlen = playlist->dirlen;
1870 if (playlist->indices && playlist->indices != current_playlist.indices)
1871 memcpy(current_playlist.indices, playlist->indices,
1872 playlist->max_playlist_size*sizeof(int));
1874 current_playlist.first_index = playlist->first_index;
1875 current_playlist.amount = playlist->amount;
1876 current_playlist.last_insert_pos = playlist->last_insert_pos;
1877 current_playlist.seed = playlist->seed;
1878 current_playlist.shuffle_modified = playlist->shuffle_modified;
1879 current_playlist.deleted = playlist->deleted;
1880 current_playlist.num_inserted_tracks = playlist->num_inserted_tracks;
1881 current_playlist.shuffle_flush = playlist->shuffle_flush;
1883 return 0;
1887 * Close files and delete control file for non-current playlist.
1889 void playlist_close(struct playlist_info* playlist)
1891 if (!playlist)
1892 return;
1894 if (playlist->fd >= 0)
1895 close(playlist->fd);
1897 if (playlist->control_fd >= 0)
1898 close(playlist->control_fd);
1900 if (playlist->control_created)
1901 remove(playlist->control_filename);
1905 * Insert track into playlist at specified position (or one of the special
1906 * positions). Returns position where track was inserted or -1 if error.
1908 int playlist_insert_track(struct playlist_info* playlist, const char *filename,
1909 int position, bool queue)
1911 int result;
1913 if (!playlist)
1914 playlist = &current_playlist;
1916 if (check_control(playlist) < 0)
1918 splash(HZ*2, true, str(LANG_PLAYLIST_CONTROL_ACCESS_ERROR));
1919 return -1;
1922 result = add_track_to_playlist(playlist, filename, position, queue, -1);
1924 if (result != -1)
1926 fsync(playlist->control_fd);
1927 mpeg_flush_and_reload_tracks();
1930 return result;
1934 * Insert all tracks from specified directory into playlist.
1936 int playlist_insert_directory(struct playlist_info* playlist,
1937 const char *dirname, int position, bool queue,
1938 bool recurse)
1940 int count = 0;
1941 int result;
1942 char *count_str;
1944 if (!playlist)
1945 playlist = &current_playlist;
1947 if (check_control(playlist) < 0)
1949 splash(HZ*2, true, str(LANG_PLAYLIST_CONTROL_ACCESS_ERROR));
1950 return -1;
1953 if (queue)
1954 count_str = str(LANG_PLAYLIST_QUEUE_COUNT);
1955 else
1956 count_str = str(LANG_PLAYLIST_INSERT_COUNT);
1958 display_playlist_count(count, count_str);
1960 result = add_directory_to_playlist(playlist, dirname, &position, queue,
1961 &count, recurse);
1962 fsync(playlist->control_fd);
1964 display_playlist_count(count, count_str);
1965 mpeg_flush_and_reload_tracks();
1967 return result;
1971 * Insert all tracks from specified playlist into dynamic playlist.
1973 int playlist_insert_playlist(struct playlist_info* playlist, char *filename,
1974 int position, bool queue)
1976 int fd;
1977 int max;
1978 char *temp_ptr;
1979 char *dir;
1980 char *count_str;
1981 char temp_buf[MAX_PATH+1];
1982 char trackname[MAX_PATH+1];
1983 int count = 0;
1984 int result = 0;
1986 if (!playlist)
1987 playlist = &current_playlist;
1989 if (check_control(playlist) < 0)
1991 splash(HZ*2, true, str(LANG_PLAYLIST_CONTROL_ACCESS_ERROR));
1992 return -1;
1995 fd = open(filename, O_RDONLY);
1996 if (fd < 0)
1998 splash(HZ*2, true, str(LANG_PLAYLIST_ACCESS_ERROR));
1999 return -1;
2002 /* we need the directory name for formatting purposes */
2003 dir = filename;
2005 temp_ptr = strrchr(filename+1,'/');
2006 if (temp_ptr)
2007 *temp_ptr = 0;
2008 else
2009 dir = "/";
2011 if (queue)
2012 count_str = str(LANG_PLAYLIST_QUEUE_COUNT);
2013 else
2014 count_str = str(LANG_PLAYLIST_INSERT_COUNT);
2016 display_playlist_count(count, count_str);
2018 while ((max = read_line(fd, temp_buf, sizeof(temp_buf))) > 0)
2020 /* user abort */
2021 if (button_get(false) == SETTINGS_CANCEL)
2022 break;
2024 if (temp_buf[0] != '#' && temp_buf[0] != '\0')
2026 int insert_pos;
2028 /* we need to format so that relative paths are correctly
2029 handled */
2030 if (format_track_path(trackname, temp_buf, sizeof(trackname), max,
2031 dir) < 0)
2033 result = -1;
2034 break;
2037 insert_pos = add_track_to_playlist(playlist, trackname, position,
2038 queue, -1);
2040 if (insert_pos < 0)
2042 result = -1;
2043 break;
2046 /* Make sure tracks are inserted in correct order if user
2047 requests INSERT_FIRST */
2048 if (position == PLAYLIST_INSERT_FIRST || position >= 0)
2049 position = insert_pos + 1;
2051 count++;
2053 if ((count%PLAYLIST_DISPLAY_COUNT) == 0)
2055 display_playlist_count(count, count_str);
2057 if (count == PLAYLIST_DISPLAY_COUNT)
2058 mpeg_flush_and_reload_tracks();
2062 /* let the other threads work */
2063 yield();
2066 close(fd);
2067 fsync(playlist->control_fd);
2069 if (temp_ptr)
2070 *temp_ptr = '/';
2072 display_playlist_count(count, count_str);
2073 mpeg_flush_and_reload_tracks();
2075 return result;
2079 * Delete track at specified index. If index is PLAYLIST_DELETE_CURRENT then
2080 * we want to delete the current playing track.
2082 int playlist_delete(struct playlist_info* playlist, int index)
2084 int result = 0;
2086 if (!playlist)
2087 playlist = &current_playlist;
2089 if (check_control(playlist) < 0)
2091 splash(HZ*2, true, str(LANG_PLAYLIST_CONTROL_ACCESS_ERROR));
2092 return -1;
2095 if (index == PLAYLIST_DELETE_CURRENT)
2096 index = playlist->index;
2098 result = remove_track_from_playlist(playlist, index, true);
2100 if (result != -1)
2101 mpeg_flush_and_reload_tracks();
2103 return result;
2107 * Move track at index to new_index. Tracks between the two are shifted
2108 * appropriately. Returns 0 on success and -1 on failure.
2110 int playlist_move(struct playlist_info* playlist, int index, int new_index)
2112 int result;
2113 int seek;
2114 bool control_file;
2115 bool queue;
2116 bool current = false;
2117 int r;
2118 char filename[MAX_PATH];
2120 if (!playlist)
2121 playlist = &current_playlist;
2123 if (check_control(playlist) < 0)
2125 splash(HZ*2, true, str(LANG_PLAYLIST_CONTROL_ACCESS_ERROR));
2126 return -1;
2129 if (index == new_index)
2130 return -1;
2132 if (index == playlist->index)
2133 /* Moving the current track */
2134 current = true;
2136 control_file = playlist->indices[index] & PLAYLIST_INSERT_TYPE_MASK;
2137 queue = playlist->indices[index] & PLAYLIST_QUEUE_MASK;
2138 seek = playlist->indices[index] & PLAYLIST_SEEK_MASK;
2140 if (get_filename(playlist, seek, control_file, filename,
2141 sizeof(filename)) < 0)
2142 return -1;
2144 /* Delete track from original position */
2145 result = remove_track_from_playlist(playlist, index, true);
2147 if (result != -1)
2149 /* We want to insert the track at the position that was specified by
2150 new_index. This may be different then new_index because of the
2151 shifting that occurred after the delete */
2152 r = rotate_index(playlist, new_index);
2154 if (r == 0)
2155 /* First index */
2156 new_index = PLAYLIST_PREPEND;
2157 else if (r == playlist->amount)
2158 /* Append */
2159 new_index = PLAYLIST_INSERT_LAST;
2160 else
2161 /* Calculate index of desired position */
2162 new_index = (r+playlist->first_index)%playlist->amount;
2164 result = add_track_to_playlist(playlist, filename, new_index, queue,
2165 -1);
2167 if (result != -1)
2169 if (current)
2171 /* Moved the current track */
2172 switch (new_index)
2174 case PLAYLIST_PREPEND:
2175 playlist->index = playlist->first_index;
2176 break;
2177 case PLAYLIST_INSERT_LAST:
2178 playlist->index = playlist->first_index - 1;
2179 if (playlist->index < 0)
2180 playlist->index += playlist->amount;
2181 break;
2182 default:
2183 playlist->index = new_index;
2184 break;
2188 fsync(playlist->control_fd);
2189 mpeg_flush_and_reload_tracks();
2193 return result;
2196 /* shuffle currently playing playlist */
2197 int playlist_randomise(struct playlist_info* playlist, unsigned int seed,
2198 bool start_current)
2200 int result;
2202 if (!playlist)
2203 playlist = &current_playlist;
2205 check_control(playlist);
2207 result = randomise_playlist(playlist, seed, start_current, true);
2209 if (result != -1)
2210 mpeg_flush_and_reload_tracks();
2212 return result;
2215 /* sort currently playing playlist */
2216 int playlist_sort(struct playlist_info* playlist, bool start_current)
2218 int result;
2220 if (!playlist)
2221 playlist = &current_playlist;
2223 check_control(playlist);
2225 result = sort_playlist(playlist, start_current, true);
2227 if (result != -1)
2228 mpeg_flush_and_reload_tracks();
2230 return result;
2233 /* returns true if playlist has been modified */
2234 bool playlist_modified(const struct playlist_info* playlist)
2236 if (!playlist)
2237 playlist = &current_playlist;
2239 if (playlist->shuffle_modified ||
2240 playlist->deleted ||
2241 playlist->num_inserted_tracks > 0)
2242 return true;
2244 return false;
2247 /* returns index of first track in playlist */
2248 int playlist_get_first_index(const struct playlist_info* playlist)
2250 if (!playlist)
2251 playlist = &current_playlist;
2253 return playlist->first_index;
2256 /* returns shuffle seed of playlist */
2257 int playlist_get_seed(const struct playlist_info* playlist)
2259 if (!playlist)
2260 playlist = &current_playlist;
2262 return playlist->seed;
2265 /* returns number of tracks in playlist (includes queued/inserted tracks) */
2266 int playlist_amount_ex(const struct playlist_info* playlist)
2268 if (!playlist)
2269 playlist = &current_playlist;
2271 return playlist->amount;
2274 /* returns full path of playlist (minus extension) */
2275 char *playlist_name(const struct playlist_info* playlist, char *buf,
2276 int buf_size)
2278 char *sep;
2280 if (!playlist)
2281 playlist = &current_playlist;
2283 snprintf(buf, buf_size, "%s", playlist->filename+playlist->dirlen);
2285 if (!buf[0])
2286 return NULL;
2288 /* Remove extension */
2289 sep = strrchr(buf, '.');
2290 if (sep)
2291 *sep = 0;
2293 return buf;
2296 /* returns the playlist filename */
2297 char *playlist_get_name(const struct playlist_info* playlist, char *buf,
2298 int buf_size)
2300 if (!playlist)
2301 playlist = &current_playlist;
2303 snprintf(buf, buf_size, "%s", playlist->filename);
2305 if (!buf[0])
2306 return NULL;
2308 return buf;
2311 /* Fills info structure with information about track at specified index.
2312 Returns 0 on success and -1 on failure */
2313 int playlist_get_track_info(struct playlist_info* playlist, int index,
2314 struct playlist_track_info* info)
2316 int seek;
2317 bool control_file;
2319 if (!playlist)
2320 playlist = &current_playlist;
2322 if (index < 0 || index >= playlist->amount)
2323 return -1;
2325 control_file = playlist->indices[index] & PLAYLIST_INSERT_TYPE_MASK;
2326 seek = playlist->indices[index] & PLAYLIST_SEEK_MASK;
2328 if (get_filename(playlist, seek, control_file, info->filename,
2329 sizeof(info->filename)) < 0)
2330 return -1;
2332 info->attr = 0;
2334 if (control_file)
2336 if (playlist->indices[index] & PLAYLIST_QUEUE_MASK)
2337 info->attr |= PLAYLIST_ATTR_QUEUED;
2338 else
2339 info->attr |= PLAYLIST_ATTR_INSERTED;
2342 info->index = index;
2343 info->display_index = rotate_index(playlist, index) + 1;
2345 return 0;
2348 /* save the current dynamic playlist to specified file */
2349 int playlist_save(struct playlist_info* playlist, char *filename)
2351 int fd;
2352 int i, index;
2353 int count = 0;
2354 char tmp_buf[MAX_PATH+1];
2355 int result = 0;
2357 if (!playlist)
2358 playlist = &current_playlist;
2360 if (playlist->amount <= 0)
2361 return -1;
2363 /* use current working directory as base for pathname */
2364 if (format_track_path(tmp_buf, filename, sizeof(tmp_buf),
2365 strlen(filename)+1, getcwd(NULL, -1)) < 0)
2366 return -1;
2368 fd = open(tmp_buf, O_CREAT|O_WRONLY|O_TRUNC);
2369 if (fd < 0)
2371 splash(HZ*2, true, str(LANG_PLAYLIST_ACCESS_ERROR));
2372 return -1;
2375 display_playlist_count(count, str(LANG_PLAYLIST_SAVE_COUNT));
2377 index = playlist->first_index;
2378 for (i=0; i<playlist->amount; i++)
2380 bool control_file;
2381 bool queue;
2382 int seek;
2384 /* user abort */
2385 if (button_get(false) == SETTINGS_CANCEL)
2386 break;
2388 control_file = playlist->indices[index] & PLAYLIST_INSERT_TYPE_MASK;
2389 queue = playlist->indices[index] & PLAYLIST_QUEUE_MASK;
2390 seek = playlist->indices[index] & PLAYLIST_SEEK_MASK;
2392 /* Don't save queued files */
2393 if (!queue)
2395 if (get_filename(playlist, seek, control_file, tmp_buf,
2396 MAX_PATH+1) < 0)
2398 result = -1;
2399 break;
2402 if (fprintf(fd, "%s\n", tmp_buf) < 0)
2404 splash(HZ*2, true, str(LANG_PLAYLIST_CONTROL_UPDATE_ERROR));
2405 result = -1;
2406 break;
2409 count++;
2411 if ((count % PLAYLIST_DISPLAY_COUNT) == 0)
2412 display_playlist_count(count, str(LANG_PLAYLIST_SAVE_COUNT));
2414 yield();
2417 index = (index+1)%playlist->amount;
2420 display_playlist_count(count, str(LANG_PLAYLIST_SAVE_COUNT));
2422 close(fd);
2424 return result;