Patch #1352575 - Shorten codec from the ffmpeg project. Rockbox implementation by...
[Rockbox.git] / apps / playlist.c
blobc43174b0b6fcd6241b65351a8fae6817c1ca6b7a
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 "audio.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 #include "abrepeat.h"
89 #ifdef HAVE_LCD_BITMAP
90 #include "icons.h"
91 #include "widgets.h"
92 #endif
94 #include "lang.h"
95 #include "talk.h"
96 #include "splash.h"
98 #define PLAYLIST_CONTROL_FILE ROCKBOX_DIR "/.playlist_control"
99 #define PLAYLIST_CONTROL_FILE_VERSION 2
102 Each playlist index has a flag associated with it which identifies what
103 type of track it is. These flags are stored in the 4 high order bits of
104 the index.
106 NOTE: This limits the playlist file size to a max of 256M.
108 Bits 31-30:
109 00 = Playlist track
110 01 = Track was prepended into playlist
111 10 = Track was inserted into playlist
112 11 = Track was appended into playlist
113 Bit 29:
114 0 = Added track
115 1 = Queued track
116 Bit 28:
117 0 = Track entry is valid
118 1 = Track does not exist on disk and should be skipped
120 #define PLAYLIST_SEEK_MASK 0x0FFFFFFF
121 #define PLAYLIST_INSERT_TYPE_MASK 0xC0000000
122 #define PLAYLIST_QUEUE_MASK 0x20000000
124 #define PLAYLIST_INSERT_TYPE_PREPEND 0x40000000
125 #define PLAYLIST_INSERT_TYPE_INSERT 0x80000000
126 #define PLAYLIST_INSERT_TYPE_APPEND 0xC0000000
128 #define PLAYLIST_QUEUED 0x20000000
129 #define PLAYLIST_SKIPPED 0x10000000
131 #define PLAYLIST_DISPLAY_COUNT 10
133 static bool changing_dir = false;
135 static struct playlist_info current_playlist;
136 static char now_playing[MAX_PATH+1];
138 static void empty_playlist(struct playlist_info* playlist, bool resume);
139 static void new_playlist(struct playlist_info* playlist, const char *dir,
140 const char *file);
141 static void create_control(struct playlist_info* playlist);
142 static int check_control(struct playlist_info* playlist);
143 static void update_playlist_filename(struct playlist_info* playlist,
144 const char *dir, const char *file);
145 static int add_indices_to_playlist(struct playlist_info* playlist,
146 char* buffer, int buflen);
147 static int add_track_to_playlist(struct playlist_info* playlist,
148 const char *filename, int position,
149 bool queue, int seek_pos);
150 static int add_directory_to_playlist(struct playlist_info* playlist,
151 const char *dirname, int *position,
152 bool queue, int *count, bool recurse);
153 static int remove_track_from_playlist(struct playlist_info* playlist,
154 int position, bool write);
155 static int randomise_playlist(struct playlist_info* playlist,
156 unsigned int seed, bool start_current,
157 bool write);
158 static int sort_playlist(struct playlist_info* playlist, bool start_current,
159 bool write);
160 static int get_next_index(const struct playlist_info* playlist, int steps,
161 int repeat_mode);
162 static void find_and_set_playlist_index(struct playlist_info* playlist,
163 unsigned int seek);
164 static int compare(const void* p1, const void* p2);
165 static int get_filename(struct playlist_info* playlist, int seek,
166 bool control_file, char *buf, int buf_length);
167 static int get_next_directory(char *dir);
168 static int get_next_dir(char *dir, bool is_forward, bool recursion);
169 static int get_previous_directory(char *dir);
170 static int check_subdir_for_music(char *dir, char *subdir);
171 static int format_track_path(char *dest, char *src, int buf_length, int max,
172 char *dir);
173 static void display_playlist_count(int count, const char *fmt);
174 static void display_buffer_full(void);
175 static int flush_pending_control(struct playlist_info* playlist);
176 static int rotate_index(const struct playlist_info* playlist, int index);
179 * remove any files and indices associated with the playlist
181 static void empty_playlist(struct playlist_info* playlist, bool resume)
183 playlist->filename[0] = '\0';
185 if(playlist->fd >= 0)
186 /* If there is an already open playlist, close it. */
187 close(playlist->fd);
188 playlist->fd = -1;
190 if(playlist->control_fd >= 0)
191 close(playlist->control_fd);
192 playlist->control_fd = -1;
193 playlist->control_created = false;
195 playlist->in_ram = false;
197 if (playlist->buffer)
198 playlist->buffer[0] = 0;
200 playlist->buffer_end_pos = 0;
202 playlist->index = 0;
203 playlist->first_index = 0;
204 playlist->amount = 0;
205 playlist->last_insert_pos = -1;
206 playlist->seed = 0;
207 playlist->shuffle_modified = false;
208 playlist->deleted = false;
209 playlist->num_inserted_tracks = 0;
210 playlist->shuffle_flush = false;
212 if (!resume && playlist->current)
214 /* start with fresh playlist control file when starting new
215 playlist */
216 create_control(playlist);
218 /* Reset resume settings */
219 global_settings.resume_first_index = 0;
220 global_settings.resume_seed = -1;
225 * Initialize a new playlist for viewing/editing/playing. dir is the
226 * directory where the playlist is located and file is the filename.
228 static void new_playlist(struct playlist_info* playlist, const char *dir,
229 const char *file)
231 empty_playlist(playlist, false);
233 if (!file)
235 file = "";
237 if (dir && playlist->current) /* !current cannot be in_ram */
238 playlist->in_ram = true;
239 else
240 dir = ""; /* empty playlist */
243 update_playlist_filename(playlist, dir, file);
245 if (playlist->control_fd >= 0)
247 if (fdprintf(playlist->control_fd, "P:%d:%s:%s\n",
248 PLAYLIST_CONTROL_FILE_VERSION, dir, file) > 0)
249 fsync(playlist->control_fd);
250 else
251 gui_syncsplash(HZ*2, true, str(LANG_PLAYLIST_CONTROL_UPDATE_ERROR));
256 * create control file for playlist
258 static void create_control(struct playlist_info* playlist)
260 playlist->control_fd = open(playlist->control_filename,
261 O_CREAT|O_RDWR|O_TRUNC);
262 if (playlist->control_fd < 0)
264 if (check_rockboxdir())
266 gui_syncsplash(HZ*2, true, "%s (%d)",
267 str(LANG_PLAYLIST_CONTROL_ACCESS_ERROR),
268 playlist->control_fd);
270 playlist->control_created = false;
272 else
274 playlist->control_created = true;
279 * validate the control file. This may include creating/initializing it if
280 * necessary;
282 static int check_control(struct playlist_info* playlist)
284 if (!playlist->control_created)
286 create_control(playlist);
288 if (playlist->control_fd >= 0)
290 char* dir = playlist->filename;
291 char* file = playlist->filename+playlist->dirlen;
292 char c = playlist->filename[playlist->dirlen-1];
294 playlist->filename[playlist->dirlen-1] = '\0';
296 if (fdprintf(playlist->control_fd, "P:%d:%s:%s\n",
297 PLAYLIST_CONTROL_FILE_VERSION, dir, file) > 0)
298 fsync(playlist->control_fd);
299 else
300 gui_syncsplash(HZ*2, true, str(LANG_PLAYLIST_CONTROL_UPDATE_ERROR));
302 playlist->filename[playlist->dirlen-1] = c;
306 if (playlist->control_fd < 0)
307 return -1;
309 return 0;
313 * store directory and name of playlist file
315 static void update_playlist_filename(struct playlist_info* playlist,
316 const char *dir, const char *file)
318 char *sep="";
319 int dirlen = strlen(dir);
321 /* If the dir does not end in trailing slash, we use a separator.
322 Otherwise we don't. */
323 if('/' != dir[dirlen-1])
325 sep="/";
326 dirlen++;
329 playlist->dirlen = dirlen;
331 snprintf(playlist->filename, sizeof(playlist->filename),
332 "%s%s%s", dir, sep, file);
336 * calculate track offsets within a playlist file
338 static int add_indices_to_playlist(struct playlist_info* playlist,
339 char* buffer, int buflen)
341 unsigned int nread;
342 unsigned int i = 0;
343 unsigned int count = 0;
344 bool store_index;
345 unsigned char *p;
347 if(-1 == playlist->fd)
348 playlist->fd = open(playlist->filename, O_RDONLY);
349 if(playlist->fd < 0)
350 return -1; /* failure */
352 #ifdef HAVE_LCD_BITMAP
353 if(global_settings.statusbar)
354 lcd_setmargins(0, STATUSBAR_HEIGHT);
355 else
356 lcd_setmargins(0, 0);
357 #endif
359 gui_syncsplash(0, true, str(LANG_PLAYLIST_LOAD));
361 if (!buffer)
363 /* use mp3 buffer for maximum load speed */
364 audio_stop();
365 talk_buffer_steal(); /* we use the mp3 buffer, need to tell */
367 buffer = audiobuf;
368 buflen = (audiobufend - audiobuf);
371 store_index = true;
373 while(1)
375 nread = read(playlist->fd, buffer, buflen);
376 /* Terminate on EOF */
377 if(nread <= 0)
378 break;
380 p = buffer;
382 for(count=0; count < nread; count++,p++) {
384 /* Are we on a new line? */
385 if((*p == '\n') || (*p == '\r'))
387 store_index = true;
389 else if(store_index)
391 store_index = false;
393 if(*p != '#')
395 /* Store a new entry */
396 playlist->indices[ playlist->amount ] = i+count;
397 playlist->amount++;
398 if ( playlist->amount >= playlist->max_playlist_size ) {
399 display_buffer_full();
400 return -1;
406 i+= count;
409 return 0;
413 * Add track to playlist at specified position. There are five special
414 * positions that can be specified:
415 * PLAYLIST_PREPEND - Add track at beginning of playlist
416 * PLAYLIST_INSERT - Add track after current song. NOTE: If
417 * there are already inserted tracks then track
418 * is added to the end of the insertion list
419 * PLAYLIST_INSERT_FIRST - Add track immediately after current song, no
420 * matter what other tracks have been inserted
421 * PLAYLIST_INSERT_LAST - Add track to end of playlist
422 * PLAYLIST_INSERT_SHUFFLED - Add track at some random point between the
423 * current playing track and end of playlist
425 static int add_track_to_playlist(struct playlist_info* playlist,
426 const char *filename, int position,
427 bool queue, int seek_pos)
429 int insert_position = position;
430 unsigned long flags = PLAYLIST_INSERT_TYPE_INSERT;
431 int i;
433 if (playlist->amount >= playlist->max_playlist_size)
435 display_buffer_full();
436 return -1;
439 switch (position)
441 case PLAYLIST_PREPEND:
442 insert_position = playlist->first_index;
443 flags = PLAYLIST_INSERT_TYPE_PREPEND;
444 break;
445 case PLAYLIST_INSERT:
446 /* if there are already inserted tracks then add track to end of
447 insertion list else add after current playing track */
448 if (playlist->last_insert_pos >= 0 &&
449 playlist->last_insert_pos < playlist->amount &&
450 (playlist->indices[playlist->last_insert_pos]&
451 PLAYLIST_INSERT_TYPE_MASK) == PLAYLIST_INSERT_TYPE_INSERT)
452 position = insert_position = playlist->last_insert_pos+1;
453 else if (playlist->amount > 0)
454 position = insert_position = playlist->index + 1;
455 else
456 position = insert_position = 0;
458 playlist->last_insert_pos = position;
459 break;
460 case PLAYLIST_INSERT_FIRST:
461 if (playlist->amount > 0)
462 position = insert_position = playlist->index + 1;
463 else
464 position = insert_position = 0;
466 if (playlist->last_insert_pos < 0)
467 playlist->last_insert_pos = position;
468 break;
469 case PLAYLIST_INSERT_LAST:
470 if (playlist->first_index > 0)
471 insert_position = playlist->first_index;
472 else
473 insert_position = playlist->amount;
475 flags = PLAYLIST_INSERT_TYPE_APPEND;
476 break;
477 case PLAYLIST_INSERT_SHUFFLED:
479 int offset;
480 int n = playlist->amount -
481 rotate_index(playlist, playlist->index);
483 if (n > 0)
484 offset = rand() % n;
485 else
486 offset = 0;
488 position = playlist->index + offset + 1;
489 if (position >= playlist->amount)
490 position -= playlist->amount;
492 insert_position = position;
493 break;
497 if (queue)
498 flags |= PLAYLIST_QUEUED;
500 /* shift indices so that track can be added */
501 for (i=playlist->amount; i>insert_position; i--)
502 playlist->indices[i] = playlist->indices[i-1];
504 /* update stored indices if needed */
505 if (playlist->amount > 0 && insert_position <= playlist->index)
506 playlist->index++;
508 if (playlist->amount > 0 && insert_position <= playlist->first_index &&
509 position != PLAYLIST_PREPEND)
511 playlist->first_index++;
513 if (seek_pos < 0 && playlist->current)
515 global_settings.resume_first_index = playlist->first_index;
516 settings_save();
520 if (insert_position < playlist->last_insert_pos ||
521 (insert_position == playlist->last_insert_pos && position < 0))
522 playlist->last_insert_pos++;
524 if (seek_pos < 0 && playlist->control_fd >= 0)
526 int result = -1;
528 if (flush_pending_control(playlist) < 0)
529 return -1;
531 mutex_lock(&playlist->control_mutex);
533 if (lseek(playlist->control_fd, 0, SEEK_END) >= 0)
535 if (fdprintf(playlist->control_fd, "%c:%d:%d:", (queue?'Q':'A'),
536 position, playlist->last_insert_pos) > 0)
538 /* save the position in file where track name is written */
539 seek_pos = lseek(playlist->control_fd, 0, SEEK_CUR);
541 if (fdprintf(playlist->control_fd, "%s\n", filename) > 0)
542 result = 0;
546 mutex_unlock(&playlist->control_mutex);
548 if (result < 0)
550 gui_syncsplash(HZ*2, true, str(LANG_PLAYLIST_CONTROL_UPDATE_ERROR));
551 return result;
555 playlist->indices[insert_position] = flags | seek_pos;
557 playlist->amount++;
558 playlist->num_inserted_tracks++;
560 return insert_position;
564 * Insert directory into playlist. May be called recursively.
566 static int add_directory_to_playlist(struct playlist_info* playlist,
567 const char *dirname, int *position,
568 bool queue, int *count, bool recurse)
570 char buf[MAX_PATH+1];
571 char *count_str;
572 int result = 0;
573 int num_files = 0;
574 int i;
575 int dirfilter = global_settings.dirfilter;
576 struct entry *files;
577 struct tree_context* tc = tree_get_context();
579 /* use the tree browser dircache to load files */
580 global_settings.dirfilter = SHOW_ALL;
582 if (ft_load(tc, dirname) < 0)
584 gui_syncsplash(HZ*2, true, str(LANG_PLAYLIST_DIRECTORY_ACCESS_ERROR));
585 global_settings.dirfilter = dirfilter;
586 return -1;
589 files = (struct entry*) tc->dircache;
590 num_files = tc->filesindir;
592 /* we've overwritten the dircache so tree browser will need to be
593 reloaded */
594 reload_directory();
596 if (queue)
597 count_str = str(LANG_PLAYLIST_QUEUE_COUNT);
598 else
599 count_str = str(LANG_PLAYLIST_INSERT_COUNT);
601 for (i=0; i<num_files; i++)
603 /* user abort */
604 if (button_get(false) == SETTINGS_CANCEL)
606 result = -1;
607 break;
610 if (files[i].attr & ATTR_DIRECTORY)
612 if (recurse)
614 /* recursively add directories */
615 snprintf(buf, sizeof(buf), "%s/%s", dirname, files[i].name);
616 result = add_directory_to_playlist(playlist, buf, position,
617 queue, count, recurse);
618 if (result < 0)
619 break;
621 /* we now need to reload our current directory */
622 if(ft_load(tc, dirname) < 0)
624 result = -1;
625 break;
628 files = (struct entry*) tc->dircache;
629 num_files = tc->filesindir;
630 if (!num_files)
632 result = -1;
633 break;
636 else
637 continue;
639 else if ((files[i].attr & TREE_ATTR_MASK) == TREE_ATTR_MPA)
641 int insert_pos;
643 snprintf(buf, sizeof(buf), "%s/%s", dirname, files[i].name);
645 insert_pos = add_track_to_playlist(playlist, buf, *position,
646 queue, -1);
647 if (insert_pos < 0)
649 result = -1;
650 break;
653 (*count)++;
655 /* Make sure tracks are inserted in correct order if user requests
656 INSERT_FIRST */
657 if (*position == PLAYLIST_INSERT_FIRST || *position >= 0)
658 *position = insert_pos + 1;
660 if ((*count%PLAYLIST_DISPLAY_COUNT) == 0)
662 display_playlist_count(*count, count_str);
664 if (*count == PLAYLIST_DISPLAY_COUNT &&
665 (audio_status() & AUDIO_STATUS_PLAY))
666 audio_flush_and_reload_tracks();
669 /* let the other threads work */
670 yield();
674 /* restore dirfilter */
675 global_settings.dirfilter = dirfilter;
677 return result;
681 * remove track at specified position
683 static int remove_track_from_playlist(struct playlist_info* playlist,
684 int position, bool write)
686 int i;
687 bool inserted;
689 if (playlist->amount <= 0)
690 return -1;
692 inserted = playlist->indices[position] & PLAYLIST_INSERT_TYPE_MASK;
694 /* shift indices now that track has been removed */
695 for (i=position; i<playlist->amount; i++)
696 playlist->indices[i] = playlist->indices[i+1];
698 playlist->amount--;
700 if (inserted)
701 playlist->num_inserted_tracks--;
702 else
703 playlist->deleted = true;
705 /* update stored indices if needed */
706 if (position < playlist->index)
707 playlist->index--;
709 if (position < playlist->first_index)
711 playlist->first_index--;
713 if (write)
715 global_settings.resume_first_index = playlist->first_index;
716 settings_save();
720 if (position <= playlist->last_insert_pos)
721 playlist->last_insert_pos--;
723 if (write && playlist->control_fd >= 0)
725 int result = -1;
727 if (flush_pending_control(playlist) < 0)
728 return -1;
730 mutex_lock(&playlist->control_mutex);
732 if (lseek(playlist->control_fd, 0, SEEK_END) >= 0)
734 if (fdprintf(playlist->control_fd, "D:%d\n", position) > 0)
736 fsync(playlist->control_fd);
737 result = 0;
741 mutex_unlock(&playlist->control_mutex);
743 if (result < 0)
745 gui_syncsplash(HZ*2, true, str(LANG_PLAYLIST_CONTROL_UPDATE_ERROR));
746 return result;
750 return 0;
754 * randomly rearrange the array of indices for the playlist. If start_current
755 * is true then update the index to the new index of the current playing track
757 static int randomise_playlist(struct playlist_info* playlist,
758 unsigned int seed, bool start_current,
759 bool write)
761 int count;
762 int candidate;
763 int store;
764 unsigned int current = playlist->indices[playlist->index];
766 /* seed 0 is used to identify sorted playlist for resume purposes */
767 if (seed == 0)
768 seed = 1;
770 /* seed with the given seed */
771 srand(seed);
773 /* randomise entire indices list */
774 for(count = playlist->amount - 1; count >= 0; count--)
776 /* the rand is from 0 to RAND_MAX, so adjust to our value range */
777 candidate = rand() % (count + 1);
779 /* now swap the values at the 'count' and 'candidate' positions */
780 store = playlist->indices[candidate];
781 playlist->indices[candidate] = playlist->indices[count];
782 playlist->indices[count] = store;
785 if (start_current)
786 find_and_set_playlist_index(playlist, current);
788 /* indices have been moved so last insert position is no longer valid */
789 playlist->last_insert_pos = -1;
791 playlist->seed = seed;
792 if (playlist->num_inserted_tracks > 0 || playlist->deleted)
793 playlist->shuffle_modified = true;
795 if (write)
797 /* Don't write to disk immediately. Instead, save in settings and
798 only flush if playlist is modified (insertion/deletion) */
799 playlist->shuffle_flush = true;
800 global_settings.resume_seed = seed;
801 settings_save();
804 return 0;
808 * Sort the array of indices for the playlist. If start_current is true then
809 * set the index to the new index of the current song.
811 static int sort_playlist(struct playlist_info* playlist, bool start_current,
812 bool write)
814 unsigned int current = playlist->indices[playlist->index];
816 if (playlist->amount > 0)
817 qsort(playlist->indices, playlist->amount,
818 sizeof(playlist->indices[0]), compare);
820 if (start_current)
821 find_and_set_playlist_index(playlist, current);
823 /* indices have been moved so last insert position is no longer valid */
824 playlist->last_insert_pos = -1;
826 if (!playlist->num_inserted_tracks && !playlist->deleted)
827 playlist->shuffle_modified = false;
828 if (write && playlist->control_fd >= 0)
830 /* Don't write to disk immediately. Instead, save in settings and
831 only flush if playlist is modified (insertion/deletion) */
832 playlist->shuffle_flush = true;
833 global_settings.resume_seed = 0;
834 settings_save();
837 return 0;
840 /* Marks the index of the track to be skipped that is "steps" away from
841 * current playing track.
843 void playlist_skip_entry(struct playlist_info *playlist, int steps)
845 int index;
847 if (playlist == NULL)
848 playlist = &current_playlist;
850 index = rotate_index(playlist, playlist->index);
851 index += steps;
852 if (index < 0 || index >= playlist->amount)
853 return ;
855 index = (index+playlist->first_index) % playlist->amount;
856 playlist->indices[index] |= PLAYLIST_SKIPPED;
859 /* Calculate how many steps we have to really step when skipping entries
860 * marked as bad.
862 static int calculate_step_count(const struct playlist_info *playlist, int steps)
864 int i, count, direction;
865 int index;
866 int stepped_count = 0;
868 if (steps < 0)
870 direction = -1;
871 count = -steps;
873 else
875 direction = 1;
876 count = steps;
879 index = playlist->index;
880 i = 0;
881 while (i < count)
883 index += direction;
884 /* Boundary check */
885 if (index < 0)
886 index += playlist->amount;
887 if (index >= playlist->amount)
888 index -= playlist->amount;
890 /* Check if we found a bad entry. */
891 if (playlist->indices[index] & PLAYLIST_SKIPPED)
893 steps += direction;
894 /* Are all entries bad? */
895 if (stepped_count++ > playlist->amount)
896 break ;
898 else
899 i++;
902 return steps;
907 * returns the index of the track that is "steps" away from current playing
908 * track.
910 static int get_next_index(const struct playlist_info* playlist, int steps,
911 int repeat_mode)
913 int current_index = playlist->index;
914 int next_index = -1;
916 if (playlist->amount <= 0)
917 return -1;
919 if (repeat_mode == -1)
920 repeat_mode = global_settings.repeat_mode;
922 if (repeat_mode == REPEAT_SHUFFLE &&
923 (!global_settings.playlist_shuffle || playlist->amount <= 1))
924 repeat_mode = REPEAT_ALL;
926 steps = calculate_step_count(playlist, steps);
927 switch (repeat_mode)
929 case REPEAT_SHUFFLE:
930 /* Treat repeat shuffle just like repeat off. At end of playlist,
931 play will be resumed in playlist_next() */
932 case REPEAT_OFF:
934 current_index = rotate_index(playlist, current_index);
935 next_index = current_index+steps;
936 if ((next_index < 0) || (next_index >= playlist->amount))
937 next_index = -1;
938 else
939 next_index = (next_index+playlist->first_index) %
940 playlist->amount;
942 break;
945 case REPEAT_ONE:
946 #ifdef AB_REPEAT_ENABLE
947 case REPEAT_AB:
948 #endif
949 next_index = current_index;
950 break;
952 case REPEAT_ALL:
953 default:
955 next_index = (current_index+steps) % playlist->amount;
956 while (next_index < 0)
957 next_index += playlist->amount;
959 if (steps >= playlist->amount)
961 int i, index;
963 index = next_index;
964 next_index = -1;
966 /* second time around so skip the queued files */
967 for (i=0; i<playlist->amount; i++)
969 if (playlist->indices[index] & PLAYLIST_QUEUE_MASK)
970 index = (index+1) % playlist->amount;
971 else
973 next_index = index;
974 break;
978 break;
982 /* No luck if the whole playlist was bad. */
983 if (playlist->indices[next_index] & PLAYLIST_SKIPPED)
984 return -1;
986 return next_index;
990 * Search for the seek track and set appropriate indices. Used after shuffle
991 * to make sure the current index is still pointing to correct track.
993 static void find_and_set_playlist_index(struct playlist_info* playlist,
994 unsigned int seek)
996 int i;
998 /* Set the index to the current song */
999 for (i=0; i<playlist->amount; i++)
1001 if (playlist->indices[i] == seek)
1003 playlist->index = playlist->first_index = i;
1005 if (playlist->current)
1007 global_settings.resume_first_index = i;
1008 settings_save();
1011 break;
1017 * used to sort track indices. Sort order is as follows:
1018 * 1. Prepended tracks (in prepend order)
1019 * 2. Playlist/directory tracks (in playlist order)
1020 * 3. Inserted/Appended tracks (in insert order)
1022 static int compare(const void* p1, const void* p2)
1024 unsigned long* e1 = (unsigned long*) p1;
1025 unsigned long* e2 = (unsigned long*) p2;
1026 unsigned long flags1 = *e1 & PLAYLIST_INSERT_TYPE_MASK;
1027 unsigned long flags2 = *e2 & PLAYLIST_INSERT_TYPE_MASK;
1029 if (flags1 == flags2)
1030 return (*e1 & PLAYLIST_SEEK_MASK) - (*e2 & PLAYLIST_SEEK_MASK);
1031 else if (flags1 == PLAYLIST_INSERT_TYPE_PREPEND ||
1032 flags2 == PLAYLIST_INSERT_TYPE_APPEND)
1033 return -1;
1034 else if (flags1 == PLAYLIST_INSERT_TYPE_APPEND ||
1035 flags2 == PLAYLIST_INSERT_TYPE_PREPEND)
1036 return 1;
1037 else if (flags1 && flags2)
1038 return (*e1 & PLAYLIST_SEEK_MASK) - (*e2 & PLAYLIST_SEEK_MASK);
1039 else
1040 return *e1 - *e2;
1044 * gets pathname for track at seek index
1046 static int get_filename(struct playlist_info* playlist, int seek,
1047 bool control_file, char *buf, int buf_length)
1049 int fd;
1050 int max = -1;
1051 char tmp_buf[MAX_PATH+1];
1052 char dir_buf[MAX_PATH+1];
1054 if (buf_length > MAX_PATH+1)
1055 buf_length = MAX_PATH+1;
1057 if (playlist->in_ram && !control_file)
1059 strncpy(tmp_buf, &playlist->buffer[seek], sizeof(tmp_buf));
1060 tmp_buf[MAX_PATH] = '\0';
1061 max = strlen(tmp_buf) + 1;
1063 else
1065 if (control_file)
1066 fd = playlist->control_fd;
1067 else
1069 if(-1 == playlist->fd)
1070 playlist->fd = open(playlist->filename, O_RDONLY);
1072 fd = playlist->fd;
1075 if(-1 != fd)
1077 if (control_file)
1078 mutex_lock(&playlist->control_mutex);
1080 if (lseek(fd, seek, SEEK_SET) != seek)
1081 max = -1;
1082 else
1083 max = read(fd, tmp_buf, buf_length);
1085 if (control_file)
1086 mutex_unlock(&playlist->control_mutex);
1089 if (max < 0)
1091 if (control_file)
1092 gui_syncsplash(HZ*2, true, str(LANG_PLAYLIST_CONTROL_ACCESS_ERROR));
1093 else
1094 gui_syncsplash(HZ*2, true, str(LANG_PLAYLIST_ACCESS_ERROR));
1096 return max;
1100 strncpy(dir_buf, playlist->filename, playlist->dirlen-1);
1101 dir_buf[playlist->dirlen-1] = 0;
1103 return (format_track_path(buf, tmp_buf, buf_length, max, dir_buf));
1106 static int get_next_directory(char *dir){
1107 return get_next_dir(dir,true,false);
1110 static int get_previous_directory(char *dir){
1111 return get_next_dir(dir,false,false);
1115 * search through all the directories (starting with the current) to find
1116 * one that has tracks to play
1118 static int get_next_dir(char *dir, bool is_forward, bool recursion)
1120 struct playlist_info* playlist = &current_playlist;
1121 int result = -1;
1122 int dirfilter = global_settings.dirfilter;
1123 int sort_dir = global_settings.sort_dir;
1124 char *start_dir = NULL;
1125 bool exit = false;
1126 struct tree_context* tc = tree_get_context();
1128 if (recursion){
1129 /* start with root */
1130 dir[0] = '\0';
1132 else{
1133 /* start with current directory */
1134 strncpy(dir, playlist->filename, playlist->dirlen-1);
1135 dir[playlist->dirlen-1] = '\0';
1138 /* use the tree browser dircache to load files */
1139 global_settings.dirfilter = SHOW_ALL;
1141 /* sort in another direction if previous dir is requested */
1142 if(!is_forward){
1143 if ((global_settings.sort_dir == 0) || (global_settings.sort_dir == 3))
1144 global_settings.sort_dir = 4;
1145 else if (global_settings.sort_dir == 1)
1146 global_settings.sort_dir = 2;
1147 else if (global_settings.sort_dir == 2)
1148 global_settings.sort_dir = 1;
1149 else if (global_settings.sort_dir == 4)
1150 global_settings.sort_dir = 0;
1153 while (!exit)
1155 struct entry *files;
1156 int num_files = 0;
1157 int i;
1159 if (ft_load(tc, (dir[0]=='\0')?"/":dir) < 0)
1161 gui_syncsplash(HZ*2, true, str(LANG_PLAYLIST_DIRECTORY_ACCESS_ERROR));
1162 exit = true;
1163 result = -1;
1164 break;
1167 files = (struct entry*) tc->dircache;
1168 num_files = tc->filesindir;
1170 for (i=0; i<num_files; i++)
1172 /* user abort */
1173 if (button_get(false) == SETTINGS_CANCEL)
1175 result = -1;
1176 exit = true;
1177 break;
1180 if (files[i].attr & ATTR_DIRECTORY)
1182 if (!start_dir)
1184 result = check_subdir_for_music(dir, files[i].name);
1185 if (result != -1)
1187 exit = true;
1188 break;
1191 else if (!strcmp(start_dir, files[i].name))
1192 start_dir = NULL;
1196 if (!exit)
1198 /* move down to parent directory. current directory name is
1199 stored as the starting point for the search in parent */
1200 start_dir = strrchr(dir, '/');
1201 if (start_dir)
1203 *start_dir = '\0';
1204 start_dir++;
1206 else
1207 break;
1211 /* we've overwritten the dircache so tree browser will need to be
1212 reloaded */
1213 reload_directory();
1215 /* restore dirfilter & sort_dir */
1216 global_settings.dirfilter = dirfilter;
1217 global_settings.sort_dir = sort_dir;
1219 /* special case if nothing found: try start searching again from root */
1220 if (result == -1 && !recursion){
1221 result = get_next_dir(dir,is_forward, true);
1224 return result;
1228 * Checks if there are any music files in the dir or any of its
1229 * subdirectories. May be called recursively.
1231 static int check_subdir_for_music(char *dir, char *subdir)
1233 int result = -1;
1234 int dirlen = strlen(dir);
1235 int num_files = 0;
1236 int i;
1237 struct entry *files;
1238 bool has_music = false;
1239 bool has_subdir = false;
1240 struct tree_context* tc = tree_get_context();
1242 snprintf(dir+dirlen, MAX_PATH-dirlen, "/%s", subdir);
1244 if (ft_load(tc, dir) < 0)
1246 gui_syncsplash(HZ*2, true, str(LANG_PLAYLIST_DIRECTORY_ACCESS_ERROR));
1247 return -2;
1250 files = (struct entry*) tc->dircache;
1251 num_files = tc->filesindir;
1253 for (i=0; i<num_files; i++)
1255 if (files[i].attr & ATTR_DIRECTORY)
1256 has_subdir = true;
1257 else if ((files[i].attr & TREE_ATTR_MASK) == TREE_ATTR_MPA)
1259 has_music = true;
1260 break;
1264 if (has_music)
1265 return 0;
1267 if (has_subdir)
1269 for (i=0; i<num_files; i++)
1271 if (button_get(false) == SETTINGS_CANCEL)
1273 result = -2;
1274 break;
1277 if (files[i].attr & ATTR_DIRECTORY)
1279 result = check_subdir_for_music(dir, files[i].name);
1280 if (!result)
1281 break;
1286 if (result < 0)
1288 if (dirlen)
1290 dir[dirlen] = '\0';
1292 else
1294 strcpy(dir, "/");
1297 /* we now need to reload our current directory */
1298 if(ft_load(tc, dir) < 0)
1299 gui_syncsplash(HZ*2, true,
1300 str(LANG_PLAYLIST_DIRECTORY_ACCESS_ERROR));
1303 return result;
1307 * Returns absolute path of track
1309 static int format_track_path(char *dest, char *src, int buf_length, int max,
1310 char *dir)
1312 int i = 0;
1313 int j;
1314 char *temp_ptr;
1316 /* Zero-terminate the file name */
1317 while((src[i] != '\n') &&
1318 (src[i] != '\r') &&
1319 (i < max))
1320 i++;
1322 /* Now work back killing white space */
1323 while((src[i-1] == ' ') ||
1324 (src[i-1] == '\t'))
1325 i--;
1327 src[i]=0;
1329 /* replace backslashes with forward slashes */
1330 for ( j=0; j<i; j++ )
1331 if ( src[j] == '\\' )
1332 src[j] = '/';
1334 if('/' == src[0])
1336 strncpy(dest, src, buf_length);
1338 else
1340 /* handle dos style drive letter */
1341 if (':' == src[1])
1342 strncpy(dest, &src[2], buf_length);
1343 else if (!strncmp(src, "../", 3))
1345 /* handle relative paths */
1346 i=3;
1347 while(!strncmp(&src[i], "../", 3))
1348 i += 3;
1349 for (j=0; j<i/3; j++) {
1350 temp_ptr = strrchr(dir, '/');
1351 if (temp_ptr)
1352 *temp_ptr = '\0';
1353 else
1354 break;
1356 snprintf(dest, buf_length, "%s/%s", dir, &src[i]);
1358 else if ( '.' == src[0] && '/' == src[1] ) {
1359 snprintf(dest, buf_length, "%s/%s", dir, &src[2]);
1361 else {
1362 snprintf(dest, buf_length, "%s/%s", dir, src);
1366 return 0;
1370 * Display splash message showing progress of playlist/directory insertion or
1371 * save.
1373 static void display_playlist_count(int count, const char *fmt)
1375 lcd_clear_display();
1377 #ifdef HAVE_LCD_BITMAP
1378 if(global_settings.statusbar)
1379 lcd_setmargins(0, STATUSBAR_HEIGHT);
1380 else
1381 lcd_setmargins(0, 0);
1382 #endif
1384 gui_syncsplash(0, true, fmt, count,
1385 #if CONFIG_KEYPAD == PLAYER_PAD
1386 str(LANG_STOP_ABORT)
1387 #else
1388 str(LANG_OFF_ABORT)
1389 #endif
1394 * Display buffer full message
1396 static void display_buffer_full(void)
1398 gui_syncsplash(HZ*2, true, "%s %s",
1399 str(LANG_PLAYINDICES_PLAYLIST),
1400 str(LANG_PLAYINDICES_BUFFER));
1404 * Flush any pending control commands to disk. Called when playlist is being
1405 * modified. Returns 0 on success and -1 on failure.
1407 static int flush_pending_control(struct playlist_info* playlist)
1409 int result = 0;
1411 if (playlist->shuffle_flush && global_settings.resume_seed >= 0)
1413 /* pending shuffle */
1414 mutex_lock(&playlist->control_mutex);
1416 if (lseek(playlist->control_fd, 0, SEEK_END) >= 0)
1418 if (global_settings.resume_seed == 0)
1419 result = fdprintf(playlist->control_fd, "U:%d\n",
1420 playlist->first_index);
1421 else
1422 result = fdprintf(playlist->control_fd, "S:%d:%d\n",
1423 global_settings.resume_seed, playlist->first_index);
1425 if (result > 0)
1427 fsync(playlist->control_fd);
1429 playlist->shuffle_flush = false;
1430 global_settings.resume_seed = -1;
1431 settings_save();
1433 result = 0;
1435 else
1436 result = -1;
1438 else
1439 result = -1;
1441 mutex_unlock(&playlist->control_mutex);
1443 if (result < 0)
1445 gui_syncsplash(HZ*2, true, str(LANG_PLAYLIST_CONTROL_UPDATE_ERROR));
1446 return result;
1450 return result;
1454 * Rotate indices such that first_index is index 0
1456 static int rotate_index(const struct playlist_info* playlist, int index)
1458 index -= playlist->first_index;
1459 if (index < 0)
1460 index += playlist->amount;
1462 return index;
1466 * Initialize playlist entries at startup
1468 void playlist_init(void)
1470 struct playlist_info* playlist = &current_playlist;
1472 playlist->current = true;
1473 snprintf(playlist->control_filename, sizeof(playlist->control_filename),
1474 "%s", PLAYLIST_CONTROL_FILE);
1475 playlist->fd = -1;
1476 playlist->control_fd = -1;
1477 playlist->max_playlist_size = global_settings.max_files_in_playlist;
1478 playlist->indices = buffer_alloc(
1479 playlist->max_playlist_size * sizeof(int));
1480 playlist->buffer_size =
1481 AVERAGE_FILENAME_LENGTH * global_settings.max_files_in_dir;
1482 playlist->buffer = buffer_alloc(playlist->buffer_size);
1483 mutex_init(&playlist->control_mutex);
1484 empty_playlist(playlist, true);
1488 * Create new playlist
1490 int playlist_create(const char *dir, const char *file)
1492 struct playlist_info* playlist = &current_playlist;
1494 new_playlist(playlist, dir, file);
1496 if (file)
1497 /* load the playlist file */
1498 add_indices_to_playlist(playlist, NULL, 0);
1500 return 0;
1503 #define PLAYLIST_COMMAND_SIZE (MAX_PATH+12)
1506 * Restore the playlist state based on control file commands. Called to
1507 * resume playback after shutdown.
1509 int playlist_resume(void)
1511 struct playlist_info* playlist = &current_playlist;
1512 char *buffer;
1513 int buflen;
1514 int nread;
1515 int total_read = 0;
1516 int control_file_size = 0;
1517 bool first = true;
1518 bool sorted = true;
1520 enum {
1521 resume_playlist,
1522 resume_add,
1523 resume_queue,
1524 resume_delete,
1525 resume_shuffle,
1526 resume_unshuffle,
1527 resume_reset,
1528 resume_comment
1531 /* use mp3 buffer for maximum load speed */
1532 #if CONFIG_CODEC != SWCODEC
1533 talk_buffer_steal(); /* we use the mp3 buffer, need to tell */
1534 buflen = (audiobufend - audiobuf);
1535 buffer = audiobuf;
1536 #else
1537 buflen = (audiobufend - audiobuf - talk_get_bufsize());
1538 buffer = &audiobuf[talk_get_bufsize()];
1539 #endif
1541 empty_playlist(playlist, true);
1543 playlist->control_fd = open(playlist->control_filename, O_RDWR);
1544 if (playlist->control_fd < 0)
1546 gui_syncsplash(HZ*2, true, str(LANG_PLAYLIST_CONTROL_ACCESS_ERROR));
1547 return -1;
1549 playlist->control_created = true;
1551 control_file_size = filesize(playlist->control_fd);
1552 if (control_file_size <= 0)
1554 gui_syncsplash(HZ*2, true, str(LANG_PLAYLIST_CONTROL_ACCESS_ERROR));
1555 return -1;
1558 /* read a small amount first to get the header */
1559 nread = read(playlist->control_fd, buffer,
1560 PLAYLIST_COMMAND_SIZE<buflen?PLAYLIST_COMMAND_SIZE:buflen);
1561 if(nread <= 0)
1563 gui_syncsplash(HZ*2, true, str(LANG_PLAYLIST_CONTROL_ACCESS_ERROR));
1564 return -1;
1567 while (1)
1569 int result = 0;
1570 int count;
1571 int current_command = resume_comment;
1572 int last_newline = 0;
1573 int str_count = -1;
1574 bool newline = true;
1575 bool exit_loop = false;
1576 char *p = buffer;
1577 char *str1 = NULL;
1578 char *str2 = NULL;
1579 char *str3 = NULL;
1581 for(count=0; count<nread && !exit_loop; count++,p++)
1583 /* Are we on a new line? */
1584 if((*p == '\n') || (*p == '\r'))
1586 *p = '\0';
1588 /* save last_newline in case we need to load more data */
1589 last_newline = count;
1591 switch (current_command)
1593 case resume_playlist:
1595 /* str1=version str2=dir str3=file */
1596 int version;
1598 if (!str1)
1600 result = -1;
1601 exit_loop = true;
1602 break;
1605 if (!str2)
1606 str2 = "";
1608 if (!str3)
1609 str3 = "";
1611 version = atoi(str1);
1613 if (version != PLAYLIST_CONTROL_FILE_VERSION)
1614 return -1;
1616 update_playlist_filename(playlist, str2, str3);
1618 if (str3[0] != '\0')
1620 /* NOTE: add_indices_to_playlist() overwrites the
1621 audiobuf so we need to reload control file
1622 data */
1623 add_indices_to_playlist(playlist, NULL, 0);
1625 else if (str2[0] != '\0')
1627 playlist->in_ram = true;
1628 resume_directory(str2);
1631 /* load the rest of the data */
1632 first = false;
1633 exit_loop = true;
1635 break;
1637 case resume_add:
1638 case resume_queue:
1640 /* str1=position str2=last_position str3=file */
1641 int position, last_position;
1642 bool queue;
1644 if (!str1 || !str2 || !str3)
1646 result = -1;
1647 exit_loop = true;
1648 break;
1651 position = atoi(str1);
1652 last_position = atoi(str2);
1654 queue = (current_command == resume_add)?false:true;
1656 /* seek position is based on str3's position in
1657 buffer */
1658 if (add_track_to_playlist(playlist, str3, position,
1659 queue, total_read+(str3-buffer)) < 0)
1660 return -1;
1662 playlist->last_insert_pos = last_position;
1664 break;
1666 case resume_delete:
1668 /* str1=position */
1669 int position;
1671 if (!str1)
1673 result = -1;
1674 exit_loop = true;
1675 break;
1678 position = atoi(str1);
1680 if (remove_track_from_playlist(playlist, position,
1681 false) < 0)
1682 return -1;
1684 break;
1686 case resume_shuffle:
1688 /* str1=seed str2=first_index */
1689 int seed;
1691 if (!str1 || !str2)
1693 result = -1;
1694 exit_loop = true;
1695 break;
1698 if (!sorted)
1700 /* Always sort list before shuffling */
1701 sort_playlist(playlist, false, false);
1704 seed = atoi(str1);
1705 playlist->first_index = atoi(str2);
1707 if (randomise_playlist(playlist, seed, false,
1708 false) < 0)
1709 return -1;
1711 sorted = false;
1712 break;
1714 case resume_unshuffle:
1716 /* str1=first_index */
1717 if (!str1)
1719 result = -1;
1720 exit_loop = true;
1721 break;
1724 playlist->first_index = atoi(str1);
1726 if (sort_playlist(playlist, false, false) < 0)
1727 return -1;
1729 sorted = true;
1730 break;
1732 case resume_reset:
1734 playlist->last_insert_pos = -1;
1735 break;
1737 case resume_comment:
1738 default:
1739 break;
1742 newline = true;
1744 /* to ignore any extra newlines */
1745 current_command = resume_comment;
1747 else if(newline)
1749 newline = false;
1751 /* first non-comment line must always specify playlist */
1752 if (first && *p != 'P' && *p != '#')
1754 result = -1;
1755 exit_loop = true;
1756 break;
1759 switch (*p)
1761 case 'P':
1762 /* playlist can only be specified once */
1763 if (!first)
1765 result = -1;
1766 exit_loop = true;
1767 break;
1770 current_command = resume_playlist;
1771 break;
1772 case 'A':
1773 current_command = resume_add;
1774 break;
1775 case 'Q':
1776 current_command = resume_queue;
1777 break;
1778 case 'D':
1779 current_command = resume_delete;
1780 break;
1781 case 'S':
1782 current_command = resume_shuffle;
1783 break;
1784 case 'U':
1785 current_command = resume_unshuffle;
1786 break;
1787 case 'R':
1788 current_command = resume_reset;
1789 break;
1790 case '#':
1791 current_command = resume_comment;
1792 break;
1793 default:
1794 result = -1;
1795 exit_loop = true;
1796 break;
1799 str_count = -1;
1800 str1 = NULL;
1801 str2 = NULL;
1802 str3 = NULL;
1804 else if(current_command != resume_comment)
1806 /* all control file strings are separated with a colon.
1807 Replace the colon with 0 to get proper strings that can be
1808 used by commands above */
1809 if (*p == ':')
1811 *p = '\0';
1812 str_count++;
1814 if ((count+1) < nread)
1816 switch (str_count)
1818 case 0:
1819 str1 = p+1;
1820 break;
1821 case 1:
1822 str2 = p+1;
1823 break;
1824 case 2:
1825 str3 = p+1;
1826 break;
1827 default:
1828 /* allow last string to contain colons */
1829 *p = ':';
1830 break;
1837 if (result < 0)
1839 gui_syncsplash(HZ*2, true, str(LANG_PLAYLIST_CONTROL_INVALID));
1840 return result;
1843 if (!newline || (exit_loop && count<nread))
1845 if ((total_read + count) >= control_file_size)
1847 /* no newline at end of control file */
1848 gui_syncsplash(HZ*2, true, str(LANG_PLAYLIST_CONTROL_INVALID));
1849 return -1;
1852 /* We didn't end on a newline or we exited loop prematurely.
1853 Either way, re-read the remainder. */
1854 count = last_newline;
1855 lseek(playlist->control_fd, total_read+count, SEEK_SET);
1858 total_read += count;
1860 if (first)
1861 /* still looking for header */
1862 nread = read(playlist->control_fd, buffer,
1863 PLAYLIST_COMMAND_SIZE<buflen?PLAYLIST_COMMAND_SIZE:buflen);
1864 else
1865 nread = read(playlist->control_fd, buffer, buflen);
1867 /* Terminate on EOF */
1868 if(nread <= 0)
1870 if (global_settings.resume_seed >= 0)
1872 /* Apply shuffle command saved in settings */
1873 if (global_settings.resume_seed == 0)
1874 sort_playlist(playlist, false, true);
1875 else
1877 if (!sorted)
1878 sort_playlist(playlist, false, false);
1880 randomise_playlist(playlist, global_settings.resume_seed,
1881 false, true);
1884 playlist->first_index = global_settings.resume_first_index;
1887 break;
1891 return 0;
1895 * Add track to in_ram playlist. Used when playing directories.
1897 int playlist_add(const char *filename)
1899 struct playlist_info* playlist = &current_playlist;
1900 int len = strlen(filename);
1902 if((len+1 > playlist->buffer_size - playlist->buffer_end_pos) ||
1903 (playlist->amount >= playlist->max_playlist_size))
1905 display_buffer_full();
1906 return -1;
1909 playlist->indices[playlist->amount++] = playlist->buffer_end_pos;
1911 strcpy(&playlist->buffer[playlist->buffer_end_pos], filename);
1912 playlist->buffer_end_pos += len;
1913 playlist->buffer[playlist->buffer_end_pos++] = '\0';
1915 return 0;
1918 /* shuffle newly created playlist using random seed. */
1919 int playlist_shuffle(int random_seed, int start_index)
1921 struct playlist_info* playlist = &current_playlist;
1923 unsigned int seek_pos = 0;
1924 bool start_current = false;
1926 if (start_index >= 0 && global_settings.play_selected)
1928 /* store the seek position before the shuffle */
1929 seek_pos = playlist->indices[start_index];
1930 playlist->index = global_settings.resume_first_index =
1931 playlist->first_index = start_index;
1932 start_current = true;
1935 gui_syncsplash(0, true, str(LANG_PLAYLIST_SHUFFLE));
1937 randomise_playlist(playlist, random_seed, start_current, true);
1939 /* Flush shuffle command to disk */
1940 flush_pending_control(playlist);
1942 return playlist->index;
1945 /* start playing current playlist at specified index/offset */
1946 int playlist_start(int start_index, int offset)
1948 struct playlist_info* playlist = &current_playlist;
1950 playlist->index = start_index;
1951 #if CONFIG_CODEC != SWCODEC
1952 talk_buffer_steal(); /* will use the mp3 buffer */
1953 #endif
1954 audio_play(offset);
1956 return 0;
1959 /* Returns false if 'steps' is out of bounds, else true */
1960 bool playlist_check(int steps)
1962 struct playlist_info* playlist = &current_playlist;
1964 /* always allow folder navigation */
1965 if (global_settings.next_folder && playlist->in_ram)
1966 return true;
1968 int index = get_next_index(playlist, steps, -1);
1970 if (index < 0 && steps >= 0 && global_settings.repeat_mode == REPEAT_SHUFFLE)
1971 index = get_next_index(playlist, steps, REPEAT_ALL);
1973 return (index >= 0);
1976 /* get trackname of track that is "steps" away from current playing track.
1977 NULL is used to identify end of playlist */
1978 char* playlist_peek(int steps)
1980 struct playlist_info* playlist = &current_playlist;
1981 int seek;
1982 int fd;
1983 char *temp_ptr;
1984 int index;
1985 bool control_file;
1987 index = get_next_index(playlist, steps, -1);
1988 if (index < 0)
1989 return NULL;
1991 control_file = playlist->indices[index] & PLAYLIST_INSERT_TYPE_MASK;
1992 seek = playlist->indices[index] & PLAYLIST_SEEK_MASK;
1994 if (get_filename(playlist, seek, control_file, now_playing,
1995 MAX_PATH+1) < 0)
1996 return NULL;
1998 temp_ptr = now_playing;
2000 if (!playlist->in_ram || control_file)
2002 /* remove bogus dirs from beginning of path
2003 (workaround for buggy playlist creation tools) */
2004 while (temp_ptr)
2006 fd = open(temp_ptr, O_RDONLY);
2007 if (fd >= 0)
2009 close(fd);
2010 break;
2013 temp_ptr = strchr(temp_ptr+1, '/');
2016 if (!temp_ptr)
2018 /* Even though this is an invalid file, we still need to pass a
2019 file name to the caller because NULL is used to indicate end
2020 of playlist */
2021 return now_playing;
2025 return temp_ptr;
2029 * Update indices as track has changed
2031 int playlist_next(int steps)
2033 struct playlist_info* playlist = &current_playlist;
2034 int index;
2036 if ( (steps > 0)
2037 #ifdef AB_REPEAT_ENABLE
2038 && (global_settings.repeat_mode != REPEAT_AB)
2039 #endif
2040 && (global_settings.repeat_mode != REPEAT_ONE) )
2042 int i, j;
2044 /* We need to delete all the queued songs */
2045 for (i=0, j=steps; i<j; i++)
2047 index = get_next_index(playlist, i, -1);
2049 if (playlist->indices[index] & PLAYLIST_QUEUE_MASK)
2051 remove_track_from_playlist(playlist, index, true);
2052 steps--; /* one less track */
2057 index = get_next_index(playlist, steps, -1);
2059 if (index < 0)
2061 /* end of playlist... or is it */
2062 if (global_settings.repeat_mode == REPEAT_SHUFFLE &&
2063 global_settings.playlist_shuffle &&
2064 playlist->amount > 1)
2066 /* Repeat shuffle mode. Re-shuffle playlist and resume play */
2067 playlist->first_index = global_settings.resume_first_index = 0;
2068 sort_playlist(playlist, false, false);
2069 randomise_playlist(playlist, current_tick, false, true);
2070 playlist_start(0, 0);
2071 index = 0;
2073 else if (playlist->in_ram && global_settings.next_folder)
2075 char dir[MAX_PATH+1];
2077 changing_dir = true;
2078 if (steps > 0)
2080 if (!get_next_directory(dir))
2082 /* start playing next directory */
2083 if (playlist_create(dir, NULL) != -1)
2085 ft_build_playlist(tree_get_context(), 0);
2086 if (global_settings.playlist_shuffle)
2087 playlist_shuffle(current_tick, -1);
2088 playlist_start(0, 0);
2089 index = 0;
2093 else
2095 if (!get_previous_directory(dir))
2097 /* start playing previous directory */
2098 if (playlist_create(dir, NULL) != -1)
2100 ft_build_playlist(tree_get_context(), 0);
2101 if (global_settings.playlist_shuffle)
2102 playlist_shuffle(current_tick, -1);
2103 playlist_start(current_playlist.amount-1,0);
2104 index = current_playlist.amount-1;
2108 changing_dir = true;
2111 return index;
2114 playlist->index = index;
2116 if (playlist->last_insert_pos >= 0 && steps > 0)
2118 /* check to see if we've gone beyond the last inserted track */
2119 int cur = rotate_index(playlist, index);
2120 int last_pos = rotate_index(playlist, playlist->last_insert_pos);
2122 if (cur > last_pos)
2124 /* reset last inserted track */
2125 playlist->last_insert_pos = -1;
2127 if (playlist->control_fd >= 0)
2129 int result = -1;
2131 mutex_lock(&playlist->control_mutex);
2133 if (lseek(playlist->control_fd, 0, SEEK_END) >= 0)
2135 if (fdprintf(playlist->control_fd, "R\n") > 0)
2137 fsync(playlist->control_fd);
2138 result = 0;
2142 mutex_unlock(&playlist->control_mutex);
2144 if (result < 0)
2146 gui_syncsplash(HZ*2, true,
2147 str(LANG_PLAYLIST_CONTROL_UPDATE_ERROR));
2148 return result;
2154 return index;
2157 /* try playing next or previous folder */
2158 bool playlist_next_dir(int direction)
2160 char dir[MAX_PATH+1];
2161 bool result;
2162 int res;
2164 /* not to mess up real playlists */
2165 if(!current_playlist.in_ram)
2166 return false;
2168 if(changing_dir)
2169 return false;
2171 changing_dir = true;
2172 if(direction > 0)
2173 res = get_next_directory(dir);
2174 else
2175 res = get_previous_directory(dir);
2176 if (!res)
2178 if (playlist_create(dir, NULL) != -1)
2180 ft_build_playlist(tree_get_context(), 0);
2181 if (global_settings.playlist_shuffle)
2182 playlist_shuffle(current_tick, -1);
2183 playlist_start(0,0);
2184 result = true;
2186 else
2187 result = false;
2189 else
2190 result = false;
2192 changing_dir = false;
2194 return result;
2197 /* Get resume info for current playing song. If return value is -1 then
2198 settings shouldn't be saved. */
2199 int playlist_get_resume_info(int *resume_index)
2201 struct playlist_info* playlist = &current_playlist;
2203 *resume_index = playlist->index;
2205 return 0;
2208 /* Update resume info for current playing song. Returns -1 on error. */
2209 int playlist_update_resume_info(const struct mp3entry* id3)
2211 struct playlist_info* playlist = &current_playlist;
2213 if (id3)
2215 if (global_settings.resume_index != playlist->index ||
2216 global_settings.resume_offset != id3->offset)
2218 global_settings.resume_index = playlist->index;
2219 global_settings.resume_offset = id3->offset;
2220 settings_save();
2223 else
2225 global_settings.resume_index = -1;
2226 global_settings.resume_offset = -1;
2227 settings_save();
2230 return 0;
2233 /* Returns index of current playing track for display purposes. This value
2234 should not be used for resume purposes as it doesn't represent the actual
2235 index into the playlist */
2236 int playlist_get_display_index(void)
2238 struct playlist_info* playlist = &current_playlist;
2240 /* first_index should always be index 0 for display purposes */
2241 int index = rotate_index(playlist, playlist->index);
2243 return (index+1);
2246 /* returns number of tracks in current playlist */
2247 int playlist_amount(void)
2249 return playlist_amount_ex(NULL);
2253 * Create a new playlist If playlist is not NULL then we're loading a
2254 * playlist off disk for viewing/editing. The index_buffer is used to store
2255 * playlist indices (required for and only used if !current playlist). The
2256 * temp_buffer (if not NULL) is used as a scratchpad when loading indices.
2258 int playlist_create_ex(struct playlist_info* playlist,
2259 const char* dir, const char* file,
2260 void* index_buffer, int index_buffer_size,
2261 void* temp_buffer, int temp_buffer_size)
2263 if (!playlist)
2264 playlist = &current_playlist;
2265 else
2267 /* Initialize playlist structure */
2268 int r = rand() % 10;
2269 playlist->current = false;
2271 /* Use random name for control file */
2272 snprintf(playlist->control_filename, sizeof(playlist->control_filename),
2273 "%s.%d", PLAYLIST_CONTROL_FILE, r);
2274 playlist->fd = -1;
2275 playlist->control_fd = -1;
2277 if (index_buffer)
2279 int num_indices = index_buffer_size / sizeof(int);
2281 if (num_indices > global_settings.max_files_in_playlist)
2282 num_indices = global_settings.max_files_in_playlist;
2284 playlist->max_playlist_size = num_indices;
2285 playlist->indices = index_buffer;
2287 else
2289 playlist->max_playlist_size = current_playlist.max_playlist_size;
2290 playlist->indices = current_playlist.indices;
2293 playlist->buffer_size = 0;
2294 playlist->buffer = NULL;
2295 mutex_init(&playlist->control_mutex);
2298 new_playlist(playlist, dir, file);
2300 if (file)
2301 /* load the playlist file */
2302 add_indices_to_playlist(playlist, temp_buffer, temp_buffer_size);
2304 return 0;
2308 * Set the specified playlist as the current.
2309 * NOTE: You will get undefined behaviour if something is already playing so
2310 * remember to stop before calling this. Also, this call will
2311 * effectively close your playlist, making it unusable.
2313 int playlist_set_current(struct playlist_info* playlist)
2315 if (!playlist || (check_control(playlist) < 0))
2316 return -1;
2318 empty_playlist(&current_playlist, false);
2320 strncpy(current_playlist.filename, playlist->filename,
2321 sizeof(current_playlist.filename));
2323 current_playlist.fd = playlist->fd;
2325 close(playlist->control_fd);
2326 remove(current_playlist.control_filename);
2327 if (rename(playlist->control_filename,
2328 current_playlist.control_filename) < 0)
2329 return -1;
2330 current_playlist.control_fd = open(current_playlist.control_filename,
2331 O_RDWR);
2332 if (current_playlist.control_fd < 0)
2333 return -1;
2334 current_playlist.control_created = true;
2336 current_playlist.dirlen = playlist->dirlen;
2338 if (playlist->indices && playlist->indices != current_playlist.indices)
2339 memcpy(current_playlist.indices, playlist->indices,
2340 playlist->max_playlist_size*sizeof(int));
2342 current_playlist.first_index = playlist->first_index;
2343 current_playlist.amount = playlist->amount;
2344 current_playlist.last_insert_pos = playlist->last_insert_pos;
2345 current_playlist.seed = playlist->seed;
2346 current_playlist.shuffle_modified = playlist->shuffle_modified;
2347 current_playlist.deleted = playlist->deleted;
2348 current_playlist.num_inserted_tracks = playlist->num_inserted_tracks;
2349 current_playlist.shuffle_flush = playlist->shuffle_flush;
2351 return 0;
2355 * Close files and delete control file for non-current playlist.
2357 void playlist_close(struct playlist_info* playlist)
2359 if (!playlist)
2360 return;
2362 if (playlist->fd >= 0)
2363 close(playlist->fd);
2365 if (playlist->control_fd >= 0)
2366 close(playlist->control_fd);
2368 if (playlist->control_created)
2369 remove(playlist->control_filename);
2373 * Insert track into playlist at specified position (or one of the special
2374 * positions). Returns position where track was inserted or -1 if error.
2376 int playlist_insert_track(struct playlist_info* playlist,
2377 const char *filename, int position, bool queue)
2379 int result;
2381 if (!playlist)
2382 playlist = &current_playlist;
2384 if (check_control(playlist) < 0)
2386 gui_syncsplash(HZ*2, true, str(LANG_PLAYLIST_CONTROL_ACCESS_ERROR));
2387 return -1;
2390 result = add_track_to_playlist(playlist, filename, position, queue, -1);
2392 if (result != -1)
2394 mutex_lock(&playlist->control_mutex);
2395 fsync(playlist->control_fd);
2396 mutex_unlock(&playlist->control_mutex);
2398 if (audio_status() & AUDIO_STATUS_PLAY)
2399 audio_flush_and_reload_tracks();
2402 return result;
2406 * Insert all tracks from specified directory into playlist.
2408 int playlist_insert_directory(struct playlist_info* playlist,
2409 const char *dirname, int position, bool queue,
2410 bool recurse)
2412 int count = 0;
2413 int result;
2414 char *count_str;
2416 if (!playlist)
2417 playlist = &current_playlist;
2419 if (check_control(playlist) < 0)
2421 gui_syncsplash(HZ*2, true, str(LANG_PLAYLIST_CONTROL_ACCESS_ERROR));
2422 return -1;
2425 if (queue)
2426 count_str = str(LANG_PLAYLIST_QUEUE_COUNT);
2427 else
2428 count_str = str(LANG_PLAYLIST_INSERT_COUNT);
2430 display_playlist_count(count, count_str);
2432 result = add_directory_to_playlist(playlist, dirname, &position, queue,
2433 &count, recurse);
2435 mutex_lock(&playlist->control_mutex);
2436 fsync(playlist->control_fd);
2437 mutex_unlock(&playlist->control_mutex);
2439 display_playlist_count(count, count_str);
2441 if (audio_status() & AUDIO_STATUS_PLAY)
2442 audio_flush_and_reload_tracks();
2444 return result;
2448 * Insert all tracks from specified playlist into dynamic playlist.
2450 int playlist_insert_playlist(struct playlist_info* playlist, char *filename,
2451 int position, bool queue)
2453 int fd;
2454 int max;
2455 char *temp_ptr;
2456 char *dir;
2457 char *count_str;
2458 char temp_buf[MAX_PATH+1];
2459 char trackname[MAX_PATH+1];
2460 int count = 0;
2461 int result = 0;
2463 if (!playlist)
2464 playlist = &current_playlist;
2466 if (check_control(playlist) < 0)
2468 gui_syncsplash(HZ*2, true, str(LANG_PLAYLIST_CONTROL_ACCESS_ERROR));
2469 return -1;
2472 fd = open(filename, O_RDONLY);
2473 if (fd < 0)
2475 gui_syncsplash(HZ*2, true, str(LANG_PLAYLIST_ACCESS_ERROR));
2476 return -1;
2479 /* we need the directory name for formatting purposes */
2480 dir = filename;
2482 temp_ptr = strrchr(filename+1,'/');
2483 if (temp_ptr)
2484 *temp_ptr = 0;
2485 else
2486 dir = "/";
2488 if (queue)
2489 count_str = str(LANG_PLAYLIST_QUEUE_COUNT);
2490 else
2491 count_str = str(LANG_PLAYLIST_INSERT_COUNT);
2493 display_playlist_count(count, count_str);
2495 while ((max = read_line(fd, temp_buf, sizeof(temp_buf))) > 0)
2497 /* user abort */
2498 if (button_get(false) == SETTINGS_CANCEL)
2499 break;
2501 if (temp_buf[0] != '#' && temp_buf[0] != '\0')
2503 int insert_pos;
2505 /* we need to format so that relative paths are correctly
2506 handled */
2507 if (format_track_path(trackname, temp_buf, sizeof(trackname), max,
2508 dir) < 0)
2510 result = -1;
2511 break;
2514 insert_pos = add_track_to_playlist(playlist, trackname, position,
2515 queue, -1);
2517 if (insert_pos < 0)
2519 result = -1;
2520 break;
2523 /* Make sure tracks are inserted in correct order if user
2524 requests INSERT_FIRST */
2525 if (position == PLAYLIST_INSERT_FIRST || position >= 0)
2526 position = insert_pos + 1;
2528 count++;
2530 if ((count%PLAYLIST_DISPLAY_COUNT) == 0)
2532 display_playlist_count(count, count_str);
2534 if (count == PLAYLIST_DISPLAY_COUNT &&
2535 (audio_status() & AUDIO_STATUS_PLAY))
2536 audio_flush_and_reload_tracks();
2540 /* let the other threads work */
2541 yield();
2544 close(fd);
2546 mutex_lock(&playlist->control_mutex);
2547 fsync(playlist->control_fd);
2548 mutex_unlock(&playlist->control_mutex);
2550 if (temp_ptr)
2551 *temp_ptr = '/';
2553 display_playlist_count(count, count_str);
2555 if (audio_status() & AUDIO_STATUS_PLAY)
2556 audio_flush_and_reload_tracks();
2558 return result;
2562 * Delete track at specified index. If index is PLAYLIST_DELETE_CURRENT then
2563 * we want to delete the current playing track.
2565 int playlist_delete(struct playlist_info* playlist, int index)
2567 int result = 0;
2569 if (!playlist)
2570 playlist = &current_playlist;
2572 if (check_control(playlist) < 0)
2574 gui_syncsplash(HZ*2, true, str(LANG_PLAYLIST_CONTROL_ACCESS_ERROR));
2575 return -1;
2578 if (index == PLAYLIST_DELETE_CURRENT)
2579 index = playlist->index;
2581 result = remove_track_from_playlist(playlist, index, true);
2583 if (result != -1 && (audio_status() & AUDIO_STATUS_PLAY))
2584 audio_flush_and_reload_tracks();
2586 return result;
2590 * Move track at index to new_index. Tracks between the two are shifted
2591 * appropriately. Returns 0 on success and -1 on failure.
2593 int playlist_move(struct playlist_info* playlist, int index, int new_index)
2595 int result;
2596 int seek;
2597 bool control_file;
2598 bool queue;
2599 bool current = false;
2600 int r;
2601 char filename[MAX_PATH];
2603 if (!playlist)
2604 playlist = &current_playlist;
2606 if (check_control(playlist) < 0)
2608 gui_syncsplash(HZ*2, true, str(LANG_PLAYLIST_CONTROL_ACCESS_ERROR));
2609 return -1;
2612 if (index == new_index)
2613 return -1;
2615 if (index == playlist->index)
2616 /* Moving the current track */
2617 current = true;
2619 control_file = playlist->indices[index] & PLAYLIST_INSERT_TYPE_MASK;
2620 queue = playlist->indices[index] & PLAYLIST_QUEUE_MASK;
2621 seek = playlist->indices[index] & PLAYLIST_SEEK_MASK;
2623 if (get_filename(playlist, seek, control_file, filename,
2624 sizeof(filename)) < 0)
2625 return -1;
2627 /* Delete track from original position */
2628 result = remove_track_from_playlist(playlist, index, true);
2630 if (result != -1)
2632 /* We want to insert the track at the position that was specified by
2633 new_index. This may be different then new_index because of the
2634 shifting that occurred after the delete */
2635 r = rotate_index(playlist, new_index);
2637 if (r == 0)
2638 /* First index */
2639 new_index = PLAYLIST_PREPEND;
2640 else if (r == playlist->amount)
2641 /* Append */
2642 new_index = PLAYLIST_INSERT_LAST;
2643 else
2644 /* Calculate index of desired position */
2645 new_index = (r+playlist->first_index)%playlist->amount;
2647 result = add_track_to_playlist(playlist, filename, new_index, queue,
2648 -1);
2650 if (result != -1)
2652 if (current)
2654 /* Moved the current track */
2655 switch (new_index)
2657 case PLAYLIST_PREPEND:
2658 playlist->index = playlist->first_index;
2659 break;
2660 case PLAYLIST_INSERT_LAST:
2661 playlist->index = playlist->first_index - 1;
2662 if (playlist->index < 0)
2663 playlist->index += playlist->amount;
2664 break;
2665 default:
2666 playlist->index = new_index;
2667 break;
2671 mutex_lock(&playlist->control_mutex);
2672 fsync(playlist->control_fd);
2673 mutex_unlock(&playlist->control_mutex);
2675 if (audio_status() & AUDIO_STATUS_PLAY)
2676 audio_flush_and_reload_tracks();
2680 return result;
2683 /* shuffle currently playing playlist */
2684 int playlist_randomise(struct playlist_info* playlist, unsigned int seed,
2685 bool start_current)
2687 int result;
2689 if (!playlist)
2690 playlist = &current_playlist;
2692 check_control(playlist);
2694 result = randomise_playlist(playlist, seed, start_current, true);
2696 if (result != -1 && (audio_status() & AUDIO_STATUS_PLAY))
2697 audio_flush_and_reload_tracks();
2699 return result;
2702 /* sort currently playing playlist */
2703 int playlist_sort(struct playlist_info* playlist, bool start_current)
2705 int result;
2707 if (!playlist)
2708 playlist = &current_playlist;
2710 check_control(playlist);
2712 result = sort_playlist(playlist, start_current, true);
2714 if (result != -1 && (audio_status() & AUDIO_STATUS_PLAY))
2715 audio_flush_and_reload_tracks();
2717 return result;
2720 /* returns true if playlist has been modified */
2721 bool playlist_modified(const struct playlist_info* playlist)
2723 if (!playlist)
2724 playlist = &current_playlist;
2726 if (playlist->shuffle_modified ||
2727 playlist->deleted ||
2728 playlist->num_inserted_tracks > 0)
2729 return true;
2731 return false;
2734 /* returns index of first track in playlist */
2735 int playlist_get_first_index(const struct playlist_info* playlist)
2737 if (!playlist)
2738 playlist = &current_playlist;
2740 return playlist->first_index;
2743 /* returns shuffle seed of playlist */
2744 int playlist_get_seed(const struct playlist_info* playlist)
2746 if (!playlist)
2747 playlist = &current_playlist;
2749 return playlist->seed;
2752 /* returns number of tracks in playlist (includes queued/inserted tracks) */
2753 int playlist_amount_ex(const struct playlist_info* playlist)
2755 if (!playlist)
2756 playlist = &current_playlist;
2758 return playlist->amount;
2761 /* returns full path of playlist (minus extension) */
2762 char *playlist_name(const struct playlist_info* playlist, char *buf,
2763 int buf_size)
2765 char *sep;
2767 if (!playlist)
2768 playlist = &current_playlist;
2770 snprintf(buf, buf_size, "%s", playlist->filename+playlist->dirlen);
2772 if (!buf[0])
2773 return NULL;
2775 /* Remove extension */
2776 sep = strrchr(buf, '.');
2777 if (sep)
2778 *sep = 0;
2780 return buf;
2783 /* returns the playlist filename */
2784 char *playlist_get_name(const struct playlist_info* playlist, char *buf,
2785 int buf_size)
2787 if (!playlist)
2788 playlist = &current_playlist;
2790 snprintf(buf, buf_size, "%s", playlist->filename);
2792 if (!buf[0])
2793 return NULL;
2795 return buf;
2798 /* Fills info structure with information about track at specified index.
2799 Returns 0 on success and -1 on failure */
2800 int playlist_get_track_info(struct playlist_info* playlist, int index,
2801 struct playlist_track_info* info)
2803 int seek;
2804 bool control_file;
2806 if (!playlist)
2807 playlist = &current_playlist;
2809 if (index < 0 || index >= playlist->amount)
2810 return -1;
2812 control_file = playlist->indices[index] & PLAYLIST_INSERT_TYPE_MASK;
2813 seek = playlist->indices[index] & PLAYLIST_SEEK_MASK;
2815 if (get_filename(playlist, seek, control_file, info->filename,
2816 sizeof(info->filename)) < 0)
2817 return -1;
2819 info->attr = 0;
2821 if (control_file)
2823 if (playlist->indices[index] & PLAYLIST_QUEUE_MASK)
2824 info->attr |= PLAYLIST_ATTR_QUEUED;
2825 else
2826 info->attr |= PLAYLIST_ATTR_INSERTED;
2830 if (playlist->indices[index] & PLAYLIST_SKIPPED)
2831 info->attr |= PLAYLIST_ATTR_SKIPPED;
2833 info->index = index;
2834 info->display_index = rotate_index(playlist, index) + 1;
2836 return 0;
2839 /* save the current dynamic playlist to specified file */
2840 int playlist_save(struct playlist_info* playlist, char *filename)
2842 int fd;
2843 int i, index;
2844 int count = 0;
2845 char tmp_buf[MAX_PATH+1];
2846 int result = 0;
2848 if (!playlist)
2849 playlist = &current_playlist;
2851 if (playlist->amount <= 0)
2852 return -1;
2854 /* use current working directory as base for pathname */
2855 if (format_track_path(tmp_buf, filename, sizeof(tmp_buf),
2856 strlen(filename)+1, getcwd(NULL, -1)) < 0)
2857 return -1;
2859 fd = open(tmp_buf, O_CREAT|O_WRONLY|O_TRUNC);
2860 if (fd < 0)
2862 gui_syncsplash(HZ*2, true, str(LANG_PLAYLIST_ACCESS_ERROR));
2863 return -1;
2866 display_playlist_count(count, str(LANG_PLAYLIST_SAVE_COUNT));
2868 index = playlist->first_index;
2869 for (i=0; i<playlist->amount; i++)
2871 bool control_file;
2872 bool queue;
2873 int seek;
2875 /* user abort */
2876 if (button_get(false) == SETTINGS_CANCEL)
2877 break;
2879 control_file = playlist->indices[index] & PLAYLIST_INSERT_TYPE_MASK;
2880 queue = playlist->indices[index] & PLAYLIST_QUEUE_MASK;
2881 seek = playlist->indices[index] & PLAYLIST_SEEK_MASK;
2883 /* Don't save queued files */
2884 if (!queue)
2886 if (get_filename(playlist, seek, control_file, tmp_buf,
2887 MAX_PATH+1) < 0)
2889 result = -1;
2890 break;
2893 if (fdprintf(fd, "%s\n", tmp_buf) < 0)
2895 gui_syncsplash(HZ*2, true, str(LANG_PLAYLIST_CONTROL_UPDATE_ERROR));
2896 result = -1;
2897 break;
2900 count++;
2902 if ((count % PLAYLIST_DISPLAY_COUNT) == 0)
2903 display_playlist_count(count, str(LANG_PLAYLIST_SAVE_COUNT));
2905 yield();
2908 index = (index+1)%playlist->amount;
2911 display_playlist_count(count, str(LANG_PLAYLIST_SAVE_COUNT));
2913 close(fd);
2915 return result;