Kill warnings that annoy me when building for Gigabeat S.
[kugel-rb.git] / apps / playlist.c
blob5f2062faeb5065b421c798da7a49fcb58325d434
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 <ctype.h>
72 #include "playlist.h"
73 #include "file.h"
74 #include "action.h"
75 #include "dir.h"
76 #include "sprintf.h"
77 #include "debug.h"
78 #include "audio.h"
79 #include "lcd.h"
80 #include "kernel.h"
81 #include "settings.h"
82 #include "status.h"
83 #include "applimits.h"
84 #include "screens.h"
85 #include "buffer.h"
86 #include "atoi.h"
87 #include "misc.h"
88 #include "button.h"
89 #include "filetree.h"
90 #include "abrepeat.h"
91 #include "thread.h"
92 #include "usb.h"
93 #include "filetypes.h"
94 #ifdef HAVE_LCD_BITMAP
95 #include "icons.h"
96 #endif
98 #include "lang.h"
99 #include "talk.h"
100 #include "splash.h"
101 #include "rbunicode.h"
102 #include "root_menu.h"
104 #define PLAYLIST_CONTROL_FILE ROCKBOX_DIR "/.playlist_control"
105 #define PLAYLIST_CONTROL_FILE_VERSION 2
108 Each playlist index has a flag associated with it which identifies what
109 type of track it is. These flags are stored in the 4 high order bits of
110 the index.
112 NOTE: This limits the playlist file size to a max of 256M.
114 Bits 31-30:
115 00 = Playlist track
116 01 = Track was prepended into playlist
117 10 = Track was inserted into playlist
118 11 = Track was appended into playlist
119 Bit 29:
120 0 = Added track
121 1 = Queued track
122 Bit 28:
123 0 = Track entry is valid
124 1 = Track does not exist on disk and should be skipped
126 #define PLAYLIST_SEEK_MASK 0x0FFFFFFF
127 #define PLAYLIST_INSERT_TYPE_MASK 0xC0000000
128 #define PLAYLIST_QUEUE_MASK 0x20000000
130 #define PLAYLIST_INSERT_TYPE_PREPEND 0x40000000
131 #define PLAYLIST_INSERT_TYPE_INSERT 0x80000000
132 #define PLAYLIST_INSERT_TYPE_APPEND 0xC0000000
134 #define PLAYLIST_QUEUED 0x20000000
135 #define PLAYLIST_SKIPPED 0x10000000
137 #define PLAYLIST_DISPLAY_COUNT 10
139 struct directory_search_context {
140 struct playlist_info* playlist;
141 int position;
142 bool queue;
143 int count;
146 static struct playlist_info current_playlist;
147 static char now_playing[MAX_PATH+1];
149 static void empty_playlist(struct playlist_info* playlist, bool resume);
150 static void new_playlist(struct playlist_info* playlist, const char *dir,
151 const char *file);
152 static void create_control(struct playlist_info* playlist);
153 static int check_control(struct playlist_info* playlist);
154 static int recreate_control(struct playlist_info* playlist);
155 static void update_playlist_filename(struct playlist_info* playlist,
156 const char *dir, const char *file);
157 static int add_indices_to_playlist(struct playlist_info* playlist,
158 char* buffer, size_t buflen);
159 static int add_track_to_playlist(struct playlist_info* playlist,
160 const char *filename, int position,
161 bool queue, int seek_pos);
162 static int directory_search_callback(char* filename, void* context);
163 static int remove_track_from_playlist(struct playlist_info* playlist,
164 int position, bool write);
165 static int randomise_playlist(struct playlist_info* playlist,
166 unsigned int seed, bool start_current,
167 bool write);
168 static int sort_playlist(struct playlist_info* playlist, bool start_current,
169 bool write);
170 static int get_next_index(const struct playlist_info* playlist, int steps,
171 int repeat_mode);
172 static void find_and_set_playlist_index(struct playlist_info* playlist,
173 unsigned int seek);
174 static int compare(const void* p1, const void* p2);
175 static int get_filename(struct playlist_info* playlist, int index, int seek,
176 bool control_file, char *buf, int buf_length);
177 static int get_next_directory(char *dir);
178 static int get_next_dir(char *dir, bool is_forward, bool recursion);
179 static int get_previous_directory(char *dir);
180 static int check_subdir_for_music(char *dir, char *subdir);
181 static int format_track_path(char *dest, char *src, int buf_length, int max,
182 char *dir);
183 static void display_playlist_count(int count, const unsigned char *fmt,
184 bool final);
185 static void display_buffer_full(void);
186 static int flush_cached_control(struct playlist_info* playlist);
187 static int update_control(struct playlist_info* playlist,
188 enum playlist_command command, int i1, int i2,
189 const char* s1, const char* s2, void* data);
190 static void sync_control(struct playlist_info* playlist, bool force);
191 static int rotate_index(const struct playlist_info* playlist, int index);
193 #ifdef HAVE_DIRCACHE
194 #define PLAYLIST_LOAD_POINTERS 1
196 static struct event_queue playlist_queue;
197 static long playlist_stack[(DEFAULT_STACK_SIZE + 0x800)/sizeof(long)];
198 static const char playlist_thread_name[] = "playlist cachectrl";
199 #endif
201 #define BOM "\xef\xbb\xbf"
202 #define BOM_SIZE 3
204 /* Check if the filename suggests M3U or M3U8 format. */
205 static bool is_m3u8(const char* filename)
207 int len = strlen(filename);
209 /* Default to M3U8 unless explicitly told otherwise. */
210 return !(len > 4 && strcasecmp(&filename[len - 4], ".m3u") == 0);
213 /* Check if a strings starts with an UTF-8 byte-order mark. */
214 static bool is_utf8_bom(const char* str, int len)
216 return len >= BOM_SIZE && memcmp(str, BOM, BOM_SIZE) == 0;
219 /* Convert a filename in an M3U playlist to UTF-8.
221 * buf - the filename to convert; can contain more than one line from the
222 * playlist.
223 * buf_len - amount of buf that is used.
224 * buf_max - total size of buf.
225 * temp - temporary conversion buffer, at least buf_max bytes.
227 * Returns the length of the converted filename.
229 static int convert_m3u(char* buf, int buf_len, int buf_max, char* temp)
231 int i = 0;
232 char* dest;
234 /* Locate EOL. */
235 while ((buf[i] != '\n') && (buf[i] != '\r') && (i < buf_len))
237 i++;
240 /* Work back killing white space. */
241 while ((i > 0) && isspace(buf[i - 1]))
243 i--;
246 buf_len = i;
247 dest = temp;
249 /* Convert char by char, so as to not overflow temp (iso_decode should
250 * preferably handle this). No more than 4 bytes should be generated for
251 * each input char.
253 for (i = 0; i < buf_len && dest < (temp + buf_max - 4); i++)
255 dest = iso_decode(&buf[i], dest, -1, 1);
258 *dest = 0;
259 strcpy(buf, temp);
260 return dest - temp;
264 * remove any files and indices associated with the playlist
266 static void empty_playlist(struct playlist_info* playlist, bool resume)
268 playlist->filename[0] = '\0';
269 playlist->utf8 = true;
271 if(playlist->fd >= 0)
272 /* If there is an already open playlist, close it. */
273 close(playlist->fd);
274 playlist->fd = -1;
276 if(playlist->control_fd >= 0)
277 close(playlist->control_fd);
278 playlist->control_fd = -1;
279 playlist->control_created = false;
281 playlist->in_ram = false;
283 if (playlist->buffer)
284 playlist->buffer[0] = 0;
286 playlist->buffer_end_pos = 0;
288 playlist->index = 0;
289 playlist->first_index = 0;
290 playlist->amount = 0;
291 playlist->last_insert_pos = -1;
292 playlist->seed = 0;
293 playlist->shuffle_modified = false;
294 playlist->deleted = false;
295 playlist->num_inserted_tracks = 0;
296 playlist->started = false;
298 playlist->num_cached = 0;
299 playlist->pending_control_sync = false;
301 if (!resume && playlist->current)
303 /* start with fresh playlist control file when starting new
304 playlist */
305 create_control(playlist);
307 /* Reset resume settings */
308 global_status.resume_first_index = 0;
309 global_status.resume_seed = -1;
314 * Initialize a new playlist for viewing/editing/playing. dir is the
315 * directory where the playlist is located and file is the filename.
317 static void new_playlist(struct playlist_info* playlist, const char *dir,
318 const char *file)
320 empty_playlist(playlist, false);
322 if (!file)
324 file = "";
326 if (dir && playlist->current) /* !current cannot be in_ram */
327 playlist->in_ram = true;
328 else
329 dir = ""; /* empty playlist */
332 update_playlist_filename(playlist, dir, file);
334 if (playlist->control_fd >= 0)
336 update_control(playlist, PLAYLIST_COMMAND_PLAYLIST,
337 PLAYLIST_CONTROL_FILE_VERSION, -1, dir, file, NULL);
338 sync_control(playlist, false);
343 * create control file for playlist
345 static void create_control(struct playlist_info* playlist)
347 playlist->control_fd = open(playlist->control_filename,
348 O_CREAT|O_RDWR|O_TRUNC);
349 if (playlist->control_fd < 0)
351 if (check_rockboxdir())
353 cond_talk_ids_fq(LANG_PLAYLIST_CONTROL_ACCESS_ERROR);
354 gui_syncsplash(HZ*2, (unsigned char *)"%s (%d)",
355 str(LANG_PLAYLIST_CONTROL_ACCESS_ERROR),
356 playlist->control_fd);
358 playlist->control_created = false;
360 else
362 playlist->control_created = true;
367 * validate the control file. This may include creating/initializing it if
368 * necessary;
370 static int check_control(struct playlist_info* playlist)
372 if (!playlist->control_created)
374 create_control(playlist);
376 if (playlist->control_fd >= 0)
378 char* dir = playlist->filename;
379 char* file = playlist->filename+playlist->dirlen;
380 char c = playlist->filename[playlist->dirlen-1];
382 playlist->filename[playlist->dirlen-1] = '\0';
384 update_control(playlist, PLAYLIST_COMMAND_PLAYLIST,
385 PLAYLIST_CONTROL_FILE_VERSION, -1, dir, file, NULL);
386 sync_control(playlist, false);
387 playlist->filename[playlist->dirlen-1] = c;
391 if (playlist->control_fd < 0)
392 return -1;
394 return 0;
398 * recreate the control file based on current playlist entries
400 static int recreate_control(struct playlist_info* playlist)
402 char temp_file[MAX_PATH+1];
403 int temp_fd = -1;
404 int i;
405 int result = 0;
407 if(playlist->control_fd >= 0)
409 char* dir = playlist->filename;
410 char* file = playlist->filename+playlist->dirlen;
411 char c = playlist->filename[playlist->dirlen-1];
413 close(playlist->control_fd);
415 snprintf(temp_file, sizeof(temp_file), "%s_temp",
416 playlist->control_filename);
418 if (rename(playlist->control_filename, temp_file) < 0)
419 return -1;
421 temp_fd = open(temp_file, O_RDONLY);
422 if (temp_fd < 0)
423 return -1;
425 playlist->control_fd = open(playlist->control_filename,
426 O_CREAT|O_RDWR|O_TRUNC);
427 if (playlist->control_fd < 0)
428 return -1;
430 playlist->filename[playlist->dirlen-1] = '\0';
432 /* cannot call update_control() because of mutex */
433 result = fdprintf(playlist->control_fd, "P:%d:%s:%s\n",
434 PLAYLIST_CONTROL_FILE_VERSION, dir, file);
436 playlist->filename[playlist->dirlen-1] = c;
438 if (result < 0)
440 close(temp_fd);
441 return result;
445 playlist->seed = 0;
446 playlist->shuffle_modified = false;
447 playlist->deleted = false;
448 playlist->num_inserted_tracks = 0;
450 if (playlist->current)
452 global_status.resume_seed = -1;
453 status_save();
456 for (i=0; i<playlist->amount; i++)
458 if (playlist->indices[i] & PLAYLIST_INSERT_TYPE_MASK)
460 bool queue = playlist->indices[i] & PLAYLIST_QUEUE_MASK;
461 char inserted_file[MAX_PATH+1];
463 lseek(temp_fd, playlist->indices[i] & PLAYLIST_SEEK_MASK,
464 SEEK_SET);
465 read_line(temp_fd, inserted_file, sizeof(inserted_file));
467 result = fdprintf(playlist->control_fd, "%c:%d:%d:",
468 queue?'Q':'A', i, playlist->last_insert_pos);
469 if (result > 0)
471 /* save the position in file where name is written */
472 int seek_pos = lseek(playlist->control_fd, 0, SEEK_CUR);
474 result = fdprintf(playlist->control_fd, "%s\n",
475 inserted_file);
477 playlist->indices[i] =
478 (playlist->indices[i] & ~PLAYLIST_SEEK_MASK) | seek_pos;
481 if (result < 0)
482 break;
484 playlist->num_inserted_tracks++;
488 close(temp_fd);
489 remove(temp_file);
490 fsync(playlist->control_fd);
492 if (result < 0)
493 return result;
495 return 0;
499 * store directory and name of playlist file
501 static void update_playlist_filename(struct playlist_info* playlist,
502 const char *dir, const char *file)
504 char *sep="";
505 int dirlen = strlen(dir);
507 playlist->utf8 = is_m3u8(file);
509 /* If the dir does not end in trailing slash, we use a separator.
510 Otherwise we don't. */
511 if('/' != dir[dirlen-1])
513 sep="/";
514 dirlen++;
517 playlist->dirlen = dirlen;
519 snprintf(playlist->filename, sizeof(playlist->filename),
520 "%s%s%s", dir, sep, file);
524 * calculate track offsets within a playlist file
526 static int add_indices_to_playlist(struct playlist_info* playlist,
527 char* buffer, size_t buflen)
529 unsigned int nread;
530 unsigned int i = 0;
531 unsigned int count = 0;
532 bool store_index;
533 unsigned char *p;
534 int result = 0;
536 if(-1 == playlist->fd)
537 playlist->fd = open(playlist->filename, O_RDONLY);
538 if(playlist->fd < 0)
539 return -1; /* failure */
541 gui_syncsplash(0, ID2P(LANG_WAIT));
543 if (!buffer)
545 /* use mp3 buffer for maximum load speed */
546 audio_stop();
547 #if CONFIG_CODEC != SWCODEC
548 talk_buffer_steal(); /* we use the mp3 buffer, need to tell */
549 buflen = (audiobufend - audiobuf);
550 buffer = (char *)audiobuf;
551 #else
552 buffer = (char *)audio_get_buffer(false, &buflen);
553 #endif
556 store_index = true;
558 while(1)
560 nread = read(playlist->fd, buffer, buflen);
561 /* Terminate on EOF */
562 if(nread <= 0)
563 break;
565 p = (unsigned char *)buffer;
567 /* utf8 BOM at beginning of file? */
568 if(i == 0 && is_utf8_bom(p, nread)) {
569 nread -= BOM_SIZE;
570 p += BOM_SIZE;
571 i += BOM_SIZE;
572 playlist->utf8 = true; /* Override any earlier indication. */
575 for(count=0; count < nread; count++,p++) {
577 /* Are we on a new line? */
578 if((*p == '\n') || (*p == '\r'))
580 store_index = true;
582 else if(store_index)
584 store_index = false;
586 if(*p != '#')
588 if ( playlist->amount >= playlist->max_playlist_size ) {
589 display_buffer_full();
590 result = -1;
591 goto exit;
594 /* Store a new entry */
595 playlist->indices[ playlist->amount ] = i+count;
596 #ifdef HAVE_DIRCACHE
597 if (playlist->filenames)
598 playlist->filenames[ playlist->amount ] = NULL;
599 #endif
600 playlist->amount++;
605 i+= count;
608 exit:
609 #ifdef HAVE_DIRCACHE
610 queue_post(&playlist_queue, PLAYLIST_LOAD_POINTERS, 0);
611 #endif
613 return result;
617 * Utility function to create a new playlist, fill it with the next or
618 * previous directory, shuffle it if needed, and start playback.
619 * If play_last is true and direction zero or negative, start playing
620 * the last file in the directory, otherwise start playing the first.
622 static int create_and_play_dir(int direction, bool play_last)
624 char dir[MAX_PATH + 1];
625 int res;
626 int index = -1;
628 if(direction > 0)
629 res = get_next_directory(dir);
630 else
631 res = get_previous_directory(dir);
633 if (!res)
635 if (playlist_create(dir, NULL) != -1)
637 ft_build_playlist(tree_get_context(), 0);
639 if (global_settings.playlist_shuffle)
640 playlist_shuffle(current_tick, -1);
642 if (play_last && direction <= 0)
643 index = current_playlist.amount - 1;
644 else
645 index = 0;
647 #if (CONFIG_CODEC != SWCODEC)
648 playlist_start(index, 0);
649 #endif
652 /* we've overwritten the dircache when getting the next/previous dir,
653 so the tree browser context will need to be reloaded */
654 reload_directory();
657 return index;
661 * Removes all tracks, from the playlist, leaving the presently playing
662 * track queued.
664 int remove_all_tracks(struct playlist_info *playlist)
666 int result;
668 if (playlist == NULL)
669 playlist = &current_playlist;
671 while (playlist->index > 0)
672 if ((result = remove_track_from_playlist(playlist, 0, true)) < 0)
673 return result;
675 while (playlist->amount > 1)
676 if ((result = remove_track_from_playlist(playlist, 1, true)) < 0)
677 return result;
679 if (playlist->amount == 1) {
680 playlist->indices[0] |= PLAYLIST_QUEUED;
683 return 0;
688 * Add track to playlist at specified position. There are five special
689 * positions that can be specified:
690 * PLAYLIST_PREPEND - Add track at beginning of playlist
691 * PLAYLIST_INSERT - Add track after current song. NOTE: If
692 * there are already inserted tracks then track
693 * is added to the end of the insertion list
694 * PLAYLIST_INSERT_FIRST - Add track immediately after current song, no
695 * matter what other tracks have been inserted
696 * PLAYLIST_INSERT_LAST - Add track to end of playlist
697 * PLAYLIST_INSERT_SHUFFLED - Add track at some random point between the
698 * current playing track and end of playlist
699 * PLAYLIST_REPLACE - Erase current playlist, Cue the current track
700 * and inster this track at the end.
702 static int add_track_to_playlist(struct playlist_info* playlist,
703 const char *filename, int position,
704 bool queue, int seek_pos)
706 int insert_position, orig_position;
707 unsigned long flags = PLAYLIST_INSERT_TYPE_INSERT;
708 int i;
710 insert_position = orig_position = position;
712 if (playlist->amount >= playlist->max_playlist_size)
714 display_buffer_full();
715 return -1;
718 switch (position)
720 case PLAYLIST_PREPEND:
721 position = insert_position = playlist->first_index;
722 break;
723 case PLAYLIST_INSERT:
724 /* if there are already inserted tracks then add track to end of
725 insertion list else add after current playing track */
726 if (playlist->last_insert_pos >= 0 &&
727 playlist->last_insert_pos < playlist->amount &&
728 (playlist->indices[playlist->last_insert_pos]&
729 PLAYLIST_INSERT_TYPE_MASK) == PLAYLIST_INSERT_TYPE_INSERT)
730 position = insert_position = playlist->last_insert_pos+1;
731 else if (playlist->amount > 0)
732 position = insert_position = playlist->index + 1;
733 else
734 position = insert_position = 0;
736 if (playlist->started)
737 playlist->last_insert_pos = position;
738 break;
739 case PLAYLIST_INSERT_FIRST:
740 if (playlist->amount > 0)
741 position = insert_position = playlist->index + 1;
742 else
743 position = insert_position = 0;
745 if (playlist->last_insert_pos < 0 && playlist->started)
746 playlist->last_insert_pos = position;
747 break;
748 case PLAYLIST_INSERT_LAST:
749 if (playlist->first_index > 0)
750 position = insert_position = playlist->first_index;
751 else
752 position = insert_position = playlist->amount;
753 break;
754 case PLAYLIST_INSERT_SHUFFLED:
756 if (playlist->started)
758 int offset;
759 int n = playlist->amount -
760 rotate_index(playlist, playlist->index);
762 if (n > 0)
763 offset = rand() % n;
764 else
765 offset = 0;
767 position = playlist->index + offset + 1;
768 if (position >= playlist->amount)
769 position -= playlist->amount;
771 insert_position = position;
773 else
774 position = insert_position = (rand() % (playlist->amount+1));
775 break;
777 case PLAYLIST_REPLACE:
778 if (remove_all_tracks(playlist) < 0)
779 return -1;
781 position = insert_position = playlist->index + 1;
782 break;
785 if (queue)
786 flags |= PLAYLIST_QUEUED;
788 /* shift indices so that track can be added */
789 for (i=playlist->amount; i>insert_position; i--)
791 playlist->indices[i] = playlist->indices[i-1];
792 #ifdef HAVE_DIRCACHE
793 if (playlist->filenames)
794 playlist->filenames[i] = playlist->filenames[i-1];
795 #endif
798 /* update stored indices if needed */
799 if (playlist->amount > 0 && insert_position <= playlist->index &&
800 playlist->started)
801 playlist->index++;
803 if (playlist->amount > 0 && insert_position <= playlist->first_index &&
804 orig_position != PLAYLIST_PREPEND && playlist->started)
806 playlist->first_index++;
808 if (seek_pos < 0 && playlist->current)
810 global_status.resume_first_index = playlist->first_index;
811 status_save();
815 if (insert_position < playlist->last_insert_pos ||
816 (insert_position == playlist->last_insert_pos && position < 0))
817 playlist->last_insert_pos++;
819 if (seek_pos < 0 && playlist->control_fd >= 0)
821 int result = update_control(playlist,
822 (queue?PLAYLIST_COMMAND_QUEUE:PLAYLIST_COMMAND_ADD), position,
823 playlist->last_insert_pos, filename, NULL, &seek_pos);
825 if (result < 0)
826 return result;
829 playlist->indices[insert_position] = flags | seek_pos;
831 #ifdef HAVE_DIRCACHE
832 if (playlist->filenames)
833 playlist->filenames[insert_position] = NULL;
834 #endif
836 playlist->amount++;
837 playlist->num_inserted_tracks++;
839 return insert_position;
843 * Callback for playlist_directory_tracksearch to insert track into
844 * playlist.
846 static int directory_search_callback(char* filename, void* context)
848 struct directory_search_context* c =
849 (struct directory_search_context*) context;
850 int insert_pos;
852 insert_pos = add_track_to_playlist(c->playlist, filename, c->position,
853 c->queue, -1);
855 if (insert_pos < 0)
856 return -1;
858 (c->count)++;
860 /* Make sure tracks are inserted in correct order if user requests
861 INSERT_FIRST */
862 if (c->position == PLAYLIST_INSERT_FIRST || c->position >= 0)
863 c->position = insert_pos + 1;
865 if (((c->count)%PLAYLIST_DISPLAY_COUNT) == 0)
867 unsigned char* count_str;
869 if (c->queue)
870 count_str = ID2P(LANG_PLAYLIST_QUEUE_COUNT);
871 else
872 count_str = ID2P(LANG_PLAYLIST_INSERT_COUNT);
874 display_playlist_count(c->count, count_str, false);
876 if ((c->count) == PLAYLIST_DISPLAY_COUNT &&
877 (audio_status() & AUDIO_STATUS_PLAY) &&
878 c->playlist->started)
879 audio_flush_and_reload_tracks();
882 return 0;
886 * remove track at specified position
888 static int remove_track_from_playlist(struct playlist_info* playlist,
889 int position, bool write)
891 int i;
892 bool inserted;
894 if (playlist->amount <= 0)
895 return -1;
897 inserted = playlist->indices[position] & PLAYLIST_INSERT_TYPE_MASK;
899 /* shift indices now that track has been removed */
900 for (i=position; i<playlist->amount; i++)
902 playlist->indices[i] = playlist->indices[i+1];
903 #ifdef HAVE_DIRCACHE
904 if (playlist->filenames)
905 playlist->filenames[i] = playlist->filenames[i+1];
906 #endif
909 playlist->amount--;
911 if (inserted)
912 playlist->num_inserted_tracks--;
913 else
914 playlist->deleted = true;
916 /* update stored indices if needed */
917 if (position < playlist->index)
918 playlist->index--;
920 if (position < playlist->first_index)
922 playlist->first_index--;
924 if (write)
926 global_status.resume_first_index = playlist->first_index;
927 status_save();
931 if (position <= playlist->last_insert_pos)
932 playlist->last_insert_pos--;
934 if (write && playlist->control_fd >= 0)
936 int result = update_control(playlist, PLAYLIST_COMMAND_DELETE,
937 position, -1, NULL, NULL, NULL);
939 if (result < 0)
940 return result;
942 sync_control(playlist, false);
945 return 0;
949 * randomly rearrange the array of indices for the playlist. If start_current
950 * is true then update the index to the new index of the current playing track
952 static int randomise_playlist(struct playlist_info* playlist,
953 unsigned int seed, bool start_current,
954 bool write)
956 int count;
957 int candidate;
958 long store;
959 unsigned int current = playlist->indices[playlist->index];
961 /* seed 0 is used to identify sorted playlist for resume purposes */
962 if (seed == 0)
963 seed = 1;
965 /* seed with the given seed */
966 srand(seed);
968 /* randomise entire indices list */
969 for(count = playlist->amount - 1; count >= 0; count--)
971 /* the rand is from 0 to RAND_MAX, so adjust to our value range */
972 candidate = rand() % (count + 1);
974 /* now swap the values at the 'count' and 'candidate' positions */
975 store = playlist->indices[candidate];
976 playlist->indices[candidate] = playlist->indices[count];
977 playlist->indices[count] = store;
978 #ifdef HAVE_DIRCACHE
979 if (playlist->filenames)
981 store = (long)playlist->filenames[candidate];
982 playlist->filenames[candidate] = playlist->filenames[count];
983 playlist->filenames[count] = (struct dircache_entry *)store;
985 #endif
988 if (start_current)
989 find_and_set_playlist_index(playlist, current);
991 /* indices have been moved so last insert position is no longer valid */
992 playlist->last_insert_pos = -1;
994 playlist->seed = seed;
995 if (playlist->num_inserted_tracks > 0 || playlist->deleted)
996 playlist->shuffle_modified = true;
998 if (write)
1000 update_control(playlist, PLAYLIST_COMMAND_SHUFFLE, seed,
1001 playlist->first_index, NULL, NULL, NULL);
1002 global_status.resume_seed = seed;
1003 status_save();
1006 return 0;
1010 * Sort the array of indices for the playlist. If start_current is true then
1011 * set the index to the new index of the current song.
1013 static int sort_playlist(struct playlist_info* playlist, bool start_current,
1014 bool write)
1016 unsigned int current = playlist->indices[playlist->index];
1018 if (playlist->amount > 0)
1019 qsort(playlist->indices, playlist->amount,
1020 sizeof(playlist->indices[0]), compare);
1022 #ifdef HAVE_DIRCACHE
1023 /** We need to re-check the song names from disk because qsort can't
1024 * sort two arrays at once :/
1025 * FIXME: Please implement a better way to do this. */
1026 memset(playlist->filenames, 0, playlist->max_playlist_size * sizeof(int));
1027 queue_post(&playlist_queue, PLAYLIST_LOAD_POINTERS, 0);
1028 #endif
1030 if (start_current)
1031 find_and_set_playlist_index(playlist, current);
1033 /* indices have been moved so last insert position is no longer valid */
1034 playlist->last_insert_pos = -1;
1036 if (!playlist->num_inserted_tracks && !playlist->deleted)
1037 playlist->shuffle_modified = false;
1038 if (write && playlist->control_fd >= 0)
1040 update_control(playlist, PLAYLIST_COMMAND_UNSHUFFLE,
1041 playlist->first_index, -1, NULL, NULL, NULL);
1042 global_status.resume_seed = 0;
1043 status_save();
1046 return 0;
1049 /* Calculate how many steps we have to really step when skipping entries
1050 * marked as bad.
1052 static int calculate_step_count(const struct playlist_info *playlist, int steps)
1054 int i, count, direction;
1055 int index;
1056 int stepped_count = 0;
1058 if (steps < 0)
1060 direction = -1;
1061 count = -steps;
1063 else
1065 direction = 1;
1066 count = steps;
1069 index = playlist->index;
1070 i = 0;
1071 do {
1072 /* Boundary check */
1073 if (index < 0)
1074 index += playlist->amount;
1075 if (index >= playlist->amount)
1076 index -= playlist->amount;
1078 /* Check if we found a bad entry. */
1079 if (playlist->indices[index] & PLAYLIST_SKIPPED)
1081 steps += direction;
1082 /* Are all entries bad? */
1083 if (stepped_count++ > playlist->amount)
1084 break ;
1086 else
1087 i++;
1089 index += direction;
1090 } while (i <= count);
1092 return steps;
1095 /* Marks the index of the track to be skipped that is "steps" away from
1096 * current playing track.
1098 void playlist_skip_entry(struct playlist_info *playlist, int steps)
1100 int index;
1102 if (playlist == NULL)
1103 playlist = &current_playlist;
1105 /* need to account for already skipped tracks */
1106 steps = calculate_step_count(playlist, steps);
1108 index = playlist->index + steps;
1109 if (index < 0)
1110 index += playlist->amount;
1111 else if (index >= playlist->amount)
1112 index -= playlist->amount;
1114 playlist->indices[index] |= PLAYLIST_SKIPPED;
1118 * returns the index of the track that is "steps" away from current playing
1119 * track.
1121 static int get_next_index(const struct playlist_info* playlist, int steps,
1122 int repeat_mode)
1124 int current_index = playlist->index;
1125 int next_index = -1;
1127 if (playlist->amount <= 0)
1128 return -1;
1130 if (repeat_mode == -1)
1131 repeat_mode = global_settings.repeat_mode;
1133 if (repeat_mode == REPEAT_SHUFFLE && playlist->amount <= 1)
1134 repeat_mode = REPEAT_ALL;
1136 steps = calculate_step_count(playlist, steps);
1137 switch (repeat_mode)
1139 case REPEAT_SHUFFLE:
1140 /* Treat repeat shuffle just like repeat off. At end of playlist,
1141 play will be resumed in playlist_next() */
1142 case REPEAT_OFF:
1144 current_index = rotate_index(playlist, current_index);
1145 next_index = current_index+steps;
1146 if ((next_index < 0) || (next_index >= playlist->amount))
1147 next_index = -1;
1148 else
1149 next_index = (next_index+playlist->first_index) %
1150 playlist->amount;
1152 break;
1155 case REPEAT_ONE:
1156 #ifdef AB_REPEAT_ENABLE
1157 case REPEAT_AB:
1158 #endif
1159 next_index = current_index;
1160 break;
1162 case REPEAT_ALL:
1163 default:
1165 next_index = (current_index+steps) % playlist->amount;
1166 while (next_index < 0)
1167 next_index += playlist->amount;
1169 if (steps >= playlist->amount)
1171 int i, index;
1173 index = next_index;
1174 next_index = -1;
1176 /* second time around so skip the queued files */
1177 for (i=0; i<playlist->amount; i++)
1179 if (playlist->indices[index] & PLAYLIST_QUEUE_MASK)
1180 index = (index+1) % playlist->amount;
1181 else
1183 next_index = index;
1184 break;
1188 break;
1192 /* No luck if the whole playlist was bad. */
1193 if (playlist->indices[next_index] & PLAYLIST_SKIPPED)
1194 return -1;
1196 return next_index;
1200 * Search for the seek track and set appropriate indices. Used after shuffle
1201 * to make sure the current index is still pointing to correct track.
1203 static void find_and_set_playlist_index(struct playlist_info* playlist,
1204 unsigned int seek)
1206 int i;
1208 /* Set the index to the current song */
1209 for (i=0; i<playlist->amount; i++)
1211 if (playlist->indices[i] == seek)
1213 playlist->index = playlist->first_index = i;
1215 if (playlist->current)
1217 global_status.resume_first_index = i;
1218 status_save();
1221 break;
1227 * used to sort track indices. Sort order is as follows:
1228 * 1. Prepended tracks (in prepend order)
1229 * 2. Playlist/directory tracks (in playlist order)
1230 * 3. Inserted/Appended tracks (in insert order)
1232 static int compare(const void* p1, const void* p2)
1234 unsigned long* e1 = (unsigned long*) p1;
1235 unsigned long* e2 = (unsigned long*) p2;
1236 unsigned long flags1 = *e1 & PLAYLIST_INSERT_TYPE_MASK;
1237 unsigned long flags2 = *e2 & PLAYLIST_INSERT_TYPE_MASK;
1239 if (flags1 == flags2)
1240 return (*e1 & PLAYLIST_SEEK_MASK) - (*e2 & PLAYLIST_SEEK_MASK);
1241 else if (flags1 == PLAYLIST_INSERT_TYPE_PREPEND ||
1242 flags2 == PLAYLIST_INSERT_TYPE_APPEND)
1243 return -1;
1244 else if (flags1 == PLAYLIST_INSERT_TYPE_APPEND ||
1245 flags2 == PLAYLIST_INSERT_TYPE_PREPEND)
1246 return 1;
1247 else if (flags1 && flags2)
1248 return (*e1 & PLAYLIST_SEEK_MASK) - (*e2 & PLAYLIST_SEEK_MASK);
1249 else
1250 return *e1 - *e2;
1253 #ifdef HAVE_DIRCACHE
1255 * Thread to update filename pointers to dircache on background
1256 * without affecting playlist load up performance. This thread also flushes
1257 * any pending control commands when the disk spins up.
1259 static void playlist_thread(void)
1261 struct queue_event ev;
1262 bool dirty_pointers = false;
1263 static char tmp[MAX_PATH+1];
1265 struct playlist_info *playlist;
1266 int index;
1267 int seek;
1268 bool control_file;
1270 int sleep_time = 5;
1272 #ifndef HAVE_FLASH_STORAGE
1273 if (global_settings.disk_spindown > 1 &&
1274 global_settings.disk_spindown <= 5)
1275 sleep_time = global_settings.disk_spindown - 1;
1276 #endif
1278 while (1)
1280 queue_wait_w_tmo(&playlist_queue, &ev, HZ*sleep_time);
1282 switch (ev.id)
1284 case PLAYLIST_LOAD_POINTERS:
1285 dirty_pointers = true;
1286 break ;
1288 /* Start the background scanning after either the disk spindown
1289 timeout or 5s, whichever is less */
1290 case SYS_TIMEOUT:
1291 playlist = &current_playlist;
1293 if (playlist->control_fd >= 0
1294 # ifndef SIMULATOR
1295 && ata_disk_is_active()
1296 # endif
1299 if (playlist->num_cached > 0)
1301 mutex_lock(&playlist->control_mutex);
1302 flush_cached_control(playlist);
1303 mutex_unlock(&playlist->control_mutex);
1306 sync_control(playlist, true);
1309 if (!dirty_pointers)
1310 break ;
1312 if (!dircache_is_enabled() || !playlist->filenames
1313 || playlist->amount <= 0)
1314 break ;
1316 #ifdef HAVE_ADJUSTABLE_CPU_FREQ
1317 cpu_boost(true);
1318 #endif
1319 for (index = 0; index < playlist->amount
1320 && queue_empty(&playlist_queue); index++)
1322 /* Process only pointers that are not already loaded. */
1323 if (playlist->filenames[index])
1324 continue ;
1326 control_file = playlist->indices[index] & PLAYLIST_INSERT_TYPE_MASK;
1327 seek = playlist->indices[index] & PLAYLIST_SEEK_MASK;
1329 /* Load the filename from playlist file. */
1330 if (get_filename(playlist, index, seek, control_file, tmp,
1331 sizeof(tmp)) < 0)
1332 break ;
1334 /* Set the dircache entry pointer. */
1335 playlist->filenames[index] = dircache_get_entry_ptr(tmp);
1337 /* And be on background so user doesn't notice any delays. */
1338 yield();
1341 #ifdef HAVE_ADJUSTABLE_CPU_FREQ
1342 cpu_boost(false);
1343 #endif
1344 dirty_pointers = false;
1345 break ;
1347 #ifndef SIMULATOR
1348 case SYS_USB_CONNECTED:
1349 usb_acknowledge(SYS_USB_CONNECTED_ACK);
1350 usb_wait_for_disconnect(&playlist_queue);
1351 break ;
1352 #endif
1356 #endif
1359 * gets pathname for track at seek index
1361 static int get_filename(struct playlist_info* playlist, int index, int seek,
1362 bool control_file, char *buf, int buf_length)
1364 int fd;
1365 int max = -1;
1366 char tmp_buf[MAX_PATH+1];
1367 char dir_buf[MAX_PATH+1];
1368 bool utf8 = playlist->utf8;
1370 if (buf_length > MAX_PATH+1)
1371 buf_length = MAX_PATH+1;
1373 #ifdef HAVE_DIRCACHE
1374 if (dircache_is_enabled() && playlist->filenames)
1376 if (playlist->filenames[index] != NULL)
1378 dircache_copy_path(playlist->filenames[index], tmp_buf, sizeof(tmp_buf)-1);
1379 max = strlen(tmp_buf) + 1;
1382 #else
1383 (void)index;
1384 #endif
1386 if (playlist->in_ram && !control_file && max < 0)
1388 strncpy(tmp_buf, &playlist->buffer[seek], sizeof(tmp_buf));
1389 tmp_buf[MAX_PATH] = '\0';
1390 max = strlen(tmp_buf) + 1;
1392 else if (max < 0)
1394 mutex_lock(&playlist->control_mutex);
1396 if (control_file)
1398 fd = playlist->control_fd;
1399 utf8 = true;
1401 else
1403 if(-1 == playlist->fd)
1404 playlist->fd = open(playlist->filename, O_RDONLY);
1406 fd = playlist->fd;
1409 if(-1 != fd)
1412 if (lseek(fd, seek, SEEK_SET) != seek)
1413 max = -1;
1414 else
1416 max = read(fd, tmp_buf, MIN((size_t) buf_length, sizeof(tmp_buf)));
1418 if ((max > 0) && !utf8)
1420 /* Use dir_buf as a temporary buffer. Note that dir_buf must
1421 * be as large as tmp_buf.
1423 max = convert_m3u(tmp_buf, max, sizeof(tmp_buf), dir_buf);
1428 mutex_unlock(&playlist->control_mutex);
1430 if (max < 0)
1432 if (control_file)
1433 gui_syncsplash(HZ*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR));
1434 else
1435 gui_syncsplash(HZ*2, ID2P(LANG_PLAYLIST_ACCESS_ERROR));
1437 return max;
1441 strncpy(dir_buf, playlist->filename, playlist->dirlen-1);
1442 dir_buf[playlist->dirlen-1] = 0;
1444 return (format_track_path(buf, tmp_buf, buf_length, max, dir_buf));
1447 static int get_next_directory(char *dir){
1448 return get_next_dir(dir,true,false);
1451 static int get_previous_directory(char *dir){
1452 return get_next_dir(dir,false,false);
1456 * search through all the directories (starting with the current) to find
1457 * one that has tracks to play
1459 static int get_next_dir(char *dir, bool is_forward, bool recursion)
1461 struct playlist_info* playlist = &current_playlist;
1462 int result = -1;
1463 int sort_dir = global_settings.sort_dir;
1464 char *start_dir = NULL;
1465 bool exit = false;
1466 struct tree_context* tc = tree_get_context();
1467 int dirfilter = *(tc->dirfilter);
1469 if (global_settings.next_folder == FOLDER_ADVANCE_RANDOM)
1471 int fd = open(ROCKBOX_DIR "/folder_advance_list.dat",O_RDONLY);
1472 char buffer[MAX_PATH];
1473 int folder_count = 0,i;
1474 srand(current_tick);
1475 *(tc->dirfilter) = SHOW_MUSIC;
1476 if (fd >= 0)
1478 read(fd,&folder_count,sizeof(int));
1479 while (!exit)
1481 i = rand()%folder_count;
1482 lseek(fd,sizeof(int) + (MAX_PATH*i),SEEK_SET);
1483 read(fd,buffer,MAX_PATH);
1484 if (check_subdir_for_music(buffer,"") ==0)
1485 exit = true;
1487 strcpy(dir,buffer);
1488 close(fd);
1489 *(tc->dirfilter) = dirfilter;
1490 reload_directory();
1491 return 0;
1494 /* not random folder advance */
1495 if (recursion){
1496 /* start with root */
1497 dir[0] = '\0';
1499 else{
1500 /* start with current directory */
1501 strncpy(dir, playlist->filename, playlist->dirlen-1);
1502 dir[playlist->dirlen-1] = '\0';
1505 /* use the tree browser dircache to load files */
1506 *(tc->dirfilter) = SHOW_ALL;
1508 /* sort in another direction if previous dir is requested */
1509 if(!is_forward){
1510 if ((global_settings.sort_dir == 0) || (global_settings.sort_dir == 3))
1511 global_settings.sort_dir = 4;
1512 else if (global_settings.sort_dir == 1)
1513 global_settings.sort_dir = 2;
1514 else if (global_settings.sort_dir == 2)
1515 global_settings.sort_dir = 1;
1516 else if (global_settings.sort_dir == 4)
1517 global_settings.sort_dir = 0;
1520 while (!exit)
1522 struct entry *files;
1523 int num_files = 0;
1524 int i;
1526 if (ft_load(tc, (dir[0]=='\0')?"/":dir) < 0)
1528 gui_syncsplash(HZ*2, ID2P(LANG_PLAYLIST_DIRECTORY_ACCESS_ERROR));
1529 exit = true;
1530 result = -1;
1531 break;
1534 files = (struct entry*) tc->dircache;
1535 num_files = tc->filesindir;
1537 for (i=0; i<num_files; i++)
1539 /* user abort */
1540 if (action_userabort(TIMEOUT_NOBLOCK))
1542 result = -1;
1543 exit = true;
1544 break;
1547 if (files[i].attr & ATTR_DIRECTORY)
1549 if (!start_dir)
1551 result = check_subdir_for_music(dir, files[i].name);
1552 if (result != -1)
1554 exit = true;
1555 break;
1558 else if (!strcmp(start_dir, files[i].name))
1559 start_dir = NULL;
1563 if (!exit)
1565 /* move down to parent directory. current directory name is
1566 stored as the starting point for the search in parent */
1567 start_dir = strrchr(dir, '/');
1568 if (start_dir)
1570 *start_dir = '\0';
1571 start_dir++;
1573 else
1574 break;
1578 /* restore dirfilter & sort_dir */
1579 *(tc->dirfilter) = dirfilter;
1580 global_settings.sort_dir = sort_dir;
1582 /* special case if nothing found: try start searching again from root */
1583 if (result == -1 && !recursion){
1584 result = get_next_dir(dir,is_forward, true);
1587 return result;
1591 * Checks if there are any music files in the dir or any of its
1592 * subdirectories. May be called recursively.
1594 static int check_subdir_for_music(char *dir, char *subdir)
1596 int result = -1;
1597 int dirlen = strlen(dir);
1598 int num_files = 0;
1599 int i;
1600 struct entry *files;
1601 bool has_music = false;
1602 bool has_subdir = false;
1603 struct tree_context* tc = tree_get_context();
1605 snprintf(dir+dirlen, MAX_PATH-dirlen, "/%s", subdir);
1607 if (ft_load(tc, dir) < 0)
1609 gui_syncsplash(HZ*2, ID2P(LANG_PLAYLIST_DIRECTORY_ACCESS_ERROR));
1610 return -2;
1613 files = (struct entry*) tc->dircache;
1614 num_files = tc->filesindir;
1616 for (i=0; i<num_files; i++)
1618 if (files[i].attr & ATTR_DIRECTORY)
1619 has_subdir = true;
1620 else if ((files[i].attr & FILE_ATTR_MASK) == FILE_ATTR_AUDIO)
1622 has_music = true;
1623 break;
1627 if (has_music)
1628 return 0;
1630 if (has_subdir)
1632 for (i=0; i<num_files; i++)
1634 if (action_userabort(TIMEOUT_NOBLOCK))
1636 result = -2;
1637 break;
1640 if (files[i].attr & ATTR_DIRECTORY)
1642 result = check_subdir_for_music(dir, files[i].name);
1643 if (!result)
1644 break;
1649 if (result < 0)
1651 if (dirlen)
1653 dir[dirlen] = '\0';
1655 else
1657 strcpy(dir, "/");
1660 /* we now need to reload our current directory */
1661 if(ft_load(tc, dir) < 0)
1662 gui_syncsplash(HZ*2,
1663 ID2P(LANG_PLAYLIST_DIRECTORY_ACCESS_ERROR));
1666 return result;
1670 * Returns absolute path of track
1672 static int format_track_path(char *dest, char *src, int buf_length, int max,
1673 char *dir)
1675 int i = 0;
1676 int j;
1677 char *temp_ptr;
1679 /* Zero-terminate the file name */
1680 while((src[i] != '\n') &&
1681 (src[i] != '\r') &&
1682 (i < max))
1683 i++;
1685 /* Now work back killing white space */
1686 while((src[i-1] == ' ') ||
1687 (src[i-1] == '\t'))
1688 i--;
1690 src[i]=0;
1692 /* replace backslashes with forward slashes */
1693 for ( j=0; j<i; j++ )
1694 if ( src[j] == '\\' )
1695 src[j] = '/';
1697 if('/' == src[0])
1699 strncpy(dest, src, buf_length);
1701 else
1703 /* handle dos style drive letter */
1704 if (':' == src[1])
1705 strncpy(dest, &src[2], buf_length);
1706 else if (!strncmp(src, "../", 3))
1708 /* handle relative paths */
1709 i=3;
1710 while(!strncmp(&src[i], "../", 3))
1711 i += 3;
1712 for (j=0; j<i/3; j++) {
1713 temp_ptr = strrchr(dir, '/');
1714 if (temp_ptr)
1715 *temp_ptr = '\0';
1716 else
1717 break;
1719 snprintf(dest, buf_length, "%s/%s", dir, &src[i]);
1721 else if ( '.' == src[0] && '/' == src[1] ) {
1722 snprintf(dest, buf_length, "%s/%s", dir, &src[2]);
1724 else {
1725 snprintf(dest, buf_length, "%s/%s", dir, src);
1729 return 0;
1733 * Display splash message showing progress of playlist/directory insertion or
1734 * save.
1736 static void display_playlist_count(int count, const unsigned char *fmt,
1737 bool final)
1739 static long talked_tick = 0;
1740 long id = P2ID(fmt);
1741 if(global_settings.talk_menu && id>=0)
1743 if(final || (count && (talked_tick == 0
1744 || TIME_AFTER(current_tick, talked_tick+5*HZ))))
1746 talked_tick = current_tick;
1747 talk_number(count, false);
1748 talk_id(id, true);
1751 fmt = P2STR(fmt);
1753 lcd_clear_display();
1755 #ifdef HAVE_LCD_BITMAP
1756 if(global_settings.statusbar)
1757 lcd_setmargins(0, STATUSBAR_HEIGHT);
1758 else
1759 lcd_setmargins(0, 0);
1760 #endif
1762 gui_syncsplash(0, fmt, count, str(LANG_OFF_ABORT));
1766 * Display buffer full message
1768 static void display_buffer_full(void)
1770 gui_syncsplash(HZ*2, ID2P(LANG_PLAYLIST_BUFFER_FULL));
1774 * Flush any cached control commands to disk. Called when playlist is being
1775 * modified. Returns 0 on success and -1 on failure.
1777 static int flush_cached_control(struct playlist_info* playlist)
1779 int result = 0;
1780 int i;
1782 if (!playlist->num_cached)
1783 return 0;
1785 lseek(playlist->control_fd, 0, SEEK_END);
1787 for (i=0; i<playlist->num_cached; i++)
1789 struct playlist_control_cache* cache =
1790 &(playlist->control_cache[i]);
1792 switch (cache->command)
1794 case PLAYLIST_COMMAND_PLAYLIST:
1795 result = fdprintf(playlist->control_fd, "P:%d:%s:%s\n",
1796 cache->i1, cache->s1, cache->s2);
1797 break;
1798 case PLAYLIST_COMMAND_ADD:
1799 case PLAYLIST_COMMAND_QUEUE:
1800 result = fdprintf(playlist->control_fd, "%c:%d:%d:",
1801 (cache->command == PLAYLIST_COMMAND_ADD)?'A':'Q',
1802 cache->i1, cache->i2);
1803 if (result > 0)
1805 /* save the position in file where name is written */
1806 int* seek_pos = (int *)cache->data;
1807 *seek_pos = lseek(playlist->control_fd, 0, SEEK_CUR);
1808 result = fdprintf(playlist->control_fd, "%s\n",
1809 cache->s1);
1811 break;
1812 case PLAYLIST_COMMAND_DELETE:
1813 result = fdprintf(playlist->control_fd, "D:%d\n", cache->i1);
1814 break;
1815 case PLAYLIST_COMMAND_SHUFFLE:
1816 result = fdprintf(playlist->control_fd, "S:%d:%d\n",
1817 cache->i1, cache->i2);
1818 break;
1819 case PLAYLIST_COMMAND_UNSHUFFLE:
1820 result = fdprintf(playlist->control_fd, "U:%d\n", cache->i1);
1821 break;
1822 case PLAYLIST_COMMAND_RESET:
1823 result = fdprintf(playlist->control_fd, "R\n");
1824 break;
1825 default:
1826 break;
1829 if (result <= 0)
1830 break;
1833 if (result > 0)
1835 if (global_status.resume_seed >= 0)
1837 global_status.resume_seed = -1;
1838 status_save();
1841 playlist->num_cached = 0;
1842 playlist->pending_control_sync = true;
1844 result = 0;
1846 else
1848 result = -1;
1849 gui_syncsplash(HZ*2, ID2P(LANG_PLAYLIST_CONTROL_UPDATE_ERROR));
1852 return result;
1856 * Update control data with new command. Depending on the command, it may be
1857 * cached or flushed to disk.
1859 static int update_control(struct playlist_info* playlist,
1860 enum playlist_command command, int i1, int i2,
1861 const char* s1, const char* s2, void* data)
1863 int result = 0;
1864 struct playlist_control_cache* cache;
1865 bool flush = false;
1867 mutex_lock(&playlist->control_mutex);
1869 cache = &(playlist->control_cache[playlist->num_cached++]);
1871 cache->command = command;
1872 cache->i1 = i1;
1873 cache->i2 = i2;
1874 cache->s1 = s1;
1875 cache->s2 = s2;
1876 cache->data = data;
1878 switch (command)
1880 case PLAYLIST_COMMAND_PLAYLIST:
1881 case PLAYLIST_COMMAND_ADD:
1882 case PLAYLIST_COMMAND_QUEUE:
1883 #ifndef HAVE_DIRCACHE
1884 case PLAYLIST_COMMAND_DELETE:
1885 case PLAYLIST_COMMAND_RESET:
1886 #endif
1887 flush = true;
1888 break;
1889 case PLAYLIST_COMMAND_SHUFFLE:
1890 case PLAYLIST_COMMAND_UNSHUFFLE:
1891 default:
1892 /* only flush when needed */
1893 break;
1896 if (flush || playlist->num_cached == PLAYLIST_MAX_CACHE)
1897 result = flush_cached_control(playlist);
1899 mutex_unlock(&playlist->control_mutex);
1901 return result;
1905 * sync control file to disk
1907 static void sync_control(struct playlist_info* playlist, bool force)
1909 #ifdef HAVE_DIRCACHE
1910 if (playlist->started && force)
1911 #else
1912 (void) force;
1914 if (playlist->started)
1915 #endif
1917 if (playlist->pending_control_sync)
1919 mutex_lock(&playlist->control_mutex);
1920 fsync(playlist->control_fd);
1921 playlist->pending_control_sync = false;
1922 mutex_unlock(&playlist->control_mutex);
1928 * Rotate indices such that first_index is index 0
1930 static int rotate_index(const struct playlist_info* playlist, int index)
1932 index -= playlist->first_index;
1933 if (index < 0)
1934 index += playlist->amount;
1936 return index;
1940 * Initialize playlist entries at startup
1942 void playlist_init(void)
1944 struct playlist_info* playlist = &current_playlist;
1946 playlist->current = true;
1947 snprintf(playlist->control_filename, sizeof(playlist->control_filename),
1948 "%s", PLAYLIST_CONTROL_FILE);
1949 playlist->fd = -1;
1950 playlist->control_fd = -1;
1951 playlist->max_playlist_size = global_settings.max_files_in_playlist;
1952 playlist->indices = buffer_alloc(
1953 playlist->max_playlist_size * sizeof(int));
1954 playlist->buffer_size =
1955 AVERAGE_FILENAME_LENGTH * global_settings.max_files_in_dir;
1956 playlist->buffer = buffer_alloc(playlist->buffer_size);
1957 mutex_init(&playlist->control_mutex);
1958 empty_playlist(playlist, true);
1960 #ifdef HAVE_DIRCACHE
1961 playlist->filenames = buffer_alloc(
1962 playlist->max_playlist_size * sizeof(int));
1963 memset(playlist->filenames, 0,
1964 playlist->max_playlist_size * sizeof(int));
1965 create_thread(playlist_thread, playlist_stack, sizeof(playlist_stack),
1966 0, playlist_thread_name IF_PRIO(, PRIORITY_BACKGROUND)
1967 IF_COP(, CPU));
1968 queue_init(&playlist_queue, true);
1969 #endif
1973 * Clean playlist at shutdown
1975 void playlist_shutdown(void)
1977 struct playlist_info* playlist = &current_playlist;
1979 if (playlist->control_fd >= 0)
1981 mutex_lock(&playlist->control_mutex);
1983 if (playlist->num_cached > 0)
1984 flush_cached_control(playlist);
1986 close(playlist->control_fd);
1988 mutex_unlock(&playlist->control_mutex);
1993 * Create new playlist
1995 int playlist_create(const char *dir, const char *file)
1997 struct playlist_info* playlist = &current_playlist;
1999 new_playlist(playlist, dir, file);
2001 if (file)
2002 /* load the playlist file */
2003 add_indices_to_playlist(playlist, NULL, 0);
2005 return 0;
2008 #define PLAYLIST_COMMAND_SIZE (MAX_PATH+12)
2011 * Restore the playlist state based on control file commands. Called to
2012 * resume playback after shutdown.
2014 int playlist_resume(void)
2016 struct playlist_info* playlist = &current_playlist;
2017 char *buffer;
2018 size_t buflen;
2019 int nread;
2020 int total_read = 0;
2021 int control_file_size = 0;
2022 bool first = true;
2023 bool sorted = true;
2025 /* use mp3 buffer for maximum load speed */
2026 #if CONFIG_CODEC != SWCODEC
2027 talk_buffer_steal(); /* we use the mp3 buffer, need to tell */
2028 buflen = (audiobufend - audiobuf);
2029 buffer = (char *)audiobuf;
2030 #else
2031 buffer = (char *)audio_get_buffer(false, &buflen);
2032 #endif
2034 empty_playlist(playlist, true);
2036 gui_syncsplash(0, ID2P(LANG_WAIT));
2037 playlist->control_fd = open(playlist->control_filename, O_RDWR);
2038 if (playlist->control_fd < 0)
2040 gui_syncsplash(HZ*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR));
2041 return -1;
2043 playlist->control_created = true;
2045 control_file_size = filesize(playlist->control_fd);
2046 if (control_file_size <= 0)
2048 gui_syncsplash(HZ*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR));
2049 return -1;
2052 /* read a small amount first to get the header */
2053 nread = read(playlist->control_fd, buffer,
2054 PLAYLIST_COMMAND_SIZE<buflen?PLAYLIST_COMMAND_SIZE:buflen);
2055 if(nread <= 0)
2057 gui_syncsplash(HZ*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR));
2058 return -1;
2061 playlist->started = true;
2063 while (1)
2065 int result = 0;
2066 int count;
2067 enum playlist_command current_command = PLAYLIST_COMMAND_COMMENT;
2068 int last_newline = 0;
2069 int str_count = -1;
2070 bool newline = true;
2071 bool exit_loop = false;
2072 char *p = buffer;
2073 char *str1 = NULL;
2074 char *str2 = NULL;
2075 char *str3 = NULL;
2076 unsigned long last_tick = current_tick;
2078 for(count=0; count<nread && !exit_loop; count++,p++)
2080 /* So a splash while we are loading. */
2081 if (current_tick - last_tick > HZ/4)
2083 gui_syncsplash(0, str(LANG_LOADING_PERCENT),
2084 (total_read+count)*100/control_file_size,
2085 str(LANG_OFF_ABORT));
2086 if (action_userabort(TIMEOUT_NOBLOCK))
2088 /* FIXME:
2089 * Not sure how to implement this, somebody more familiar
2090 * with the code, please fix this. */
2092 last_tick = current_tick;
2095 /* Are we on a new line? */
2096 if((*p == '\n') || (*p == '\r'))
2098 *p = '\0';
2100 /* save last_newline in case we need to load more data */
2101 last_newline = count;
2103 switch (current_command)
2105 case PLAYLIST_COMMAND_PLAYLIST:
2107 /* str1=version str2=dir str3=file */
2108 int version;
2110 if (!str1)
2112 result = -1;
2113 exit_loop = true;
2114 break;
2117 if (!str2)
2118 str2 = "";
2120 if (!str3)
2121 str3 = "";
2123 version = atoi(str1);
2125 if (version != PLAYLIST_CONTROL_FILE_VERSION)
2126 return -1;
2128 update_playlist_filename(playlist, str2, str3);
2130 if (str3[0] != '\0')
2132 /* NOTE: add_indices_to_playlist() overwrites the
2133 audiobuf so we need to reload control file
2134 data */
2135 add_indices_to_playlist(playlist, NULL, 0);
2137 else if (str2[0] != '\0')
2139 playlist->in_ram = true;
2140 resume_directory(str2);
2143 /* load the rest of the data */
2144 first = false;
2145 exit_loop = true;
2147 break;
2149 case PLAYLIST_COMMAND_ADD:
2150 case PLAYLIST_COMMAND_QUEUE:
2152 /* str1=position str2=last_position str3=file */
2153 int position, last_position;
2154 bool queue;
2156 if (!str1 || !str2 || !str3)
2158 result = -1;
2159 exit_loop = true;
2160 break;
2163 position = atoi(str1);
2164 last_position = atoi(str2);
2166 queue = (current_command == PLAYLIST_COMMAND_ADD)?
2167 false:true;
2169 /* seek position is based on str3's position in
2170 buffer */
2171 if (add_track_to_playlist(playlist, str3, position,
2172 queue, total_read+(str3-buffer)) < 0)
2173 return -1;
2175 playlist->last_insert_pos = last_position;
2177 break;
2179 case PLAYLIST_COMMAND_DELETE:
2181 /* str1=position */
2182 int position;
2184 if (!str1)
2186 result = -1;
2187 exit_loop = true;
2188 break;
2191 position = atoi(str1);
2193 if (remove_track_from_playlist(playlist, position,
2194 false) < 0)
2195 return -1;
2197 break;
2199 case PLAYLIST_COMMAND_SHUFFLE:
2201 /* str1=seed str2=first_index */
2202 int seed;
2204 if (!str1 || !str2)
2206 result = -1;
2207 exit_loop = true;
2208 break;
2211 if (!sorted)
2213 /* Always sort list before shuffling */
2214 sort_playlist(playlist, false, false);
2217 seed = atoi(str1);
2218 playlist->first_index = atoi(str2);
2220 if (randomise_playlist(playlist, seed, false,
2221 false) < 0)
2222 return -1;
2224 sorted = false;
2225 break;
2227 case PLAYLIST_COMMAND_UNSHUFFLE:
2229 /* str1=first_index */
2230 if (!str1)
2232 result = -1;
2233 exit_loop = true;
2234 break;
2237 playlist->first_index = atoi(str1);
2239 if (sort_playlist(playlist, false, false) < 0)
2240 return -1;
2242 sorted = true;
2243 break;
2245 case PLAYLIST_COMMAND_RESET:
2247 playlist->last_insert_pos = -1;
2248 break;
2250 case PLAYLIST_COMMAND_COMMENT:
2251 default:
2252 break;
2255 newline = true;
2257 /* to ignore any extra newlines */
2258 current_command = PLAYLIST_COMMAND_COMMENT;
2260 else if(newline)
2262 newline = false;
2264 /* first non-comment line must always specify playlist */
2265 if (first && *p != 'P' && *p != '#')
2267 result = -1;
2268 exit_loop = true;
2269 break;
2272 switch (*p)
2274 case 'P':
2275 /* playlist can only be specified once */
2276 if (!first)
2278 result = -1;
2279 exit_loop = true;
2280 break;
2283 current_command = PLAYLIST_COMMAND_PLAYLIST;
2284 break;
2285 case 'A':
2286 current_command = PLAYLIST_COMMAND_ADD;
2287 break;
2288 case 'Q':
2289 current_command = PLAYLIST_COMMAND_QUEUE;
2290 break;
2291 case 'D':
2292 current_command = PLAYLIST_COMMAND_DELETE;
2293 break;
2294 case 'S':
2295 current_command = PLAYLIST_COMMAND_SHUFFLE;
2296 break;
2297 case 'U':
2298 current_command = PLAYLIST_COMMAND_UNSHUFFLE;
2299 break;
2300 case 'R':
2301 current_command = PLAYLIST_COMMAND_RESET;
2302 break;
2303 case '#':
2304 current_command = PLAYLIST_COMMAND_COMMENT;
2305 break;
2306 default:
2307 result = -1;
2308 exit_loop = true;
2309 break;
2312 str_count = -1;
2313 str1 = NULL;
2314 str2 = NULL;
2315 str3 = NULL;
2317 else if(current_command != PLAYLIST_COMMAND_COMMENT)
2319 /* all control file strings are separated with a colon.
2320 Replace the colon with 0 to get proper strings that can be
2321 used by commands above */
2322 if (*p == ':')
2324 *p = '\0';
2325 str_count++;
2327 if ((count+1) < nread)
2329 switch (str_count)
2331 case 0:
2332 str1 = p+1;
2333 break;
2334 case 1:
2335 str2 = p+1;
2336 break;
2337 case 2:
2338 str3 = p+1;
2339 break;
2340 default:
2341 /* allow last string to contain colons */
2342 *p = ':';
2343 break;
2350 if (result < 0)
2352 gui_syncsplash(HZ*2, ID2P(LANG_PLAYLIST_CONTROL_INVALID));
2353 return result;
2356 if (!newline || (exit_loop && count<nread))
2358 if ((total_read + count) >= control_file_size)
2360 /* no newline at end of control file */
2361 gui_syncsplash(HZ*2, ID2P(LANG_PLAYLIST_CONTROL_INVALID));
2362 return -1;
2365 /* We didn't end on a newline or we exited loop prematurely.
2366 Either way, re-read the remainder. */
2367 count = last_newline;
2368 lseek(playlist->control_fd, total_read+count, SEEK_SET);
2371 total_read += count;
2373 if (first)
2374 /* still looking for header */
2375 nread = read(playlist->control_fd, buffer,
2376 PLAYLIST_COMMAND_SIZE<buflen?PLAYLIST_COMMAND_SIZE:buflen);
2377 else
2378 nread = read(playlist->control_fd, buffer, buflen);
2380 /* Terminate on EOF */
2381 if(nread <= 0)
2383 if (global_status.resume_seed >= 0)
2385 /* Apply shuffle command saved in settings */
2386 if (global_status.resume_seed == 0)
2387 sort_playlist(playlist, false, true);
2388 else
2390 if (!sorted)
2391 sort_playlist(playlist, false, false);
2393 randomise_playlist(playlist, global_status.resume_seed,
2394 false, true);
2398 playlist->first_index = global_status.resume_first_index;
2399 break;
2403 #ifdef HAVE_DIRCACHE
2404 queue_post(&playlist_queue, PLAYLIST_LOAD_POINTERS, 0);
2405 #endif
2407 return 0;
2411 * Add track to in_ram playlist. Used when playing directories.
2413 int playlist_add(const char *filename)
2415 struct playlist_info* playlist = &current_playlist;
2416 int len = strlen(filename);
2418 if((len+1 > playlist->buffer_size - playlist->buffer_end_pos) ||
2419 (playlist->amount >= playlist->max_playlist_size))
2421 display_buffer_full();
2422 return -1;
2425 playlist->indices[playlist->amount] = playlist->buffer_end_pos;
2426 #ifdef HAVE_DIRCACHE
2427 playlist->filenames[playlist->amount] = NULL;
2428 #endif
2429 playlist->amount++;
2431 strcpy(&playlist->buffer[playlist->buffer_end_pos], filename);
2432 playlist->buffer_end_pos += len;
2433 playlist->buffer[playlist->buffer_end_pos++] = '\0';
2435 return 0;
2438 /* shuffle newly created playlist using random seed. */
2439 int playlist_shuffle(int random_seed, int start_index)
2441 struct playlist_info* playlist = &current_playlist;
2443 unsigned int seek_pos = 0;
2444 bool start_current = false;
2446 if (start_index >= 0 && global_settings.play_selected)
2448 /* store the seek position before the shuffle */
2449 seek_pos = playlist->indices[start_index];
2450 playlist->index = global_status.resume_first_index =
2451 playlist->first_index = start_index;
2452 start_current = true;
2455 randomise_playlist(playlist, random_seed, start_current, true);
2457 return playlist->index;
2460 /* start playing current playlist at specified index/offset */
2461 int playlist_start(int start_index, int offset)
2463 struct playlist_info* playlist = &current_playlist;
2465 /* Cancel FM radio selection as previous music. For cases where we start
2466 playback without going to the WPS, such as playlist insert.. or
2467 playlist catalog. */
2468 previous_music_is_wps();
2470 playlist->index = start_index;
2472 #if CONFIG_CODEC != SWCODEC
2473 talk_buffer_steal(); /* will use the mp3 buffer */
2474 #endif
2476 playlist->started = true;
2477 sync_control(playlist, false);
2478 audio_play(offset);
2480 return 0;
2483 /* Returns false if 'steps' is out of bounds, else true */
2484 bool playlist_check(int steps)
2486 struct playlist_info* playlist = &current_playlist;
2488 /* always allow folder navigation */
2489 if (global_settings.next_folder && playlist->in_ram)
2490 return true;
2492 int index = get_next_index(playlist, steps, -1);
2494 if (index < 0 && steps >= 0 && global_settings.repeat_mode == REPEAT_SHUFFLE)
2495 index = get_next_index(playlist, steps, REPEAT_ALL);
2497 return (index >= 0);
2500 /* get trackname of track that is "steps" away from current playing track.
2501 NULL is used to identify end of playlist */
2502 char* playlist_peek(int steps)
2504 struct playlist_info* playlist = &current_playlist;
2505 int seek;
2506 char *temp_ptr;
2507 int index;
2508 bool control_file;
2510 index = get_next_index(playlist, steps, -1);
2511 if (index < 0)
2512 return NULL;
2514 control_file = playlist->indices[index] & PLAYLIST_INSERT_TYPE_MASK;
2515 seek = playlist->indices[index] & PLAYLIST_SEEK_MASK;
2517 if (get_filename(playlist, index, seek, control_file, now_playing,
2518 MAX_PATH+1) < 0)
2519 return NULL;
2521 temp_ptr = now_playing;
2523 if (!playlist->in_ram || control_file)
2525 /* remove bogus dirs from beginning of path
2526 (workaround for buggy playlist creation tools) */
2527 while (temp_ptr)
2529 if (file_exists(temp_ptr))
2530 break;
2532 temp_ptr = strchr(temp_ptr+1, '/');
2535 if (!temp_ptr)
2537 /* Even though this is an invalid file, we still need to pass a
2538 file name to the caller because NULL is used to indicate end
2539 of playlist */
2540 return now_playing;
2544 return temp_ptr;
2548 * Update indices as track has changed
2550 int playlist_next(int steps)
2552 struct playlist_info* playlist = &current_playlist;
2553 int index;
2555 if ( (steps > 0)
2556 #ifdef AB_REPEAT_ENABLE
2557 && (global_settings.repeat_mode != REPEAT_AB)
2558 #endif
2559 && (global_settings.repeat_mode != REPEAT_ONE) )
2561 int i, j;
2563 /* We need to delete all the queued songs */
2564 for (i=0, j=steps; i<j; i++)
2566 index = get_next_index(playlist, i, -1);
2568 if (playlist->indices[index] & PLAYLIST_QUEUE_MASK)
2570 remove_track_from_playlist(playlist, index, true);
2571 steps--; /* one less track */
2576 index = get_next_index(playlist, steps, -1);
2578 if (index < 0)
2580 /* end of playlist... or is it */
2581 if (global_settings.repeat_mode == REPEAT_SHUFFLE &&
2582 playlist->amount > 1)
2584 /* Repeat shuffle mode. Re-shuffle playlist and resume play */
2585 playlist->first_index = global_status.resume_first_index = 0;
2586 sort_playlist(playlist, false, false);
2587 randomise_playlist(playlist, current_tick, false, true);
2588 #if CONFIG_CODEC != SWCODEC
2589 playlist_start(0, 0);
2590 #endif
2591 playlist->index = 0;
2592 index = 0;
2594 else if (playlist->in_ram && global_settings.next_folder)
2596 index = create_and_play_dir(steps, true);
2598 if (index >= 0)
2600 playlist->index = index;
2604 return index;
2607 playlist->index = index;
2609 if (playlist->last_insert_pos >= 0 && steps > 0)
2611 /* check to see if we've gone beyond the last inserted track */
2612 int cur = rotate_index(playlist, index);
2613 int last_pos = rotate_index(playlist, playlist->last_insert_pos);
2615 if (cur > last_pos)
2617 /* reset last inserted track */
2618 playlist->last_insert_pos = -1;
2620 if (playlist->control_fd >= 0)
2622 int result = update_control(playlist, PLAYLIST_COMMAND_RESET,
2623 -1, -1, NULL, NULL, NULL);
2625 if (result < 0)
2626 return result;
2628 sync_control(playlist, false);
2633 return index;
2636 /* try playing next or previous folder */
2637 bool playlist_next_dir(int direction)
2639 /* not to mess up real playlists */
2640 if(!current_playlist.in_ram)
2641 return false;
2643 return create_and_play_dir(direction, false) >= 0;
2646 /* Get resume info for current playing song. If return value is -1 then
2647 settings shouldn't be saved. */
2648 int playlist_get_resume_info(int *resume_index)
2650 struct playlist_info* playlist = &current_playlist;
2652 *resume_index = playlist->index;
2654 return 0;
2657 /* Update resume info for current playing song. Returns -1 on error. */
2658 int playlist_update_resume_info(const struct mp3entry* id3)
2660 struct playlist_info* playlist = &current_playlist;
2662 if (id3)
2664 if (global_status.resume_index != playlist->index ||
2665 global_status.resume_offset != id3->offset)
2667 global_status.resume_index = playlist->index;
2668 global_status.resume_offset = id3->offset;
2669 status_save();
2672 else
2674 global_status.resume_index = -1;
2675 global_status.resume_offset = -1;
2676 status_save();
2679 return 0;
2682 /* Returns index of current playing track for display purposes. This value
2683 should not be used for resume purposes as it doesn't represent the actual
2684 index into the playlist */
2685 int playlist_get_display_index(void)
2687 struct playlist_info* playlist = &current_playlist;
2689 /* first_index should always be index 0 for display purposes */
2690 int index = rotate_index(playlist, playlist->index);
2692 return (index+1);
2695 /* returns number of tracks in current playlist */
2696 int playlist_amount(void)
2698 return playlist_amount_ex(NULL);
2702 * Create a new playlist If playlist is not NULL then we're loading a
2703 * playlist off disk for viewing/editing. The index_buffer is used to store
2704 * playlist indices (required for and only used if !current playlist). The
2705 * temp_buffer (if not NULL) is used as a scratchpad when loading indices.
2707 int playlist_create_ex(struct playlist_info* playlist,
2708 const char* dir, const char* file,
2709 void* index_buffer, int index_buffer_size,
2710 void* temp_buffer, int temp_buffer_size)
2712 if (!playlist)
2713 playlist = &current_playlist;
2714 else
2716 /* Initialize playlist structure */
2717 int r = rand() % 10;
2718 playlist->current = false;
2720 /* Use random name for control file */
2721 snprintf(playlist->control_filename, sizeof(playlist->control_filename),
2722 "%s.%d", PLAYLIST_CONTROL_FILE, r);
2723 playlist->fd = -1;
2724 playlist->control_fd = -1;
2726 if (index_buffer)
2728 int num_indices = index_buffer_size / sizeof(int);
2730 #ifdef HAVE_DIRCACHE
2731 num_indices /= 2;
2732 #endif
2733 if (num_indices > global_settings.max_files_in_playlist)
2734 num_indices = global_settings.max_files_in_playlist;
2736 playlist->max_playlist_size = num_indices;
2737 playlist->indices = index_buffer;
2738 #ifdef HAVE_DIRCACHE
2739 playlist->filenames = (const struct dircache_entry **)
2740 &playlist->indices[num_indices];
2741 #endif
2743 else
2745 playlist->max_playlist_size = current_playlist.max_playlist_size;
2746 playlist->indices = current_playlist.indices;
2747 #ifdef HAVE_DIRCACHE
2748 playlist->filenames = current_playlist.filenames;
2749 #endif
2752 playlist->buffer_size = 0;
2753 playlist->buffer = NULL;
2754 mutex_init(&playlist->control_mutex);
2757 new_playlist(playlist, dir, file);
2759 if (file)
2760 /* load the playlist file */
2761 add_indices_to_playlist(playlist, temp_buffer, temp_buffer_size);
2763 return 0;
2767 * Set the specified playlist as the current.
2768 * NOTE: You will get undefined behaviour if something is already playing so
2769 * remember to stop before calling this. Also, this call will
2770 * effectively close your playlist, making it unusable.
2772 int playlist_set_current(struct playlist_info* playlist)
2774 if (!playlist || (check_control(playlist) < 0))
2775 return -1;
2777 empty_playlist(&current_playlist, false);
2779 strncpy(current_playlist.filename, playlist->filename,
2780 sizeof(current_playlist.filename));
2782 current_playlist.utf8 = playlist->utf8;
2783 current_playlist.fd = playlist->fd;
2785 close(playlist->control_fd);
2786 close(current_playlist.control_fd);
2787 remove(current_playlist.control_filename);
2788 if (rename(playlist->control_filename,
2789 current_playlist.control_filename) < 0)
2790 return -1;
2791 current_playlist.control_fd = open(current_playlist.control_filename,
2792 O_RDWR);
2793 if (current_playlist.control_fd < 0)
2794 return -1;
2795 current_playlist.control_created = true;
2797 current_playlist.dirlen = playlist->dirlen;
2799 if (playlist->indices && playlist->indices != current_playlist.indices)
2801 memcpy(current_playlist.indices, playlist->indices,
2802 playlist->max_playlist_size*sizeof(int));
2803 #ifdef HAVE_DIRCACHE
2804 memcpy(current_playlist.filenames, playlist->filenames,
2805 playlist->max_playlist_size*sizeof(int));
2806 #endif
2809 current_playlist.first_index = playlist->first_index;
2810 current_playlist.amount = playlist->amount;
2811 current_playlist.last_insert_pos = playlist->last_insert_pos;
2812 current_playlist.seed = playlist->seed;
2813 current_playlist.shuffle_modified = playlist->shuffle_modified;
2814 current_playlist.deleted = playlist->deleted;
2815 current_playlist.num_inserted_tracks = playlist->num_inserted_tracks;
2817 memcpy(current_playlist.control_cache, playlist->control_cache,
2818 sizeof(current_playlist.control_cache));
2819 current_playlist.num_cached = playlist->num_cached;
2820 current_playlist.pending_control_sync = playlist->pending_control_sync;
2822 return 0;
2826 * Close files and delete control file for non-current playlist.
2828 void playlist_close(struct playlist_info* playlist)
2830 if (!playlist)
2831 return;
2833 if (playlist->fd >= 0)
2834 close(playlist->fd);
2836 if (playlist->control_fd >= 0)
2837 close(playlist->control_fd);
2839 if (playlist->control_created)
2840 remove(playlist->control_filename);
2843 void playlist_sync(struct playlist_info* playlist)
2845 if (!playlist)
2846 playlist = &current_playlist;
2848 sync_control(playlist, false);
2849 if ((audio_status() & AUDIO_STATUS_PLAY) && playlist->started)
2850 audio_flush_and_reload_tracks();
2852 #ifdef HAVE_DIRCACHE
2853 queue_post(&playlist_queue, PLAYLIST_LOAD_POINTERS, 0);
2854 #endif
2858 * Insert track into playlist at specified position (or one of the special
2859 * positions). Returns position where track was inserted or -1 if error.
2861 int playlist_insert_track(struct playlist_info* playlist, const char *filename,
2862 int position, bool queue, bool sync)
2864 int result;
2866 if (!playlist)
2867 playlist = &current_playlist;
2869 if (check_control(playlist) < 0)
2871 gui_syncsplash(HZ*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR));
2872 return -1;
2875 result = add_track_to_playlist(playlist, filename, position, queue, -1);
2877 /* Check if we want manually sync later. For example when adding
2878 * bunch of files from tagcache, syncing after every file wouldn't be
2879 * a good thing to do. */
2880 if (sync && result >= 0)
2881 playlist_sync(playlist);
2883 return result;
2887 * Insert all tracks from specified directory into playlist.
2889 int playlist_insert_directory(struct playlist_info* playlist,
2890 const char *dirname, int position, bool queue,
2891 bool recurse)
2893 int result;
2894 unsigned char *count_str;
2895 struct directory_search_context context;
2897 if (!playlist)
2898 playlist = &current_playlist;
2900 if (check_control(playlist) < 0)
2902 gui_syncsplash(HZ*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR));
2903 return -1;
2906 if (position == PLAYLIST_REPLACE)
2908 if (remove_all_tracks(playlist) == 0)
2909 position = PLAYLIST_INSERT_LAST;
2910 else
2911 return -1;
2914 if (queue)
2915 count_str = ID2P(LANG_PLAYLIST_QUEUE_COUNT);
2916 else
2917 count_str = ID2P(LANG_PLAYLIST_INSERT_COUNT);
2919 display_playlist_count(0, count_str, false);
2921 context.playlist = playlist;
2922 context.position = position;
2923 context.queue = queue;
2924 context.count = 0;
2926 cpu_boost(true);
2928 result = playlist_directory_tracksearch(dirname, recurse,
2929 directory_search_callback, &context);
2931 sync_control(playlist, false);
2933 cpu_boost(false);
2935 display_playlist_count(context.count, count_str, true);
2937 if ((audio_status() & AUDIO_STATUS_PLAY) && playlist->started)
2938 audio_flush_and_reload_tracks();
2940 #ifdef HAVE_DIRCACHE
2941 queue_post(&playlist_queue, PLAYLIST_LOAD_POINTERS, 0);
2942 #endif
2944 return result;
2948 * Insert all tracks from specified playlist into dynamic playlist.
2950 int playlist_insert_playlist(struct playlist_info* playlist, char *filename,
2951 int position, bool queue)
2953 int fd;
2954 int max;
2955 char *temp_ptr;
2956 char *dir;
2957 unsigned char *count_str;
2958 char temp_buf[MAX_PATH+1];
2959 char trackname[MAX_PATH+1];
2960 int count = 0;
2961 int result = 0;
2962 bool utf8 = is_m3u8(filename);
2964 if (!playlist)
2965 playlist = &current_playlist;
2967 if (check_control(playlist) < 0)
2969 gui_syncsplash(HZ*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR));
2970 return -1;
2973 fd = open(filename, O_RDONLY);
2974 if (fd < 0)
2976 gui_syncsplash(HZ*2, ID2P(LANG_PLAYLIST_ACCESS_ERROR));
2977 return -1;
2980 /* we need the directory name for formatting purposes */
2981 dir = filename;
2983 temp_ptr = strrchr(filename+1,'/');
2984 if (temp_ptr)
2985 *temp_ptr = 0;
2986 else
2987 dir = "/";
2989 if (queue)
2990 count_str = ID2P(LANG_PLAYLIST_QUEUE_COUNT);
2991 else
2992 count_str = ID2P(LANG_PLAYLIST_INSERT_COUNT);
2994 display_playlist_count(count, count_str, false);
2996 if (position == PLAYLIST_REPLACE)
2998 if (remove_all_tracks(playlist) == 0)
2999 position = PLAYLIST_INSERT_LAST;
3000 else return -1;
3003 cpu_boost(true);
3005 while ((max = read_line(fd, temp_buf, sizeof(temp_buf))) > 0)
3007 /* user abort */
3008 if (action_userabort(TIMEOUT_NOBLOCK))
3009 break;
3011 if (count == 0 && is_utf8_bom(temp_buf, max))
3013 max -= BOM_SIZE;
3014 memmove(temp_buf, temp_buf + BOM_SIZE, max);
3017 if (temp_buf[0] != '#' && temp_buf[0] != '\0')
3019 int insert_pos;
3021 if (!utf8)
3023 /* Use trackname as a temporay buffer. Note that trackname must
3024 * be as large as temp_buf.
3026 max = convert_m3u(temp_buf, max, sizeof(temp_buf), trackname);
3029 /* we need to format so that relative paths are correctly
3030 handled */
3031 if (format_track_path(trackname, temp_buf, sizeof(trackname), max,
3032 dir) < 0)
3034 result = -1;
3035 break;
3038 insert_pos = add_track_to_playlist(playlist, trackname, position,
3039 queue, -1);
3041 if (insert_pos < 0)
3043 result = -1;
3044 break;
3047 /* Make sure tracks are inserted in correct order if user
3048 requests INSERT_FIRST */
3049 if (position == PLAYLIST_INSERT_FIRST || position >= 0)
3050 position = insert_pos + 1;
3052 count++;
3054 if ((count%PLAYLIST_DISPLAY_COUNT) == 0)
3056 display_playlist_count(count, count_str, false);
3058 if (count == PLAYLIST_DISPLAY_COUNT &&
3059 (audio_status() & AUDIO_STATUS_PLAY) &&
3060 playlist->started)
3061 audio_flush_and_reload_tracks();
3065 /* let the other threads work */
3066 yield();
3069 close(fd);
3071 if (temp_ptr)
3072 *temp_ptr = '/';
3074 sync_control(playlist, false);
3076 cpu_boost(false);
3078 display_playlist_count(count, count_str, true);
3080 if ((audio_status() & AUDIO_STATUS_PLAY) && playlist->started)
3081 audio_flush_and_reload_tracks();
3083 #ifdef HAVE_DIRCACHE
3084 queue_post(&playlist_queue, PLAYLIST_LOAD_POINTERS, 0);
3085 #endif
3087 return result;
3091 * Delete track at specified index. If index is PLAYLIST_DELETE_CURRENT then
3092 * we want to delete the current playing track.
3094 int playlist_delete(struct playlist_info* playlist, int index)
3096 int result = 0;
3098 if (!playlist)
3099 playlist = &current_playlist;
3101 if (check_control(playlist) < 0)
3103 gui_syncsplash(HZ*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR));
3104 return -1;
3107 if (index == PLAYLIST_DELETE_CURRENT)
3108 index = playlist->index;
3110 result = remove_track_from_playlist(playlist, index, true);
3112 if (result != -1 && (audio_status() & AUDIO_STATUS_PLAY) &&
3113 playlist->started)
3114 audio_flush_and_reload_tracks();
3116 return result;
3120 * Move track at index to new_index. Tracks between the two are shifted
3121 * appropriately. Returns 0 on success and -1 on failure.
3123 int playlist_move(struct playlist_info* playlist, int index, int new_index)
3125 int result;
3126 int seek;
3127 bool control_file;
3128 bool queue;
3129 bool current = false;
3130 int r;
3131 char filename[MAX_PATH];
3133 if (!playlist)
3134 playlist = &current_playlist;
3136 if (check_control(playlist) < 0)
3138 gui_syncsplash(HZ*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR));
3139 return -1;
3142 if (index == new_index)
3143 return -1;
3145 if (index == playlist->index)
3146 /* Moving the current track */
3147 current = true;
3149 control_file = playlist->indices[index] & PLAYLIST_INSERT_TYPE_MASK;
3150 queue = playlist->indices[index] & PLAYLIST_QUEUE_MASK;
3151 seek = playlist->indices[index] & PLAYLIST_SEEK_MASK;
3153 if (get_filename(playlist, index, seek, control_file, filename,
3154 sizeof(filename)) < 0)
3155 return -1;
3157 /* Delete track from original position */
3158 result = remove_track_from_playlist(playlist, index, true);
3160 if (result != -1)
3162 /* We want to insert the track at the position that was specified by
3163 new_index. This may be different then new_index because of the
3164 shifting that occurred after the delete */
3165 r = rotate_index(playlist, new_index);
3167 if (r == 0)
3168 /* First index */
3169 new_index = PLAYLIST_PREPEND;
3170 else if (r == playlist->amount)
3171 /* Append */
3172 new_index = PLAYLIST_INSERT_LAST;
3173 else
3174 /* Calculate index of desired position */
3175 new_index = (r+playlist->first_index)%playlist->amount;
3177 result = add_track_to_playlist(playlist, filename, new_index, queue,
3178 -1);
3180 if (result != -1)
3182 if (current)
3184 /* Moved the current track */
3185 switch (new_index)
3187 case PLAYLIST_PREPEND:
3188 playlist->index = playlist->first_index;
3189 break;
3190 case PLAYLIST_INSERT_LAST:
3191 playlist->index = playlist->first_index - 1;
3192 if (playlist->index < 0)
3193 playlist->index += playlist->amount;
3194 break;
3195 default:
3196 playlist->index = new_index;
3197 break;
3201 if ((audio_status() & AUDIO_STATUS_PLAY) && playlist->started)
3202 audio_flush_and_reload_tracks();
3206 #ifdef HAVE_DIRCACHE
3207 queue_post(&playlist_queue, PLAYLIST_LOAD_POINTERS, 0);
3208 #endif
3210 return result;
3213 /* shuffle currently playing playlist */
3214 int playlist_randomise(struct playlist_info* playlist, unsigned int seed,
3215 bool start_current)
3217 int result;
3219 if (!playlist)
3220 playlist = &current_playlist;
3222 check_control(playlist);
3224 result = randomise_playlist(playlist, seed, start_current, true);
3226 if (result != -1 && (audio_status() & AUDIO_STATUS_PLAY) &&
3227 playlist->started)
3228 audio_flush_and_reload_tracks();
3230 return result;
3233 /* sort currently playing playlist */
3234 int playlist_sort(struct playlist_info* playlist, bool start_current)
3236 int result;
3238 if (!playlist)
3239 playlist = &current_playlist;
3241 check_control(playlist);
3243 result = sort_playlist(playlist, start_current, true);
3245 if (result != -1 && (audio_status() & AUDIO_STATUS_PLAY) &&
3246 playlist->started)
3247 audio_flush_and_reload_tracks();
3249 return result;
3252 /* returns true if playlist has been modified */
3253 bool playlist_modified(const struct playlist_info* playlist)
3255 if (!playlist)
3256 playlist = &current_playlist;
3258 if (playlist->shuffle_modified ||
3259 playlist->deleted ||
3260 playlist->num_inserted_tracks > 0)
3261 return true;
3263 return false;
3266 /* returns index of first track in playlist */
3267 int playlist_get_first_index(const struct playlist_info* playlist)
3269 if (!playlist)
3270 playlist = &current_playlist;
3272 return playlist->first_index;
3275 /* returns shuffle seed of playlist */
3276 int playlist_get_seed(const struct playlist_info* playlist)
3278 if (!playlist)
3279 playlist = &current_playlist;
3281 return playlist->seed;
3284 /* returns number of tracks in playlist (includes queued/inserted tracks) */
3285 int playlist_amount_ex(const struct playlist_info* playlist)
3287 if (!playlist)
3288 playlist = &current_playlist;
3290 return playlist->amount;
3293 /* returns full path of playlist (minus extension) */
3294 char *playlist_name(const struct playlist_info* playlist, char *buf,
3295 int buf_size)
3297 char *sep;
3299 if (!playlist)
3300 playlist = &current_playlist;
3302 snprintf(buf, buf_size, "%s", playlist->filename+playlist->dirlen);
3304 if (!buf[0])
3305 return NULL;
3307 /* Remove extension */
3308 sep = strrchr(buf, '.');
3309 if (sep)
3310 *sep = 0;
3312 return buf;
3315 /* returns the playlist filename */
3316 char *playlist_get_name(const struct playlist_info* playlist, char *buf,
3317 int buf_size)
3319 if (!playlist)
3320 playlist = &current_playlist;
3322 snprintf(buf, buf_size, "%s", playlist->filename);
3324 if (!buf[0])
3325 return NULL;
3327 return buf;
3330 /* Fills info structure with information about track at specified index.
3331 Returns 0 on success and -1 on failure */
3332 int playlist_get_track_info(struct playlist_info* playlist, int index,
3333 struct playlist_track_info* info)
3335 int seek;
3336 bool control_file;
3338 if (!playlist)
3339 playlist = &current_playlist;
3341 if (index < 0 || index >= playlist->amount)
3342 return -1;
3344 control_file = playlist->indices[index] & PLAYLIST_INSERT_TYPE_MASK;
3345 seek = playlist->indices[index] & PLAYLIST_SEEK_MASK;
3347 if (get_filename(playlist, index, seek, control_file, info->filename,
3348 sizeof(info->filename)) < 0)
3349 return -1;
3351 info->attr = 0;
3353 if (control_file)
3355 if (playlist->indices[index] & PLAYLIST_QUEUE_MASK)
3356 info->attr |= PLAYLIST_ATTR_QUEUED;
3357 else
3358 info->attr |= PLAYLIST_ATTR_INSERTED;
3362 if (playlist->indices[index] & PLAYLIST_SKIPPED)
3363 info->attr |= PLAYLIST_ATTR_SKIPPED;
3365 info->index = index;
3366 info->display_index = rotate_index(playlist, index) + 1;
3368 return 0;
3371 /* save the current dynamic playlist to specified file */
3372 int playlist_save(struct playlist_info* playlist, char *filename)
3374 int fd;
3375 int i, index;
3376 int count = 0;
3377 char path[MAX_PATH+1];
3378 char tmp_buf[MAX_PATH+1];
3379 int result = 0;
3380 bool overwrite_current = false;
3381 int* index_buf = NULL;
3383 if (!playlist)
3384 playlist = &current_playlist;
3386 if (playlist->amount <= 0)
3387 return -1;
3389 /* use current working directory as base for pathname */
3390 if (format_track_path(path, filename, sizeof(tmp_buf),
3391 strlen(filename)+1, getcwd(NULL, -1)) < 0)
3392 return -1;
3394 if (!strncmp(playlist->filename, path, strlen(path)))
3396 /* Attempting to overwrite current playlist file.*/
3398 if (playlist->buffer_size < (int)(playlist->amount * sizeof(int)))
3400 /* not enough buffer space to store updated indices */
3401 gui_syncsplash(HZ*2, ID2P(LANG_PLAYLIST_ACCESS_ERROR));
3402 return -1;
3405 /* in_ram buffer is unused for m3u files so we'll use for storing
3406 updated indices */
3407 index_buf = (int*)playlist->buffer;
3409 /* use temporary pathname */
3410 snprintf(path, sizeof(path), "%s_temp", playlist->filename);
3411 overwrite_current = true;
3414 fd = open(path, O_CREAT|O_WRONLY|O_TRUNC);
3415 if (fd < 0)
3417 gui_syncsplash(HZ*2, ID2P(LANG_PLAYLIST_ACCESS_ERROR));
3418 return -1;
3421 display_playlist_count(count, ID2P(LANG_PLAYLIST_SAVE_COUNT), false);
3423 cpu_boost(true);
3425 index = playlist->first_index;
3426 for (i=0; i<playlist->amount; i++)
3428 bool control_file;
3429 bool queue;
3430 int seek;
3432 /* user abort */
3433 if (action_userabort(TIMEOUT_NOBLOCK))
3435 result = -1;
3436 break;
3439 control_file = playlist->indices[index] & PLAYLIST_INSERT_TYPE_MASK;
3440 queue = playlist->indices[index] & PLAYLIST_QUEUE_MASK;
3441 seek = playlist->indices[index] & PLAYLIST_SEEK_MASK;
3443 /* Don't save queued files */
3444 if (!queue)
3446 if (get_filename(playlist, index, seek, control_file, tmp_buf,
3447 MAX_PATH+1) < 0)
3449 result = -1;
3450 break;
3453 if (overwrite_current)
3454 index_buf[count] = lseek(fd, 0, SEEK_CUR);
3456 if (fdprintf(fd, "%s\n", tmp_buf) < 0)
3458 gui_syncsplash(HZ*2, ID2P(LANG_PLAYLIST_ACCESS_ERROR));
3459 result = -1;
3460 break;
3463 count++;
3465 if ((count % PLAYLIST_DISPLAY_COUNT) == 0)
3466 display_playlist_count(count, ID2P(LANG_PLAYLIST_SAVE_COUNT),
3467 false);
3469 yield();
3472 index = (index+1)%playlist->amount;
3475 display_playlist_count(count, ID2P(LANG_PLAYLIST_SAVE_COUNT), true);
3477 close(fd);
3479 if (overwrite_current && result >= 0)
3481 result = -1;
3483 mutex_lock(&playlist->control_mutex);
3485 /* Replace the current playlist with the new one and update indices */
3486 close(playlist->fd);
3487 if (remove(playlist->filename) >= 0)
3489 if (rename(path, playlist->filename) >= 0)
3491 playlist->fd = open(playlist->filename, O_RDONLY);
3492 if (playlist->fd >= 0)
3494 index = playlist->first_index;
3495 for (i=0, count=0; i<playlist->amount; i++)
3497 if (!(playlist->indices[index] & PLAYLIST_QUEUE_MASK))
3499 playlist->indices[index] = index_buf[count];
3500 count++;
3502 index = (index+1)%playlist->amount;
3505 /* we need to recreate control because inserted tracks are
3506 now part of the playlist and shuffle has been
3507 invalidated */
3508 result = recreate_control(playlist);
3513 mutex_unlock(&playlist->control_mutex);
3517 cpu_boost(false);
3519 return result;
3523 * Search specified directory for tracks and notify via callback. May be
3524 * called recursively.
3526 int playlist_directory_tracksearch(const char* dirname, bool recurse,
3527 int (*callback)(char*, void*),
3528 void* context)
3530 char buf[MAX_PATH+1];
3531 int result = 0;
3532 int num_files = 0;
3533 int i;
3534 struct entry *files;
3535 struct tree_context* tc = tree_get_context();
3536 int old_dirfilter = *(tc->dirfilter);
3538 if (!callback)
3539 return -1;
3541 /* use the tree browser dircache to load files */
3542 *(tc->dirfilter) = SHOW_ALL;
3544 if (ft_load(tc, dirname) < 0)
3546 gui_syncsplash(HZ*2, ID2P(LANG_PLAYLIST_DIRECTORY_ACCESS_ERROR));
3547 *(tc->dirfilter) = old_dirfilter;
3548 return -1;
3551 files = (struct entry*) tc->dircache;
3552 num_files = tc->filesindir;
3554 /* we've overwritten the dircache so tree browser will need to be
3555 reloaded */
3556 reload_directory();
3558 for (i=0; i<num_files; i++)
3560 /* user abort */
3561 if (action_userabort(TIMEOUT_NOBLOCK))
3563 result = -1;
3564 break;
3567 if (files[i].attr & ATTR_DIRECTORY)
3569 if (recurse)
3571 /* recursively add directories */
3572 snprintf(buf, sizeof(buf), "%s/%s", dirname, files[i].name);
3573 result = playlist_directory_tracksearch(buf, recurse,
3574 callback, context);
3575 if (result < 0)
3576 break;
3578 /* we now need to reload our current directory */
3579 if(ft_load(tc, dirname) < 0)
3581 result = -1;
3582 break;
3585 files = (struct entry*) tc->dircache;
3586 num_files = tc->filesindir;
3587 if (!num_files)
3589 result = -1;
3590 break;
3593 else
3594 continue;
3596 else if ((files[i].attr & FILE_ATTR_MASK) == FILE_ATTR_AUDIO)
3598 snprintf(buf, sizeof(buf), "%s/%s", dirname, files[i].name);
3600 if (callback(buf, context) != 0)
3602 result = -1;
3603 break;
3606 /* let the other threads work */
3607 yield();
3611 /* restore dirfilter */
3612 *(tc->dirfilter) = old_dirfilter;
3614 return result;