make the dialog box asking about replacing an up-to-date bootloader less confusing.
[Rockbox.git] / apps / playlist.c
blobad60c84d38f658a1871e154ecfa171ea2e72ee67
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 bool changing_dir = false;
148 static struct playlist_info current_playlist;
149 static char now_playing[MAX_PATH+1];
151 static void empty_playlist(struct playlist_info* playlist, bool resume);
152 static void new_playlist(struct playlist_info* playlist, const char *dir,
153 const char *file);
154 static void create_control(struct playlist_info* playlist);
155 static int check_control(struct playlist_info* playlist);
156 static int recreate_control(struct playlist_info* playlist);
157 static void update_playlist_filename(struct playlist_info* playlist,
158 const char *dir, const char *file);
159 static int add_indices_to_playlist(struct playlist_info* playlist,
160 char* buffer, size_t buflen);
161 static int add_track_to_playlist(struct playlist_info* playlist,
162 const char *filename, int position,
163 bool queue, int seek_pos);
164 static int directory_search_callback(char* filename, void* context);
165 static int remove_track_from_playlist(struct playlist_info* playlist,
166 int position, bool write);
167 static int randomise_playlist(struct playlist_info* playlist,
168 unsigned int seed, bool start_current,
169 bool write);
170 static int sort_playlist(struct playlist_info* playlist, bool start_current,
171 bool write);
172 static int get_next_index(const struct playlist_info* playlist, int steps,
173 int repeat_mode);
174 static void find_and_set_playlist_index(struct playlist_info* playlist,
175 unsigned int seek);
176 static int compare(const void* p1, const void* p2);
177 static int get_filename(struct playlist_info* playlist, int index, int seek,
178 bool control_file, char *buf, int buf_length);
179 static int get_next_directory(char *dir);
180 static int get_next_dir(char *dir, bool is_forward, bool recursion);
181 static int get_previous_directory(char *dir);
182 static int check_subdir_for_music(char *dir, char *subdir);
183 static int format_track_path(char *dest, char *src, int buf_length, int max,
184 char *dir);
185 static void display_playlist_count(int count, const unsigned char *fmt,
186 bool final);
187 static void display_buffer_full(void);
188 static int flush_cached_control(struct playlist_info* playlist);
189 static int update_control(struct playlist_info* playlist,
190 enum playlist_command command, int i1, int i2,
191 const char* s1, const char* s2, void* data);
192 static void sync_control(struct playlist_info* playlist, bool force);
193 static int rotate_index(const struct playlist_info* playlist, int index);
195 #ifdef HAVE_DIRCACHE
196 #define PLAYLIST_LOAD_POINTERS 1
198 static struct event_queue playlist_queue;
199 static long playlist_stack[(DEFAULT_STACK_SIZE + 0x800)/sizeof(long)];
200 static const char playlist_thread_name[] = "playlist cachectrl";
201 #endif
204 * remove any files and indices associated with the playlist
206 static void empty_playlist(struct playlist_info* playlist, bool resume)
208 playlist->filename[0] = '\0';
209 playlist->utf8 = true;
211 if(playlist->fd >= 0)
212 /* If there is an already open playlist, close it. */
213 close(playlist->fd);
214 playlist->fd = -1;
216 if(playlist->control_fd >= 0)
217 close(playlist->control_fd);
218 playlist->control_fd = -1;
219 playlist->control_created = false;
221 playlist->in_ram = false;
223 if (playlist->buffer)
224 playlist->buffer[0] = 0;
226 playlist->buffer_end_pos = 0;
228 playlist->index = 0;
229 playlist->first_index = 0;
230 playlist->amount = 0;
231 playlist->last_insert_pos = -1;
232 playlist->seed = 0;
233 playlist->shuffle_modified = false;
234 playlist->deleted = false;
235 playlist->num_inserted_tracks = 0;
236 playlist->started = false;
238 playlist->num_cached = 0;
239 playlist->pending_control_sync = false;
241 if (!resume && playlist->current)
243 /* start with fresh playlist control file when starting new
244 playlist */
245 create_control(playlist);
247 /* Reset resume settings */
248 global_status.resume_first_index = 0;
249 global_status.resume_seed = -1;
254 * Initialize a new playlist for viewing/editing/playing. dir is the
255 * directory where the playlist is located and file is the filename.
257 static void new_playlist(struct playlist_info* playlist, const char *dir,
258 const char *file)
260 empty_playlist(playlist, false);
262 if (!file)
264 file = "";
266 if (dir && playlist->current) /* !current cannot be in_ram */
267 playlist->in_ram = true;
268 else
269 dir = ""; /* empty playlist */
272 update_playlist_filename(playlist, dir, file);
274 if (playlist->control_fd >= 0)
276 update_control(playlist, PLAYLIST_COMMAND_PLAYLIST,
277 PLAYLIST_CONTROL_FILE_VERSION, -1, dir, file, NULL);
278 sync_control(playlist, false);
283 * create control file for playlist
285 static void create_control(struct playlist_info* playlist)
287 playlist->control_fd = open(playlist->control_filename,
288 O_CREAT|O_RDWR|O_TRUNC);
289 if (playlist->control_fd < 0)
291 if (check_rockboxdir())
293 cond_talk_ids_fq(LANG_PLAYLIST_CONTROL_ACCESS_ERROR);
294 gui_syncsplash(HZ*2, (unsigned char *)"%s (%d)",
295 str(LANG_PLAYLIST_CONTROL_ACCESS_ERROR),
296 playlist->control_fd);
298 playlist->control_created = false;
300 else
302 playlist->control_created = true;
307 * validate the control file. This may include creating/initializing it if
308 * necessary;
310 static int check_control(struct playlist_info* playlist)
312 if (!playlist->control_created)
314 create_control(playlist);
316 if (playlist->control_fd >= 0)
318 char* dir = playlist->filename;
319 char* file = playlist->filename+playlist->dirlen;
320 char c = playlist->filename[playlist->dirlen-1];
322 playlist->filename[playlist->dirlen-1] = '\0';
324 update_control(playlist, PLAYLIST_COMMAND_PLAYLIST,
325 PLAYLIST_CONTROL_FILE_VERSION, -1, dir, file, NULL);
326 sync_control(playlist, false);
327 playlist->filename[playlist->dirlen-1] = c;
331 if (playlist->control_fd < 0)
332 return -1;
334 return 0;
338 * recreate the control file based on current playlist entries
340 static int recreate_control(struct playlist_info* playlist)
342 char temp_file[MAX_PATH+1];
343 int temp_fd = -1;
344 int i;
345 int result = 0;
347 if(playlist->control_fd >= 0)
349 char* dir = playlist->filename;
350 char* file = playlist->filename+playlist->dirlen;
351 char c = playlist->filename[playlist->dirlen-1];
353 close(playlist->control_fd);
355 snprintf(temp_file, sizeof(temp_file), "%s_temp",
356 playlist->control_filename);
358 if (rename(playlist->control_filename, temp_file) < 0)
359 return -1;
361 temp_fd = open(temp_file, O_RDONLY);
362 if (temp_fd < 0)
363 return -1;
365 playlist->control_fd = open(playlist->control_filename,
366 O_CREAT|O_RDWR|O_TRUNC);
367 if (playlist->control_fd < 0)
368 return -1;
370 playlist->filename[playlist->dirlen-1] = '\0';
372 /* cannot call update_control() because of mutex */
373 result = fdprintf(playlist->control_fd, "P:%d:%s:%s\n",
374 PLAYLIST_CONTROL_FILE_VERSION, dir, file);
376 playlist->filename[playlist->dirlen-1] = c;
378 if (result < 0)
380 close(temp_fd);
381 return result;
385 playlist->seed = 0;
386 playlist->shuffle_modified = false;
387 playlist->deleted = false;
388 playlist->num_inserted_tracks = 0;
390 if (playlist->current)
392 global_status.resume_seed = -1;
393 status_save();
396 for (i=0; i<playlist->amount; i++)
398 if (playlist->indices[i] & PLAYLIST_INSERT_TYPE_MASK)
400 bool queue = playlist->indices[i] & PLAYLIST_QUEUE_MASK;
401 char inserted_file[MAX_PATH+1];
403 lseek(temp_fd, playlist->indices[i] & PLAYLIST_SEEK_MASK,
404 SEEK_SET);
405 read_line(temp_fd, inserted_file, sizeof(inserted_file));
407 result = fdprintf(playlist->control_fd, "%c:%d:%d:",
408 queue?'Q':'A', i, playlist->last_insert_pos);
409 if (result > 0)
411 /* save the position in file where name is written */
412 int seek_pos = lseek(playlist->control_fd, 0, SEEK_CUR);
414 result = fdprintf(playlist->control_fd, "%s\n",
415 inserted_file);
417 playlist->indices[i] =
418 (playlist->indices[i] & ~PLAYLIST_SEEK_MASK) | seek_pos;
421 if (result < 0)
422 break;
424 playlist->num_inserted_tracks++;
428 close(temp_fd);
429 remove(temp_file);
430 fsync(playlist->control_fd);
432 if (result < 0)
433 return result;
435 return 0;
439 * store directory and name of playlist file
441 static void update_playlist_filename(struct playlist_info* playlist,
442 const char *dir, const char *file)
444 char *sep="";
445 int dirlen = strlen(dir);
446 int filelen = strlen(file);
448 /* Default to utf8 unless explicitly told otherwise. */
449 playlist->utf8 = !(filelen > 4 && strcasecmp(&file[filelen - 4], ".m3u") == 0);
451 /* If the dir does not end in trailing slash, we use a separator.
452 Otherwise we don't. */
453 if('/' != dir[dirlen-1])
455 sep="/";
456 dirlen++;
459 playlist->dirlen = dirlen;
461 snprintf(playlist->filename, sizeof(playlist->filename),
462 "%s%s%s", dir, sep, file);
466 * calculate track offsets within a playlist file
468 static int add_indices_to_playlist(struct playlist_info* playlist,
469 char* buffer, size_t buflen)
471 unsigned int nread;
472 unsigned int i = 0;
473 unsigned int count = 0;
474 bool store_index;
475 unsigned char *p;
476 int result = 0;
478 if(-1 == playlist->fd)
479 playlist->fd = open(playlist->filename, O_RDONLY);
480 if(playlist->fd < 0)
481 return -1; /* failure */
483 #ifdef HAVE_LCD_BITMAP
484 if(global_settings.statusbar)
485 lcd_setmargins(0, STATUSBAR_HEIGHT);
486 else
487 lcd_setmargins(0, 0);
488 #endif
489 gui_syncsplash(0, ID2P(LANG_WAIT));
491 if (!buffer)
493 /* use mp3 buffer for maximum load speed */
494 audio_stop();
495 #if CONFIG_CODEC != SWCODEC
496 talk_buffer_steal(); /* we use the mp3 buffer, need to tell */
497 buflen = (audiobufend - audiobuf);
498 buffer = (char *)audiobuf;
499 #else
500 buffer = (char *)audio_get_buffer(false, &buflen);
501 #endif
504 store_index = true;
506 while(1)
508 nread = read(playlist->fd, buffer, buflen);
509 /* Terminate on EOF */
510 if(nread <= 0)
511 break;
513 p = (unsigned char *)buffer;
515 /* utf8 BOM at beginning of file? */
516 if(i == 0 && nread > 3
517 && *p == 0xef && *(p+1) == 0xbb && *(p+2) == 0xbf) {
518 nread -= 3;
519 p += 3;
520 i += 3;
521 playlist->utf8 = true; /* Override any earlier indication. */
524 for(count=0; count < nread; count++,p++) {
526 /* Are we on a new line? */
527 if((*p == '\n') || (*p == '\r'))
529 store_index = true;
531 else if(store_index)
533 store_index = false;
535 if(*p != '#')
537 if ( playlist->amount >= playlist->max_playlist_size ) {
538 display_buffer_full();
539 result = -1;
540 goto exit;
543 /* Store a new entry */
544 playlist->indices[ playlist->amount ] = i+count;
545 #ifdef HAVE_DIRCACHE
546 if (playlist->filenames)
547 playlist->filenames[ playlist->amount ] = NULL;
548 #endif
549 playlist->amount++;
554 i+= count;
557 exit:
558 #ifdef HAVE_DIRCACHE
559 queue_post(&playlist_queue, PLAYLIST_LOAD_POINTERS, 0);
560 #endif
562 return result;
566 * Removes all tracks, from the playlist, leaving the presently playing
567 * track queued.
569 int remove_all_tracks(struct playlist_info *playlist)
571 int result;
573 if (playlist == NULL)
574 playlist = &current_playlist;
576 while (playlist->index > 0)
577 if ((result = remove_track_from_playlist(playlist, 0, true)) < 0)
578 return result;
580 while (playlist->amount > 1)
581 if ((result = remove_track_from_playlist(playlist, 1, true)) < 0)
582 return result;
584 if (playlist->amount == 1) {
585 playlist->indices[0] |= PLAYLIST_QUEUED;
588 return 0;
593 * Add track to playlist at specified position. There are five special
594 * positions that can be specified:
595 * PLAYLIST_PREPEND - Add track at beginning of playlist
596 * PLAYLIST_INSERT - Add track after current song. NOTE: If
597 * there are already inserted tracks then track
598 * is added to the end of the insertion list
599 * PLAYLIST_INSERT_FIRST - Add track immediately after current song, no
600 * matter what other tracks have been inserted
601 * PLAYLIST_INSERT_LAST - Add track to end of playlist
602 * PLAYLIST_INSERT_SHUFFLED - Add track at some random point between the
603 * current playing track and end of playlist
604 * PLAYLIST_REPLACE - Erase current playlist, Cue the current track
605 * and inster this track at the end.
607 static int add_track_to_playlist(struct playlist_info* playlist,
608 const char *filename, int position,
609 bool queue, int seek_pos)
611 int insert_position, orig_position;
612 unsigned long flags = PLAYLIST_INSERT_TYPE_INSERT;
613 int i;
615 insert_position = orig_position = position;
617 if (playlist->amount >= playlist->max_playlist_size)
619 display_buffer_full();
620 return -1;
623 switch (position)
625 case PLAYLIST_PREPEND:
626 position = insert_position = playlist->first_index;
627 break;
628 case PLAYLIST_INSERT:
629 /* if there are already inserted tracks then add track to end of
630 insertion list else add after current playing track */
631 if (playlist->last_insert_pos >= 0 &&
632 playlist->last_insert_pos < playlist->amount &&
633 (playlist->indices[playlist->last_insert_pos]&
634 PLAYLIST_INSERT_TYPE_MASK) == PLAYLIST_INSERT_TYPE_INSERT)
635 position = insert_position = playlist->last_insert_pos+1;
636 else if (playlist->amount > 0)
637 position = insert_position = playlist->index + 1;
638 else
639 position = insert_position = 0;
641 if (playlist->started)
642 playlist->last_insert_pos = position;
643 break;
644 case PLAYLIST_INSERT_FIRST:
645 if (playlist->amount > 0)
646 position = insert_position = playlist->index + 1;
647 else
648 position = insert_position = 0;
650 if (playlist->last_insert_pos < 0 && playlist->started)
651 playlist->last_insert_pos = position;
652 break;
653 case PLAYLIST_INSERT_LAST:
654 if (playlist->first_index > 0)
655 position = insert_position = playlist->first_index;
656 else
657 position = insert_position = playlist->amount;
658 break;
659 case PLAYLIST_INSERT_SHUFFLED:
661 if (playlist->started)
663 int offset;
664 int n = playlist->amount -
665 rotate_index(playlist, playlist->index);
667 if (n > 0)
668 offset = rand() % n;
669 else
670 offset = 0;
672 position = playlist->index + offset + 1;
673 if (position >= playlist->amount)
674 position -= playlist->amount;
676 insert_position = position;
678 else
679 position = insert_position = (rand() % (playlist->amount+1));
680 break;
682 case PLAYLIST_REPLACE:
683 if (remove_all_tracks(playlist) < 0)
684 return -1;
686 position = insert_position = playlist->index + 1;
687 break;
690 if (queue)
691 flags |= PLAYLIST_QUEUED;
693 /* shift indices so that track can be added */
694 for (i=playlist->amount; i>insert_position; i--)
696 playlist->indices[i] = playlist->indices[i-1];
697 #ifdef HAVE_DIRCACHE
698 if (playlist->filenames)
699 playlist->filenames[i] = playlist->filenames[i-1];
700 #endif
703 /* update stored indices if needed */
704 if (playlist->amount > 0 && insert_position <= playlist->index &&
705 playlist->started)
706 playlist->index++;
708 if (playlist->amount > 0 && insert_position <= playlist->first_index &&
709 orig_position != PLAYLIST_PREPEND && playlist->started)
711 playlist->first_index++;
713 if (seek_pos < 0 && playlist->current)
715 global_status.resume_first_index = playlist->first_index;
716 status_save();
720 if (insert_position < playlist->last_insert_pos ||
721 (insert_position == playlist->last_insert_pos && position < 0))
722 playlist->last_insert_pos++;
724 if (seek_pos < 0 && playlist->control_fd >= 0)
726 int result = update_control(playlist,
727 (queue?PLAYLIST_COMMAND_QUEUE:PLAYLIST_COMMAND_ADD), position,
728 playlist->last_insert_pos, filename, NULL, &seek_pos);
730 if (result < 0)
731 return result;
734 playlist->indices[insert_position] = flags | seek_pos;
736 #ifdef HAVE_DIRCACHE
737 if (playlist->filenames)
738 playlist->filenames[insert_position] = NULL;
739 #endif
741 playlist->amount++;
742 playlist->num_inserted_tracks++;
744 return insert_position;
748 * Callback for playlist_directory_tracksearch to insert track into
749 * playlist.
751 static int directory_search_callback(char* filename, void* context)
753 struct directory_search_context* c =
754 (struct directory_search_context*) context;
755 int insert_pos;
757 insert_pos = add_track_to_playlist(c->playlist, filename, c->position,
758 c->queue, -1);
760 if (insert_pos < 0)
761 return -1;
763 (c->count)++;
765 /* Make sure tracks are inserted in correct order if user requests
766 INSERT_FIRST */
767 if (c->position == PLAYLIST_INSERT_FIRST || c->position >= 0)
768 c->position = insert_pos + 1;
770 if (((c->count)%PLAYLIST_DISPLAY_COUNT) == 0)
772 unsigned char* count_str;
774 if (c->queue)
775 count_str = ID2P(LANG_PLAYLIST_QUEUE_COUNT);
776 else
777 count_str = ID2P(LANG_PLAYLIST_INSERT_COUNT);
779 display_playlist_count(c->count, count_str, false);
781 if ((c->count) == PLAYLIST_DISPLAY_COUNT &&
782 (audio_status() & AUDIO_STATUS_PLAY) &&
783 c->playlist->started)
784 audio_flush_and_reload_tracks();
787 return 0;
791 * remove track at specified position
793 static int remove_track_from_playlist(struct playlist_info* playlist,
794 int position, bool write)
796 int i;
797 bool inserted;
799 if (playlist->amount <= 0)
800 return -1;
802 inserted = playlist->indices[position] & PLAYLIST_INSERT_TYPE_MASK;
804 /* shift indices now that track has been removed */
805 for (i=position; i<playlist->amount; i++)
807 playlist->indices[i] = playlist->indices[i+1];
808 #ifdef HAVE_DIRCACHE
809 if (playlist->filenames)
810 playlist->filenames[i] = playlist->filenames[i+1];
811 #endif
814 playlist->amount--;
816 if (inserted)
817 playlist->num_inserted_tracks--;
818 else
819 playlist->deleted = true;
821 /* update stored indices if needed */
822 if (position < playlist->index)
823 playlist->index--;
825 if (position < playlist->first_index)
827 playlist->first_index--;
829 if (write)
831 global_status.resume_first_index = playlist->first_index;
832 status_save();
836 if (position <= playlist->last_insert_pos)
837 playlist->last_insert_pos--;
839 if (write && playlist->control_fd >= 0)
841 int result = update_control(playlist, PLAYLIST_COMMAND_DELETE,
842 position, -1, NULL, NULL, NULL);
844 if (result < 0)
845 return result;
847 sync_control(playlist, false);
850 return 0;
854 * randomly rearrange the array of indices for the playlist. If start_current
855 * is true then update the index to the new index of the current playing track
857 static int randomise_playlist(struct playlist_info* playlist,
858 unsigned int seed, bool start_current,
859 bool write)
861 int count;
862 int candidate;
863 long store;
864 unsigned int current = playlist->indices[playlist->index];
866 /* seed 0 is used to identify sorted playlist for resume purposes */
867 if (seed == 0)
868 seed = 1;
870 /* seed with the given seed */
871 srand(seed);
873 /* randomise entire indices list */
874 for(count = playlist->amount - 1; count >= 0; count--)
876 /* the rand is from 0 to RAND_MAX, so adjust to our value range */
877 candidate = rand() % (count + 1);
879 /* now swap the values at the 'count' and 'candidate' positions */
880 store = playlist->indices[candidate];
881 playlist->indices[candidate] = playlist->indices[count];
882 playlist->indices[count] = store;
883 #ifdef HAVE_DIRCACHE
884 if (playlist->filenames)
886 store = (long)playlist->filenames[candidate];
887 playlist->filenames[candidate] = playlist->filenames[count];
888 playlist->filenames[count] = (struct dircache_entry *)store;
890 #endif
893 if (start_current)
894 find_and_set_playlist_index(playlist, current);
896 /* indices have been moved so last insert position is no longer valid */
897 playlist->last_insert_pos = -1;
899 playlist->seed = seed;
900 if (playlist->num_inserted_tracks > 0 || playlist->deleted)
901 playlist->shuffle_modified = true;
903 if (write)
905 update_control(playlist, PLAYLIST_COMMAND_SHUFFLE, seed,
906 playlist->first_index, NULL, NULL, NULL);
907 global_status.resume_seed = seed;
908 status_save();
911 return 0;
915 * Sort the array of indices for the playlist. If start_current is true then
916 * set the index to the new index of the current song.
918 static int sort_playlist(struct playlist_info* playlist, bool start_current,
919 bool write)
921 unsigned int current = playlist->indices[playlist->index];
923 if (playlist->amount > 0)
924 qsort(playlist->indices, playlist->amount,
925 sizeof(playlist->indices[0]), compare);
927 #ifdef HAVE_DIRCACHE
928 /** We need to re-check the song names from disk because qsort can't
929 * sort two arrays at once :/
930 * FIXME: Please implement a better way to do this. */
931 memset(playlist->filenames, 0, playlist->max_playlist_size * sizeof(int));
932 queue_post(&playlist_queue, PLAYLIST_LOAD_POINTERS, 0);
933 #endif
935 if (start_current)
936 find_and_set_playlist_index(playlist, current);
938 /* indices have been moved so last insert position is no longer valid */
939 playlist->last_insert_pos = -1;
941 if (!playlist->num_inserted_tracks && !playlist->deleted)
942 playlist->shuffle_modified = false;
943 if (write && playlist->control_fd >= 0)
945 update_control(playlist, PLAYLIST_COMMAND_UNSHUFFLE,
946 playlist->first_index, -1, NULL, NULL, NULL);
947 global_status.resume_seed = 0;
948 status_save();
951 return 0;
954 /* Calculate how many steps we have to really step when skipping entries
955 * marked as bad.
957 static int calculate_step_count(const struct playlist_info *playlist, int steps)
959 int i, count, direction;
960 int index;
961 int stepped_count = 0;
963 if (steps < 0)
965 direction = -1;
966 count = -steps;
968 else
970 direction = 1;
971 count = steps;
974 index = playlist->index;
975 i = 0;
976 do {
977 /* Boundary check */
978 if (index < 0)
979 index += playlist->amount;
980 if (index >= playlist->amount)
981 index -= playlist->amount;
983 /* Check if we found a bad entry. */
984 if (playlist->indices[index] & PLAYLIST_SKIPPED)
986 steps += direction;
987 /* Are all entries bad? */
988 if (stepped_count++ > playlist->amount)
989 break ;
991 else
992 i++;
994 index += direction;
995 } while (i <= count);
997 return steps;
1000 /* Marks the index of the track to be skipped that is "steps" away from
1001 * current playing track.
1003 void playlist_skip_entry(struct playlist_info *playlist, int steps)
1005 int index;
1007 if (playlist == NULL)
1008 playlist = &current_playlist;
1010 /* need to account for already skipped tracks */
1011 steps = calculate_step_count(playlist, steps);
1013 index = playlist->index + steps;
1014 if (index < 0)
1015 index += playlist->amount;
1016 else if (index >= playlist->amount)
1017 index -= playlist->amount;
1019 playlist->indices[index] |= PLAYLIST_SKIPPED;
1023 * returns the index of the track that is "steps" away from current playing
1024 * track.
1026 static int get_next_index(const struct playlist_info* playlist, int steps,
1027 int repeat_mode)
1029 int current_index = playlist->index;
1030 int next_index = -1;
1032 if (playlist->amount <= 0)
1033 return -1;
1035 if (repeat_mode == -1)
1036 repeat_mode = global_settings.repeat_mode;
1038 if (repeat_mode == REPEAT_SHUFFLE && playlist->amount <= 1)
1039 repeat_mode = REPEAT_ALL;
1041 steps = calculate_step_count(playlist, steps);
1042 switch (repeat_mode)
1044 case REPEAT_SHUFFLE:
1045 /* Treat repeat shuffle just like repeat off. At end of playlist,
1046 play will be resumed in playlist_next() */
1047 case REPEAT_OFF:
1049 current_index = rotate_index(playlist, current_index);
1050 next_index = current_index+steps;
1051 if ((next_index < 0) || (next_index >= playlist->amount))
1052 next_index = -1;
1053 else
1054 next_index = (next_index+playlist->first_index) %
1055 playlist->amount;
1057 break;
1060 case REPEAT_ONE:
1061 #ifdef AB_REPEAT_ENABLE
1062 case REPEAT_AB:
1063 #endif
1064 next_index = current_index;
1065 break;
1067 case REPEAT_ALL:
1068 default:
1070 next_index = (current_index+steps) % playlist->amount;
1071 while (next_index < 0)
1072 next_index += playlist->amount;
1074 if (steps >= playlist->amount)
1076 int i, index;
1078 index = next_index;
1079 next_index = -1;
1081 /* second time around so skip the queued files */
1082 for (i=0; i<playlist->amount; i++)
1084 if (playlist->indices[index] & PLAYLIST_QUEUE_MASK)
1085 index = (index+1) % playlist->amount;
1086 else
1088 next_index = index;
1089 break;
1093 break;
1097 /* No luck if the whole playlist was bad. */
1098 if (playlist->indices[next_index] & PLAYLIST_SKIPPED)
1099 return -1;
1101 return next_index;
1105 * Search for the seek track and set appropriate indices. Used after shuffle
1106 * to make sure the current index is still pointing to correct track.
1108 static void find_and_set_playlist_index(struct playlist_info* playlist,
1109 unsigned int seek)
1111 int i;
1113 /* Set the index to the current song */
1114 for (i=0; i<playlist->amount; i++)
1116 if (playlist->indices[i] == seek)
1118 playlist->index = playlist->first_index = i;
1120 if (playlist->current)
1122 global_status.resume_first_index = i;
1123 status_save();
1126 break;
1132 * used to sort track indices. Sort order is as follows:
1133 * 1. Prepended tracks (in prepend order)
1134 * 2. Playlist/directory tracks (in playlist order)
1135 * 3. Inserted/Appended tracks (in insert order)
1137 static int compare(const void* p1, const void* p2)
1139 unsigned long* e1 = (unsigned long*) p1;
1140 unsigned long* e2 = (unsigned long*) p2;
1141 unsigned long flags1 = *e1 & PLAYLIST_INSERT_TYPE_MASK;
1142 unsigned long flags2 = *e2 & PLAYLIST_INSERT_TYPE_MASK;
1144 if (flags1 == flags2)
1145 return (*e1 & PLAYLIST_SEEK_MASK) - (*e2 & PLAYLIST_SEEK_MASK);
1146 else if (flags1 == PLAYLIST_INSERT_TYPE_PREPEND ||
1147 flags2 == PLAYLIST_INSERT_TYPE_APPEND)
1148 return -1;
1149 else if (flags1 == PLAYLIST_INSERT_TYPE_APPEND ||
1150 flags2 == PLAYLIST_INSERT_TYPE_PREPEND)
1151 return 1;
1152 else if (flags1 && flags2)
1153 return (*e1 & PLAYLIST_SEEK_MASK) - (*e2 & PLAYLIST_SEEK_MASK);
1154 else
1155 return *e1 - *e2;
1158 #ifdef HAVE_DIRCACHE
1160 * Thread to update filename pointers to dircache on background
1161 * without affecting playlist load up performance. This thread also flushes
1162 * any pending control commands when the disk spins up.
1164 static void playlist_thread(void)
1166 struct queue_event ev;
1167 bool dirty_pointers = false;
1168 static char tmp[MAX_PATH+1];
1170 struct playlist_info *playlist;
1171 int index;
1172 int seek;
1173 bool control_file;
1175 int sleep_time = 5;
1177 #ifndef HAVE_FLASH_STORAGE
1178 if (global_settings.disk_spindown > 1 &&
1179 global_settings.disk_spindown <= 5)
1180 sleep_time = global_settings.disk_spindown - 1;
1181 #endif
1183 while (1)
1185 queue_wait_w_tmo(&playlist_queue, &ev, HZ*sleep_time);
1187 switch (ev.id)
1189 case PLAYLIST_LOAD_POINTERS:
1190 dirty_pointers = true;
1191 break ;
1193 /* Start the background scanning after either the disk spindown
1194 timeout or 5s, whichever is less */
1195 case SYS_TIMEOUT:
1196 playlist = &current_playlist;
1198 if (playlist->control_fd >= 0
1199 # ifndef SIMULATOR
1200 && ata_disk_is_active()
1201 # endif
1204 if (playlist->num_cached > 0)
1206 mutex_lock(&playlist->control_mutex);
1207 flush_cached_control(playlist);
1208 mutex_unlock(&playlist->control_mutex);
1211 sync_control(playlist, true);
1214 if (!dirty_pointers)
1215 break ;
1217 if (!dircache_is_enabled() || !playlist->filenames
1218 || playlist->amount <= 0)
1219 break ;
1221 #ifdef HAVE_ADJUSTABLE_CPU_FREQ
1222 cpu_boost(true);
1223 #endif
1224 for (index = 0; index < playlist->amount
1225 && queue_empty(&playlist_queue); index++)
1227 /* Process only pointers that are not already loaded. */
1228 if (playlist->filenames[index])
1229 continue ;
1231 control_file = playlist->indices[index] & PLAYLIST_INSERT_TYPE_MASK;
1232 seek = playlist->indices[index] & PLAYLIST_SEEK_MASK;
1234 /* Load the filename from playlist file. */
1235 if (get_filename(playlist, index, seek, control_file, tmp,
1236 sizeof(tmp)) < 0)
1237 break ;
1239 /* Set the dircache entry pointer. */
1240 playlist->filenames[index] = dircache_get_entry_ptr(tmp);
1242 /* And be on background so user doesn't notice any delays. */
1243 yield();
1246 #ifdef HAVE_ADJUSTABLE_CPU_FREQ
1247 cpu_boost(false);
1248 #endif
1249 dirty_pointers = false;
1250 break ;
1252 #ifndef SIMULATOR
1253 case SYS_USB_CONNECTED:
1254 usb_acknowledge(SYS_USB_CONNECTED_ACK);
1255 usb_wait_for_disconnect(&playlist_queue);
1256 break ;
1257 #endif
1261 #endif
1264 * gets pathname for track at seek index
1266 static int get_filename(struct playlist_info* playlist, int index, int seek,
1267 bool control_file, char *buf, int buf_length)
1269 int fd;
1270 int max = -1;
1271 char tmp_buf[MAX_PATH+1];
1272 char dir_buf[MAX_PATH+1];
1274 if (buf_length > MAX_PATH+1)
1275 buf_length = MAX_PATH+1;
1277 #ifdef HAVE_DIRCACHE
1278 if (dircache_is_enabled() && playlist->filenames)
1280 if (playlist->filenames[index] != NULL)
1282 dircache_copy_path(playlist->filenames[index], tmp_buf, sizeof(tmp_buf)-1);
1283 max = strlen(tmp_buf) + 1;
1286 #else
1287 (void)index;
1288 #endif
1290 if (playlist->in_ram && !control_file && max < 0)
1292 strncpy(tmp_buf, &playlist->buffer[seek], sizeof(tmp_buf));
1293 tmp_buf[MAX_PATH] = '\0';
1294 max = strlen(tmp_buf) + 1;
1296 else if (max < 0)
1298 mutex_lock(&playlist->control_mutex);
1300 if (control_file)
1301 fd = playlist->control_fd;
1302 else
1304 if(-1 == playlist->fd)
1305 playlist->fd = open(playlist->filename, O_RDONLY);
1307 fd = playlist->fd;
1310 if(-1 != fd)
1313 if (lseek(fd, seek, SEEK_SET) != seek)
1314 max = -1;
1315 else
1317 max = read(fd, tmp_buf, MIN((size_t) buf_length, sizeof(tmp_buf)));
1319 if ((max > 0) && !playlist->utf8)
1321 char* end;
1322 int i = 0;
1324 /* Locate EOL. */
1325 while ((tmp_buf[i] != '\n') && (tmp_buf[i] != '\r')
1326 && (i < max))
1328 i++;
1331 /* Now work back killing white space. */
1332 while ((i > 0) && isspace(tmp_buf[i - 1]))
1334 i--;
1337 /* Borrow dir_buf a little... */
1338 /* TODO: iso_decode can overflow dir_buf; it really
1339 * should take a dest size argument.
1341 end = iso_decode(tmp_buf, dir_buf, -1, i);
1342 *end = 0;
1343 strncpy(tmp_buf, dir_buf, sizeof(tmp_buf));
1344 tmp_buf[sizeof(tmp_buf) - 1] = 0;
1345 max = strlen(tmp_buf);
1350 mutex_unlock(&playlist->control_mutex);
1352 if (max < 0)
1354 if (control_file)
1355 gui_syncsplash(HZ*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR));
1356 else
1357 gui_syncsplash(HZ*2, ID2P(LANG_PLAYLIST_ACCESS_ERROR));
1359 return max;
1363 strncpy(dir_buf, playlist->filename, playlist->dirlen-1);
1364 dir_buf[playlist->dirlen-1] = 0;
1366 return (format_track_path(buf, tmp_buf, buf_length, max, dir_buf));
1369 static int get_next_directory(char *dir){
1370 return get_next_dir(dir,true,false);
1373 static int get_previous_directory(char *dir){
1374 return get_next_dir(dir,false,false);
1378 * search through all the directories (starting with the current) to find
1379 * one that has tracks to play
1381 static int get_next_dir(char *dir, bool is_forward, bool recursion)
1383 struct playlist_info* playlist = &current_playlist;
1384 int result = -1;
1385 int sort_dir = global_settings.sort_dir;
1386 char *start_dir = NULL;
1387 bool exit = false;
1388 struct tree_context* tc = tree_get_context();
1389 int dirfilter = *(tc->dirfilter);
1391 if (global_settings.next_folder == FOLDER_ADVANCE_RANDOM)
1393 int fd = open(ROCKBOX_DIR "/folder_advance_list.dat",O_RDONLY);
1394 char buffer[MAX_PATH];
1395 int folder_count = 0,i;
1396 srand(current_tick);
1397 *(tc->dirfilter) = SHOW_MUSIC;
1398 if (fd >= 0)
1400 read(fd,&folder_count,sizeof(int));
1401 while (!exit)
1403 i = rand()%folder_count;
1404 lseek(fd,sizeof(int) + (MAX_PATH*i),SEEK_SET);
1405 read(fd,buffer,MAX_PATH);
1406 if (check_subdir_for_music(buffer,"") ==0)
1407 exit = true;
1409 strcpy(dir,buffer);
1410 close(fd);
1411 *(tc->dirfilter) = dirfilter;
1412 reload_directory();
1413 return 0;
1416 /* not random folder advance */
1417 if (recursion){
1418 /* start with root */
1419 dir[0] = '\0';
1421 else{
1422 /* start with current directory */
1423 strncpy(dir, playlist->filename, playlist->dirlen-1);
1424 dir[playlist->dirlen-1] = '\0';
1427 /* use the tree browser dircache to load files */
1428 *(tc->dirfilter) = SHOW_ALL;
1430 /* sort in another direction if previous dir is requested */
1431 if(!is_forward){
1432 if ((global_settings.sort_dir == 0) || (global_settings.sort_dir == 3))
1433 global_settings.sort_dir = 4;
1434 else if (global_settings.sort_dir == 1)
1435 global_settings.sort_dir = 2;
1436 else if (global_settings.sort_dir == 2)
1437 global_settings.sort_dir = 1;
1438 else if (global_settings.sort_dir == 4)
1439 global_settings.sort_dir = 0;
1442 while (!exit)
1444 struct entry *files;
1445 int num_files = 0;
1446 int i;
1448 if (ft_load(tc, (dir[0]=='\0')?"/":dir) < 0)
1450 gui_syncsplash(HZ*2, ID2P(LANG_PLAYLIST_DIRECTORY_ACCESS_ERROR));
1451 exit = true;
1452 result = -1;
1453 break;
1456 files = (struct entry*) tc->dircache;
1457 num_files = tc->filesindir;
1459 for (i=0; i<num_files; i++)
1461 /* user abort */
1462 if (action_userabort(TIMEOUT_NOBLOCK))
1464 result = -1;
1465 exit = true;
1466 break;
1469 if (files[i].attr & ATTR_DIRECTORY)
1471 if (!start_dir)
1473 result = check_subdir_for_music(dir, files[i].name);
1474 if (result != -1)
1476 exit = true;
1477 break;
1480 else if (!strcmp(start_dir, files[i].name))
1481 start_dir = NULL;
1485 if (!exit)
1487 /* move down to parent directory. current directory name is
1488 stored as the starting point for the search in parent */
1489 start_dir = strrchr(dir, '/');
1490 if (start_dir)
1492 *start_dir = '\0';
1493 start_dir++;
1495 else
1496 break;
1500 /* we've overwritten the dircache so tree browser will need to be
1501 reloaded */
1502 reload_directory();
1504 /* restore dirfilter & sort_dir */
1505 *(tc->dirfilter) = dirfilter;
1506 global_settings.sort_dir = sort_dir;
1508 /* special case if nothing found: try start searching again from root */
1509 if (result == -1 && !recursion){
1510 result = get_next_dir(dir,is_forward, true);
1513 return result;
1517 * Checks if there are any music files in the dir or any of its
1518 * subdirectories. May be called recursively.
1520 static int check_subdir_for_music(char *dir, char *subdir)
1522 int result = -1;
1523 int dirlen = strlen(dir);
1524 int num_files = 0;
1525 int i;
1526 struct entry *files;
1527 bool has_music = false;
1528 bool has_subdir = false;
1529 struct tree_context* tc = tree_get_context();
1531 snprintf(dir+dirlen, MAX_PATH-dirlen, "/%s", subdir);
1533 if (ft_load(tc, dir) < 0)
1535 gui_syncsplash(HZ*2, ID2P(LANG_PLAYLIST_DIRECTORY_ACCESS_ERROR));
1536 return -2;
1539 files = (struct entry*) tc->dircache;
1540 num_files = tc->filesindir;
1542 for (i=0; i<num_files; i++)
1544 if (files[i].attr & ATTR_DIRECTORY)
1545 has_subdir = true;
1546 else if ((files[i].attr & FILE_ATTR_MASK) == FILE_ATTR_AUDIO)
1548 has_music = true;
1549 break;
1553 if (has_music)
1554 return 0;
1556 if (has_subdir)
1558 for (i=0; i<num_files; i++)
1560 if (action_userabort(TIMEOUT_NOBLOCK))
1562 result = -2;
1563 break;
1566 if (files[i].attr & ATTR_DIRECTORY)
1568 result = check_subdir_for_music(dir, files[i].name);
1569 if (!result)
1570 break;
1575 if (result < 0)
1577 if (dirlen)
1579 dir[dirlen] = '\0';
1581 else
1583 strcpy(dir, "/");
1586 /* we now need to reload our current directory */
1587 if(ft_load(tc, dir) < 0)
1588 gui_syncsplash(HZ*2,
1589 ID2P(LANG_PLAYLIST_DIRECTORY_ACCESS_ERROR));
1592 return result;
1596 * Returns absolute path of track
1598 static int format_track_path(char *dest, char *src, int buf_length, int max,
1599 char *dir)
1601 int i = 0;
1602 int j;
1603 char *temp_ptr;
1605 /* Zero-terminate the file name */
1606 while((src[i] != '\n') &&
1607 (src[i] != '\r') &&
1608 (i < max))
1609 i++;
1611 /* Now work back killing white space */
1612 while((src[i-1] == ' ') ||
1613 (src[i-1] == '\t'))
1614 i--;
1616 src[i]=0;
1618 /* replace backslashes with forward slashes */
1619 for ( j=0; j<i; j++ )
1620 if ( src[j] == '\\' )
1621 src[j] = '/';
1623 if('/' == src[0])
1625 strncpy(dest, src, buf_length);
1627 else
1629 /* handle dos style drive letter */
1630 if (':' == src[1])
1631 strncpy(dest, &src[2], buf_length);
1632 else if (!strncmp(src, "../", 3))
1634 /* handle relative paths */
1635 i=3;
1636 while(!strncmp(&src[i], "../", 3))
1637 i += 3;
1638 for (j=0; j<i/3; j++) {
1639 temp_ptr = strrchr(dir, '/');
1640 if (temp_ptr)
1641 *temp_ptr = '\0';
1642 else
1643 break;
1645 snprintf(dest, buf_length, "%s/%s", dir, &src[i]);
1647 else if ( '.' == src[0] && '/' == src[1] ) {
1648 snprintf(dest, buf_length, "%s/%s", dir, &src[2]);
1650 else {
1651 snprintf(dest, buf_length, "%s/%s", dir, src);
1655 return 0;
1659 * Display splash message showing progress of playlist/directory insertion or
1660 * save.
1662 static void display_playlist_count(int count, const unsigned char *fmt,
1663 bool final)
1665 static long talked_tick = 0;
1666 long id = P2ID(fmt);
1667 if(global_settings.talk_menu && id>=0)
1669 if(final || (count && (talked_tick == 0
1670 || TIME_AFTER(current_tick, talked_tick+5*HZ))))
1672 talked_tick = current_tick;
1673 talk_number(count, false);
1674 talk_id(id, true);
1677 fmt = P2STR(fmt);
1679 lcd_clear_display();
1681 #ifdef HAVE_LCD_BITMAP
1682 if(global_settings.statusbar)
1683 lcd_setmargins(0, STATUSBAR_HEIGHT);
1684 else
1685 lcd_setmargins(0, 0);
1686 #endif
1688 gui_syncsplash(0, fmt, count, str(LANG_OFF_ABORT));
1692 * Display buffer full message
1694 static void display_buffer_full(void)
1696 gui_syncsplash(HZ*2, ID2P(LANG_PLAYLIST_BUFFER_FULL));
1700 * Flush any cached control commands to disk. Called when playlist is being
1701 * modified. Returns 0 on success and -1 on failure.
1703 static int flush_cached_control(struct playlist_info* playlist)
1705 int result = 0;
1706 int i;
1708 if (!playlist->num_cached)
1709 return 0;
1711 lseek(playlist->control_fd, 0, SEEK_END);
1713 for (i=0; i<playlist->num_cached; i++)
1715 struct playlist_control_cache* cache =
1716 &(playlist->control_cache[i]);
1718 switch (cache->command)
1720 case PLAYLIST_COMMAND_PLAYLIST:
1721 result = fdprintf(playlist->control_fd, "P:%d:%s:%s\n",
1722 cache->i1, cache->s1, cache->s2);
1723 break;
1724 case PLAYLIST_COMMAND_ADD:
1725 case PLAYLIST_COMMAND_QUEUE:
1726 result = fdprintf(playlist->control_fd, "%c:%d:%d:",
1727 (cache->command == PLAYLIST_COMMAND_ADD)?'A':'Q',
1728 cache->i1, cache->i2);
1729 if (result > 0)
1731 /* save the position in file where name is written */
1732 int* seek_pos = (int *)cache->data;
1733 *seek_pos = lseek(playlist->control_fd, 0, SEEK_CUR);
1734 result = fdprintf(playlist->control_fd, "%s\n",
1735 cache->s1);
1737 break;
1738 case PLAYLIST_COMMAND_DELETE:
1739 result = fdprintf(playlist->control_fd, "D:%d\n", cache->i1);
1740 break;
1741 case PLAYLIST_COMMAND_SHUFFLE:
1742 result = fdprintf(playlist->control_fd, "S:%d:%d\n",
1743 cache->i1, cache->i2);
1744 break;
1745 case PLAYLIST_COMMAND_UNSHUFFLE:
1746 result = fdprintf(playlist->control_fd, "U:%d\n", cache->i1);
1747 break;
1748 case PLAYLIST_COMMAND_RESET:
1749 result = fdprintf(playlist->control_fd, "R\n");
1750 break;
1751 default:
1752 break;
1755 if (result <= 0)
1756 break;
1759 if (result > 0)
1761 if (global_status.resume_seed >= 0)
1763 global_status.resume_seed = -1;
1764 status_save();
1767 playlist->num_cached = 0;
1768 playlist->pending_control_sync = true;
1770 result = 0;
1772 else
1774 result = -1;
1775 gui_syncsplash(HZ*2, ID2P(LANG_PLAYLIST_CONTROL_UPDATE_ERROR));
1778 return result;
1782 * Update control data with new command. Depending on the command, it may be
1783 * cached or flushed to disk.
1785 static int update_control(struct playlist_info* playlist,
1786 enum playlist_command command, int i1, int i2,
1787 const char* s1, const char* s2, void* data)
1789 int result = 0;
1790 struct playlist_control_cache* cache;
1791 bool flush = false;
1793 mutex_lock(&playlist->control_mutex);
1795 cache = &(playlist->control_cache[playlist->num_cached++]);
1797 cache->command = command;
1798 cache->i1 = i1;
1799 cache->i2 = i2;
1800 cache->s1 = s1;
1801 cache->s2 = s2;
1802 cache->data = data;
1804 switch (command)
1806 case PLAYLIST_COMMAND_PLAYLIST:
1807 case PLAYLIST_COMMAND_ADD:
1808 case PLAYLIST_COMMAND_QUEUE:
1809 #ifndef HAVE_DIRCACHE
1810 case PLAYLIST_COMMAND_DELETE:
1811 case PLAYLIST_COMMAND_RESET:
1812 #endif
1813 flush = true;
1814 break;
1815 case PLAYLIST_COMMAND_SHUFFLE:
1816 case PLAYLIST_COMMAND_UNSHUFFLE:
1817 default:
1818 /* only flush when needed */
1819 break;
1822 if (flush || playlist->num_cached == PLAYLIST_MAX_CACHE)
1823 result = flush_cached_control(playlist);
1825 mutex_unlock(&playlist->control_mutex);
1827 return result;
1831 * sync control file to disk
1833 static void sync_control(struct playlist_info* playlist, bool force)
1835 #ifdef HAVE_DIRCACHE
1836 if (playlist->started && force)
1837 #else
1838 (void) force;
1840 if (playlist->started)
1841 #endif
1843 if (playlist->pending_control_sync)
1845 mutex_lock(&playlist->control_mutex);
1846 fsync(playlist->control_fd);
1847 playlist->pending_control_sync = false;
1848 mutex_unlock(&playlist->control_mutex);
1854 * Rotate indices such that first_index is index 0
1856 static int rotate_index(const struct playlist_info* playlist, int index)
1858 index -= playlist->first_index;
1859 if (index < 0)
1860 index += playlist->amount;
1862 return index;
1866 * Initialize playlist entries at startup
1868 void playlist_init(void)
1870 struct playlist_info* playlist = &current_playlist;
1872 playlist->current = true;
1873 snprintf(playlist->control_filename, sizeof(playlist->control_filename),
1874 "%s", PLAYLIST_CONTROL_FILE);
1875 playlist->fd = -1;
1876 playlist->control_fd = -1;
1877 playlist->max_playlist_size = global_settings.max_files_in_playlist;
1878 playlist->indices = buffer_alloc(
1879 playlist->max_playlist_size * sizeof(int));
1880 playlist->buffer_size =
1881 AVERAGE_FILENAME_LENGTH * global_settings.max_files_in_dir;
1882 playlist->buffer = buffer_alloc(playlist->buffer_size);
1883 mutex_init(&playlist->control_mutex);
1884 empty_playlist(playlist, true);
1886 #ifdef HAVE_DIRCACHE
1887 playlist->filenames = buffer_alloc(
1888 playlist->max_playlist_size * sizeof(int));
1889 memset(playlist->filenames, 0,
1890 playlist->max_playlist_size * sizeof(int));
1891 create_thread(playlist_thread, playlist_stack, sizeof(playlist_stack),
1892 0, playlist_thread_name IF_PRIO(, PRIORITY_BACKGROUND)
1893 IF_COP(, CPU));
1894 queue_init(&playlist_queue, true);
1895 #endif
1899 * Clean playlist at shutdown
1901 void playlist_shutdown(void)
1903 struct playlist_info* playlist = &current_playlist;
1905 if (playlist->control_fd >= 0)
1907 mutex_lock(&playlist->control_mutex);
1909 if (playlist->num_cached > 0)
1910 flush_cached_control(playlist);
1912 close(playlist->control_fd);
1914 mutex_unlock(&playlist->control_mutex);
1919 * Create new playlist
1921 int playlist_create(const char *dir, const char *file)
1923 struct playlist_info* playlist = &current_playlist;
1925 new_playlist(playlist, dir, file);
1927 if (file)
1928 /* load the playlist file */
1929 add_indices_to_playlist(playlist, NULL, 0);
1931 return 0;
1934 #define PLAYLIST_COMMAND_SIZE (MAX_PATH+12)
1937 * Restore the playlist state based on control file commands. Called to
1938 * resume playback after shutdown.
1940 int playlist_resume(void)
1942 struct playlist_info* playlist = &current_playlist;
1943 char *buffer;
1944 size_t buflen;
1945 int nread;
1946 int total_read = 0;
1947 int control_file_size = 0;
1948 bool first = true;
1949 bool sorted = true;
1951 /* use mp3 buffer for maximum load speed */
1952 #if CONFIG_CODEC != SWCODEC
1953 talk_buffer_steal(); /* we use the mp3 buffer, need to tell */
1954 buflen = (audiobufend - audiobuf);
1955 buffer = (char *)audiobuf;
1956 #else
1957 buffer = (char *)audio_get_buffer(false, &buflen);
1958 #endif
1960 empty_playlist(playlist, true);
1962 gui_syncsplash(0, ID2P(LANG_WAIT));
1963 playlist->control_fd = open(playlist->control_filename, O_RDWR);
1964 if (playlist->control_fd < 0)
1966 gui_syncsplash(HZ*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR));
1967 return -1;
1969 playlist->control_created = true;
1971 control_file_size = filesize(playlist->control_fd);
1972 if (control_file_size <= 0)
1974 gui_syncsplash(HZ*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR));
1975 return -1;
1978 /* read a small amount first to get the header */
1979 nread = read(playlist->control_fd, buffer,
1980 PLAYLIST_COMMAND_SIZE<buflen?PLAYLIST_COMMAND_SIZE:buflen);
1981 if(nread <= 0)
1983 gui_syncsplash(HZ*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR));
1984 return -1;
1987 playlist->started = true;
1989 while (1)
1991 int result = 0;
1992 int count;
1993 enum playlist_command current_command = PLAYLIST_COMMAND_COMMENT;
1994 int last_newline = 0;
1995 int str_count = -1;
1996 bool newline = true;
1997 bool exit_loop = false;
1998 char *p = buffer;
1999 char *str1 = NULL;
2000 char *str2 = NULL;
2001 char *str3 = NULL;
2002 unsigned long last_tick = current_tick;
2004 for(count=0; count<nread && !exit_loop; count++,p++)
2006 /* So a splash while we are loading. */
2007 if (current_tick - last_tick > HZ/4)
2009 gui_syncsplash(0, str(LANG_LOADING_PERCENT),
2010 (total_read+count)*100/control_file_size,
2011 str(LANG_OFF_ABORT));
2012 if (action_userabort(TIMEOUT_NOBLOCK))
2014 /* FIXME:
2015 * Not sure how to implement this, somebody more familiar
2016 * with the code, please fix this. */
2018 last_tick = current_tick;
2021 /* Are we on a new line? */
2022 if((*p == '\n') || (*p == '\r'))
2024 *p = '\0';
2026 /* save last_newline in case we need to load more data */
2027 last_newline = count;
2029 switch (current_command)
2031 case PLAYLIST_COMMAND_PLAYLIST:
2033 /* str1=version str2=dir str3=file */
2034 int version;
2036 if (!str1)
2038 result = -1;
2039 exit_loop = true;
2040 break;
2043 if (!str2)
2044 str2 = "";
2046 if (!str3)
2047 str3 = "";
2049 version = atoi(str1);
2051 if (version != PLAYLIST_CONTROL_FILE_VERSION)
2052 return -1;
2054 update_playlist_filename(playlist, str2, str3);
2056 if (str3[0] != '\0')
2058 /* NOTE: add_indices_to_playlist() overwrites the
2059 audiobuf so we need to reload control file
2060 data */
2061 add_indices_to_playlist(playlist, NULL, 0);
2063 else if (str2[0] != '\0')
2065 playlist->in_ram = true;
2066 resume_directory(str2);
2069 /* load the rest of the data */
2070 first = false;
2071 exit_loop = true;
2073 break;
2075 case PLAYLIST_COMMAND_ADD:
2076 case PLAYLIST_COMMAND_QUEUE:
2078 /* str1=position str2=last_position str3=file */
2079 int position, last_position;
2080 bool queue;
2082 if (!str1 || !str2 || !str3)
2084 result = -1;
2085 exit_loop = true;
2086 break;
2089 position = atoi(str1);
2090 last_position = atoi(str2);
2092 queue = (current_command == PLAYLIST_COMMAND_ADD)?
2093 false:true;
2095 /* seek position is based on str3's position in
2096 buffer */
2097 if (add_track_to_playlist(playlist, str3, position,
2098 queue, total_read+(str3-buffer)) < 0)
2099 return -1;
2101 playlist->last_insert_pos = last_position;
2103 break;
2105 case PLAYLIST_COMMAND_DELETE:
2107 /* str1=position */
2108 int position;
2110 if (!str1)
2112 result = -1;
2113 exit_loop = true;
2114 break;
2117 position = atoi(str1);
2119 if (remove_track_from_playlist(playlist, position,
2120 false) < 0)
2121 return -1;
2123 break;
2125 case PLAYLIST_COMMAND_SHUFFLE:
2127 /* str1=seed str2=first_index */
2128 int seed;
2130 if (!str1 || !str2)
2132 result = -1;
2133 exit_loop = true;
2134 break;
2137 if (!sorted)
2139 /* Always sort list before shuffling */
2140 sort_playlist(playlist, false, false);
2143 seed = atoi(str1);
2144 playlist->first_index = atoi(str2);
2146 if (randomise_playlist(playlist, seed, false,
2147 false) < 0)
2148 return -1;
2150 sorted = false;
2151 break;
2153 case PLAYLIST_COMMAND_UNSHUFFLE:
2155 /* str1=first_index */
2156 if (!str1)
2158 result = -1;
2159 exit_loop = true;
2160 break;
2163 playlist->first_index = atoi(str1);
2165 if (sort_playlist(playlist, false, false) < 0)
2166 return -1;
2168 sorted = true;
2169 break;
2171 case PLAYLIST_COMMAND_RESET:
2173 playlist->last_insert_pos = -1;
2174 break;
2176 case PLAYLIST_COMMAND_COMMENT:
2177 default:
2178 break;
2181 newline = true;
2183 /* to ignore any extra newlines */
2184 current_command = PLAYLIST_COMMAND_COMMENT;
2186 else if(newline)
2188 newline = false;
2190 /* first non-comment line must always specify playlist */
2191 if (first && *p != 'P' && *p != '#')
2193 result = -1;
2194 exit_loop = true;
2195 break;
2198 switch (*p)
2200 case 'P':
2201 /* playlist can only be specified once */
2202 if (!first)
2204 result = -1;
2205 exit_loop = true;
2206 break;
2209 current_command = PLAYLIST_COMMAND_PLAYLIST;
2210 break;
2211 case 'A':
2212 current_command = PLAYLIST_COMMAND_ADD;
2213 break;
2214 case 'Q':
2215 current_command = PLAYLIST_COMMAND_QUEUE;
2216 break;
2217 case 'D':
2218 current_command = PLAYLIST_COMMAND_DELETE;
2219 break;
2220 case 'S':
2221 current_command = PLAYLIST_COMMAND_SHUFFLE;
2222 break;
2223 case 'U':
2224 current_command = PLAYLIST_COMMAND_UNSHUFFLE;
2225 break;
2226 case 'R':
2227 current_command = PLAYLIST_COMMAND_RESET;
2228 break;
2229 case '#':
2230 current_command = PLAYLIST_COMMAND_COMMENT;
2231 break;
2232 default:
2233 result = -1;
2234 exit_loop = true;
2235 break;
2238 str_count = -1;
2239 str1 = NULL;
2240 str2 = NULL;
2241 str3 = NULL;
2243 else if(current_command != PLAYLIST_COMMAND_COMMENT)
2245 /* all control file strings are separated with a colon.
2246 Replace the colon with 0 to get proper strings that can be
2247 used by commands above */
2248 if (*p == ':')
2250 *p = '\0';
2251 str_count++;
2253 if ((count+1) < nread)
2255 switch (str_count)
2257 case 0:
2258 str1 = p+1;
2259 break;
2260 case 1:
2261 str2 = p+1;
2262 break;
2263 case 2:
2264 str3 = p+1;
2265 break;
2266 default:
2267 /* allow last string to contain colons */
2268 *p = ':';
2269 break;
2276 if (result < 0)
2278 gui_syncsplash(HZ*2, ID2P(LANG_PLAYLIST_CONTROL_INVALID));
2279 return result;
2282 if (!newline || (exit_loop && count<nread))
2284 if ((total_read + count) >= control_file_size)
2286 /* no newline at end of control file */
2287 gui_syncsplash(HZ*2, ID2P(LANG_PLAYLIST_CONTROL_INVALID));
2288 return -1;
2291 /* We didn't end on a newline or we exited loop prematurely.
2292 Either way, re-read the remainder. */
2293 count = last_newline;
2294 lseek(playlist->control_fd, total_read+count, SEEK_SET);
2297 total_read += count;
2299 if (first)
2300 /* still looking for header */
2301 nread = read(playlist->control_fd, buffer,
2302 PLAYLIST_COMMAND_SIZE<buflen?PLAYLIST_COMMAND_SIZE:buflen);
2303 else
2304 nread = read(playlist->control_fd, buffer, buflen);
2306 /* Terminate on EOF */
2307 if(nread <= 0)
2309 if (global_status.resume_seed >= 0)
2311 /* Apply shuffle command saved in settings */
2312 if (global_status.resume_seed == 0)
2313 sort_playlist(playlist, false, true);
2314 else
2316 if (!sorted)
2317 sort_playlist(playlist, false, false);
2319 randomise_playlist(playlist, global_status.resume_seed,
2320 false, true);
2324 playlist->first_index = global_status.resume_first_index;
2325 break;
2329 #ifdef HAVE_DIRCACHE
2330 queue_post(&playlist_queue, PLAYLIST_LOAD_POINTERS, 0);
2331 #endif
2333 return 0;
2337 * Add track to in_ram playlist. Used when playing directories.
2339 int playlist_add(const char *filename)
2341 struct playlist_info* playlist = &current_playlist;
2342 int len = strlen(filename);
2344 if((len+1 > playlist->buffer_size - playlist->buffer_end_pos) ||
2345 (playlist->amount >= playlist->max_playlist_size))
2347 display_buffer_full();
2348 return -1;
2351 playlist->indices[playlist->amount] = playlist->buffer_end_pos;
2352 #ifdef HAVE_DIRCACHE
2353 playlist->filenames[playlist->amount] = NULL;
2354 #endif
2355 playlist->amount++;
2357 strcpy(&playlist->buffer[playlist->buffer_end_pos], filename);
2358 playlist->buffer_end_pos += len;
2359 playlist->buffer[playlist->buffer_end_pos++] = '\0';
2361 return 0;
2364 /* shuffle newly created playlist using random seed. */
2365 int playlist_shuffle(int random_seed, int start_index)
2367 struct playlist_info* playlist = &current_playlist;
2369 unsigned int seek_pos = 0;
2370 bool start_current = false;
2372 if (start_index >= 0 && global_settings.play_selected)
2374 /* store the seek position before the shuffle */
2375 seek_pos = playlist->indices[start_index];
2376 playlist->index = global_status.resume_first_index =
2377 playlist->first_index = start_index;
2378 start_current = true;
2381 randomise_playlist(playlist, random_seed, start_current, true);
2383 return playlist->index;
2386 /* start playing current playlist at specified index/offset */
2387 int playlist_start(int start_index, int offset)
2389 struct playlist_info* playlist = &current_playlist;
2391 /* Cancel FM radio selection as previous music. For cases where we start
2392 playback without going to the WPS, such as playlist insert.. or
2393 playlist catalog. */
2394 previous_music_is_wps();
2396 playlist->index = start_index;
2398 #if CONFIG_CODEC != SWCODEC
2399 talk_buffer_steal(); /* will use the mp3 buffer */
2400 #endif
2402 playlist->started = true;
2403 sync_control(playlist, false);
2404 audio_play(offset);
2406 return 0;
2409 /* Returns false if 'steps' is out of bounds, else true */
2410 bool playlist_check(int steps)
2412 struct playlist_info* playlist = &current_playlist;
2414 /* always allow folder navigation */
2415 if (global_settings.next_folder && playlist->in_ram)
2416 return true;
2418 int index = get_next_index(playlist, steps, -1);
2420 if (index < 0 && steps >= 0 && global_settings.repeat_mode == REPEAT_SHUFFLE)
2421 index = get_next_index(playlist, steps, REPEAT_ALL);
2423 return (index >= 0);
2426 /* get trackname of track that is "steps" away from current playing track.
2427 NULL is used to identify end of playlist */
2428 char* playlist_peek(int steps)
2430 struct playlist_info* playlist = &current_playlist;
2431 int seek;
2432 int fd;
2433 char *temp_ptr;
2434 int index;
2435 bool control_file;
2437 index = get_next_index(playlist, steps, -1);
2438 if (index < 0)
2439 return NULL;
2441 control_file = playlist->indices[index] & PLAYLIST_INSERT_TYPE_MASK;
2442 seek = playlist->indices[index] & PLAYLIST_SEEK_MASK;
2444 if (get_filename(playlist, index, seek, control_file, now_playing,
2445 MAX_PATH+1) < 0)
2446 return NULL;
2448 temp_ptr = now_playing;
2450 if (!playlist->in_ram || control_file)
2452 /* remove bogus dirs from beginning of path
2453 (workaround for buggy playlist creation tools) */
2454 while (temp_ptr)
2456 #ifdef HAVE_DIRCACHE
2457 if (dircache_is_enabled())
2459 if (dircache_get_entry_ptr(temp_ptr))
2460 break;
2462 else
2463 #endif
2465 fd = open(temp_ptr, O_RDONLY);
2466 if (fd >= 0)
2468 close(fd);
2469 break;
2473 temp_ptr = strchr(temp_ptr+1, '/');
2476 if (!temp_ptr)
2478 /* Even though this is an invalid file, we still need to pass a
2479 file name to the caller because NULL is used to indicate end
2480 of playlist */
2481 return now_playing;
2485 return temp_ptr;
2489 * Update indices as track has changed
2491 int playlist_next(int steps)
2493 struct playlist_info* playlist = &current_playlist;
2494 int index;
2496 if ( (steps > 0)
2497 #ifdef AB_REPEAT_ENABLE
2498 && (global_settings.repeat_mode != REPEAT_AB)
2499 #endif
2500 && (global_settings.repeat_mode != REPEAT_ONE) )
2502 int i, j;
2504 /* We need to delete all the queued songs */
2505 for (i=0, j=steps; i<j; i++)
2507 index = get_next_index(playlist, i, -1);
2509 if (playlist->indices[index] & PLAYLIST_QUEUE_MASK)
2511 remove_track_from_playlist(playlist, index, true);
2512 steps--; /* one less track */
2517 index = get_next_index(playlist, steps, -1);
2519 if (index < 0)
2521 /* end of playlist... or is it */
2522 if (global_settings.repeat_mode == REPEAT_SHUFFLE &&
2523 playlist->amount > 1)
2525 /* Repeat shuffle mode. Re-shuffle playlist and resume play */
2526 playlist->first_index = global_status.resume_first_index = 0;
2527 sort_playlist(playlist, false, false);
2528 randomise_playlist(playlist, current_tick, false, true);
2529 #if CONFIG_CODEC != SWCODEC
2530 playlist_start(0, 0);
2531 #endif
2532 playlist->index = 0;
2533 index = 0;
2535 else if (playlist->in_ram && global_settings.next_folder)
2537 char dir[MAX_PATH+1];
2539 changing_dir = true;
2540 if (steps > 0)
2542 if (!get_next_directory(dir))
2544 /* start playing next directory */
2545 if (playlist_create(dir, NULL) != -1)
2547 ft_build_playlist(tree_get_context(), 0);
2548 if (global_settings.playlist_shuffle)
2549 playlist_shuffle(current_tick, -1);
2550 #if CONFIG_CODEC != SWCODEC
2551 playlist_start(0, 0);
2552 #endif
2553 playlist->index = index = 0;
2557 else
2559 if (!get_previous_directory(dir))
2561 /* start playing previous directory */
2562 if (playlist_create(dir, NULL) != -1)
2564 ft_build_playlist(tree_get_context(), 0);
2565 if (global_settings.playlist_shuffle)
2566 playlist_shuffle(current_tick, -1);
2567 #if CONFIG_CODEC != SWCODEC
2568 playlist_start(current_playlist.amount-1, 0);
2569 #endif
2570 playlist->index = index = current_playlist.amount - 1;
2574 changing_dir = false;
2577 return index;
2580 playlist->index = index;
2582 if (playlist->last_insert_pos >= 0 && steps > 0)
2584 /* check to see if we've gone beyond the last inserted track */
2585 int cur = rotate_index(playlist, index);
2586 int last_pos = rotate_index(playlist, playlist->last_insert_pos);
2588 if (cur > last_pos)
2590 /* reset last inserted track */
2591 playlist->last_insert_pos = -1;
2593 if (playlist->control_fd >= 0)
2595 int result = update_control(playlist, PLAYLIST_COMMAND_RESET,
2596 -1, -1, NULL, NULL, NULL);
2598 if (result < 0)
2599 return result;
2601 sync_control(playlist, false);
2606 return index;
2609 /* try playing next or previous folder */
2610 bool playlist_next_dir(int direction)
2612 char dir[MAX_PATH+1];
2613 bool result;
2614 int res;
2616 /* not to mess up real playlists */
2617 if(!current_playlist.in_ram)
2618 return false;
2620 if(changing_dir)
2621 return false;
2623 changing_dir = true;
2624 if(direction > 0)
2625 res = get_next_directory(dir);
2626 else
2627 res = get_previous_directory(dir);
2628 if (!res)
2630 if (playlist_create(dir, NULL) != -1)
2632 ft_build_playlist(tree_get_context(), 0);
2633 if (global_settings.playlist_shuffle)
2634 playlist_shuffle(current_tick, -1);
2635 #if (CONFIG_CODEC != SWCODEC)
2636 playlist_start(0,0);
2637 #endif
2638 result = true;
2640 else
2641 result = false;
2643 else
2644 result = false;
2646 changing_dir = false;
2648 return result;
2651 /* Get resume info for current playing song. If return value is -1 then
2652 settings shouldn't be saved. */
2653 int playlist_get_resume_info(int *resume_index)
2655 struct playlist_info* playlist = &current_playlist;
2657 *resume_index = playlist->index;
2659 return 0;
2662 /* Update resume info for current playing song. Returns -1 on error. */
2663 int playlist_update_resume_info(const struct mp3entry* id3)
2665 struct playlist_info* playlist = &current_playlist;
2667 if (id3)
2669 if (global_status.resume_index != playlist->index ||
2670 global_status.resume_offset != id3->offset)
2672 global_status.resume_index = playlist->index;
2673 global_status.resume_offset = id3->offset;
2674 status_save();
2677 else
2679 global_status.resume_index = -1;
2680 global_status.resume_offset = -1;
2681 status_save();
2684 return 0;
2687 /* Returns index of current playing track for display purposes. This value
2688 should not be used for resume purposes as it doesn't represent the actual
2689 index into the playlist */
2690 int playlist_get_display_index(void)
2692 struct playlist_info* playlist = &current_playlist;
2694 /* first_index should always be index 0 for display purposes */
2695 int index = rotate_index(playlist, playlist->index);
2697 return (index+1);
2700 /* returns number of tracks in current playlist */
2701 int playlist_amount(void)
2703 return playlist_amount_ex(NULL);
2707 * Create a new playlist If playlist is not NULL then we're loading a
2708 * playlist off disk for viewing/editing. The index_buffer is used to store
2709 * playlist indices (required for and only used if !current playlist). The
2710 * temp_buffer (if not NULL) is used as a scratchpad when loading indices.
2712 int playlist_create_ex(struct playlist_info* playlist,
2713 const char* dir, const char* file,
2714 void* index_buffer, int index_buffer_size,
2715 void* temp_buffer, int temp_buffer_size)
2717 if (!playlist)
2718 playlist = &current_playlist;
2719 else
2721 /* Initialize playlist structure */
2722 int r = rand() % 10;
2723 playlist->current = false;
2725 /* Use random name for control file */
2726 snprintf(playlist->control_filename, sizeof(playlist->control_filename),
2727 "%s.%d", PLAYLIST_CONTROL_FILE, r);
2728 playlist->fd = -1;
2729 playlist->control_fd = -1;
2731 if (index_buffer)
2733 int num_indices = index_buffer_size / sizeof(int);
2735 #ifdef HAVE_DIRCACHE
2736 num_indices /= 2;
2737 #endif
2738 if (num_indices > global_settings.max_files_in_playlist)
2739 num_indices = global_settings.max_files_in_playlist;
2741 playlist->max_playlist_size = num_indices;
2742 playlist->indices = index_buffer;
2743 #ifdef HAVE_DIRCACHE
2744 playlist->filenames = (const struct dircache_entry **)
2745 &playlist->indices[num_indices];
2746 #endif
2748 else
2750 playlist->max_playlist_size = current_playlist.max_playlist_size;
2751 playlist->indices = current_playlist.indices;
2752 #ifdef HAVE_DIRCACHE
2753 playlist->filenames = current_playlist.filenames;
2754 #endif
2757 playlist->buffer_size = 0;
2758 playlist->buffer = NULL;
2759 mutex_init(&playlist->control_mutex);
2762 new_playlist(playlist, dir, file);
2764 if (file)
2765 /* load the playlist file */
2766 add_indices_to_playlist(playlist, temp_buffer, temp_buffer_size);
2768 return 0;
2772 * Set the specified playlist as the current.
2773 * NOTE: You will get undefined behaviour if something is already playing so
2774 * remember to stop before calling this. Also, this call will
2775 * effectively close your playlist, making it unusable.
2777 int playlist_set_current(struct playlist_info* playlist)
2779 if (!playlist || (check_control(playlist) < 0))
2780 return -1;
2782 empty_playlist(&current_playlist, false);
2784 strncpy(current_playlist.filename, playlist->filename,
2785 sizeof(current_playlist.filename));
2787 current_playlist.utf8 = playlist->utf8;
2788 current_playlist.fd = playlist->fd;
2790 close(playlist->control_fd);
2791 close(current_playlist.control_fd);
2792 remove(current_playlist.control_filename);
2793 if (rename(playlist->control_filename,
2794 current_playlist.control_filename) < 0)
2795 return -1;
2796 current_playlist.control_fd = open(current_playlist.control_filename,
2797 O_RDWR);
2798 if (current_playlist.control_fd < 0)
2799 return -1;
2800 current_playlist.control_created = true;
2802 current_playlist.dirlen = playlist->dirlen;
2804 if (playlist->indices && playlist->indices != current_playlist.indices)
2806 memcpy(current_playlist.indices, playlist->indices,
2807 playlist->max_playlist_size*sizeof(int));
2808 #ifdef HAVE_DIRCACHE
2809 memcpy(current_playlist.filenames, playlist->filenames,
2810 playlist->max_playlist_size*sizeof(int));
2811 #endif
2814 current_playlist.first_index = playlist->first_index;
2815 current_playlist.amount = playlist->amount;
2816 current_playlist.last_insert_pos = playlist->last_insert_pos;
2817 current_playlist.seed = playlist->seed;
2818 current_playlist.shuffle_modified = playlist->shuffle_modified;
2819 current_playlist.deleted = playlist->deleted;
2820 current_playlist.num_inserted_tracks = playlist->num_inserted_tracks;
2822 memcpy(current_playlist.control_cache, playlist->control_cache,
2823 sizeof(current_playlist.control_cache));
2824 current_playlist.num_cached = playlist->num_cached;
2825 current_playlist.pending_control_sync = playlist->pending_control_sync;
2827 return 0;
2831 * Close files and delete control file for non-current playlist.
2833 void playlist_close(struct playlist_info* playlist)
2835 if (!playlist)
2836 return;
2838 if (playlist->fd >= 0)
2839 close(playlist->fd);
2841 if (playlist->control_fd >= 0)
2842 close(playlist->control_fd);
2844 if (playlist->control_created)
2845 remove(playlist->control_filename);
2848 void playlist_sync(struct playlist_info* playlist)
2850 if (!playlist)
2851 playlist = &current_playlist;
2853 sync_control(playlist, false);
2854 if ((audio_status() & AUDIO_STATUS_PLAY) && playlist->started)
2855 audio_flush_and_reload_tracks();
2857 #ifdef HAVE_DIRCACHE
2858 queue_post(&playlist_queue, PLAYLIST_LOAD_POINTERS, 0);
2859 #endif
2863 * Insert track into playlist at specified position (or one of the special
2864 * positions). Returns position where track was inserted or -1 if error.
2866 int playlist_insert_track(struct playlist_info* playlist, const char *filename,
2867 int position, bool queue, bool sync)
2869 int result;
2871 if (!playlist)
2872 playlist = &current_playlist;
2874 if (check_control(playlist) < 0)
2876 gui_syncsplash(HZ*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR));
2877 return -1;
2880 result = add_track_to_playlist(playlist, filename, position, queue, -1);
2882 /* Check if we want manually sync later. For example when adding
2883 * bunch of files from tagcache, syncing after every file wouldn't be
2884 * a good thing to do. */
2885 if (sync && result >= 0)
2886 playlist_sync(playlist);
2888 return result;
2892 * Insert all tracks from specified directory into playlist.
2894 int playlist_insert_directory(struct playlist_info* playlist,
2895 const char *dirname, int position, bool queue,
2896 bool recurse)
2898 int result;
2899 unsigned char *count_str;
2900 struct directory_search_context context;
2902 if (!playlist)
2903 playlist = &current_playlist;
2905 if (check_control(playlist) < 0)
2907 gui_syncsplash(HZ*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR));
2908 return -1;
2911 if (position == PLAYLIST_REPLACE)
2913 if (remove_all_tracks(playlist) == 0)
2914 position = PLAYLIST_INSERT_LAST;
2915 else
2916 return -1;
2919 if (queue)
2920 count_str = ID2P(LANG_PLAYLIST_QUEUE_COUNT);
2921 else
2922 count_str = ID2P(LANG_PLAYLIST_INSERT_COUNT);
2924 display_playlist_count(0, count_str, false);
2926 context.playlist = playlist;
2927 context.position = position;
2928 context.queue = queue;
2929 context.count = 0;
2931 cpu_boost(true);
2933 result = playlist_directory_tracksearch(dirname, recurse,
2934 directory_search_callback, &context);
2936 sync_control(playlist, false);
2938 cpu_boost(false);
2940 display_playlist_count(context.count, count_str, true);
2942 if ((audio_status() & AUDIO_STATUS_PLAY) && playlist->started)
2943 audio_flush_and_reload_tracks();
2945 #ifdef HAVE_DIRCACHE
2946 queue_post(&playlist_queue, PLAYLIST_LOAD_POINTERS, 0);
2947 #endif
2949 return result;
2953 * Insert all tracks from specified playlist into dynamic playlist.
2955 int playlist_insert_playlist(struct playlist_info* playlist, char *filename,
2956 int position, bool queue)
2958 int fd;
2959 int max;
2960 char *temp_ptr;
2961 char *dir;
2962 unsigned char *count_str;
2963 char temp_buf[MAX_PATH+1];
2964 char trackname[MAX_PATH+1];
2965 int count = 0;
2966 int result = 0;
2968 if (!playlist)
2969 playlist = &current_playlist;
2971 if (check_control(playlist) < 0)
2973 gui_syncsplash(HZ*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR));
2974 return -1;
2977 fd = open(filename, O_RDONLY);
2978 if (fd < 0)
2980 gui_syncsplash(HZ*2, ID2P(LANG_PLAYLIST_ACCESS_ERROR));
2981 return -1;
2984 /* we need the directory name for formatting purposes */
2985 dir = filename;
2987 temp_ptr = strrchr(filename+1,'/');
2988 if (temp_ptr)
2989 *temp_ptr = 0;
2990 else
2991 dir = "/";
2993 if (queue)
2994 count_str = ID2P(LANG_PLAYLIST_QUEUE_COUNT);
2995 else
2996 count_str = ID2P(LANG_PLAYLIST_INSERT_COUNT);
2998 display_playlist_count(count, count_str, false);
3000 if (position == PLAYLIST_REPLACE)
3002 if (remove_all_tracks(playlist) == 0)
3003 position = PLAYLIST_INSERT_LAST;
3004 else return -1;
3007 cpu_boost(true);
3009 while ((max = read_line(fd, temp_buf, sizeof(temp_buf))) > 0)
3011 /* user abort */
3012 if (action_userabort(TIMEOUT_NOBLOCK))
3013 break;
3015 if (temp_buf[0] != '#' && temp_buf[0] != '\0')
3017 int insert_pos;
3019 /* we need to format so that relative paths are correctly
3020 handled */
3021 if (format_track_path(trackname, temp_buf, sizeof(trackname), max,
3022 dir) < 0)
3024 result = -1;
3025 break;
3028 insert_pos = add_track_to_playlist(playlist, trackname, position,
3029 queue, -1);
3031 if (insert_pos < 0)
3033 result = -1;
3034 break;
3037 /* Make sure tracks are inserted in correct order if user
3038 requests INSERT_FIRST */
3039 if (position == PLAYLIST_INSERT_FIRST || position >= 0)
3040 position = insert_pos + 1;
3042 count++;
3044 if ((count%PLAYLIST_DISPLAY_COUNT) == 0)
3046 display_playlist_count(count, count_str, false);
3048 if (count == PLAYLIST_DISPLAY_COUNT &&
3049 (audio_status() & AUDIO_STATUS_PLAY) &&
3050 playlist->started)
3051 audio_flush_and_reload_tracks();
3055 /* let the other threads work */
3056 yield();
3059 close(fd);
3061 if (temp_ptr)
3062 *temp_ptr = '/';
3064 sync_control(playlist, false);
3066 cpu_boost(false);
3068 display_playlist_count(count, count_str, true);
3070 if ((audio_status() & AUDIO_STATUS_PLAY) && playlist->started)
3071 audio_flush_and_reload_tracks();
3073 #ifdef HAVE_DIRCACHE
3074 queue_post(&playlist_queue, PLAYLIST_LOAD_POINTERS, 0);
3075 #endif
3077 return result;
3081 * Delete track at specified index. If index is PLAYLIST_DELETE_CURRENT then
3082 * we want to delete the current playing track.
3084 int playlist_delete(struct playlist_info* playlist, int index)
3086 int result = 0;
3088 if (!playlist)
3089 playlist = &current_playlist;
3091 if (check_control(playlist) < 0)
3093 gui_syncsplash(HZ*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR));
3094 return -1;
3097 if (index == PLAYLIST_DELETE_CURRENT)
3098 index = playlist->index;
3100 result = remove_track_from_playlist(playlist, index, true);
3102 if (result != -1 && (audio_status() & AUDIO_STATUS_PLAY) &&
3103 playlist->started)
3104 audio_flush_and_reload_tracks();
3106 return result;
3110 * Move track at index to new_index. Tracks between the two are shifted
3111 * appropriately. Returns 0 on success and -1 on failure.
3113 int playlist_move(struct playlist_info* playlist, int index, int new_index)
3115 int result;
3116 int seek;
3117 bool control_file;
3118 bool queue;
3119 bool current = false;
3120 int r;
3121 char filename[MAX_PATH];
3123 if (!playlist)
3124 playlist = &current_playlist;
3126 if (check_control(playlist) < 0)
3128 gui_syncsplash(HZ*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR));
3129 return -1;
3132 if (index == new_index)
3133 return -1;
3135 if (index == playlist->index)
3136 /* Moving the current track */
3137 current = true;
3139 control_file = playlist->indices[index] & PLAYLIST_INSERT_TYPE_MASK;
3140 queue = playlist->indices[index] & PLAYLIST_QUEUE_MASK;
3141 seek = playlist->indices[index] & PLAYLIST_SEEK_MASK;
3143 if (get_filename(playlist, index, seek, control_file, filename,
3144 sizeof(filename)) < 0)
3145 return -1;
3147 /* Delete track from original position */
3148 result = remove_track_from_playlist(playlist, index, true);
3150 if (result != -1)
3152 /* We want to insert the track at the position that was specified by
3153 new_index. This may be different then new_index because of the
3154 shifting that occurred after the delete */
3155 r = rotate_index(playlist, new_index);
3157 if (r == 0)
3158 /* First index */
3159 new_index = PLAYLIST_PREPEND;
3160 else if (r == playlist->amount)
3161 /* Append */
3162 new_index = PLAYLIST_INSERT_LAST;
3163 else
3164 /* Calculate index of desired position */
3165 new_index = (r+playlist->first_index)%playlist->amount;
3167 result = add_track_to_playlist(playlist, filename, new_index, queue,
3168 -1);
3170 if (result != -1)
3172 if (current)
3174 /* Moved the current track */
3175 switch (new_index)
3177 case PLAYLIST_PREPEND:
3178 playlist->index = playlist->first_index;
3179 break;
3180 case PLAYLIST_INSERT_LAST:
3181 playlist->index = playlist->first_index - 1;
3182 if (playlist->index < 0)
3183 playlist->index += playlist->amount;
3184 break;
3185 default:
3186 playlist->index = new_index;
3187 break;
3191 if ((audio_status() & AUDIO_STATUS_PLAY) && playlist->started)
3192 audio_flush_and_reload_tracks();
3196 #ifdef HAVE_DIRCACHE
3197 queue_post(&playlist_queue, PLAYLIST_LOAD_POINTERS, 0);
3198 #endif
3200 return result;
3203 /* shuffle currently playing playlist */
3204 int playlist_randomise(struct playlist_info* playlist, unsigned int seed,
3205 bool start_current)
3207 int result;
3209 if (!playlist)
3210 playlist = &current_playlist;
3212 check_control(playlist);
3214 result = randomise_playlist(playlist, seed, start_current, true);
3216 if (result != -1 && (audio_status() & AUDIO_STATUS_PLAY) &&
3217 playlist->started)
3218 audio_flush_and_reload_tracks();
3220 return result;
3223 /* sort currently playing playlist */
3224 int playlist_sort(struct playlist_info* playlist, bool start_current)
3226 int result;
3228 if (!playlist)
3229 playlist = &current_playlist;
3231 check_control(playlist);
3233 result = sort_playlist(playlist, start_current, true);
3235 if (result != -1 && (audio_status() & AUDIO_STATUS_PLAY) &&
3236 playlist->started)
3237 audio_flush_and_reload_tracks();
3239 return result;
3242 /* returns true if playlist has been modified */
3243 bool playlist_modified(const struct playlist_info* playlist)
3245 if (!playlist)
3246 playlist = &current_playlist;
3248 if (playlist->shuffle_modified ||
3249 playlist->deleted ||
3250 playlist->num_inserted_tracks > 0)
3251 return true;
3253 return false;
3256 /* returns index of first track in playlist */
3257 int playlist_get_first_index(const struct playlist_info* playlist)
3259 if (!playlist)
3260 playlist = &current_playlist;
3262 return playlist->first_index;
3265 /* returns shuffle seed of playlist */
3266 int playlist_get_seed(const struct playlist_info* playlist)
3268 if (!playlist)
3269 playlist = &current_playlist;
3271 return playlist->seed;
3274 /* returns number of tracks in playlist (includes queued/inserted tracks) */
3275 int playlist_amount_ex(const struct playlist_info* playlist)
3277 if (!playlist)
3278 playlist = &current_playlist;
3280 return playlist->amount;
3283 /* returns full path of playlist (minus extension) */
3284 char *playlist_name(const struct playlist_info* playlist, char *buf,
3285 int buf_size)
3287 char *sep;
3289 if (!playlist)
3290 playlist = &current_playlist;
3292 snprintf(buf, buf_size, "%s", playlist->filename+playlist->dirlen);
3294 if (!buf[0])
3295 return NULL;
3297 /* Remove extension */
3298 sep = strrchr(buf, '.');
3299 if (sep)
3300 *sep = 0;
3302 return buf;
3305 /* returns the playlist filename */
3306 char *playlist_get_name(const struct playlist_info* playlist, char *buf,
3307 int buf_size)
3309 if (!playlist)
3310 playlist = &current_playlist;
3312 snprintf(buf, buf_size, "%s", playlist->filename);
3314 if (!buf[0])
3315 return NULL;
3317 return buf;
3320 /* Fills info structure with information about track at specified index.
3321 Returns 0 on success and -1 on failure */
3322 int playlist_get_track_info(struct playlist_info* playlist, int index,
3323 struct playlist_track_info* info)
3325 int seek;
3326 bool control_file;
3328 if (!playlist)
3329 playlist = &current_playlist;
3331 if (index < 0 || index >= playlist->amount)
3332 return -1;
3334 control_file = playlist->indices[index] & PLAYLIST_INSERT_TYPE_MASK;
3335 seek = playlist->indices[index] & PLAYLIST_SEEK_MASK;
3337 if (get_filename(playlist, index, seek, control_file, info->filename,
3338 sizeof(info->filename)) < 0)
3339 return -1;
3341 info->attr = 0;
3343 if (control_file)
3345 if (playlist->indices[index] & PLAYLIST_QUEUE_MASK)
3346 info->attr |= PLAYLIST_ATTR_QUEUED;
3347 else
3348 info->attr |= PLAYLIST_ATTR_INSERTED;
3352 if (playlist->indices[index] & PLAYLIST_SKIPPED)
3353 info->attr |= PLAYLIST_ATTR_SKIPPED;
3355 info->index = index;
3356 info->display_index = rotate_index(playlist, index) + 1;
3358 return 0;
3361 /* save the current dynamic playlist to specified file */
3362 int playlist_save(struct playlist_info* playlist, char *filename)
3364 int fd;
3365 int i, index;
3366 int count = 0;
3367 char path[MAX_PATH+1];
3368 char tmp_buf[MAX_PATH+1];
3369 int result = 0;
3370 bool overwrite_current = false;
3371 int* index_buf = NULL;
3373 if (!playlist)
3374 playlist = &current_playlist;
3376 if (playlist->amount <= 0)
3377 return -1;
3379 /* use current working directory as base for pathname */
3380 if (format_track_path(path, filename, sizeof(tmp_buf),
3381 strlen(filename)+1, getcwd(NULL, -1)) < 0)
3382 return -1;
3384 if (!strncmp(playlist->filename, path, strlen(path)))
3386 /* Attempting to overwrite current playlist file.*/
3388 if (playlist->buffer_size < (int)(playlist->amount * sizeof(int)))
3390 /* not enough buffer space to store updated indices */
3391 gui_syncsplash(HZ*2, ID2P(LANG_PLAYLIST_ACCESS_ERROR));
3392 return -1;
3395 /* in_ram buffer is unused for m3u files so we'll use for storing
3396 updated indices */
3397 index_buf = (int*)playlist->buffer;
3399 /* use temporary pathname */
3400 snprintf(path, sizeof(path), "%s_temp", playlist->filename);
3401 overwrite_current = true;
3404 fd = open(path, O_CREAT|O_WRONLY|O_TRUNC);
3405 if (fd < 0)
3407 gui_syncsplash(HZ*2, ID2P(LANG_PLAYLIST_ACCESS_ERROR));
3408 return -1;
3411 display_playlist_count(count, ID2P(LANG_PLAYLIST_SAVE_COUNT), false);
3413 cpu_boost(true);
3415 index = playlist->first_index;
3416 for (i=0; i<playlist->amount; i++)
3418 bool control_file;
3419 bool queue;
3420 int seek;
3422 /* user abort */
3423 if (action_userabort(TIMEOUT_NOBLOCK))
3425 result = -1;
3426 break;
3429 control_file = playlist->indices[index] & PLAYLIST_INSERT_TYPE_MASK;
3430 queue = playlist->indices[index] & PLAYLIST_QUEUE_MASK;
3431 seek = playlist->indices[index] & PLAYLIST_SEEK_MASK;
3433 /* Don't save queued files */
3434 if (!queue)
3436 if (get_filename(playlist, index, seek, control_file, tmp_buf,
3437 MAX_PATH+1) < 0)
3439 result = -1;
3440 break;
3443 if (overwrite_current)
3444 index_buf[count] = lseek(fd, 0, SEEK_CUR);
3446 if (fdprintf(fd, "%s\n", tmp_buf) < 0)
3448 gui_syncsplash(HZ*2, ID2P(LANG_PLAYLIST_ACCESS_ERROR));
3449 result = -1;
3450 break;
3453 count++;
3455 if ((count % PLAYLIST_DISPLAY_COUNT) == 0)
3456 display_playlist_count(count, ID2P(LANG_PLAYLIST_SAVE_COUNT),
3457 false);
3459 yield();
3462 index = (index+1)%playlist->amount;
3465 display_playlist_count(count, ID2P(LANG_PLAYLIST_SAVE_COUNT), true);
3467 close(fd);
3469 if (overwrite_current && result >= 0)
3471 result = -1;
3473 mutex_lock(&playlist->control_mutex);
3475 /* Replace the current playlist with the new one and update indices */
3476 close(playlist->fd);
3477 if (remove(playlist->filename) >= 0)
3479 if (rename(path, playlist->filename) >= 0)
3481 playlist->fd = open(playlist->filename, O_RDONLY);
3482 if (playlist->fd >= 0)
3484 index = playlist->first_index;
3485 for (i=0, count=0; i<playlist->amount; i++)
3487 if (!(playlist->indices[index] & PLAYLIST_QUEUE_MASK))
3489 playlist->indices[index] = index_buf[count];
3490 count++;
3492 index = (index+1)%playlist->amount;
3495 /* we need to recreate control because inserted tracks are
3496 now part of the playlist and shuffle has been
3497 invalidated */
3498 result = recreate_control(playlist);
3503 mutex_unlock(&playlist->control_mutex);
3507 cpu_boost(false);
3509 return result;
3513 * Search specified directory for tracks and notify via callback. May be
3514 * called recursively.
3516 int playlist_directory_tracksearch(const char* dirname, bool recurse,
3517 int (*callback)(char*, void*),
3518 void* context)
3520 char buf[MAX_PATH+1];
3521 int result = 0;
3522 int num_files = 0;
3523 int i;
3524 struct entry *files;
3525 struct tree_context* tc = tree_get_context();
3526 int old_dirfilter = *(tc->dirfilter);
3528 if (!callback)
3529 return -1;
3531 /* use the tree browser dircache to load files */
3532 *(tc->dirfilter) = SHOW_ALL;
3534 if (ft_load(tc, dirname) < 0)
3536 gui_syncsplash(HZ*2, ID2P(LANG_PLAYLIST_DIRECTORY_ACCESS_ERROR));
3537 *(tc->dirfilter) = old_dirfilter;
3538 return -1;
3541 files = (struct entry*) tc->dircache;
3542 num_files = tc->filesindir;
3544 /* we've overwritten the dircache so tree browser will need to be
3545 reloaded */
3546 reload_directory();
3548 for (i=0; i<num_files; i++)
3550 /* user abort */
3551 if (action_userabort(TIMEOUT_NOBLOCK))
3553 result = -1;
3554 break;
3557 if (files[i].attr & ATTR_DIRECTORY)
3559 if (recurse)
3561 /* recursively add directories */
3562 snprintf(buf, sizeof(buf), "%s/%s", dirname, files[i].name);
3563 result = playlist_directory_tracksearch(buf, recurse,
3564 callback, context);
3565 if (result < 0)
3566 break;
3568 /* we now need to reload our current directory */
3569 if(ft_load(tc, dirname) < 0)
3571 result = -1;
3572 break;
3575 files = (struct entry*) tc->dircache;
3576 num_files = tc->filesindir;
3577 if (!num_files)
3579 result = -1;
3580 break;
3583 else
3584 continue;
3586 else if ((files[i].attr & FILE_ATTR_MASK) == FILE_ATTR_AUDIO)
3588 snprintf(buf, sizeof(buf), "%s/%s", dirname, files[i].name);
3590 if (callback(buf, context) != 0)
3592 result = -1;
3593 break;
3596 /* let the other threads work */
3597 yield();
3601 /* restore dirfilter */
3602 *(tc->dirfilter) = old_dirfilter;
3604 return result;