Added .S files in drivers
[kugel-rb.git] / apps / playlist.c
blob29a8269dc32ac1dd2f3132d3135af54f23f10bf2
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 "tree.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, char *dir,
132 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 char *dir, 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 char *filename, int position, bool queue,
141 int seek_pos);
142 static int add_directory_to_playlist(struct playlist_info* playlist,
143 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(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, char *fmt);
161 static void display_buffer_full(void);
162 static int flush_pending_control(struct playlist_info* playlist);
163 static int rotate_index(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, char *dir,
216 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)
249 splash(HZ*2, true, str(LANG_PLAYLIST_CONTROL_ACCESS_ERROR));
250 playlist->control_created = true;
254 * validate the control file. This may include creating/initializing it if
255 * necessary;
257 static int check_control(struct playlist_info* playlist)
259 if (!playlist->control_created)
261 create_control(playlist);
263 if (playlist->control_fd >= 0)
265 char* dir = playlist->filename;
266 char* file = playlist->filename+playlist->dirlen;
267 char c = playlist->filename[playlist->dirlen-1];
269 playlist->filename[playlist->dirlen-1] = '\0';
271 if (fprintf(playlist->control_fd, "P:%d:%s:%s\n",
272 PLAYLIST_CONTROL_FILE_VERSION, dir, file) > 0)
273 fsync(playlist->control_fd);
274 else
275 splash(HZ*2, true, str(LANG_PLAYLIST_CONTROL_UPDATE_ERROR));
277 playlist->filename[playlist->dirlen-1] = c;
281 if (playlist->control_fd < 0)
282 return -1;
284 return 0;
288 * store directory and name of playlist file
290 static void update_playlist_filename(struct playlist_info* playlist,
291 char *dir, char *file)
293 char *sep="";
294 int dirlen = strlen(dir);
296 /* If the dir does not end in trailing slash, we use a separator.
297 Otherwise we don't. */
298 if('/' != dir[dirlen-1])
300 sep="/";
301 dirlen++;
304 playlist->dirlen = dirlen;
306 snprintf(playlist->filename, sizeof(playlist->filename),
307 "%s%s%s",
308 dir, sep, file);
312 * calculate track offsets within a playlist file
314 static int add_indices_to_playlist(struct playlist_info* playlist,
315 char* buffer, int buflen)
317 unsigned int nread;
318 unsigned int i = 0;
319 unsigned int count = 0;
320 bool store_index;
321 unsigned char *p;
323 if(-1 == playlist->fd)
324 playlist->fd = open(playlist->filename, O_RDONLY);
325 if(playlist->fd < 0)
326 return -1; /* failure */
328 #ifdef HAVE_LCD_BITMAP
329 if(global_settings.statusbar)
330 lcd_setmargins(0, STATUSBAR_HEIGHT);
331 else
332 lcd_setmargins(0, 0);
333 #endif
335 splash(0, true, str(LANG_PLAYLIST_LOAD));
337 if (!buffer)
339 /* use mp3 buffer for maximum load speed */
340 mpeg_stop();
341 talk_buffer_steal(); /* we use the mp3 buffer, need to tell */
343 buffer = mp3buf;
344 buflen = (mp3end - mp3buf);
347 store_index = true;
349 while(1)
351 nread = read(playlist->fd, buffer, buflen);
352 /* Terminate on EOF */
353 if(nread <= 0)
354 break;
356 p = buffer;
358 for(count=0; count < nread; count++,p++) {
360 /* Are we on a new line? */
361 if((*p == '\n') || (*p == '\r'))
363 store_index = true;
365 else if(store_index)
367 store_index = false;
369 if(*p != '#')
371 /* Store a new entry */
372 playlist->indices[ playlist->amount ] = i+count;
373 playlist->amount++;
374 if ( playlist->amount >= playlist->max_playlist_size ) {
375 display_buffer_full();
376 return -1;
382 i+= count;
385 return 0;
389 * Add track to playlist at specified position. There are three special
390 * positions that can be specified:
391 * PLAYLIST_PREPEND - Add track at beginning of playlist
392 * PLAYLIST_INSERT - Add track after current song. NOTE: If there
393 * are already inserted tracks then track is added
394 * to the end of the insertion list.
395 * PLAYLIST_INSERT_FIRST - Add track immediately after current song, no
396 * matter what other tracks have been inserted.
397 * PLAYLIST_INSERT_LAST - Add track to end of playlist
399 static int add_track_to_playlist(struct playlist_info* playlist,
400 char *filename, int position, bool queue,
401 int seek_pos)
403 int insert_position = position;
404 unsigned int flags = PLAYLIST_INSERT_TYPE_INSERT;
405 int i;
407 if (playlist->amount >= playlist->max_playlist_size)
409 display_buffer_full();
410 return -1;
413 switch (position)
415 case PLAYLIST_PREPEND:
416 insert_position = playlist->first_index;
417 flags = PLAYLIST_INSERT_TYPE_PREPEND;
418 break;
419 case PLAYLIST_INSERT:
420 /* if there are already inserted tracks then add track to end of
421 insertion list else add after current playing track */
422 if (playlist->last_insert_pos >= 0 &&
423 playlist->last_insert_pos < playlist->amount &&
424 (playlist->indices[playlist->last_insert_pos]&
425 PLAYLIST_INSERT_TYPE_MASK) == PLAYLIST_INSERT_TYPE_INSERT)
426 position = insert_position = playlist->last_insert_pos+1;
427 else if (playlist->amount > 0)
428 position = insert_position = playlist->index + 1;
429 else
430 position = insert_position = 0;
432 playlist->last_insert_pos = position;
433 break;
434 case PLAYLIST_INSERT_FIRST:
435 if (playlist->amount > 0)
436 position = insert_position = playlist->index + 1;
437 else
438 position = insert_position = 0;
440 if (playlist->last_insert_pos < 0)
441 playlist->last_insert_pos = position;
442 break;
443 case PLAYLIST_INSERT_LAST:
444 if (playlist->first_index > 0)
445 insert_position = playlist->first_index;
446 else
447 insert_position = playlist->amount;
449 flags = PLAYLIST_INSERT_TYPE_APPEND;
450 break;
453 if (queue)
454 flags |= PLAYLIST_QUEUED;
456 /* shift indices so that track can be added */
457 for (i=playlist->amount; i>insert_position; i--)
458 playlist->indices[i] = playlist->indices[i-1];
460 /* update stored indices if needed */
461 if (playlist->amount > 0 && insert_position <= playlist->index)
462 playlist->index++;
464 if (playlist->amount > 0 && insert_position <= playlist->first_index &&
465 position != PLAYLIST_PREPEND)
467 playlist->first_index++;
469 if (seek_pos < 0 && playlist->current)
471 global_settings.resume_first_index = playlist->first_index;
472 settings_save();
476 if (insert_position < playlist->last_insert_pos ||
477 (insert_position == playlist->last_insert_pos && position < 0))
478 playlist->last_insert_pos++;
480 if (seek_pos < 0 && playlist->control_fd >= 0)
482 int result = -1;
484 if (flush_pending_control(playlist) < 0)
485 return -1;
487 mutex_lock(&playlist->control_mutex);
489 if (lseek(playlist->control_fd, 0, SEEK_END) >= 0)
491 if (fprintf(playlist->control_fd, "%c:%d:%d:", (queue?'Q':'A'),
492 position, playlist->last_insert_pos) > 0)
494 /* save the position in file where track name is written */
495 seek_pos = lseek(playlist->control_fd, 0, SEEK_CUR);
497 if (fprintf(playlist->control_fd, "%s\n", filename) > 0)
498 result = 0;
502 mutex_unlock(&playlist->control_mutex);
504 if (result < 0)
506 splash(HZ*2, true, str(LANG_PLAYLIST_CONTROL_UPDATE_ERROR));
507 return result;
511 playlist->indices[insert_position] = flags | seek_pos;
513 playlist->amount++;
514 playlist->num_inserted_tracks++;
516 return insert_position;
520 * Insert directory into playlist. May be called recursively.
522 static int add_directory_to_playlist(struct playlist_info* playlist,
523 char *dirname, int *position, bool queue,
524 int *count, bool recurse)
526 char buf[MAX_PATH+1];
527 char *count_str;
528 int result = 0;
529 int num_files = 0;
530 bool buffer_full = false;
531 int i;
532 int dirfilter = SHOW_ALL;
533 struct entry *files;
535 /* use the tree browser dircache to load files */
536 files = load_and_sort_directory(dirname, &dirfilter, &num_files,
537 &buffer_full);
539 if(!files)
541 splash(HZ*2, true, str(LANG_PLAYLIST_DIRECTORY_ACCESS_ERROR));
542 return 0;
545 /* we've overwritten the dircache so tree browser will need to be
546 reloaded */
547 reload_directory();
549 if (queue)
550 count_str = str(LANG_PLAYLIST_QUEUE_COUNT);
551 else
552 count_str = str(LANG_PLAYLIST_INSERT_COUNT);
554 for (i=0; i<num_files; i++)
556 /* user abort */
557 #if defined(HAVE_PLAYER_KEYPAD) || defined(HAVE_NEO_KEYPAD)
558 if (button_get(false) == BUTTON_STOP)
559 #else
560 if (button_get(false) == BUTTON_OFF)
561 #endif
563 result = -1;
564 break;
567 if (files[i].attr & ATTR_DIRECTORY)
569 if (recurse)
571 /* recursively add directories */
572 snprintf(buf, sizeof(buf), "%s/%s", dirname, files[i].name);
573 result = add_directory_to_playlist(playlist, buf, position,
574 queue, count, recurse);
575 if (result < 0)
576 break;
578 /* we now need to reload our current directory */
579 files = load_and_sort_directory(dirname, &dirfilter, &num_files,
580 &buffer_full);
581 if (!files)
583 result = -1;
584 break;
587 else
588 continue;
590 else if ((files[i].attr & TREE_ATTR_MASK) == TREE_ATTR_MPA)
592 int insert_pos;
594 snprintf(buf, sizeof(buf), "%s/%s", dirname, files[i].name);
596 insert_pos = add_track_to_playlist(playlist, buf, *position,
597 queue, -1);
598 if (insert_pos < 0)
600 result = -1;
601 break;
604 (*count)++;
606 /* Make sure tracks are inserted in correct order if user requests
607 INSERT_FIRST */
608 if (*position == PLAYLIST_INSERT_FIRST || *position >= 0)
609 *position = insert_pos + 1;
611 if ((*count%PLAYLIST_DISPLAY_COUNT) == 0)
613 display_playlist_count(*count, count_str);
615 if (*count == PLAYLIST_DISPLAY_COUNT)
616 mpeg_flush_and_reload_tracks();
619 /* let the other threads work */
620 yield();
624 return result;
628 * remove track at specified position
630 static int remove_track_from_playlist(struct playlist_info* playlist,
631 int position, bool write)
633 int i;
634 bool inserted;
636 if (playlist->amount <= 0)
637 return -1;
639 inserted = playlist->indices[position] & PLAYLIST_INSERT_TYPE_MASK;
641 /* shift indices now that track has been removed */
642 for (i=position; i<playlist->amount; i++)
643 playlist->indices[i] = playlist->indices[i+1];
645 playlist->amount--;
647 if (inserted)
648 playlist->num_inserted_tracks--;
649 else
650 playlist->deleted = true;
652 /* update stored indices if needed */
653 if (position < playlist->index)
654 playlist->index--;
656 if (position < playlist->first_index)
658 playlist->first_index--;
660 if (write)
662 global_settings.resume_first_index = playlist->first_index;
663 settings_save();
667 if (position <= playlist->last_insert_pos)
668 playlist->last_insert_pos--;
670 if (write && playlist->control_fd >= 0)
672 int result = -1;
674 if (flush_pending_control(playlist) < 0)
675 return -1;
677 mutex_lock(&playlist->control_mutex);
679 if (lseek(playlist->control_fd, 0, SEEK_END) >= 0)
681 if (fprintf(playlist->control_fd, "D:%d\n", position) > 0)
683 fsync(playlist->control_fd);
684 result = 0;
688 mutex_unlock(&playlist->control_mutex);
690 if (result < 0)
692 splash(HZ*2, true, str(LANG_PLAYLIST_CONTROL_UPDATE_ERROR));
693 return result;
697 return 0;
701 * randomly rearrange the array of indices for the playlist. If start_current
702 * is true then update the index to the new index of the current playing track
704 static int randomise_playlist(struct playlist_info* playlist,
705 unsigned int seed, bool start_current,
706 bool write)
708 int count;
709 int candidate;
710 int store;
711 unsigned int current = playlist->indices[playlist->index];
713 /* seed 0 is used to identify sorted playlist for resume purposes */
714 if (seed == 0)
715 seed = 1;
717 /* seed with the given seed */
718 srand(seed);
720 /* randomise entire indices list */
721 for(count = playlist->amount - 1; count >= 0; count--)
723 /* the rand is from 0 to RAND_MAX, so adjust to our value range */
724 candidate = rand() % (count + 1);
726 /* now swap the values at the 'count' and 'candidate' positions */
727 store = playlist->indices[candidate];
728 playlist->indices[candidate] = playlist->indices[count];
729 playlist->indices[count] = store;
732 if (start_current)
733 find_and_set_playlist_index(playlist, current);
735 /* indices have been moved so last insert position is no longer valid */
736 playlist->last_insert_pos = -1;
738 playlist->seed = seed;
739 if (playlist->num_inserted_tracks > 0 || playlist->deleted)
740 playlist->shuffle_modified = true;
742 if (write)
744 /* Don't write to disk immediately. Instead, save in settings and
745 only flush if playlist is modified (insertion/deletion) */
746 playlist->shuffle_flush = true;
747 global_settings.resume_seed = seed;
748 settings_save();
751 return 0;
755 * Sort the array of indices for the playlist. If start_current is true then
756 * set the index to the new index of the current song.
758 static int sort_playlist(struct playlist_info* playlist, bool start_current,
759 bool write)
761 unsigned int current = playlist->indices[playlist->index];
763 if (playlist->amount > 0)
764 qsort(playlist->indices, playlist->amount, sizeof(playlist->indices[0]),
765 compare);
767 if (start_current)
768 find_and_set_playlist_index(playlist, current);
770 /* indices have been moved so last insert position is no longer valid */
771 playlist->last_insert_pos = -1;
773 if (!playlist->num_inserted_tracks && !playlist->deleted)
774 playlist->shuffle_modified = false;
775 if (write && playlist->control_fd >= 0)
777 /* Don't write to disk immediately. Instead, save in settings and
778 only flush if playlist is modified (insertion/deletion) */
779 playlist->shuffle_flush = true;
780 global_settings.resume_seed = 0;
781 settings_save();
784 return 0;
788 * returns the index of the track that is "steps" away from current playing
789 * track.
791 static int get_next_index(struct playlist_info* playlist, int steps)
793 int current_index = playlist->index;
794 int next_index = -1;
796 if (playlist->amount <= 0)
797 return -1;
799 switch (global_settings.repeat_mode)
801 case REPEAT_OFF:
803 current_index = rotate_index(playlist, current_index);
805 next_index = current_index+steps;
806 if ((next_index < 0) || (next_index >= playlist->amount))
807 next_index = -1;
808 else
809 next_index = (next_index+playlist->first_index) %
810 playlist->amount;
812 break;
815 case REPEAT_ONE:
816 next_index = current_index;
817 break;
819 case REPEAT_ALL:
820 default:
822 next_index = (current_index+steps) % playlist->amount;
823 while (next_index < 0)
824 next_index += playlist->amount;
826 if (steps >= playlist->amount)
828 int i, index;
830 index = next_index;
831 next_index = -1;
833 /* second time around so skip the queued files */
834 for (i=0; i<playlist->amount; i++)
836 if (playlist->indices[index] & PLAYLIST_QUEUE_MASK)
837 index = (index+1) % playlist->amount;
838 else
840 next_index = index;
841 break;
845 break;
849 return next_index;
853 * Search for the seek track and set appropriate indices. Used after shuffle
854 * to make sure the current index is still pointing to correct track.
856 static void find_and_set_playlist_index(struct playlist_info* playlist,
857 unsigned int seek)
859 int i;
861 /* Set the index to the current song */
862 for (i=0; i<playlist->amount; i++)
864 if (playlist->indices[i] == seek)
866 playlist->index = playlist->first_index = i;
868 if (playlist->current)
870 global_settings.resume_first_index = i;
871 settings_save();
874 break;
880 * used to sort track indices. Sort order is as follows:
881 * 1. Prepended tracks (in prepend order)
882 * 2. Playlist/directory tracks (in playlist order)
883 * 3. Inserted/Appended tracks (in insert order)
885 static int compare(const void* p1, const void* p2)
887 unsigned int* e1 = (unsigned int*) p1;
888 unsigned int* e2 = (unsigned int*) p2;
889 unsigned int flags1 = *e1 & PLAYLIST_INSERT_TYPE_MASK;
890 unsigned int flags2 = *e2 & PLAYLIST_INSERT_TYPE_MASK;
892 if (flags1 == flags2)
893 return (*e1 & PLAYLIST_SEEK_MASK) - (*e2 & PLAYLIST_SEEK_MASK);
894 else if (flags1 == PLAYLIST_INSERT_TYPE_PREPEND ||
895 flags2 == PLAYLIST_INSERT_TYPE_APPEND)
896 return -1;
897 else if (flags1 == PLAYLIST_INSERT_TYPE_APPEND ||
898 flags2 == PLAYLIST_INSERT_TYPE_PREPEND)
899 return 1;
900 else if (flags1 && flags2)
901 return (*e1 & PLAYLIST_SEEK_MASK) - (*e2 & PLAYLIST_SEEK_MASK);
902 else
903 return *e1 - *e2;
907 * gets pathname for track at seek index
909 static int get_filename(struct playlist_info* playlist, int seek,
910 bool control_file, char *buf, int buf_length)
912 int fd;
913 int max = -1;
914 char tmp_buf[MAX_PATH+1];
915 char dir_buf[MAX_PATH+1];
917 if (buf_length > MAX_PATH+1)
918 buf_length = MAX_PATH+1;
920 if (playlist->in_ram && !control_file)
922 strncpy(tmp_buf, &playlist->buffer[seek], sizeof(tmp_buf));
923 tmp_buf[MAX_PATH] = '\0';
924 max = strlen(tmp_buf) + 1;
926 else
928 if (control_file)
929 fd = playlist->control_fd;
930 else
932 if(-1 == playlist->fd)
933 playlist->fd = open(playlist->filename, O_RDONLY);
935 fd = playlist->fd;
938 if(-1 != fd)
940 if (control_file)
941 mutex_lock(&playlist->control_mutex);
943 lseek(fd, seek, SEEK_SET);
944 max = read(fd, tmp_buf, buf_length);
946 if (control_file)
947 mutex_unlock(&playlist->control_mutex);
950 if (max < 0)
952 if (control_file)
953 splash(HZ*2, true,
954 str(LANG_PLAYLIST_CONTROL_ACCESS_ERROR));
955 else
956 splash(HZ*2, true, str(LANG_PLAYLIST_ACCESS_ERROR));
958 return max;
962 strncpy(dir_buf, playlist->filename, playlist->dirlen-1);
963 dir_buf[playlist->dirlen-1] = 0;
965 return (format_track_path(buf, tmp_buf, buf_length, max, dir_buf));
969 * Returns absolute path of track
971 static int format_track_path(char *dest, char *src, int buf_length, int max,
972 char *dir)
974 int i = 0;
975 int j;
976 char *temp_ptr;
978 /* Zero-terminate the file name */
979 while((src[i] != '\n') &&
980 (src[i] != '\r') &&
981 (i < max))
982 i++;
984 /* Now work back killing white space */
985 while((src[i-1] == ' ') ||
986 (src[i-1] == '\t'))
987 i--;
989 src[i]=0;
991 /* replace backslashes with forward slashes */
992 for ( j=0; j<i; j++ )
993 if ( src[j] == '\\' )
994 src[j] = '/';
996 if('/' == src[0])
998 strncpy(dest, src, buf_length);
1000 else
1002 /* handle dos style drive letter */
1003 if (':' == src[1])
1004 strncpy(dest, &src[2], buf_length);
1005 else if ('.' == src[0] && '.' == src[1] && '/' == src[2])
1007 /* handle relative paths */
1008 i=3;
1009 while(src[i] == '.' &&
1010 src[i] == '.' &&
1011 src[i] == '/')
1012 i += 3;
1013 for (j=0; j<i/3; j++) {
1014 temp_ptr = strrchr(dir, '/');
1015 if (temp_ptr)
1016 *temp_ptr = '\0';
1017 else
1018 break;
1020 snprintf(dest, buf_length, "%s/%s", dir, &src[i]);
1022 else if ( '.' == src[0] && '/' == src[1] ) {
1023 snprintf(dest, buf_length, "%s/%s", dir, &src[2]);
1025 else {
1026 snprintf(dest, buf_length, "%s/%s", dir, src);
1030 return 0;
1034 * Display splash message showing progress of playlist/directory insertion or
1035 * save.
1037 static void display_playlist_count(int count, char *fmt)
1039 lcd_clear_display();
1041 #ifdef HAVE_LCD_BITMAP
1042 if(global_settings.statusbar)
1043 lcd_setmargins(0, STATUSBAR_HEIGHT);
1044 else
1045 lcd_setmargins(0, 0);
1046 #endif
1048 splash(0, true, fmt, count,
1049 #ifdef HAVE_PLAYER_KEYPAD
1050 str(LANG_STOP_ABORT)
1051 #else
1052 str(LANG_OFF_ABORT)
1053 #endif
1058 * Display buffer full message
1060 static void display_buffer_full(void)
1062 splash(HZ*2, true, "%s %s",
1063 str(LANG_PLAYINDICES_PLAYLIST),
1064 str(LANG_PLAYINDICES_BUFFER));
1068 * Flush any pending control commands to disk. Called when playlist is being
1069 * modified. Returns 0 on success and -1 on failure.
1071 static int flush_pending_control(struct playlist_info* playlist)
1073 int result = 0;
1075 if (playlist->shuffle_flush && global_settings.resume_seed >= 0)
1077 /* pending shuffle */
1078 mutex_lock(&playlist->control_mutex);
1080 if (lseek(playlist->control_fd, 0, SEEK_END) >= 0)
1082 if (global_settings.resume_seed == 0)
1083 result = fprintf(playlist->control_fd, "U:%d\n",
1084 playlist->first_index);
1085 else
1086 result = fprintf(playlist->control_fd, "S:%d:%d\n",
1087 global_settings.resume_seed, playlist->first_index);
1089 if (result > 0)
1091 fsync(playlist->control_fd);
1093 playlist->shuffle_flush = false;
1094 global_settings.resume_seed = -1;
1095 settings_save();
1097 result = 0;
1099 else
1100 result = -1;
1102 else
1103 result = -1;
1105 mutex_unlock(&playlist->control_mutex);
1107 if (result < 0)
1109 splash(HZ*2, true, str(LANG_PLAYLIST_CONTROL_UPDATE_ERROR));
1110 return result;
1114 return result;
1118 * Rotate indices such that first_index is index 0
1120 static int rotate_index(struct playlist_info* playlist, int index)
1122 index -= playlist->first_index;
1123 if (index < 0)
1124 index += playlist->amount;
1126 return index;
1130 * Initialize playlist entries at startup
1132 void playlist_init(void)
1134 struct playlist_info* playlist = &current_playlist;
1136 playlist->current = true;
1137 snprintf(playlist->control_filename, sizeof(playlist->control_filename),
1138 "%s", PLAYLIST_CONTROL_FILE);
1139 playlist->fd = -1;
1140 playlist->control_fd = -1;
1141 playlist->max_playlist_size = global_settings.max_files_in_playlist;
1142 playlist->indices = buffer_alloc(playlist->max_playlist_size * sizeof(int));
1143 playlist->buffer_size =
1144 AVERAGE_FILENAME_LENGTH * global_settings.max_files_in_dir;
1145 playlist->buffer = buffer_alloc(playlist->buffer_size);
1146 mutex_init(&playlist->control_mutex);
1147 empty_playlist(playlist, true);
1151 * Create new playlist
1153 int playlist_create(char *dir, char *file)
1155 struct playlist_info* playlist = &current_playlist;
1157 new_playlist(playlist, dir, file);
1159 if (file)
1160 /* load the playlist file */
1161 add_indices_to_playlist(playlist, NULL, 0);
1163 return 0;
1166 #define PLAYLIST_COMMAND_SIZE (MAX_PATH+12)
1169 * Restore the playlist state based on control file commands. Called to
1170 * resume playback after shutdown.
1172 int playlist_resume(void)
1174 struct playlist_info* playlist = &current_playlist;
1175 char *buffer;
1176 int buflen;
1177 int nread;
1178 int total_read = 0;
1179 bool first = true;
1180 bool sorted = true;
1182 enum {
1183 resume_playlist,
1184 resume_add,
1185 resume_queue,
1186 resume_delete,
1187 resume_shuffle,
1188 resume_unshuffle,
1189 resume_reset,
1190 resume_comment
1193 /* use mp3 buffer for maximum load speed */
1194 talk_buffer_steal(); /* we use the mp3 buffer, need to tell */
1195 buflen = (mp3end - mp3buf);
1196 buffer = mp3buf;
1198 empty_playlist(playlist, true);
1200 playlist->control_fd = open(playlist->control_filename, O_RDWR);
1201 if (playlist->control_fd < 0)
1203 splash(HZ*2, true, str(LANG_PLAYLIST_CONTROL_ACCESS_ERROR));
1204 return -1;
1206 playlist->control_created = true;
1208 /* read a small amount first to get the header */
1209 nread = read(playlist->control_fd, buffer,
1210 PLAYLIST_COMMAND_SIZE<buflen?PLAYLIST_COMMAND_SIZE:buflen);
1211 if(nread <= 0)
1213 splash(HZ*2, true, str(LANG_PLAYLIST_CONTROL_ACCESS_ERROR));
1214 return -1;
1217 while (1)
1219 int result = 0;
1220 int count;
1221 int current_command = resume_comment;
1222 int last_newline = 0;
1223 int str_count = -1;
1224 bool newline = true;
1225 bool exit_loop = false;
1226 char *p = buffer;
1227 char *str1 = NULL;
1228 char *str2 = NULL;
1229 char *str3 = NULL;
1231 for(count=0; count<nread && !exit_loop; count++,p++)
1233 /* Are we on a new line? */
1234 if((*p == '\n') || (*p == '\r'))
1236 *p = '\0';
1238 /* save last_newline in case we need to load more data */
1239 last_newline = count;
1241 switch (current_command)
1243 case resume_playlist:
1245 /* str1=version str2=dir str3=file */
1246 int version;
1248 if (!str1)
1250 result = -1;
1251 exit_loop = true;
1252 break;
1255 if (!str2)
1256 str2 = "";
1258 if (!str3)
1259 str3 = "";
1261 version = atoi(str1);
1263 if (version != PLAYLIST_CONTROL_FILE_VERSION)
1264 return -1;
1266 update_playlist_filename(playlist, str2, str3);
1268 if (str3[0] != '\0')
1270 /* NOTE: add_indices_to_playlist() overwrites the
1271 mp3buf so we need to reload control file
1272 data */
1273 add_indices_to_playlist(playlist, NULL, 0);
1275 else if (str2[0] != '\0')
1277 playlist->in_ram = true;
1278 resume_directory(str2);
1281 /* load the rest of the data */
1282 first = false;
1283 exit_loop = true;
1285 break;
1287 case resume_add:
1288 case resume_queue:
1290 /* str1=position str2=last_position str3=file */
1291 int position, last_position;
1292 bool queue;
1294 if (!str1 || !str2 || !str3)
1296 result = -1;
1297 exit_loop = true;
1298 break;
1301 position = atoi(str1);
1302 last_position = atoi(str2);
1304 queue = (current_command == resume_add)?false:true;
1306 /* seek position is based on str3's position in
1307 buffer */
1308 if (add_track_to_playlist(playlist, str3, position,
1309 queue, total_read+(str3-buffer)) < 0)
1310 return -1;
1312 playlist->last_insert_pos = last_position;
1314 break;
1316 case resume_delete:
1318 /* str1=position */
1319 int position;
1321 if (!str1)
1323 result = -1;
1324 exit_loop = true;
1325 break;
1328 position = atoi(str1);
1330 if (remove_track_from_playlist(playlist, position,
1331 false) < 0)
1332 return -1;
1334 break;
1336 case resume_shuffle:
1338 /* str1=seed str2=first_index */
1339 int seed;
1341 if (!str1 || !str2)
1343 result = -1;
1344 exit_loop = true;
1345 break;
1348 if (!sorted)
1350 /* Always sort list before shuffling */
1351 sort_playlist(playlist, false, false);
1354 seed = atoi(str1);
1355 playlist->first_index = atoi(str2);
1357 if (randomise_playlist(playlist, seed, false,
1358 false) < 0)
1359 return -1;
1361 sorted = false;
1362 break;
1364 case resume_unshuffle:
1366 /* str1=first_index */
1367 if (!str1)
1369 result = -1;
1370 exit_loop = true;
1371 break;
1374 playlist->first_index = atoi(str1);
1376 if (sort_playlist(playlist, false, false) < 0)
1377 return -1;
1379 sorted = true;
1380 break;
1382 case resume_reset:
1384 playlist->last_insert_pos = -1;
1385 break;
1387 case resume_comment:
1388 default:
1389 break;
1392 newline = true;
1394 /* to ignore any extra newlines */
1395 current_command = resume_comment;
1397 else if(newline)
1399 newline = false;
1401 /* first non-comment line must always specify playlist */
1402 if (first && *p != 'P' && *p != '#')
1404 result = -1;
1405 exit_loop = true;
1406 break;
1409 switch (*p)
1411 case 'P':
1412 /* playlist can only be specified once */
1413 if (!first)
1415 result = -1;
1416 exit_loop = true;
1417 break;
1420 current_command = resume_playlist;
1421 break;
1422 case 'A':
1423 current_command = resume_add;
1424 break;
1425 case 'Q':
1426 current_command = resume_queue;
1427 break;
1428 case 'D':
1429 current_command = resume_delete;
1430 break;
1431 case 'S':
1432 current_command = resume_shuffle;
1433 break;
1434 case 'U':
1435 current_command = resume_unshuffle;
1436 break;
1437 case 'R':
1438 current_command = resume_reset;
1439 break;
1440 case '#':
1441 current_command = resume_comment;
1442 break;
1443 default:
1444 result = -1;
1445 exit_loop = true;
1446 break;
1449 str_count = -1;
1450 str1 = NULL;
1451 str2 = NULL;
1452 str3 = NULL;
1454 else if(current_command != resume_comment)
1456 /* all control file strings are separated with a colon.
1457 Replace the colon with 0 to get proper strings that can be
1458 used by commands above */
1459 if (*p == ':')
1461 *p = '\0';
1462 str_count++;
1464 if ((count+1) < nread)
1466 switch (str_count)
1468 case 0:
1469 str1 = p+1;
1470 break;
1471 case 1:
1472 str2 = p+1;
1473 break;
1474 case 2:
1475 str3 = p+1;
1476 break;
1477 default:
1478 /* allow last string to contain colons */
1479 *p = ':';
1480 break;
1487 if (result < 0)
1489 splash(HZ*2, true, str(LANG_PLAYLIST_CONTROL_INVALID));
1490 return result;
1493 if (!newline || (exit_loop && count<nread))
1495 /* We didn't end on a newline or we exited loop prematurely.
1496 Either way, re-read the remainder.
1497 NOTE: because of this, control file must always end with a
1498 newline */
1499 count = last_newline;
1500 lseek(playlist->control_fd, total_read+count, SEEK_SET);
1503 total_read += count;
1505 if (first)
1506 /* still looking for header */
1507 nread = read(playlist->control_fd, buffer,
1508 PLAYLIST_COMMAND_SIZE<buflen?PLAYLIST_COMMAND_SIZE:buflen);
1509 else
1510 nread = read(playlist->control_fd, buffer, buflen);
1512 /* Terminate on EOF */
1513 if(nread <= 0)
1515 if (global_settings.resume_seed >= 0)
1517 /* Apply shuffle command saved in settings */
1518 if (global_settings.resume_seed == 0)
1519 sort_playlist(playlist, false, true);
1520 else
1522 if (!sorted)
1523 sort_playlist(playlist, false, false);
1525 randomise_playlist(playlist, global_settings.resume_seed,
1526 false, true);
1529 playlist->first_index = global_settings.resume_first_index;
1532 break;
1536 return 0;
1540 * Add track to in_ram playlist. Used when playing directories.
1542 int playlist_add(char *filename)
1544 struct playlist_info* playlist = &current_playlist;
1545 int len = strlen(filename);
1547 if((len+1 > playlist->buffer_size - playlist->buffer_end_pos) ||
1548 (playlist->amount >= playlist->max_playlist_size))
1550 display_buffer_full();
1551 return -1;
1554 playlist->indices[playlist->amount++] = playlist->buffer_end_pos;
1556 strcpy(&playlist->buffer[playlist->buffer_end_pos], filename);
1557 playlist->buffer_end_pos += len;
1558 playlist->buffer[playlist->buffer_end_pos++] = '\0';
1560 return 0;
1563 /* shuffle newly created playlist using random seed. */
1564 int playlist_shuffle(int random_seed, int start_index)
1566 struct playlist_info* playlist = &current_playlist;
1568 unsigned int seek_pos = 0;
1569 bool start_current = false;
1571 if (start_index >= 0 && global_settings.play_selected)
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;
1580 splash(0, true, str(LANG_PLAYLIST_SHUFFLE));
1582 randomise_playlist(playlist, random_seed, start_current, true);
1584 /* Flush shuffle command to disk */
1585 flush_pending_control(playlist);
1587 return playlist->index;
1590 /* start playing current playlist at specified index/offset */
1591 int playlist_start(int start_index, int offset)
1593 struct playlist_info* playlist = &current_playlist;
1595 playlist->index = start_index;
1596 talk_buffer_steal(); /* will use the mp3 buffer */
1597 mpeg_play(offset);
1599 return 0;
1602 /* Returns false if 'steps' is out of bounds, else true */
1603 bool playlist_check(int steps)
1605 struct playlist_info* playlist = &current_playlist;
1606 int index = get_next_index(playlist, steps);
1607 return (index >= 0);
1610 /* get trackname of track that is "steps" away from current playing track.
1611 NULL is used to identify end of playlist */
1612 char* playlist_peek(int steps)
1614 struct playlist_info* playlist = &current_playlist;
1615 int seek;
1616 int fd;
1617 char *temp_ptr;
1618 int index;
1619 bool control_file;
1621 index = get_next_index(playlist, steps);
1622 if (index < 0)
1623 return NULL;
1625 control_file = playlist->indices[index] & PLAYLIST_INSERT_TYPE_MASK;
1626 seek = playlist->indices[index] & PLAYLIST_SEEK_MASK;
1628 if (get_filename(playlist, seek, control_file, now_playing,
1629 MAX_PATH+1) < 0)
1630 return NULL;
1632 temp_ptr = now_playing;
1634 if (!playlist->in_ram || control_file)
1636 /* remove bogus dirs from beginning of path
1637 (workaround for buggy playlist creation tools) */
1638 while (temp_ptr)
1640 fd = open(temp_ptr, O_RDONLY);
1641 if (fd >= 0)
1643 close(fd);
1644 break;
1647 temp_ptr = strchr(temp_ptr+1, '/');
1650 if (!temp_ptr)
1652 /* Even though this is an invalid file, we still need to pass a
1653 file name to the caller because NULL is used to indicate end
1654 of playlist */
1655 return now_playing;
1659 return temp_ptr;
1663 * Update indices as track has changed
1665 int playlist_next(int steps)
1667 struct playlist_info* playlist = &current_playlist;
1668 int index;
1670 if (steps > 0 && global_settings.repeat_mode != REPEAT_ONE)
1672 int i, j;
1674 /* We need to delete all the queued songs */
1675 for (i=0, j=steps; i<j; i++)
1677 index = get_next_index(playlist, i);
1679 if (playlist->indices[index] & PLAYLIST_QUEUE_MASK)
1681 remove_track_from_playlist(playlist, index, true);
1682 steps--; /* one less track */
1687 index = get_next_index(playlist, steps);
1688 playlist->index = index;
1690 if (playlist->last_insert_pos >= 0 && steps > 0)
1692 /* check to see if we've gone beyond the last inserted track */
1693 int cur = rotate_index(playlist, index);
1694 int last_pos = rotate_index(playlist, playlist->last_insert_pos);
1696 if (cur > last_pos)
1698 /* reset last inserted track */
1699 playlist->last_insert_pos = -1;
1701 if (playlist->control_fd >= 0)
1703 int result = -1;
1705 mutex_lock(&playlist->control_mutex);
1707 if (lseek(playlist->control_fd, 0, SEEK_END) >= 0)
1709 if (fprintf(playlist->control_fd, "R\n") > 0)
1711 fsync(playlist->control_fd);
1712 result = 0;
1716 mutex_unlock(&playlist->control_mutex);
1718 if (result < 0)
1720 splash(HZ*2, true,
1721 str(LANG_PLAYLIST_CONTROL_UPDATE_ERROR));
1722 return result;
1728 return index;
1731 /* Get resume info for current playing song. If return value is -1 then
1732 settings shouldn't be saved. */
1733 int playlist_get_resume_info(int *resume_index)
1735 struct playlist_info* playlist = &current_playlist;
1737 *resume_index = playlist->index;
1739 return 0;
1742 /* Returns index of current playing track for display purposes. This value
1743 should not be used for resume purposes as it doesn't represent the actual
1744 index into the playlist */
1745 int playlist_get_display_index(void)
1747 struct playlist_info* playlist = &current_playlist;
1749 /* first_index should always be index 0 for display purposes */
1750 int index = rotate_index(playlist, playlist->index);
1752 return (index+1);
1755 /* returns number of tracks in current playlist */
1756 int playlist_amount(void)
1758 return playlist_amount_ex(NULL);
1762 * Create a new playlist If playlist is not NULL then we're loading a
1763 * playlist off disk for viewing/editing. The index_buffer is used to store
1764 * playlist indices (required for and only used if !current playlist). The
1765 * temp_buffer (if not NULL) is used as a scratchpad when loading indices.
1767 int playlist_create_ex(struct playlist_info* playlist, char* dir, char* file,
1768 void* index_buffer, int index_buffer_size,
1769 void* temp_buffer, int temp_buffer_size)
1771 if (!playlist)
1772 playlist = &current_playlist;
1773 else
1775 /* Initialize playlist structure */
1776 int r = rand() % 10;
1777 playlist->current = false;
1779 /* Use random name for control file */
1780 snprintf(playlist->control_filename, sizeof(playlist->control_filename),
1781 "%s.%d", PLAYLIST_CONTROL_FILE, r);
1782 playlist->fd = -1;
1783 playlist->control_fd = -1;
1785 if (index_buffer)
1787 int num_indices = index_buffer_size / sizeof(int);
1789 if (num_indices > global_settings.max_files_in_playlist)
1790 num_indices = global_settings.max_files_in_playlist;
1792 playlist->max_playlist_size = num_indices;
1793 playlist->indices = index_buffer;
1795 else
1797 playlist->max_playlist_size = current_playlist.max_playlist_size;
1798 playlist->indices = current_playlist.indices;
1801 playlist->buffer_size = 0;
1802 playlist->buffer = NULL;
1803 mutex_init(&playlist->control_mutex);
1806 new_playlist(playlist, dir, file);
1808 if (file)
1809 /* load the playlist file */
1810 add_indices_to_playlist(playlist, temp_buffer, temp_buffer_size);
1812 return 0;
1816 * Set the specified playlist as the current.
1817 * NOTE: You will get undefined behaviour if something is already playing so
1818 * remember to stop before calling this. Also, this call will
1819 * effectively close your playlist, making it unusable.
1821 int playlist_set_current(struct playlist_info* playlist)
1823 if (!playlist || (check_control(playlist) < 0))
1824 return -1;
1826 empty_playlist(&current_playlist, false);
1828 strncpy(current_playlist.filename, playlist->filename,
1829 sizeof(current_playlist.filename));
1831 current_playlist.fd = playlist->fd;
1833 close(playlist->control_fd);
1834 remove(current_playlist.control_filename);
1835 if (rename(playlist->control_filename,
1836 current_playlist.control_filename) < 0)
1837 return -1;
1838 current_playlist.control_fd = open(current_playlist.control_filename,
1839 O_RDWR);
1840 if (current_playlist.control_fd < 0)
1841 return -1;
1842 current_playlist.control_created = true;
1844 current_playlist.dirlen = playlist->dirlen;
1846 if (playlist->indices && playlist->indices != current_playlist.indices)
1847 memcpy(current_playlist.indices, playlist->indices,
1848 playlist->max_playlist_size*sizeof(int));
1850 current_playlist.first_index = playlist->first_index;
1851 current_playlist.amount = playlist->amount;
1852 current_playlist.last_insert_pos = playlist->last_insert_pos;
1853 current_playlist.seed = playlist->seed;
1854 current_playlist.shuffle_modified = playlist->shuffle_modified;
1855 current_playlist.deleted = playlist->deleted;
1856 current_playlist.num_inserted_tracks = playlist->num_inserted_tracks;
1857 current_playlist.shuffle_flush = playlist->shuffle_flush;
1859 return 0;
1863 * Close files and delete control file for non-current playlist.
1865 void playlist_close(struct playlist_info* playlist)
1867 if (!playlist)
1868 return;
1870 if (playlist->fd >= 0)
1871 close(playlist->fd);
1873 if (playlist->control_fd >= 0)
1874 close(playlist->control_fd);
1876 if (playlist->control_created)
1877 remove(playlist->control_filename);
1881 * Insert track into playlist at specified position (or one of the special
1882 * positions). Returns position where track was inserted or -1 if error.
1884 int playlist_insert_track(struct playlist_info* playlist, char *filename,
1885 int position, bool queue)
1887 int result;
1889 if (!playlist)
1890 playlist = &current_playlist;
1892 if (check_control(playlist) < 0)
1894 splash(HZ*2, true, str(LANG_PLAYLIST_CONTROL_ACCESS_ERROR));
1895 return -1;
1898 result = add_track_to_playlist(playlist, filename, position, queue, -1);
1900 if (result != -1)
1902 fsync(playlist->control_fd);
1903 mpeg_flush_and_reload_tracks();
1906 return result;
1910 * Insert all tracks from specified directory into playlist.
1912 int playlist_insert_directory(struct playlist_info* playlist, char *dirname,
1913 int position, bool queue, bool recurse)
1915 int count = 0;
1916 int result;
1917 char *count_str;
1919 if (!playlist)
1920 playlist = &current_playlist;
1922 if (check_control(playlist) < 0)
1924 splash(HZ*2, true, str(LANG_PLAYLIST_CONTROL_ACCESS_ERROR));
1925 return -1;
1928 if (queue)
1929 count_str = str(LANG_PLAYLIST_QUEUE_COUNT);
1930 else
1931 count_str = str(LANG_PLAYLIST_INSERT_COUNT);
1933 display_playlist_count(count, count_str);
1935 result = add_directory_to_playlist(playlist, dirname, &position, queue,
1936 &count, recurse);
1937 fsync(playlist->control_fd);
1939 display_playlist_count(count, count_str);
1940 mpeg_flush_and_reload_tracks();
1942 return result;
1946 * Insert all tracks from specified playlist into dynamic playlist.
1948 int playlist_insert_playlist(struct playlist_info* playlist, char *filename,
1949 int position, bool queue)
1951 int fd;
1952 int max;
1953 char *temp_ptr;
1954 char *dir;
1955 char *count_str;
1956 char temp_buf[MAX_PATH+1];
1957 char trackname[MAX_PATH+1];
1958 int count = 0;
1959 int result = 0;
1961 if (!playlist)
1962 playlist = &current_playlist;
1964 if (check_control(playlist) < 0)
1966 splash(HZ*2, true, str(LANG_PLAYLIST_CONTROL_ACCESS_ERROR));
1967 return -1;
1970 fd = open(filename, O_RDONLY);
1971 if (fd < 0)
1973 splash(HZ*2, true, str(LANG_PLAYLIST_ACCESS_ERROR));
1974 return -1;
1977 /* we need the directory name for formatting purposes */
1978 dir = filename;
1980 temp_ptr = strrchr(filename+1,'/');
1981 if (temp_ptr)
1982 *temp_ptr = 0;
1983 else
1984 dir = "/";
1986 if (queue)
1987 count_str = str(LANG_PLAYLIST_QUEUE_COUNT);
1988 else
1989 count_str = str(LANG_PLAYLIST_INSERT_COUNT);
1991 display_playlist_count(count, count_str);
1993 while ((max = read_line(fd, temp_buf, sizeof(temp_buf))) > 0)
1995 /* user abort */
1996 #if defined(HAVE_PLAYER_KEYPAD) || defined(HAVE_NEO_KEYPAD)
1997 if (button_get(false) == BUTTON_STOP)
1998 #else
1999 if (button_get(false) == BUTTON_OFF)
2000 #endif
2001 break;
2003 if (temp_buf[0] != '#' && temp_buf[0] != '\0')
2005 int insert_pos;
2007 /* we need to format so that relative paths are correctly
2008 handled */
2009 if (format_track_path(trackname, temp_buf, sizeof(trackname), max,
2010 dir) < 0)
2012 result = -1;
2013 break;
2016 insert_pos = add_track_to_playlist(playlist, trackname, position,
2017 queue, -1);
2019 if (insert_pos < 0)
2021 result = -1;
2022 break;
2025 /* Make sure tracks are inserted in correct order if user
2026 requests INSERT_FIRST */
2027 if (position == PLAYLIST_INSERT_FIRST || position >= 0)
2028 position = insert_pos + 1;
2030 count++;
2032 if ((count%PLAYLIST_DISPLAY_COUNT) == 0)
2034 display_playlist_count(count, count_str);
2036 if (count == PLAYLIST_DISPLAY_COUNT)
2037 mpeg_flush_and_reload_tracks();
2041 /* let the other threads work */
2042 yield();
2045 close(fd);
2046 fsync(playlist->control_fd);
2048 if (temp_ptr)
2049 *temp_ptr = '/';
2051 display_playlist_count(count, count_str);
2052 mpeg_flush_and_reload_tracks();
2054 return result;
2058 * Delete track at specified index. If index is PLAYLIST_DELETE_CURRENT then
2059 * we want to delete the current playing track.
2061 int playlist_delete(struct playlist_info* playlist, int index)
2063 int result = 0;
2065 if (!playlist)
2066 playlist = &current_playlist;
2068 if (check_control(playlist) < 0)
2070 splash(HZ*2, true, str(LANG_PLAYLIST_CONTROL_ACCESS_ERROR));
2071 return -1;
2074 if (index == PLAYLIST_DELETE_CURRENT)
2075 index = playlist->index;
2077 result = remove_track_from_playlist(playlist, index, true);
2079 if (result != -1)
2080 mpeg_flush_and_reload_tracks();
2082 return result;
2086 * Move track at index to new_index. Tracks between the two are shifted
2087 * appropriately. Returns 0 on success and -1 on failure.
2089 int playlist_move(struct playlist_info* playlist, int index, int new_index)
2091 int result;
2092 int seek;
2093 bool control_file;
2094 bool queue;
2095 bool current = false;
2096 int r;
2097 char filename[MAX_PATH];
2099 if (!playlist)
2100 playlist = &current_playlist;
2102 if (check_control(playlist) < 0)
2104 splash(HZ*2, true, str(LANG_PLAYLIST_CONTROL_ACCESS_ERROR));
2105 return -1;
2108 if (index == new_index)
2109 return -1;
2111 if (index == playlist->index)
2112 /* Moving the current track */
2113 current = true;
2115 control_file = playlist->indices[index] & PLAYLIST_INSERT_TYPE_MASK;
2116 queue = playlist->indices[index] & PLAYLIST_QUEUE_MASK;
2117 seek = playlist->indices[index] & PLAYLIST_SEEK_MASK;
2119 if (get_filename(playlist, seek, control_file, filename,
2120 sizeof(filename)) < 0)
2121 return -1;
2123 /* Delete track from original position */
2124 result = remove_track_from_playlist(playlist, index, true);
2126 if (result != -1)
2128 /* We want to insert the track at the position that was specified by
2129 new_index. This may be different then new_index because of the
2130 shifting that occurred after the delete */
2131 r = rotate_index(playlist, new_index);
2133 if (r == 0)
2134 /* First index */
2135 new_index = PLAYLIST_PREPEND;
2136 else if (r == playlist->amount)
2137 /* Append */
2138 new_index = PLAYLIST_INSERT_LAST;
2139 else
2140 /* Calculate index of desired position */
2141 new_index = (r+playlist->first_index)%playlist->amount;
2143 result = add_track_to_playlist(playlist, filename, new_index, queue,
2144 -1);
2146 if (result != -1)
2148 if (current)
2150 /* Moved the current track */
2151 switch (new_index)
2153 case PLAYLIST_PREPEND:
2154 playlist->index = playlist->first_index;
2155 break;
2156 case PLAYLIST_INSERT_LAST:
2157 playlist->index = playlist->first_index - 1;
2158 if (playlist->index < 0)
2159 playlist->index += playlist->amount;
2160 break;
2161 default:
2162 playlist->index = new_index;
2163 break;
2167 fsync(playlist->control_fd);
2168 mpeg_flush_and_reload_tracks();
2172 return result;
2175 /* shuffle currently playing playlist */
2176 int playlist_randomise(struct playlist_info* playlist, unsigned int seed,
2177 bool start_current)
2179 int result;
2181 if (!playlist)
2182 playlist = &current_playlist;
2184 check_control(playlist);
2186 result = randomise_playlist(playlist, seed, start_current, true);
2188 if (result != -1)
2189 mpeg_flush_and_reload_tracks();
2191 return result;
2194 /* sort currently playing playlist */
2195 int playlist_sort(struct playlist_info* playlist, bool start_current)
2197 int result;
2199 if (!playlist)
2200 playlist = &current_playlist;
2202 check_control(playlist);
2204 result = sort_playlist(playlist, start_current, true);
2206 if (result != -1)
2207 mpeg_flush_and_reload_tracks();
2209 return result;
2212 /* returns true if playlist has been modified */
2213 bool playlist_modified(struct playlist_info* playlist)
2215 if (!playlist)
2216 playlist = &current_playlist;
2218 if (playlist->shuffle_modified ||
2219 playlist->deleted ||
2220 playlist->num_inserted_tracks > 0)
2221 return true;
2223 return false;
2226 /* returns index of first track in playlist */
2227 int playlist_get_first_index(struct playlist_info* playlist)
2229 if (!playlist)
2230 playlist = &current_playlist;
2232 return playlist->first_index;
2235 /* returns shuffle seed of playlist */
2236 int playlist_get_seed(struct playlist_info* playlist)
2238 if (!playlist)
2239 playlist = &current_playlist;
2241 return playlist->seed;
2244 /* returns number of tracks in playlist (includes queued/inserted tracks) */
2245 int playlist_amount_ex(struct playlist_info* playlist)
2247 if (!playlist)
2248 playlist = &current_playlist;
2250 return playlist->amount;
2253 /* returns full path of playlist (minus extension) */
2254 char *playlist_name(struct playlist_info* playlist, char *buf, int buf_size)
2256 char *sep;
2258 if (!playlist)
2259 playlist = &current_playlist;
2261 snprintf(buf, buf_size, "%s", playlist->filename+playlist->dirlen);
2263 if (!buf[0])
2264 return NULL;
2266 /* Remove extension */
2267 sep = strrchr(buf, '.');
2268 if (sep)
2269 *sep = 0;
2271 return buf;
2274 /* returns the playlist filename */
2275 char *playlist_get_name(struct playlist_info* playlist, char *buf,
2276 int buf_size)
2278 if (!playlist)
2279 playlist = &current_playlist;
2281 snprintf(buf, buf_size, "%s", playlist->filename);
2283 if (!buf[0])
2284 return NULL;
2286 return buf;
2289 /* Fills info structure with information about track at specified index.
2290 Returns 0 on success and -1 on failure */
2291 int playlist_get_track_info(struct playlist_info* playlist, int index,
2292 struct playlist_track_info* info)
2294 int seek;
2295 bool control_file;
2297 if (!playlist)
2298 playlist = &current_playlist;
2300 if (index < 0 || index >= playlist->amount)
2301 return -1;
2303 control_file = playlist->indices[index] & PLAYLIST_INSERT_TYPE_MASK;
2304 seek = playlist->indices[index] & PLAYLIST_SEEK_MASK;
2306 if (get_filename(playlist, seek, control_file, info->filename,
2307 sizeof(info->filename)) < 0)
2308 return -1;
2310 info->attr = 0;
2312 if (control_file)
2314 if (playlist->indices[index] & PLAYLIST_QUEUE_MASK)
2315 info->attr |= PLAYLIST_ATTR_QUEUED;
2316 else
2317 info->attr |= PLAYLIST_ATTR_INSERTED;
2320 info->index = index;
2321 info->display_index = rotate_index(playlist, index) + 1;
2323 return 0;
2326 /* save the current dynamic playlist to specified file */
2327 int playlist_save(struct playlist_info* playlist, char *filename)
2329 int fd;
2330 int i, index;
2331 int count = 0;
2332 char tmp_buf[MAX_PATH+1];
2333 int result = 0;
2335 if (!playlist)
2336 playlist = &current_playlist;
2338 if (playlist->amount <= 0)
2339 return -1;
2341 /* use current working directory as base for pathname */
2342 if (format_track_path(tmp_buf, filename, sizeof(tmp_buf),
2343 strlen(filename)+1, getcwd(NULL, -1)) < 0)
2344 return -1;
2346 fd = open(tmp_buf, O_CREAT|O_WRONLY|O_TRUNC);
2347 if (fd < 0)
2349 splash(HZ*2, true, str(LANG_PLAYLIST_ACCESS_ERROR));
2350 return -1;
2353 display_playlist_count(count, str(LANG_PLAYLIST_SAVE_COUNT));
2355 index = playlist->first_index;
2356 for (i=0; i<playlist->amount; i++)
2358 bool control_file;
2359 bool queue;
2360 int seek;
2362 /* user abort */
2363 #if defined(HAVE_PLAYER_KEYPAD) || defined(HAVE_NEO_KEYPAD)
2364 if (button_get(false) == BUTTON_STOP)
2365 #else
2366 if (button_get(false) == BUTTON_OFF)
2367 #endif
2368 break;
2370 control_file = playlist->indices[index] & PLAYLIST_INSERT_TYPE_MASK;
2371 queue = playlist->indices[index] & PLAYLIST_QUEUE_MASK;
2372 seek = playlist->indices[index] & PLAYLIST_SEEK_MASK;
2374 /* Don't save queued files */
2375 if (!queue)
2377 if (get_filename(playlist, seek, control_file, tmp_buf,
2378 MAX_PATH+1) < 0)
2380 result = -1;
2381 break;
2384 if (fprintf(fd, "%s\n", tmp_buf) < 0)
2386 splash(HZ*2, true, str(LANG_PLAYLIST_CONTROL_UPDATE_ERROR));
2387 result = -1;
2388 break;
2391 count++;
2393 if ((count % PLAYLIST_DISPLAY_COUNT) == 0)
2394 display_playlist_count(count, str(LANG_PLAYLIST_SAVE_COUNT));
2396 yield();
2399 index = (index+1)%playlist->amount;
2402 display_playlist_count(count, str(LANG_PLAYLIST_SAVE_COUNT));
2404 close(fd);
2406 return result;