PP502x: Make RAM physical addresses uncached. Cache the flash ROM on targets with...
[Rockbox.git] / apps / playlist.c
blob99b8c68eb482b7b5995f3ca7d56d74ed01c145b8
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"
103 #define PLAYLIST_CONTROL_FILE ROCKBOX_DIR "/.playlist_control"
104 #define PLAYLIST_CONTROL_FILE_VERSION 2
107 Each playlist index has a flag associated with it which identifies what
108 type of track it is. These flags are stored in the 4 high order bits of
109 the index.
111 NOTE: This limits the playlist file size to a max of 256M.
113 Bits 31-30:
114 00 = Playlist track
115 01 = Track was prepended into playlist
116 10 = Track was inserted into playlist
117 11 = Track was appended into playlist
118 Bit 29:
119 0 = Added track
120 1 = Queued track
121 Bit 28:
122 0 = Track entry is valid
123 1 = Track does not exist on disk and should be skipped
125 #define PLAYLIST_SEEK_MASK 0x0FFFFFFF
126 #define PLAYLIST_INSERT_TYPE_MASK 0xC0000000
127 #define PLAYLIST_QUEUE_MASK 0x20000000
129 #define PLAYLIST_INSERT_TYPE_PREPEND 0x40000000
130 #define PLAYLIST_INSERT_TYPE_INSERT 0x80000000
131 #define PLAYLIST_INSERT_TYPE_APPEND 0xC0000000
133 #define PLAYLIST_QUEUED 0x20000000
134 #define PLAYLIST_SKIPPED 0x10000000
136 #define PLAYLIST_DISPLAY_COUNT 10
138 struct directory_search_context {
139 struct playlist_info* playlist;
140 int position;
141 bool queue;
142 int count;
145 static bool changing_dir = false;
147 static struct playlist_info current_playlist;
148 static char now_playing[MAX_PATH+1];
150 static void empty_playlist(struct playlist_info* playlist, bool resume);
151 static void new_playlist(struct playlist_info* playlist, const char *dir,
152 const char *file);
153 static void create_control(struct playlist_info* playlist);
154 static int check_control(struct playlist_info* playlist);
155 static int recreate_control(struct playlist_info* playlist);
156 static void update_playlist_filename(struct playlist_info* playlist,
157 const char *dir, const char *file);
158 static int add_indices_to_playlist(struct playlist_info* playlist,
159 char* buffer, size_t buflen);
160 static int add_track_to_playlist(struct playlist_info* playlist,
161 const char *filename, int position,
162 bool queue, int seek_pos);
163 static int directory_search_callback(char* filename, void* context);
164 static int remove_track_from_playlist(struct playlist_info* playlist,
165 int position, bool write);
166 static int randomise_playlist(struct playlist_info* playlist,
167 unsigned int seed, bool start_current,
168 bool write);
169 static int sort_playlist(struct playlist_info* playlist, bool start_current,
170 bool write);
171 static int get_next_index(const struct playlist_info* playlist, int steps,
172 int repeat_mode);
173 static void find_and_set_playlist_index(struct playlist_info* playlist,
174 unsigned int seek);
175 static int compare(const void* p1, const void* p2);
176 static int get_filename(struct playlist_info* playlist, int index, int seek,
177 bool control_file, char *buf, int buf_length);
178 static int get_next_directory(char *dir);
179 static int get_next_dir(char *dir, bool is_forward, bool recursion);
180 static int get_previous_directory(char *dir);
181 static int check_subdir_for_music(char *dir, char *subdir);
182 static int format_track_path(char *dest, char *src, int buf_length, int max,
183 char *dir);
184 static void display_playlist_count(int count, const unsigned char *fmt,
185 bool final);
186 static void display_buffer_full(void);
187 static int flush_cached_control(struct playlist_info* playlist);
188 static int update_control(struct playlist_info* playlist,
189 enum playlist_command command, int i1, int i2,
190 const char* s1, const char* s2, void* data);
191 static void sync_control(struct playlist_info* playlist, bool force);
192 static int rotate_index(const struct playlist_info* playlist, int index);
194 #ifdef HAVE_DIRCACHE
195 #define PLAYLIST_LOAD_POINTERS 1
197 static struct event_queue playlist_queue;
198 static long playlist_stack[(DEFAULT_STACK_SIZE + 0x800)/sizeof(long)];
199 static const char playlist_thread_name[] = "playlist cachectrl";
200 #endif
203 * remove any files and indices associated with the playlist
205 static void empty_playlist(struct playlist_info* playlist, bool resume)
207 playlist->filename[0] = '\0';
208 playlist->utf8 = true;
210 if(playlist->fd >= 0)
211 /* If there is an already open playlist, close it. */
212 close(playlist->fd);
213 playlist->fd = -1;
215 if(playlist->control_fd >= 0)
216 close(playlist->control_fd);
217 playlist->control_fd = -1;
218 playlist->control_created = false;
220 playlist->in_ram = false;
222 if (playlist->buffer)
223 playlist->buffer[0] = 0;
225 playlist->buffer_end_pos = 0;
227 playlist->index = 0;
228 playlist->first_index = 0;
229 playlist->amount = 0;
230 playlist->last_insert_pos = -1;
231 playlist->seed = 0;
232 playlist->shuffle_modified = false;
233 playlist->deleted = false;
234 playlist->num_inserted_tracks = 0;
235 playlist->started = false;
237 playlist->num_cached = 0;
238 playlist->pending_control_sync = false;
240 if (!resume && playlist->current)
242 /* start with fresh playlist control file when starting new
243 playlist */
244 create_control(playlist);
246 /* Reset resume settings */
247 global_status.resume_first_index = 0;
248 global_status.resume_seed = -1;
253 * Initialize a new playlist for viewing/editing/playing. dir is the
254 * directory where the playlist is located and file is the filename.
256 static void new_playlist(struct playlist_info* playlist, const char *dir,
257 const char *file)
259 empty_playlist(playlist, false);
261 if (!file)
263 file = "";
265 if (dir && playlist->current) /* !current cannot be in_ram */
266 playlist->in_ram = true;
267 else
268 dir = ""; /* empty playlist */
271 update_playlist_filename(playlist, dir, file);
273 if (playlist->control_fd >= 0)
275 update_control(playlist, PLAYLIST_COMMAND_PLAYLIST,
276 PLAYLIST_CONTROL_FILE_VERSION, -1, dir, file, NULL);
277 sync_control(playlist, false);
282 * create control file for playlist
284 static void create_control(struct playlist_info* playlist)
286 playlist->control_fd = open(playlist->control_filename,
287 O_CREAT|O_RDWR|O_TRUNC);
288 if (playlist->control_fd < 0)
290 if (check_rockboxdir())
292 cond_talk_ids_fq(LANG_PLAYLIST_CONTROL_ACCESS_ERROR);
293 gui_syncsplash(HZ*2, (unsigned char *)"%s (%d)",
294 str(LANG_PLAYLIST_CONTROL_ACCESS_ERROR),
295 playlist->control_fd);
297 playlist->control_created = false;
299 else
301 playlist->control_created = true;
306 * validate the control file. This may include creating/initializing it if
307 * necessary;
309 static int check_control(struct playlist_info* playlist)
311 if (!playlist->control_created)
313 create_control(playlist);
315 if (playlist->control_fd >= 0)
317 char* dir = playlist->filename;
318 char* file = playlist->filename+playlist->dirlen;
319 char c = playlist->filename[playlist->dirlen-1];
321 playlist->filename[playlist->dirlen-1] = '\0';
323 update_control(playlist, PLAYLIST_COMMAND_PLAYLIST,
324 PLAYLIST_CONTROL_FILE_VERSION, -1, dir, file, NULL);
325 sync_control(playlist, false);
326 playlist->filename[playlist->dirlen-1] = c;
330 if (playlist->control_fd < 0)
331 return -1;
333 return 0;
337 * recreate the control file based on current playlist entries
339 static int recreate_control(struct playlist_info* playlist)
341 char temp_file[MAX_PATH+1];
342 int temp_fd = -1;
343 int i;
344 int result = 0;
346 if(playlist->control_fd >= 0)
348 char* dir = playlist->filename;
349 char* file = playlist->filename+playlist->dirlen;
350 char c = playlist->filename[playlist->dirlen-1];
352 close(playlist->control_fd);
354 snprintf(temp_file, sizeof(temp_file), "%s_temp",
355 playlist->control_filename);
357 if (rename(playlist->control_filename, temp_file) < 0)
358 return -1;
360 temp_fd = open(temp_file, O_RDONLY);
361 if (temp_fd < 0)
362 return -1;
364 playlist->control_fd = open(playlist->control_filename,
365 O_CREAT|O_RDWR|O_TRUNC);
366 if (playlist->control_fd < 0)
367 return -1;
369 playlist->filename[playlist->dirlen-1] = '\0';
371 /* cannot call update_control() because of mutex */
372 result = fdprintf(playlist->control_fd, "P:%d:%s:%s\n",
373 PLAYLIST_CONTROL_FILE_VERSION, dir, file);
375 playlist->filename[playlist->dirlen-1] = c;
377 if (result < 0)
379 close(temp_fd);
380 return result;
384 playlist->seed = 0;
385 playlist->shuffle_modified = false;
386 playlist->deleted = false;
387 playlist->num_inserted_tracks = 0;
389 if (playlist->current)
391 global_status.resume_seed = -1;
392 status_save();
395 for (i=0; i<playlist->amount; i++)
397 if (playlist->indices[i] & PLAYLIST_INSERT_TYPE_MASK)
399 bool queue = playlist->indices[i] & PLAYLIST_QUEUE_MASK;
400 char inserted_file[MAX_PATH+1];
402 lseek(temp_fd, playlist->indices[i] & PLAYLIST_SEEK_MASK,
403 SEEK_SET);
404 read_line(temp_fd, inserted_file, sizeof(inserted_file));
406 result = fdprintf(playlist->control_fd, "%c:%d:%d:",
407 queue?'Q':'A', i, playlist->last_insert_pos);
408 if (result > 0)
410 /* save the position in file where name is written */
411 int seek_pos = lseek(playlist->control_fd, 0, SEEK_CUR);
413 result = fdprintf(playlist->control_fd, "%s\n",
414 inserted_file);
416 playlist->indices[i] =
417 (playlist->indices[i] & ~PLAYLIST_SEEK_MASK) | seek_pos;
420 if (result < 0)
421 break;
423 playlist->num_inserted_tracks++;
427 close(temp_fd);
428 remove(temp_file);
429 fsync(playlist->control_fd);
431 if (result < 0)
432 return result;
434 return 0;
438 * store directory and name of playlist file
440 static void update_playlist_filename(struct playlist_info* playlist,
441 const char *dir, const char *file)
443 char *sep="";
444 int dirlen = strlen(dir);
445 int filelen = strlen(file);
447 /* Default to utf8 unless explicitly told otherwise. */
448 playlist->utf8 = !(filelen > 4 && strcasecmp(&file[filelen - 4], ".m3u") == 0);
450 /* If the dir does not end in trailing slash, we use a separator.
451 Otherwise we don't. */
452 if('/' != dir[dirlen-1])
454 sep="/";
455 dirlen++;
458 playlist->dirlen = dirlen;
460 snprintf(playlist->filename, sizeof(playlist->filename),
461 "%s%s%s", dir, sep, file);
465 * calculate track offsets within a playlist file
467 static int add_indices_to_playlist(struct playlist_info* playlist,
468 char* buffer, size_t buflen)
470 unsigned int nread;
471 unsigned int i = 0;
472 unsigned int count = 0;
473 bool store_index;
474 unsigned char *p;
475 int result = 0;
477 if(-1 == playlist->fd)
478 playlist->fd = open(playlist->filename, O_RDONLY);
479 if(playlist->fd < 0)
480 return -1; /* failure */
482 #ifdef HAVE_LCD_BITMAP
483 if(global_settings.statusbar)
484 lcd_setmargins(0, STATUSBAR_HEIGHT);
485 else
486 lcd_setmargins(0, 0);
487 #endif
488 gui_syncsplash(0, ID2P(LANG_WAIT));
490 if (!buffer)
492 /* use mp3 buffer for maximum load speed */
493 audio_stop();
494 #if CONFIG_CODEC != SWCODEC
495 talk_buffer_steal(); /* we use the mp3 buffer, need to tell */
496 buflen = (audiobufend - audiobuf);
497 buffer = (char *)audiobuf;
498 #else
499 buffer = (char *)audio_get_buffer(false, &buflen);
500 #endif
503 store_index = true;
505 while(1)
507 nread = read(playlist->fd, buffer, buflen);
508 /* Terminate on EOF */
509 if(nread <= 0)
510 break;
512 p = (unsigned char *)buffer;
514 /* utf8 BOM at beginning of file? */
515 if(i == 0 && nread > 3
516 && *p == 0xef && *(p+1) == 0xbb && *(p+2) == 0xbf) {
517 nread -= 3;
518 p += 3;
519 i += 3;
520 playlist->utf8 = true; /* Override any earlier indication. */
523 for(count=0; count < nread; count++,p++) {
525 /* Are we on a new line? */
526 if((*p == '\n') || (*p == '\r'))
528 store_index = true;
530 else if(store_index)
532 store_index = false;
534 if(*p != '#')
536 if ( playlist->amount >= playlist->max_playlist_size ) {
537 display_buffer_full();
538 result = -1;
539 goto exit;
542 /* Store a new entry */
543 playlist->indices[ playlist->amount ] = i+count;
544 #ifdef HAVE_DIRCACHE
545 if (playlist->filenames)
546 playlist->filenames[ playlist->amount ] = NULL;
547 #endif
548 playlist->amount++;
553 i+= count;
556 exit:
557 #ifdef HAVE_DIRCACHE
558 queue_post(&playlist_queue, PLAYLIST_LOAD_POINTERS, 0);
559 #endif
561 return result;
565 * Removes all tracks, from the playlist, leaving the presently playing
566 * track queued.
568 int remove_all_tracks(struct playlist_info *playlist)
570 int result;
572 if (playlist == NULL)
573 playlist = &current_playlist;
575 while (playlist->index > 0)
576 if ((result = remove_track_from_playlist(playlist, 0, true)) < 0)
577 return result;
579 while (playlist->amount > 1)
580 if ((result = remove_track_from_playlist(playlist, 1, true)) < 0)
581 return result;
583 if (playlist->amount == 1) {
584 playlist->indices[0] |= PLAYLIST_QUEUED;
587 return 0;
592 * Add track to playlist at specified position. There are five special
593 * positions that can be specified:
594 * PLAYLIST_PREPEND - Add track at beginning of playlist
595 * PLAYLIST_INSERT - Add track after current song. NOTE: If
596 * there are already inserted tracks then track
597 * is added to the end of the insertion list
598 * PLAYLIST_INSERT_FIRST - Add track immediately after current song, no
599 * matter what other tracks have been inserted
600 * PLAYLIST_INSERT_LAST - Add track to end of playlist
601 * PLAYLIST_INSERT_SHUFFLED - Add track at some random point between the
602 * current playing track and end of playlist
603 * PLAYLIST_REPLACE - Erase current playlist, Cue the current track
604 * and inster this track at the end.
606 static int add_track_to_playlist(struct playlist_info* playlist,
607 const char *filename, int position,
608 bool queue, int seek_pos)
610 int insert_position, orig_position;
611 unsigned long flags = PLAYLIST_INSERT_TYPE_INSERT;
612 int i;
614 insert_position = orig_position = position;
616 if (playlist->amount >= playlist->max_playlist_size)
618 display_buffer_full();
619 return -1;
622 switch (position)
624 case PLAYLIST_PREPEND:
625 position = insert_position = playlist->first_index;
626 break;
627 case PLAYLIST_INSERT:
628 /* if there are already inserted tracks then add track to end of
629 insertion list else add after current playing track */
630 if (playlist->last_insert_pos >= 0 &&
631 playlist->last_insert_pos < playlist->amount &&
632 (playlist->indices[playlist->last_insert_pos]&
633 PLAYLIST_INSERT_TYPE_MASK) == PLAYLIST_INSERT_TYPE_INSERT)
634 position = insert_position = playlist->last_insert_pos+1;
635 else if (playlist->amount > 0)
636 position = insert_position = playlist->index + 1;
637 else
638 position = insert_position = 0;
640 if (playlist->started)
641 playlist->last_insert_pos = position;
642 break;
643 case PLAYLIST_INSERT_FIRST:
644 if (playlist->amount > 0)
645 position = insert_position = playlist->index + 1;
646 else
647 position = insert_position = 0;
649 if (playlist->last_insert_pos < 0 && playlist->started)
650 playlist->last_insert_pos = position;
651 break;
652 case PLAYLIST_INSERT_LAST:
653 if (playlist->first_index > 0)
654 position = insert_position = playlist->first_index;
655 else
656 position = insert_position = playlist->amount;
657 break;
658 case PLAYLIST_INSERT_SHUFFLED:
660 if (playlist->started)
662 int offset;
663 int n = playlist->amount -
664 rotate_index(playlist, playlist->index);
666 if (n > 0)
667 offset = rand() % n;
668 else
669 offset = 0;
671 position = playlist->index + offset + 1;
672 if (position >= playlist->amount)
673 position -= playlist->amount;
675 insert_position = position;
677 else
678 position = insert_position = (rand() % (playlist->amount+1));
679 break;
681 case PLAYLIST_REPLACE:
682 if (remove_all_tracks(playlist) < 0)
683 return -1;
685 position = insert_position = playlist->index + 1;
686 break;
689 if (queue)
690 flags |= PLAYLIST_QUEUED;
692 /* shift indices so that track can be added */
693 for (i=playlist->amount; i>insert_position; i--)
695 playlist->indices[i] = playlist->indices[i-1];
696 #ifdef HAVE_DIRCACHE
697 if (playlist->filenames)
698 playlist->filenames[i] = playlist->filenames[i-1];
699 #endif
702 /* update stored indices if needed */
703 if (playlist->amount > 0 && insert_position <= playlist->index &&
704 playlist->started)
705 playlist->index++;
707 if (playlist->amount > 0 && insert_position <= playlist->first_index &&
708 orig_position != PLAYLIST_PREPEND && playlist->started)
710 playlist->first_index++;
712 if (seek_pos < 0 && playlist->current)
714 global_status.resume_first_index = playlist->first_index;
715 status_save();
719 if (insert_position < playlist->last_insert_pos ||
720 (insert_position == playlist->last_insert_pos && position < 0))
721 playlist->last_insert_pos++;
723 if (seek_pos < 0 && playlist->control_fd >= 0)
725 int result = update_control(playlist,
726 (queue?PLAYLIST_COMMAND_QUEUE:PLAYLIST_COMMAND_ADD), position,
727 playlist->last_insert_pos, filename, NULL, &seek_pos);
729 if (result < 0)
730 return result;
733 playlist->indices[insert_position] = flags | seek_pos;
735 #ifdef HAVE_DIRCACHE
736 if (playlist->filenames)
737 playlist->filenames[insert_position] = NULL;
738 #endif
740 playlist->amount++;
741 playlist->num_inserted_tracks++;
743 return insert_position;
747 * Callback for playlist_directory_tracksearch to insert track into
748 * playlist.
750 static int directory_search_callback(char* filename, void* context)
752 struct directory_search_context* c =
753 (struct directory_search_context*) context;
754 int insert_pos;
756 insert_pos = add_track_to_playlist(c->playlist, filename, c->position,
757 c->queue, -1);
759 if (insert_pos < 0)
760 return -1;
762 (c->count)++;
764 /* Make sure tracks are inserted in correct order if user requests
765 INSERT_FIRST */
766 if (c->position == PLAYLIST_INSERT_FIRST || c->position >= 0)
767 c->position = insert_pos + 1;
769 if (((c->count)%PLAYLIST_DISPLAY_COUNT) == 0)
771 unsigned char* count_str;
773 if (c->queue)
774 count_str = ID2P(LANG_PLAYLIST_QUEUE_COUNT);
775 else
776 count_str = ID2P(LANG_PLAYLIST_INSERT_COUNT);
778 display_playlist_count(c->count, count_str, false);
780 if ((c->count) == PLAYLIST_DISPLAY_COUNT &&
781 (audio_status() & AUDIO_STATUS_PLAY) &&
782 c->playlist->started)
783 audio_flush_and_reload_tracks();
786 return 0;
790 * remove track at specified position
792 static int remove_track_from_playlist(struct playlist_info* playlist,
793 int position, bool write)
795 int i;
796 bool inserted;
798 if (playlist->amount <= 0)
799 return -1;
801 inserted = playlist->indices[position] & PLAYLIST_INSERT_TYPE_MASK;
803 /* shift indices now that track has been removed */
804 for (i=position; i<playlist->amount; i++)
806 playlist->indices[i] = playlist->indices[i+1];
807 #ifdef HAVE_DIRCACHE
808 if (playlist->filenames)
809 playlist->filenames[i] = playlist->filenames[i+1];
810 #endif
813 playlist->amount--;
815 if (inserted)
816 playlist->num_inserted_tracks--;
817 else
818 playlist->deleted = true;
820 /* update stored indices if needed */
821 if (position < playlist->index)
822 playlist->index--;
824 if (position < playlist->first_index)
826 playlist->first_index--;
828 if (write)
830 global_status.resume_first_index = playlist->first_index;
831 status_save();
835 if (position <= playlist->last_insert_pos)
836 playlist->last_insert_pos--;
838 if (write && playlist->control_fd >= 0)
840 int result = update_control(playlist, PLAYLIST_COMMAND_DELETE,
841 position, -1, NULL, NULL, NULL);
843 if (result < 0)
844 return result;
846 sync_control(playlist, false);
849 return 0;
853 * randomly rearrange the array of indices for the playlist. If start_current
854 * is true then update the index to the new index of the current playing track
856 static int randomise_playlist(struct playlist_info* playlist,
857 unsigned int seed, bool start_current,
858 bool write)
860 int count;
861 int candidate;
862 long store;
863 unsigned int current = playlist->indices[playlist->index];
865 /* seed 0 is used to identify sorted playlist for resume purposes */
866 if (seed == 0)
867 seed = 1;
869 /* seed with the given seed */
870 srand(seed);
872 /* randomise entire indices list */
873 for(count = playlist->amount - 1; count >= 0; count--)
875 /* the rand is from 0 to RAND_MAX, so adjust to our value range */
876 candidate = rand() % (count + 1);
878 /* now swap the values at the 'count' and 'candidate' positions */
879 store = playlist->indices[candidate];
880 playlist->indices[candidate] = playlist->indices[count];
881 playlist->indices[count] = store;
882 #ifdef HAVE_DIRCACHE
883 if (playlist->filenames)
885 store = (long)playlist->filenames[candidate];
886 playlist->filenames[candidate] = playlist->filenames[count];
887 playlist->filenames[count] = (struct dircache_entry *)store;
889 #endif
892 if (start_current)
893 find_and_set_playlist_index(playlist, current);
895 /* indices have been moved so last insert position is no longer valid */
896 playlist->last_insert_pos = -1;
898 playlist->seed = seed;
899 if (playlist->num_inserted_tracks > 0 || playlist->deleted)
900 playlist->shuffle_modified = true;
902 if (write)
904 update_control(playlist, PLAYLIST_COMMAND_SHUFFLE, seed,
905 playlist->first_index, NULL, NULL, NULL);
906 global_status.resume_seed = seed;
907 status_save();
910 return 0;
914 * Sort the array of indices for the playlist. If start_current is true then
915 * set the index to the new index of the current song.
917 static int sort_playlist(struct playlist_info* playlist, bool start_current,
918 bool write)
920 unsigned int current = playlist->indices[playlist->index];
922 if (playlist->amount > 0)
923 qsort(playlist->indices, playlist->amount,
924 sizeof(playlist->indices[0]), compare);
926 #ifdef HAVE_DIRCACHE
927 /** We need to re-check the song names from disk because qsort can't
928 * sort two arrays at once :/
929 * FIXME: Please implement a better way to do this. */
930 memset(playlist->filenames, 0, playlist->max_playlist_size * sizeof(int));
931 queue_post(&playlist_queue, PLAYLIST_LOAD_POINTERS, 0);
932 #endif
934 if (start_current)
935 find_and_set_playlist_index(playlist, current);
937 /* indices have been moved so last insert position is no longer valid */
938 playlist->last_insert_pos = -1;
940 if (!playlist->num_inserted_tracks && !playlist->deleted)
941 playlist->shuffle_modified = false;
942 if (write && playlist->control_fd >= 0)
944 update_control(playlist, PLAYLIST_COMMAND_UNSHUFFLE,
945 playlist->first_index, -1, NULL, NULL, NULL);
946 global_status.resume_seed = 0;
947 status_save();
950 return 0;
953 /* Calculate how many steps we have to really step when skipping entries
954 * marked as bad.
956 static int calculate_step_count(const struct playlist_info *playlist, int steps)
958 int i, count, direction;
959 int index;
960 int stepped_count = 0;
962 if (steps < 0)
964 direction = -1;
965 count = -steps;
967 else
969 direction = 1;
970 count = steps;
973 index = playlist->index;
974 i = 0;
975 do {
976 /* Boundary check */
977 if (index < 0)
978 index += playlist->amount;
979 if (index >= playlist->amount)
980 index -= playlist->amount;
982 /* Check if we found a bad entry. */
983 if (playlist->indices[index] & PLAYLIST_SKIPPED)
985 steps += direction;
986 /* Are all entries bad? */
987 if (stepped_count++ > playlist->amount)
988 break ;
990 else
991 i++;
993 index += direction;
994 } while (i <= count);
996 return steps;
999 /* Marks the index of the track to be skipped that is "steps" away from
1000 * current playing track.
1002 void playlist_skip_entry(struct playlist_info *playlist, int steps)
1004 int index;
1006 if (playlist == NULL)
1007 playlist = &current_playlist;
1009 /* need to account for already skipped tracks */
1010 steps = calculate_step_count(playlist, steps);
1012 index = playlist->index + steps;
1013 if (index < 0)
1014 index += playlist->amount;
1015 else if (index >= playlist->amount)
1016 index -= playlist->amount;
1018 playlist->indices[index] |= PLAYLIST_SKIPPED;
1022 * returns the index of the track that is "steps" away from current playing
1023 * track.
1025 static int get_next_index(const struct playlist_info* playlist, int steps,
1026 int repeat_mode)
1028 int current_index = playlist->index;
1029 int next_index = -1;
1031 if (playlist->amount <= 0)
1032 return -1;
1034 if (repeat_mode == -1)
1035 repeat_mode = global_settings.repeat_mode;
1037 if (repeat_mode == REPEAT_SHUFFLE && playlist->amount <= 1)
1038 repeat_mode = REPEAT_ALL;
1040 steps = calculate_step_count(playlist, steps);
1041 switch (repeat_mode)
1043 case REPEAT_SHUFFLE:
1044 /* Treat repeat shuffle just like repeat off. At end of playlist,
1045 play will be resumed in playlist_next() */
1046 case REPEAT_OFF:
1048 current_index = rotate_index(playlist, current_index);
1049 next_index = current_index+steps;
1050 if ((next_index < 0) || (next_index >= playlist->amount))
1051 next_index = -1;
1052 else
1053 next_index = (next_index+playlist->first_index) %
1054 playlist->amount;
1056 break;
1059 case REPEAT_ONE:
1060 #ifdef AB_REPEAT_ENABLE
1061 case REPEAT_AB:
1062 #endif
1063 next_index = current_index;
1064 break;
1066 case REPEAT_ALL:
1067 default:
1069 next_index = (current_index+steps) % playlist->amount;
1070 while (next_index < 0)
1071 next_index += playlist->amount;
1073 if (steps >= playlist->amount)
1075 int i, index;
1077 index = next_index;
1078 next_index = -1;
1080 /* second time around so skip the queued files */
1081 for (i=0; i<playlist->amount; i++)
1083 if (playlist->indices[index] & PLAYLIST_QUEUE_MASK)
1084 index = (index+1) % playlist->amount;
1085 else
1087 next_index = index;
1088 break;
1092 break;
1096 /* No luck if the whole playlist was bad. */
1097 if (playlist->indices[next_index] & PLAYLIST_SKIPPED)
1098 return -1;
1100 return next_index;
1104 * Search for the seek track and set appropriate indices. Used after shuffle
1105 * to make sure the current index is still pointing to correct track.
1107 static void find_and_set_playlist_index(struct playlist_info* playlist,
1108 unsigned int seek)
1110 int i;
1112 /* Set the index to the current song */
1113 for (i=0; i<playlist->amount; i++)
1115 if (playlist->indices[i] == seek)
1117 playlist->index = playlist->first_index = i;
1119 if (playlist->current)
1121 global_status.resume_first_index = i;
1122 status_save();
1125 break;
1131 * used to sort track indices. Sort order is as follows:
1132 * 1. Prepended tracks (in prepend order)
1133 * 2. Playlist/directory tracks (in playlist order)
1134 * 3. Inserted/Appended tracks (in insert order)
1136 static int compare(const void* p1, const void* p2)
1138 unsigned long* e1 = (unsigned long*) p1;
1139 unsigned long* e2 = (unsigned long*) p2;
1140 unsigned long flags1 = *e1 & PLAYLIST_INSERT_TYPE_MASK;
1141 unsigned long flags2 = *e2 & PLAYLIST_INSERT_TYPE_MASK;
1143 if (flags1 == flags2)
1144 return (*e1 & PLAYLIST_SEEK_MASK) - (*e2 & PLAYLIST_SEEK_MASK);
1145 else if (flags1 == PLAYLIST_INSERT_TYPE_PREPEND ||
1146 flags2 == PLAYLIST_INSERT_TYPE_APPEND)
1147 return -1;
1148 else if (flags1 == PLAYLIST_INSERT_TYPE_APPEND ||
1149 flags2 == PLAYLIST_INSERT_TYPE_PREPEND)
1150 return 1;
1151 else if (flags1 && flags2)
1152 return (*e1 & PLAYLIST_SEEK_MASK) - (*e2 & PLAYLIST_SEEK_MASK);
1153 else
1154 return *e1 - *e2;
1157 #ifdef HAVE_DIRCACHE
1159 * Thread to update filename pointers to dircache on background
1160 * without affecting playlist load up performance. This thread also flushes
1161 * any pending control commands when the disk spins up.
1163 static void playlist_thread(void)
1165 struct event ev;
1166 bool dirty_pointers = false;
1167 static char tmp[MAX_PATH+1];
1169 struct playlist_info *playlist;
1170 int index;
1171 int seek;
1172 bool control_file;
1174 int sleep_time = 5;
1176 #ifndef HAVE_FLASH_STORAGE
1177 if (global_settings.disk_spindown > 1 &&
1178 global_settings.disk_spindown <= 5)
1179 sleep_time = global_settings.disk_spindown - 1;
1180 #endif
1182 while (1)
1184 queue_wait_w_tmo(&playlist_queue, &ev, HZ*sleep_time);
1186 switch (ev.id)
1188 case PLAYLIST_LOAD_POINTERS:
1189 dirty_pointers = true;
1190 break ;
1192 /* Start the background scanning after either the disk spindown
1193 timeout or 5s, whichever is less */
1194 case SYS_TIMEOUT:
1195 playlist = &current_playlist;
1197 if (playlist->control_fd >= 0
1198 # ifndef SIMULATOR
1199 && ata_disk_is_active()
1200 # endif
1203 if (playlist->num_cached > 0)
1205 mutex_lock(&playlist->control_mutex);
1206 flush_cached_control(playlist);
1207 mutex_unlock(&playlist->control_mutex);
1210 sync_control(playlist, true);
1213 if (!dirty_pointers)
1214 break ;
1216 if (!dircache_is_enabled() || !playlist->filenames
1217 || playlist->amount <= 0)
1218 break ;
1220 #ifdef HAVE_ADJUSTABLE_CPU_FREQ
1221 cpu_boost(true);
1222 #endif
1223 for (index = 0; index < playlist->amount
1224 && queue_empty(&playlist_queue); index++)
1226 /* Process only pointers that are not already loaded. */
1227 if (playlist->filenames[index])
1228 continue ;
1230 control_file = playlist->indices[index] & PLAYLIST_INSERT_TYPE_MASK;
1231 seek = playlist->indices[index] & PLAYLIST_SEEK_MASK;
1233 /* Load the filename from playlist file. */
1234 if (get_filename(playlist, index, seek, control_file, tmp,
1235 sizeof(tmp)) < 0)
1236 break ;
1238 /* Set the dircache entry pointer. */
1239 playlist->filenames[index] = dircache_get_entry_ptr(tmp);
1241 /* And be on background so user doesn't notice any delays. */
1242 yield();
1245 #ifdef HAVE_ADJUSTABLE_CPU_FREQ
1246 cpu_boost(false);
1247 #endif
1248 dirty_pointers = false;
1249 break ;
1251 #ifndef SIMULATOR
1252 case SYS_USB_CONNECTED:
1253 usb_acknowledge(SYS_USB_CONNECTED_ACK);
1254 usb_wait_for_disconnect(&playlist_queue);
1255 break ;
1256 #endif
1260 #endif
1263 * gets pathname for track at seek index
1265 static int get_filename(struct playlist_info* playlist, int index, int seek,
1266 bool control_file, char *buf, int buf_length)
1268 int fd;
1269 int max = -1;
1270 char tmp_buf[MAX_PATH+1];
1271 char dir_buf[MAX_PATH+1];
1273 if (buf_length > MAX_PATH+1)
1274 buf_length = MAX_PATH+1;
1276 #ifdef HAVE_DIRCACHE
1277 if (dircache_is_enabled() && playlist->filenames)
1279 if (playlist->filenames[index] != NULL)
1281 dircache_copy_path(playlist->filenames[index], tmp_buf, sizeof(tmp_buf)-1);
1282 max = strlen(tmp_buf) + 1;
1285 #else
1286 (void)index;
1287 #endif
1289 if (playlist->in_ram && !control_file && max < 0)
1291 strncpy(tmp_buf, &playlist->buffer[seek], sizeof(tmp_buf));
1292 tmp_buf[MAX_PATH] = '\0';
1293 max = strlen(tmp_buf) + 1;
1295 else if (max < 0)
1297 mutex_lock(&playlist->control_mutex);
1299 if (control_file)
1300 fd = playlist->control_fd;
1301 else
1303 if(-1 == playlist->fd)
1304 playlist->fd = open(playlist->filename, O_RDONLY);
1306 fd = playlist->fd;
1309 if(-1 != fd)
1312 if (lseek(fd, seek, SEEK_SET) != seek)
1313 max = -1;
1314 else
1316 max = read(fd, tmp_buf, MIN((size_t) buf_length, sizeof(tmp_buf)));
1318 if ((max > 0) && !playlist->utf8)
1320 char* end;
1321 int i = 0;
1323 /* Locate EOL. */
1324 while ((tmp_buf[i] != '\n') && (tmp_buf[i] != '\r')
1325 && (i < max))
1327 i++;
1330 /* Now work back killing white space. */
1331 while ((i > 0) && isspace(tmp_buf[i - 1]))
1333 i--;
1336 /* Borrow dir_buf a little... */
1337 /* TODO: iso_decode can overflow dir_buf; it really
1338 * should take a dest size argument.
1340 end = iso_decode(tmp_buf, dir_buf, -1, i);
1341 *end = 0;
1342 strncpy(tmp_buf, dir_buf, sizeof(tmp_buf));
1343 tmp_buf[sizeof(tmp_buf) - 1] = 0;
1344 max = strlen(tmp_buf);
1349 mutex_unlock(&playlist->control_mutex);
1351 if (max < 0)
1353 if (control_file)
1354 gui_syncsplash(HZ*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR));
1355 else
1356 gui_syncsplash(HZ*2, ID2P(LANG_PLAYLIST_ACCESS_ERROR));
1358 return max;
1362 strncpy(dir_buf, playlist->filename, playlist->dirlen-1);
1363 dir_buf[playlist->dirlen-1] = 0;
1365 return (format_track_path(buf, tmp_buf, buf_length, max, dir_buf));
1368 static int get_next_directory(char *dir){
1369 return get_next_dir(dir,true,false);
1372 static int get_previous_directory(char *dir){
1373 return get_next_dir(dir,false,false);
1377 * search through all the directories (starting with the current) to find
1378 * one that has tracks to play
1380 static int get_next_dir(char *dir, bool is_forward, bool recursion)
1382 struct playlist_info* playlist = &current_playlist;
1383 int result = -1;
1384 int sort_dir = global_settings.sort_dir;
1385 char *start_dir = NULL;
1386 bool exit = false;
1387 struct tree_context* tc = tree_get_context();
1388 int dirfilter = *(tc->dirfilter);
1390 if (global_settings.next_folder == FOLDER_ADVANCE_RANDOM)
1392 int fd = open(ROCKBOX_DIR "/folder_advance_list.dat",O_RDONLY);
1393 char buffer[MAX_PATH];
1394 int folder_count = 0,i;
1395 srand(current_tick);
1396 *(tc->dirfilter) = SHOW_MUSIC;
1397 if (fd >= 0)
1399 read(fd,&folder_count,sizeof(int));
1400 while (!exit)
1402 i = rand()%folder_count;
1403 lseek(fd,sizeof(int) + (MAX_PATH*i),SEEK_SET);
1404 read(fd,buffer,MAX_PATH);
1405 if (check_subdir_for_music(buffer,"") ==0)
1406 exit = true;
1408 strcpy(dir,buffer);
1409 close(fd);
1410 *(tc->dirfilter) = dirfilter;
1411 reload_directory();
1412 return 0;
1415 /* not random folder advance */
1416 if (recursion){
1417 /* start with root */
1418 dir[0] = '\0';
1420 else{
1421 /* start with current directory */
1422 strncpy(dir, playlist->filename, playlist->dirlen-1);
1423 dir[playlist->dirlen-1] = '\0';
1426 /* use the tree browser dircache to load files */
1427 *(tc->dirfilter) = SHOW_ALL;
1429 /* sort in another direction if previous dir is requested */
1430 if(!is_forward){
1431 if ((global_settings.sort_dir == 0) || (global_settings.sort_dir == 3))
1432 global_settings.sort_dir = 4;
1433 else if (global_settings.sort_dir == 1)
1434 global_settings.sort_dir = 2;
1435 else if (global_settings.sort_dir == 2)
1436 global_settings.sort_dir = 1;
1437 else if (global_settings.sort_dir == 4)
1438 global_settings.sort_dir = 0;
1441 while (!exit)
1443 struct entry *files;
1444 int num_files = 0;
1445 int i;
1447 if (ft_load(tc, (dir[0]=='\0')?"/":dir) < 0)
1449 gui_syncsplash(HZ*2, ID2P(LANG_PLAYLIST_DIRECTORY_ACCESS_ERROR));
1450 exit = true;
1451 result = -1;
1452 break;
1455 files = (struct entry*) tc->dircache;
1456 num_files = tc->filesindir;
1458 for (i=0; i<num_files; i++)
1460 /* user abort */
1461 if (action_userabort(TIMEOUT_NOBLOCK))
1463 result = -1;
1464 exit = true;
1465 break;
1468 if (files[i].attr & ATTR_DIRECTORY)
1470 if (!start_dir)
1472 result = check_subdir_for_music(dir, files[i].name);
1473 if (result != -1)
1475 exit = true;
1476 break;
1479 else if (!strcmp(start_dir, files[i].name))
1480 start_dir = NULL;
1484 if (!exit)
1486 /* move down to parent directory. current directory name is
1487 stored as the starting point for the search in parent */
1488 start_dir = strrchr(dir, '/');
1489 if (start_dir)
1491 *start_dir = '\0';
1492 start_dir++;
1494 else
1495 break;
1499 /* we've overwritten the dircache so tree browser will need to be
1500 reloaded */
1501 reload_directory();
1503 /* restore dirfilter & sort_dir */
1504 *(tc->dirfilter) = dirfilter;
1505 global_settings.sort_dir = sort_dir;
1507 /* special case if nothing found: try start searching again from root */
1508 if (result == -1 && !recursion){
1509 result = get_next_dir(dir,is_forward, true);
1512 return result;
1516 * Checks if there are any music files in the dir or any of its
1517 * subdirectories. May be called recursively.
1519 static int check_subdir_for_music(char *dir, char *subdir)
1521 int result = -1;
1522 int dirlen = strlen(dir);
1523 int num_files = 0;
1524 int i;
1525 struct entry *files;
1526 bool has_music = false;
1527 bool has_subdir = false;
1528 struct tree_context* tc = tree_get_context();
1530 snprintf(dir+dirlen, MAX_PATH-dirlen, "/%s", subdir);
1532 if (ft_load(tc, dir) < 0)
1534 gui_syncsplash(HZ*2, ID2P(LANG_PLAYLIST_DIRECTORY_ACCESS_ERROR));
1535 return -2;
1538 files = (struct entry*) tc->dircache;
1539 num_files = tc->filesindir;
1541 for (i=0; i<num_files; i++)
1543 if (files[i].attr & ATTR_DIRECTORY)
1544 has_subdir = true;
1545 else if ((files[i].attr & FILE_ATTR_MASK) == FILE_ATTR_AUDIO)
1547 has_music = true;
1548 break;
1552 if (has_music)
1553 return 0;
1555 if (has_subdir)
1557 for (i=0; i<num_files; i++)
1559 if (action_userabort(TIMEOUT_NOBLOCK))
1561 result = -2;
1562 break;
1565 if (files[i].attr & ATTR_DIRECTORY)
1567 result = check_subdir_for_music(dir, files[i].name);
1568 if (!result)
1569 break;
1574 if (result < 0)
1576 if (dirlen)
1578 dir[dirlen] = '\0';
1580 else
1582 strcpy(dir, "/");
1585 /* we now need to reload our current directory */
1586 if(ft_load(tc, dir) < 0)
1587 gui_syncsplash(HZ*2,
1588 ID2P(LANG_PLAYLIST_DIRECTORY_ACCESS_ERROR));
1591 return result;
1595 * Returns absolute path of track
1597 static int format_track_path(char *dest, char *src, int buf_length, int max,
1598 char *dir)
1600 int i = 0;
1601 int j;
1602 char *temp_ptr;
1604 /* Zero-terminate the file name */
1605 while((src[i] != '\n') &&
1606 (src[i] != '\r') &&
1607 (i < max))
1608 i++;
1610 /* Now work back killing white space */
1611 while((src[i-1] == ' ') ||
1612 (src[i-1] == '\t'))
1613 i--;
1615 src[i]=0;
1617 /* replace backslashes with forward slashes */
1618 for ( j=0; j<i; j++ )
1619 if ( src[j] == '\\' )
1620 src[j] = '/';
1622 if('/' == src[0])
1624 strncpy(dest, src, buf_length);
1626 else
1628 /* handle dos style drive letter */
1629 if (':' == src[1])
1630 strncpy(dest, &src[2], buf_length);
1631 else if (!strncmp(src, "../", 3))
1633 /* handle relative paths */
1634 i=3;
1635 while(!strncmp(&src[i], "../", 3))
1636 i += 3;
1637 for (j=0; j<i/3; j++) {
1638 temp_ptr = strrchr(dir, '/');
1639 if (temp_ptr)
1640 *temp_ptr = '\0';
1641 else
1642 break;
1644 snprintf(dest, buf_length, "%s/%s", dir, &src[i]);
1646 else if ( '.' == src[0] && '/' == src[1] ) {
1647 snprintf(dest, buf_length, "%s/%s", dir, &src[2]);
1649 else {
1650 snprintf(dest, buf_length, "%s/%s", dir, src);
1654 return 0;
1658 * Display splash message showing progress of playlist/directory insertion or
1659 * save.
1661 static void display_playlist_count(int count, const unsigned char *fmt,
1662 bool final)
1664 static long talked_tick = 0;
1665 long id = P2ID(fmt);
1666 if(talk_menus_enabled() && id>=0)
1668 if(final || (count && (talked_tick == 0
1669 || TIME_AFTER(current_tick, talked_tick+5*HZ))))
1671 talked_tick = current_tick;
1672 talk_number(count, false);
1673 talk_id(id, true);
1676 fmt = P2STR(fmt);
1678 lcd_clear_display();
1680 #ifdef HAVE_LCD_BITMAP
1681 if(global_settings.statusbar)
1682 lcd_setmargins(0, STATUSBAR_HEIGHT);
1683 else
1684 lcd_setmargins(0, 0);
1685 #endif
1687 gui_syncsplash(0, fmt, count, str(LANG_OFF_ABORT));
1691 * Display buffer full message
1693 static void display_buffer_full(void)
1695 gui_syncsplash(HZ*2, ID2P(LANG_PLAYLIST_BUFFER_FULL));
1699 * Flush any cached control commands to disk. Called when playlist is being
1700 * modified. Returns 0 on success and -1 on failure.
1702 static int flush_cached_control(struct playlist_info* playlist)
1704 int result = 0;
1705 int i;
1707 if (!playlist->num_cached)
1708 return 0;
1710 lseek(playlist->control_fd, 0, SEEK_END);
1712 for (i=0; i<playlist->num_cached; i++)
1714 struct playlist_control_cache* cache =
1715 &(playlist->control_cache[i]);
1717 switch (cache->command)
1719 case PLAYLIST_COMMAND_PLAYLIST:
1720 result = fdprintf(playlist->control_fd, "P:%d:%s:%s\n",
1721 cache->i1, cache->s1, cache->s2);
1722 break;
1723 case PLAYLIST_COMMAND_ADD:
1724 case PLAYLIST_COMMAND_QUEUE:
1725 result = fdprintf(playlist->control_fd, "%c:%d:%d:",
1726 (cache->command == PLAYLIST_COMMAND_ADD)?'A':'Q',
1727 cache->i1, cache->i2);
1728 if (result > 0)
1730 /* save the position in file where name is written */
1731 int* seek_pos = (int *)cache->data;
1732 *seek_pos = lseek(playlist->control_fd, 0, SEEK_CUR);
1733 result = fdprintf(playlist->control_fd, "%s\n",
1734 cache->s1);
1736 break;
1737 case PLAYLIST_COMMAND_DELETE:
1738 result = fdprintf(playlist->control_fd, "D:%d\n", cache->i1);
1739 break;
1740 case PLAYLIST_COMMAND_SHUFFLE:
1741 result = fdprintf(playlist->control_fd, "S:%d:%d\n",
1742 cache->i1, cache->i2);
1743 break;
1744 case PLAYLIST_COMMAND_UNSHUFFLE:
1745 result = fdprintf(playlist->control_fd, "U:%d\n", cache->i1);
1746 break;
1747 case PLAYLIST_COMMAND_RESET:
1748 result = fdprintf(playlist->control_fd, "R\n");
1749 break;
1750 default:
1751 break;
1754 if (result <= 0)
1755 break;
1758 if (result > 0)
1760 if (global_status.resume_seed >= 0)
1762 global_status.resume_seed = -1;
1763 status_save();
1766 playlist->num_cached = 0;
1767 playlist->pending_control_sync = true;
1769 result = 0;
1771 else
1773 result = -1;
1774 gui_syncsplash(HZ*2, ID2P(LANG_PLAYLIST_CONTROL_UPDATE_ERROR));
1777 return result;
1781 * Update control data with new command. Depending on the command, it may be
1782 * cached or flushed to disk.
1784 static int update_control(struct playlist_info* playlist,
1785 enum playlist_command command, int i1, int i2,
1786 const char* s1, const char* s2, void* data)
1788 int result = 0;
1789 struct playlist_control_cache* cache;
1790 bool flush = false;
1792 mutex_lock(&playlist->control_mutex);
1794 cache = &(playlist->control_cache[playlist->num_cached++]);
1796 cache->command = command;
1797 cache->i1 = i1;
1798 cache->i2 = i2;
1799 cache->s1 = s1;
1800 cache->s2 = s2;
1801 cache->data = data;
1803 switch (command)
1805 case PLAYLIST_COMMAND_PLAYLIST:
1806 case PLAYLIST_COMMAND_ADD:
1807 case PLAYLIST_COMMAND_QUEUE:
1808 #ifndef HAVE_DIRCACHE
1809 case PLAYLIST_COMMAND_DELETE:
1810 case PLAYLIST_COMMAND_RESET:
1811 #endif
1812 flush = true;
1813 break;
1814 case PLAYLIST_COMMAND_SHUFFLE:
1815 case PLAYLIST_COMMAND_UNSHUFFLE:
1816 default:
1817 /* only flush when needed */
1818 break;
1821 if (flush || playlist->num_cached == PLAYLIST_MAX_CACHE)
1822 result = flush_cached_control(playlist);
1824 mutex_unlock(&playlist->control_mutex);
1826 return result;
1830 * sync control file to disk
1832 static void sync_control(struct playlist_info* playlist, bool force)
1834 #ifdef HAVE_DIRCACHE
1835 if (playlist->started && force)
1836 #else
1837 (void) force;
1839 if (playlist->started)
1840 #endif
1842 if (playlist->pending_control_sync)
1844 mutex_lock(&playlist->control_mutex);
1845 fsync(playlist->control_fd);
1846 playlist->pending_control_sync = false;
1847 mutex_unlock(&playlist->control_mutex);
1853 * Rotate indices such that first_index is index 0
1855 static int rotate_index(const struct playlist_info* playlist, int index)
1857 index -= playlist->first_index;
1858 if (index < 0)
1859 index += playlist->amount;
1861 return index;
1865 * Initialize playlist entries at startup
1867 void playlist_init(void)
1869 struct playlist_info* playlist = &current_playlist;
1871 playlist->current = true;
1872 snprintf(playlist->control_filename, sizeof(playlist->control_filename),
1873 "%s", PLAYLIST_CONTROL_FILE);
1874 playlist->fd = -1;
1875 playlist->control_fd = -1;
1876 playlist->max_playlist_size = global_settings.max_files_in_playlist;
1877 playlist->indices = buffer_alloc(
1878 playlist->max_playlist_size * sizeof(int));
1879 playlist->buffer_size =
1880 AVERAGE_FILENAME_LENGTH * global_settings.max_files_in_dir;
1881 playlist->buffer = buffer_alloc(playlist->buffer_size);
1882 mutex_init(&playlist->control_mutex);
1883 empty_playlist(playlist, true);
1885 #ifdef HAVE_DIRCACHE
1886 playlist->filenames = buffer_alloc(
1887 playlist->max_playlist_size * sizeof(int));
1888 memset(playlist->filenames, 0,
1889 playlist->max_playlist_size * sizeof(int));
1890 create_thread(playlist_thread, playlist_stack, sizeof(playlist_stack),
1891 playlist_thread_name IF_PRIO(, PRIORITY_BACKGROUND)
1892 IF_COP(, CPU, false));
1893 queue_init(&playlist_queue, true);
1894 #endif
1898 * Clean playlist at shutdown
1900 void playlist_shutdown(void)
1902 struct playlist_info* playlist = &current_playlist;
1904 if (playlist->control_fd >= 0)
1906 mutex_lock(&playlist->control_mutex);
1908 if (playlist->num_cached > 0)
1909 flush_cached_control(playlist);
1911 close(playlist->control_fd);
1913 mutex_unlock(&playlist->control_mutex);
1918 * Create new playlist
1920 int playlist_create(const char *dir, const char *file)
1922 struct playlist_info* playlist = &current_playlist;
1924 new_playlist(playlist, dir, file);
1926 if (file)
1927 /* load the playlist file */
1928 add_indices_to_playlist(playlist, NULL, 0);
1930 return 0;
1933 #define PLAYLIST_COMMAND_SIZE (MAX_PATH+12)
1936 * Restore the playlist state based on control file commands. Called to
1937 * resume playback after shutdown.
1939 int playlist_resume(void)
1941 struct playlist_info* playlist = &current_playlist;
1942 char *buffer;
1943 size_t buflen;
1944 int nread;
1945 int total_read = 0;
1946 int control_file_size = 0;
1947 bool first = true;
1948 bool sorted = true;
1950 /* use mp3 buffer for maximum load speed */
1951 #if CONFIG_CODEC != SWCODEC
1952 talk_buffer_steal(); /* we use the mp3 buffer, need to tell */
1953 buflen = (audiobufend - audiobuf);
1954 buffer = (char *)audiobuf;
1955 #else
1956 buffer = (char *)audio_get_buffer(false, &buflen);
1957 #endif
1959 empty_playlist(playlist, true);
1961 gui_syncsplash(0, ID2P(LANG_WAIT));
1962 playlist->control_fd = open(playlist->control_filename, O_RDWR);
1963 if (playlist->control_fd < 0)
1965 gui_syncsplash(HZ*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR));
1966 return -1;
1968 playlist->control_created = true;
1970 control_file_size = filesize(playlist->control_fd);
1971 if (control_file_size <= 0)
1973 gui_syncsplash(HZ*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR));
1974 return -1;
1977 /* read a small amount first to get the header */
1978 nread = read(playlist->control_fd, buffer,
1979 PLAYLIST_COMMAND_SIZE<buflen?PLAYLIST_COMMAND_SIZE:buflen);
1980 if(nread <= 0)
1982 gui_syncsplash(HZ*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR));
1983 return -1;
1986 playlist->started = true;
1988 while (1)
1990 int result = 0;
1991 int count;
1992 enum playlist_command current_command = PLAYLIST_COMMAND_COMMENT;
1993 int last_newline = 0;
1994 int str_count = -1;
1995 bool newline = true;
1996 bool exit_loop = false;
1997 char *p = buffer;
1998 char *str1 = NULL;
1999 char *str2 = NULL;
2000 char *str3 = NULL;
2001 unsigned long last_tick = current_tick;
2003 for(count=0; count<nread && !exit_loop; count++,p++)
2005 /* So a splash while we are loading. */
2006 if (current_tick - last_tick > HZ/4)
2008 gui_syncsplash(0, str(LANG_LOADING_PERCENT),
2009 (total_read+count)*100/control_file_size,
2010 str(LANG_OFF_ABORT));
2011 if (action_userabort(TIMEOUT_NOBLOCK))
2013 /* FIXME:
2014 * Not sure how to implement this, somebody more familiar
2015 * with the code, please fix this. */
2017 last_tick = current_tick;
2020 /* Are we on a new line? */
2021 if((*p == '\n') || (*p == '\r'))
2023 *p = '\0';
2025 /* save last_newline in case we need to load more data */
2026 last_newline = count;
2028 switch (current_command)
2030 case PLAYLIST_COMMAND_PLAYLIST:
2032 /* str1=version str2=dir str3=file */
2033 int version;
2035 if (!str1)
2037 result = -1;
2038 exit_loop = true;
2039 break;
2042 if (!str2)
2043 str2 = "";
2045 if (!str3)
2046 str3 = "";
2048 version = atoi(str1);
2050 if (version != PLAYLIST_CONTROL_FILE_VERSION)
2051 return -1;
2053 update_playlist_filename(playlist, str2, str3);
2055 if (str3[0] != '\0')
2057 /* NOTE: add_indices_to_playlist() overwrites the
2058 audiobuf so we need to reload control file
2059 data */
2060 add_indices_to_playlist(playlist, NULL, 0);
2062 else if (str2[0] != '\0')
2064 playlist->in_ram = true;
2065 resume_directory(str2);
2068 /* load the rest of the data */
2069 first = false;
2070 exit_loop = true;
2072 break;
2074 case PLAYLIST_COMMAND_ADD:
2075 case PLAYLIST_COMMAND_QUEUE:
2077 /* str1=position str2=last_position str3=file */
2078 int position, last_position;
2079 bool queue;
2081 if (!str1 || !str2 || !str3)
2083 result = -1;
2084 exit_loop = true;
2085 break;
2088 position = atoi(str1);
2089 last_position = atoi(str2);
2091 queue = (current_command == PLAYLIST_COMMAND_ADD)?
2092 false:true;
2094 /* seek position is based on str3's position in
2095 buffer */
2096 if (add_track_to_playlist(playlist, str3, position,
2097 queue, total_read+(str3-buffer)) < 0)
2098 return -1;
2100 playlist->last_insert_pos = last_position;
2102 break;
2104 case PLAYLIST_COMMAND_DELETE:
2106 /* str1=position */
2107 int position;
2109 if (!str1)
2111 result = -1;
2112 exit_loop = true;
2113 break;
2116 position = atoi(str1);
2118 if (remove_track_from_playlist(playlist, position,
2119 false) < 0)
2120 return -1;
2122 break;
2124 case PLAYLIST_COMMAND_SHUFFLE:
2126 /* str1=seed str2=first_index */
2127 int seed;
2129 if (!str1 || !str2)
2131 result = -1;
2132 exit_loop = true;
2133 break;
2136 if (!sorted)
2138 /* Always sort list before shuffling */
2139 sort_playlist(playlist, false, false);
2142 seed = atoi(str1);
2143 playlist->first_index = atoi(str2);
2145 if (randomise_playlist(playlist, seed, false,
2146 false) < 0)
2147 return -1;
2149 sorted = false;
2150 break;
2152 case PLAYLIST_COMMAND_UNSHUFFLE:
2154 /* str1=first_index */
2155 if (!str1)
2157 result = -1;
2158 exit_loop = true;
2159 break;
2162 playlist->first_index = atoi(str1);
2164 if (sort_playlist(playlist, false, false) < 0)
2165 return -1;
2167 sorted = true;
2168 break;
2170 case PLAYLIST_COMMAND_RESET:
2172 playlist->last_insert_pos = -1;
2173 break;
2175 case PLAYLIST_COMMAND_COMMENT:
2176 default:
2177 break;
2180 newline = true;
2182 /* to ignore any extra newlines */
2183 current_command = PLAYLIST_COMMAND_COMMENT;
2185 else if(newline)
2187 newline = false;
2189 /* first non-comment line must always specify playlist */
2190 if (first && *p != 'P' && *p != '#')
2192 result = -1;
2193 exit_loop = true;
2194 break;
2197 switch (*p)
2199 case 'P':
2200 /* playlist can only be specified once */
2201 if (!first)
2203 result = -1;
2204 exit_loop = true;
2205 break;
2208 current_command = PLAYLIST_COMMAND_PLAYLIST;
2209 break;
2210 case 'A':
2211 current_command = PLAYLIST_COMMAND_ADD;
2212 break;
2213 case 'Q':
2214 current_command = PLAYLIST_COMMAND_QUEUE;
2215 break;
2216 case 'D':
2217 current_command = PLAYLIST_COMMAND_DELETE;
2218 break;
2219 case 'S':
2220 current_command = PLAYLIST_COMMAND_SHUFFLE;
2221 break;
2222 case 'U':
2223 current_command = PLAYLIST_COMMAND_UNSHUFFLE;
2224 break;
2225 case 'R':
2226 current_command = PLAYLIST_COMMAND_RESET;
2227 break;
2228 case '#':
2229 current_command = PLAYLIST_COMMAND_COMMENT;
2230 break;
2231 default:
2232 result = -1;
2233 exit_loop = true;
2234 break;
2237 str_count = -1;
2238 str1 = NULL;
2239 str2 = NULL;
2240 str3 = NULL;
2242 else if(current_command != PLAYLIST_COMMAND_COMMENT)
2244 /* all control file strings are separated with a colon.
2245 Replace the colon with 0 to get proper strings that can be
2246 used by commands above */
2247 if (*p == ':')
2249 *p = '\0';
2250 str_count++;
2252 if ((count+1) < nread)
2254 switch (str_count)
2256 case 0:
2257 str1 = p+1;
2258 break;
2259 case 1:
2260 str2 = p+1;
2261 break;
2262 case 2:
2263 str3 = p+1;
2264 break;
2265 default:
2266 /* allow last string to contain colons */
2267 *p = ':';
2268 break;
2275 if (result < 0)
2277 gui_syncsplash(HZ*2, ID2P(LANG_PLAYLIST_CONTROL_INVALID));
2278 return result;
2281 if (!newline || (exit_loop && count<nread))
2283 if ((total_read + count) >= control_file_size)
2285 /* no newline at end of control file */
2286 gui_syncsplash(HZ*2, ID2P(LANG_PLAYLIST_CONTROL_INVALID));
2287 return -1;
2290 /* We didn't end on a newline or we exited loop prematurely.
2291 Either way, re-read the remainder. */
2292 count = last_newline;
2293 lseek(playlist->control_fd, total_read+count, SEEK_SET);
2296 total_read += count;
2298 if (first)
2299 /* still looking for header */
2300 nread = read(playlist->control_fd, buffer,
2301 PLAYLIST_COMMAND_SIZE<buflen?PLAYLIST_COMMAND_SIZE:buflen);
2302 else
2303 nread = read(playlist->control_fd, buffer, buflen);
2305 /* Terminate on EOF */
2306 if(nread <= 0)
2308 if (global_status.resume_seed >= 0)
2310 /* Apply shuffle command saved in settings */
2311 if (global_status.resume_seed == 0)
2312 sort_playlist(playlist, false, true);
2313 else
2315 if (!sorted)
2316 sort_playlist(playlist, false, false);
2318 randomise_playlist(playlist, global_status.resume_seed,
2319 false, true);
2323 playlist->first_index = global_status.resume_first_index;
2324 break;
2328 #ifdef HAVE_DIRCACHE
2329 queue_post(&playlist_queue, PLAYLIST_LOAD_POINTERS, 0);
2330 #endif
2332 return 0;
2336 * Add track to in_ram playlist. Used when playing directories.
2338 int playlist_add(const char *filename)
2340 struct playlist_info* playlist = &current_playlist;
2341 int len = strlen(filename);
2343 if((len+1 > playlist->buffer_size - playlist->buffer_end_pos) ||
2344 (playlist->amount >= playlist->max_playlist_size))
2346 display_buffer_full();
2347 return -1;
2350 playlist->indices[playlist->amount] = playlist->buffer_end_pos;
2351 #ifdef HAVE_DIRCACHE
2352 playlist->filenames[playlist->amount] = NULL;
2353 #endif
2354 playlist->amount++;
2356 strcpy(&playlist->buffer[playlist->buffer_end_pos], filename);
2357 playlist->buffer_end_pos += len;
2358 playlist->buffer[playlist->buffer_end_pos++] = '\0';
2360 return 0;
2363 /* shuffle newly created playlist using random seed. */
2364 int playlist_shuffle(int random_seed, int start_index)
2366 struct playlist_info* playlist = &current_playlist;
2368 unsigned int seek_pos = 0;
2369 bool start_current = false;
2371 if (start_index >= 0 && global_settings.play_selected)
2373 /* store the seek position before the shuffle */
2374 seek_pos = playlist->indices[start_index];
2375 playlist->index = global_status.resume_first_index =
2376 playlist->first_index = start_index;
2377 start_current = true;
2380 randomise_playlist(playlist, random_seed, start_current, true);
2382 return playlist->index;
2385 /* start playing current playlist at specified index/offset */
2386 int playlist_start(int start_index, int offset)
2388 struct playlist_info* playlist = &current_playlist;
2390 playlist->index = start_index;
2392 #if CONFIG_CODEC != SWCODEC
2393 talk_buffer_steal(); /* will use the mp3 buffer */
2394 #endif
2396 playlist->started = true;
2397 sync_control(playlist, false);
2398 audio_play(offset);
2400 return 0;
2403 /* Returns false if 'steps' is out of bounds, else true */
2404 bool playlist_check(int steps)
2406 struct playlist_info* playlist = &current_playlist;
2408 /* always allow folder navigation */
2409 if (global_settings.next_folder && playlist->in_ram)
2410 return true;
2412 int index = get_next_index(playlist, steps, -1);
2414 if (index < 0 && steps >= 0 && global_settings.repeat_mode == REPEAT_SHUFFLE)
2415 index = get_next_index(playlist, steps, REPEAT_ALL);
2417 return (index >= 0);
2420 /* get trackname of track that is "steps" away from current playing track.
2421 NULL is used to identify end of playlist */
2422 char* playlist_peek(int steps)
2424 struct playlist_info* playlist = &current_playlist;
2425 int seek;
2426 int fd;
2427 char *temp_ptr;
2428 int index;
2429 bool control_file;
2431 index = get_next_index(playlist, steps, -1);
2432 if (index < 0)
2433 return NULL;
2435 control_file = playlist->indices[index] & PLAYLIST_INSERT_TYPE_MASK;
2436 seek = playlist->indices[index] & PLAYLIST_SEEK_MASK;
2438 if (get_filename(playlist, index, seek, control_file, now_playing,
2439 MAX_PATH+1) < 0)
2440 return NULL;
2442 temp_ptr = now_playing;
2444 if (!playlist->in_ram || control_file)
2446 /* remove bogus dirs from beginning of path
2447 (workaround for buggy playlist creation tools) */
2448 while (temp_ptr)
2450 #ifdef HAVE_DIRCACHE
2451 if (dircache_is_enabled())
2453 if (dircache_get_entry_ptr(temp_ptr))
2454 break;
2456 else
2457 #endif
2459 fd = open(temp_ptr, O_RDONLY);
2460 if (fd >= 0)
2462 close(fd);
2463 break;
2467 temp_ptr = strchr(temp_ptr+1, '/');
2470 if (!temp_ptr)
2472 /* Even though this is an invalid file, we still need to pass a
2473 file name to the caller because NULL is used to indicate end
2474 of playlist */
2475 return now_playing;
2479 return temp_ptr;
2483 * Update indices as track has changed
2485 int playlist_next(int steps)
2487 struct playlist_info* playlist = &current_playlist;
2488 int index;
2490 if ( (steps > 0)
2491 #ifdef AB_REPEAT_ENABLE
2492 && (global_settings.repeat_mode != REPEAT_AB)
2493 #endif
2494 && (global_settings.repeat_mode != REPEAT_ONE) )
2496 int i, j;
2498 /* We need to delete all the queued songs */
2499 for (i=0, j=steps; i<j; i++)
2501 index = get_next_index(playlist, i, -1);
2503 if (playlist->indices[index] & PLAYLIST_QUEUE_MASK)
2505 remove_track_from_playlist(playlist, index, true);
2506 steps--; /* one less track */
2511 index = get_next_index(playlist, steps, -1);
2513 if (index < 0)
2515 /* end of playlist... or is it */
2516 if (global_settings.repeat_mode == REPEAT_SHUFFLE &&
2517 playlist->amount > 1)
2519 /* Repeat shuffle mode. Re-shuffle playlist and resume play */
2520 playlist->first_index = global_status.resume_first_index = 0;
2521 sort_playlist(playlist, false, false);
2522 randomise_playlist(playlist, current_tick, false, true);
2523 #if CONFIG_CODEC != SWCODEC
2524 playlist_start(0, 0);
2525 #endif
2526 playlist->index = 0;
2527 index = 0;
2529 else if (playlist->in_ram && global_settings.next_folder)
2531 char dir[MAX_PATH+1];
2533 changing_dir = true;
2534 if (steps > 0)
2536 if (!get_next_directory(dir))
2538 /* start playing next directory */
2539 if (playlist_create(dir, NULL) != -1)
2541 ft_build_playlist(tree_get_context(), 0);
2542 if (global_settings.playlist_shuffle)
2543 playlist_shuffle(current_tick, -1);
2544 #if CONFIG_CODEC != SWCODEC
2545 playlist_start(0, 0);
2546 #endif
2547 playlist->index = index = 0;
2551 else
2553 if (!get_previous_directory(dir))
2555 /* start playing previous directory */
2556 if (playlist_create(dir, NULL) != -1)
2558 ft_build_playlist(tree_get_context(), 0);
2559 if (global_settings.playlist_shuffle)
2560 playlist_shuffle(current_tick, -1);
2561 #if CONFIG_CODEC != SWCODEC
2562 playlist_start(current_playlist.amount-1, 0);
2563 #endif
2564 playlist->index = index = current_playlist.amount - 1;
2568 changing_dir = false;
2571 return index;
2574 playlist->index = index;
2576 if (playlist->last_insert_pos >= 0 && steps > 0)
2578 /* check to see if we've gone beyond the last inserted track */
2579 int cur = rotate_index(playlist, index);
2580 int last_pos = rotate_index(playlist, playlist->last_insert_pos);
2582 if (cur > last_pos)
2584 /* reset last inserted track */
2585 playlist->last_insert_pos = -1;
2587 if (playlist->control_fd >= 0)
2589 int result = update_control(playlist, PLAYLIST_COMMAND_RESET,
2590 -1, -1, NULL, NULL, NULL);
2592 if (result < 0)
2593 return result;
2595 sync_control(playlist, false);
2600 return index;
2603 /* try playing next or previous folder */
2604 bool playlist_next_dir(int direction)
2606 char dir[MAX_PATH+1];
2607 bool result;
2608 int res;
2610 /* not to mess up real playlists */
2611 if(!current_playlist.in_ram)
2612 return false;
2614 if(changing_dir)
2615 return false;
2617 changing_dir = true;
2618 if(direction > 0)
2619 res = get_next_directory(dir);
2620 else
2621 res = get_previous_directory(dir);
2622 if (!res)
2624 if (playlist_create(dir, NULL) != -1)
2626 ft_build_playlist(tree_get_context(), 0);
2627 if (global_settings.playlist_shuffle)
2628 playlist_shuffle(current_tick, -1);
2629 #if (CONFIG_CODEC != SWCODEC)
2630 playlist_start(0,0);
2631 #endif
2632 result = true;
2634 else
2635 result = false;
2637 else
2638 result = false;
2640 changing_dir = false;
2642 return result;
2645 /* Get resume info for current playing song. If return value is -1 then
2646 settings shouldn't be saved. */
2647 int playlist_get_resume_info(int *resume_index)
2649 struct playlist_info* playlist = &current_playlist;
2651 *resume_index = playlist->index;
2653 return 0;
2656 /* Update resume info for current playing song. Returns -1 on error. */
2657 int playlist_update_resume_info(const struct mp3entry* id3)
2659 struct playlist_info* playlist = &current_playlist;
2661 if (id3)
2663 if (global_status.resume_index != playlist->index ||
2664 global_status.resume_offset != id3->offset)
2666 global_status.resume_index = playlist->index;
2667 global_status.resume_offset = id3->offset;
2668 status_save();
2671 else
2673 global_status.resume_index = -1;
2674 global_status.resume_offset = -1;
2675 status_save();
2678 return 0;
2681 /* Returns index of current playing track for display purposes. This value
2682 should not be used for resume purposes as it doesn't represent the actual
2683 index into the playlist */
2684 int playlist_get_display_index(void)
2686 struct playlist_info* playlist = &current_playlist;
2688 /* first_index should always be index 0 for display purposes */
2689 int index = rotate_index(playlist, playlist->index);
2691 return (index+1);
2694 /* returns number of tracks in current playlist */
2695 int playlist_amount(void)
2697 return playlist_amount_ex(NULL);
2701 * Create a new playlist If playlist is not NULL then we're loading a
2702 * playlist off disk for viewing/editing. The index_buffer is used to store
2703 * playlist indices (required for and only used if !current playlist). The
2704 * temp_buffer (if not NULL) is used as a scratchpad when loading indices.
2706 int playlist_create_ex(struct playlist_info* playlist,
2707 const char* dir, const char* file,
2708 void* index_buffer, int index_buffer_size,
2709 void* temp_buffer, int temp_buffer_size)
2711 if (!playlist)
2712 playlist = &current_playlist;
2713 else
2715 /* Initialize playlist structure */
2716 int r = rand() % 10;
2717 playlist->current = false;
2719 /* Use random name for control file */
2720 snprintf(playlist->control_filename, sizeof(playlist->control_filename),
2721 "%s.%d", PLAYLIST_CONTROL_FILE, r);
2722 playlist->fd = -1;
2723 playlist->control_fd = -1;
2725 if (index_buffer)
2727 int num_indices = index_buffer_size / sizeof(int);
2729 #ifdef HAVE_DIRCACHE
2730 num_indices /= 2;
2731 #endif
2732 if (num_indices > global_settings.max_files_in_playlist)
2733 num_indices = global_settings.max_files_in_playlist;
2735 playlist->max_playlist_size = num_indices;
2736 playlist->indices = index_buffer;
2737 #ifdef HAVE_DIRCACHE
2738 playlist->filenames = (const struct dircache_entry **)
2739 &playlist->indices[num_indices];
2740 #endif
2742 else
2744 playlist->max_playlist_size = current_playlist.max_playlist_size;
2745 playlist->indices = current_playlist.indices;
2746 #ifdef HAVE_DIRCACHE
2747 playlist->filenames = current_playlist.filenames;
2748 #endif
2751 playlist->buffer_size = 0;
2752 playlist->buffer = NULL;
2753 mutex_init(&playlist->control_mutex);
2756 new_playlist(playlist, dir, file);
2758 if (file)
2759 /* load the playlist file */
2760 add_indices_to_playlist(playlist, temp_buffer, temp_buffer_size);
2762 return 0;
2766 * Set the specified playlist as the current.
2767 * NOTE: You will get undefined behaviour if something is already playing so
2768 * remember to stop before calling this. Also, this call will
2769 * effectively close your playlist, making it unusable.
2771 int playlist_set_current(struct playlist_info* playlist)
2773 if (!playlist || (check_control(playlist) < 0))
2774 return -1;
2776 empty_playlist(&current_playlist, false);
2778 strncpy(current_playlist.filename, playlist->filename,
2779 sizeof(current_playlist.filename));
2781 current_playlist.utf8 = playlist->utf8;
2782 current_playlist.fd = playlist->fd;
2784 close(playlist->control_fd);
2785 close(current_playlist.control_fd);
2786 remove(current_playlist.control_filename);
2787 if (rename(playlist->control_filename,
2788 current_playlist.control_filename) < 0)
2789 return -1;
2790 current_playlist.control_fd = open(current_playlist.control_filename,
2791 O_RDWR);
2792 if (current_playlist.control_fd < 0)
2793 return -1;
2794 current_playlist.control_created = true;
2796 current_playlist.dirlen = playlist->dirlen;
2798 if (playlist->indices && playlist->indices != current_playlist.indices)
2800 memcpy(current_playlist.indices, playlist->indices,
2801 playlist->max_playlist_size*sizeof(int));
2802 #ifdef HAVE_DIRCACHE
2803 memcpy(current_playlist.filenames, playlist->filenames,
2804 playlist->max_playlist_size*sizeof(int));
2805 #endif
2808 current_playlist.first_index = playlist->first_index;
2809 current_playlist.amount = playlist->amount;
2810 current_playlist.last_insert_pos = playlist->last_insert_pos;
2811 current_playlist.seed = playlist->seed;
2812 current_playlist.shuffle_modified = playlist->shuffle_modified;
2813 current_playlist.deleted = playlist->deleted;
2814 current_playlist.num_inserted_tracks = playlist->num_inserted_tracks;
2816 memcpy(current_playlist.control_cache, playlist->control_cache,
2817 sizeof(current_playlist.control_cache));
2818 current_playlist.num_cached = playlist->num_cached;
2819 current_playlist.pending_control_sync = playlist->pending_control_sync;
2821 return 0;
2825 * Close files and delete control file for non-current playlist.
2827 void playlist_close(struct playlist_info* playlist)
2829 if (!playlist)
2830 return;
2832 if (playlist->fd >= 0)
2833 close(playlist->fd);
2835 if (playlist->control_fd >= 0)
2836 close(playlist->control_fd);
2838 if (playlist->control_created)
2839 remove(playlist->control_filename);
2842 void playlist_sync(struct playlist_info* playlist)
2844 if (!playlist)
2845 playlist = &current_playlist;
2847 sync_control(playlist, false);
2848 if ((audio_status() & AUDIO_STATUS_PLAY) && playlist->started)
2849 audio_flush_and_reload_tracks();
2851 #ifdef HAVE_DIRCACHE
2852 queue_post(&playlist_queue, PLAYLIST_LOAD_POINTERS, 0);
2853 #endif
2857 * Insert track into playlist at specified position (or one of the special
2858 * positions). Returns position where track was inserted or -1 if error.
2860 int playlist_insert_track(struct playlist_info* playlist, const char *filename,
2861 int position, bool queue, bool sync)
2863 int result;
2865 if (!playlist)
2866 playlist = &current_playlist;
2868 if (check_control(playlist) < 0)
2870 gui_syncsplash(HZ*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR));
2871 return -1;
2874 result = add_track_to_playlist(playlist, filename, position, queue, -1);
2876 /* Check if we want manually sync later. For example when adding
2877 * bunch of files from tagcache, syncing after every file wouldn't be
2878 * a good thing to do. */
2879 if (sync && result >= 0)
2880 playlist_sync(playlist);
2882 return result;
2886 * Insert all tracks from specified directory into playlist.
2888 int playlist_insert_directory(struct playlist_info* playlist,
2889 const char *dirname, int position, bool queue,
2890 bool recurse)
2892 int result;
2893 unsigned char *count_str;
2894 struct directory_search_context context;
2896 if (!playlist)
2897 playlist = &current_playlist;
2899 if (check_control(playlist) < 0)
2901 gui_syncsplash(HZ*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR));
2902 return -1;
2905 if (position == PLAYLIST_REPLACE)
2907 if (remove_all_tracks(playlist) == 0)
2908 position = PLAYLIST_INSERT_LAST;
2909 else
2910 return -1;
2913 if (queue)
2914 count_str = ID2P(LANG_PLAYLIST_QUEUE_COUNT);
2915 else
2916 count_str = ID2P(LANG_PLAYLIST_INSERT_COUNT);
2918 display_playlist_count(0, count_str, false);
2920 context.playlist = playlist;
2921 context.position = position;
2922 context.queue = queue;
2923 context.count = 0;
2925 cpu_boost(true);
2927 result = playlist_directory_tracksearch(dirname, recurse,
2928 directory_search_callback, &context);
2930 sync_control(playlist, false);
2932 cpu_boost(false);
2934 display_playlist_count(context.count, count_str, true);
2936 if ((audio_status() & AUDIO_STATUS_PLAY) && playlist->started)
2937 audio_flush_and_reload_tracks();
2939 #ifdef HAVE_DIRCACHE
2940 queue_post(&playlist_queue, PLAYLIST_LOAD_POINTERS, 0);
2941 #endif
2943 return result;
2947 * Insert all tracks from specified playlist into dynamic playlist.
2949 int playlist_insert_playlist(struct playlist_info* playlist, char *filename,
2950 int position, bool queue)
2952 int fd;
2953 int max;
2954 char *temp_ptr;
2955 char *dir;
2956 unsigned char *count_str;
2957 char temp_buf[MAX_PATH+1];
2958 char trackname[MAX_PATH+1];
2959 int count = 0;
2960 int result = 0;
2962 if (!playlist)
2963 playlist = &current_playlist;
2965 if (check_control(playlist) < 0)
2967 gui_syncsplash(HZ*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR));
2968 return -1;
2971 fd = open(filename, O_RDONLY);
2972 if (fd < 0)
2974 gui_syncsplash(HZ*2, ID2P(LANG_PLAYLIST_ACCESS_ERROR));
2975 return -1;
2978 /* we need the directory name for formatting purposes */
2979 dir = filename;
2981 temp_ptr = strrchr(filename+1,'/');
2982 if (temp_ptr)
2983 *temp_ptr = 0;
2984 else
2985 dir = "/";
2987 if (queue)
2988 count_str = ID2P(LANG_PLAYLIST_QUEUE_COUNT);
2989 else
2990 count_str = ID2P(LANG_PLAYLIST_INSERT_COUNT);
2992 display_playlist_count(count, count_str, false);
2994 if (position == PLAYLIST_REPLACE)
2996 if (remove_all_tracks(playlist) == 0)
2997 position = PLAYLIST_INSERT_LAST;
2998 else return -1;
3001 cpu_boost(true);
3003 while ((max = read_line(fd, temp_buf, sizeof(temp_buf))) > 0)
3005 /* user abort */
3006 if (action_userabort(TIMEOUT_NOBLOCK))
3007 break;
3009 if (temp_buf[0] != '#' && temp_buf[0] != '\0')
3011 int insert_pos;
3013 /* we need to format so that relative paths are correctly
3014 handled */
3015 if (format_track_path(trackname, temp_buf, sizeof(trackname), max,
3016 dir) < 0)
3018 result = -1;
3019 break;
3022 insert_pos = add_track_to_playlist(playlist, trackname, position,
3023 queue, -1);
3025 if (insert_pos < 0)
3027 result = -1;
3028 break;
3031 /* Make sure tracks are inserted in correct order if user
3032 requests INSERT_FIRST */
3033 if (position == PLAYLIST_INSERT_FIRST || position >= 0)
3034 position = insert_pos + 1;
3036 count++;
3038 if ((count%PLAYLIST_DISPLAY_COUNT) == 0)
3040 display_playlist_count(count, count_str, false);
3042 if (count == PLAYLIST_DISPLAY_COUNT &&
3043 (audio_status() & AUDIO_STATUS_PLAY) &&
3044 playlist->started)
3045 audio_flush_and_reload_tracks();
3049 /* let the other threads work */
3050 yield();
3053 close(fd);
3055 if (temp_ptr)
3056 *temp_ptr = '/';
3058 sync_control(playlist, false);
3060 cpu_boost(false);
3062 display_playlist_count(count, count_str, true);
3064 if ((audio_status() & AUDIO_STATUS_PLAY) && playlist->started)
3065 audio_flush_and_reload_tracks();
3067 #ifdef HAVE_DIRCACHE
3068 queue_post(&playlist_queue, PLAYLIST_LOAD_POINTERS, 0);
3069 #endif
3071 return result;
3075 * Delete track at specified index. If index is PLAYLIST_DELETE_CURRENT then
3076 * we want to delete the current playing track.
3078 int playlist_delete(struct playlist_info* playlist, int index)
3080 int result = 0;
3082 if (!playlist)
3083 playlist = &current_playlist;
3085 if (check_control(playlist) < 0)
3087 gui_syncsplash(HZ*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR));
3088 return -1;
3091 if (index == PLAYLIST_DELETE_CURRENT)
3092 index = playlist->index;
3094 result = remove_track_from_playlist(playlist, index, true);
3096 if (result != -1 && (audio_status() & AUDIO_STATUS_PLAY) &&
3097 playlist->started)
3098 audio_flush_and_reload_tracks();
3100 return result;
3104 * Move track at index to new_index. Tracks between the two are shifted
3105 * appropriately. Returns 0 on success and -1 on failure.
3107 int playlist_move(struct playlist_info* playlist, int index, int new_index)
3109 int result;
3110 int seek;
3111 bool control_file;
3112 bool queue;
3113 bool current = false;
3114 int r;
3115 char filename[MAX_PATH];
3117 if (!playlist)
3118 playlist = &current_playlist;
3120 if (check_control(playlist) < 0)
3122 gui_syncsplash(HZ*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR));
3123 return -1;
3126 if (index == new_index)
3127 return -1;
3129 if (index == playlist->index)
3130 /* Moving the current track */
3131 current = true;
3133 control_file = playlist->indices[index] & PLAYLIST_INSERT_TYPE_MASK;
3134 queue = playlist->indices[index] & PLAYLIST_QUEUE_MASK;
3135 seek = playlist->indices[index] & PLAYLIST_SEEK_MASK;
3137 if (get_filename(playlist, index, seek, control_file, filename,
3138 sizeof(filename)) < 0)
3139 return -1;
3141 /* Delete track from original position */
3142 result = remove_track_from_playlist(playlist, index, true);
3144 if (result != -1)
3146 /* We want to insert the track at the position that was specified by
3147 new_index. This may be different then new_index because of the
3148 shifting that occurred after the delete */
3149 r = rotate_index(playlist, new_index);
3151 if (r == 0)
3152 /* First index */
3153 new_index = PLAYLIST_PREPEND;
3154 else if (r == playlist->amount)
3155 /* Append */
3156 new_index = PLAYLIST_INSERT_LAST;
3157 else
3158 /* Calculate index of desired position */
3159 new_index = (r+playlist->first_index)%playlist->amount;
3161 result = add_track_to_playlist(playlist, filename, new_index, queue,
3162 -1);
3164 if (result != -1)
3166 if (current)
3168 /* Moved the current track */
3169 switch (new_index)
3171 case PLAYLIST_PREPEND:
3172 playlist->index = playlist->first_index;
3173 break;
3174 case PLAYLIST_INSERT_LAST:
3175 playlist->index = playlist->first_index - 1;
3176 if (playlist->index < 0)
3177 playlist->index += playlist->amount;
3178 break;
3179 default:
3180 playlist->index = new_index;
3181 break;
3185 if ((audio_status() & AUDIO_STATUS_PLAY) && playlist->started)
3186 audio_flush_and_reload_tracks();
3190 #ifdef HAVE_DIRCACHE
3191 queue_post(&playlist_queue, PLAYLIST_LOAD_POINTERS, 0);
3192 #endif
3194 return result;
3197 /* shuffle currently playing playlist */
3198 int playlist_randomise(struct playlist_info* playlist, unsigned int seed,
3199 bool start_current)
3201 int result;
3203 if (!playlist)
3204 playlist = &current_playlist;
3206 check_control(playlist);
3208 result = randomise_playlist(playlist, seed, start_current, true);
3210 if (result != -1 && (audio_status() & AUDIO_STATUS_PLAY) &&
3211 playlist->started)
3212 audio_flush_and_reload_tracks();
3214 return result;
3217 /* sort currently playing playlist */
3218 int playlist_sort(struct playlist_info* playlist, bool start_current)
3220 int result;
3222 if (!playlist)
3223 playlist = &current_playlist;
3225 check_control(playlist);
3227 result = sort_playlist(playlist, start_current, true);
3229 if (result != -1 && (audio_status() & AUDIO_STATUS_PLAY) &&
3230 playlist->started)
3231 audio_flush_and_reload_tracks();
3233 return result;
3236 /* returns true if playlist has been modified */
3237 bool playlist_modified(const struct playlist_info* playlist)
3239 if (!playlist)
3240 playlist = &current_playlist;
3242 if (playlist->shuffle_modified ||
3243 playlist->deleted ||
3244 playlist->num_inserted_tracks > 0)
3245 return true;
3247 return false;
3250 /* returns index of first track in playlist */
3251 int playlist_get_first_index(const struct playlist_info* playlist)
3253 if (!playlist)
3254 playlist = &current_playlist;
3256 return playlist->first_index;
3259 /* returns shuffle seed of playlist */
3260 int playlist_get_seed(const struct playlist_info* playlist)
3262 if (!playlist)
3263 playlist = &current_playlist;
3265 return playlist->seed;
3268 /* returns number of tracks in playlist (includes queued/inserted tracks) */
3269 int playlist_amount_ex(const struct playlist_info* playlist)
3271 if (!playlist)
3272 playlist = &current_playlist;
3274 return playlist->amount;
3277 /* returns full path of playlist (minus extension) */
3278 char *playlist_name(const struct playlist_info* playlist, char *buf,
3279 int buf_size)
3281 char *sep;
3283 if (!playlist)
3284 playlist = &current_playlist;
3286 snprintf(buf, buf_size, "%s", playlist->filename+playlist->dirlen);
3288 if (!buf[0])
3289 return NULL;
3291 /* Remove extension */
3292 sep = strrchr(buf, '.');
3293 if (sep)
3294 *sep = 0;
3296 return buf;
3299 /* returns the playlist filename */
3300 char *playlist_get_name(const struct playlist_info* playlist, char *buf,
3301 int buf_size)
3303 if (!playlist)
3304 playlist = &current_playlist;
3306 snprintf(buf, buf_size, "%s", playlist->filename);
3308 if (!buf[0])
3309 return NULL;
3311 return buf;
3314 /* Fills info structure with information about track at specified index.
3315 Returns 0 on success and -1 on failure */
3316 int playlist_get_track_info(struct playlist_info* playlist, int index,
3317 struct playlist_track_info* info)
3319 int seek;
3320 bool control_file;
3322 if (!playlist)
3323 playlist = &current_playlist;
3325 if (index < 0 || index >= playlist->amount)
3326 return -1;
3328 control_file = playlist->indices[index] & PLAYLIST_INSERT_TYPE_MASK;
3329 seek = playlist->indices[index] & PLAYLIST_SEEK_MASK;
3331 if (get_filename(playlist, index, seek, control_file, info->filename,
3332 sizeof(info->filename)) < 0)
3333 return -1;
3335 info->attr = 0;
3337 if (control_file)
3339 if (playlist->indices[index] & PLAYLIST_QUEUE_MASK)
3340 info->attr |= PLAYLIST_ATTR_QUEUED;
3341 else
3342 info->attr |= PLAYLIST_ATTR_INSERTED;
3346 if (playlist->indices[index] & PLAYLIST_SKIPPED)
3347 info->attr |= PLAYLIST_ATTR_SKIPPED;
3349 info->index = index;
3350 info->display_index = rotate_index(playlist, index) + 1;
3352 return 0;
3355 /* save the current dynamic playlist to specified file */
3356 int playlist_save(struct playlist_info* playlist, char *filename)
3358 int fd;
3359 int i, index;
3360 int count = 0;
3361 char path[MAX_PATH+1];
3362 char tmp_buf[MAX_PATH+1];
3363 int result = 0;
3364 bool overwrite_current = false;
3365 int* index_buf = NULL;
3367 if (!playlist)
3368 playlist = &current_playlist;
3370 if (playlist->amount <= 0)
3371 return -1;
3373 /* use current working directory as base for pathname */
3374 if (format_track_path(path, filename, sizeof(tmp_buf),
3375 strlen(filename)+1, getcwd(NULL, -1)) < 0)
3376 return -1;
3378 if (!strncmp(playlist->filename, path, strlen(path)))
3380 /* Attempting to overwrite current playlist file.*/
3382 if (playlist->buffer_size < (int)(playlist->amount * sizeof(int)))
3384 /* not enough buffer space to store updated indices */
3385 gui_syncsplash(HZ*2, ID2P(LANG_PLAYLIST_ACCESS_ERROR));
3386 return -1;
3389 /* in_ram buffer is unused for m3u files so we'll use for storing
3390 updated indices */
3391 index_buf = (int*)playlist->buffer;
3393 /* use temporary pathname */
3394 snprintf(path, sizeof(path), "%s_temp", playlist->filename);
3395 overwrite_current = true;
3398 fd = open(path, O_CREAT|O_WRONLY|O_TRUNC);
3399 if (fd < 0)
3401 gui_syncsplash(HZ*2, ID2P(LANG_PLAYLIST_ACCESS_ERROR));
3402 return -1;
3405 display_playlist_count(count, ID2P(LANG_PLAYLIST_SAVE_COUNT), false);
3407 cpu_boost(true);
3409 index = playlist->first_index;
3410 for (i=0; i<playlist->amount; i++)
3412 bool control_file;
3413 bool queue;
3414 int seek;
3416 /* user abort */
3417 if (action_userabort(TIMEOUT_NOBLOCK))
3419 result = -1;
3420 break;
3423 control_file = playlist->indices[index] & PLAYLIST_INSERT_TYPE_MASK;
3424 queue = playlist->indices[index] & PLAYLIST_QUEUE_MASK;
3425 seek = playlist->indices[index] & PLAYLIST_SEEK_MASK;
3427 /* Don't save queued files */
3428 if (!queue)
3430 if (get_filename(playlist, index, seek, control_file, tmp_buf,
3431 MAX_PATH+1) < 0)
3433 result = -1;
3434 break;
3437 if (overwrite_current)
3438 index_buf[count] = lseek(fd, 0, SEEK_CUR);
3440 if (fdprintf(fd, "%s\n", tmp_buf) < 0)
3442 gui_syncsplash(HZ*2, ID2P(LANG_PLAYLIST_ACCESS_ERROR));
3443 result = -1;
3444 break;
3447 count++;
3449 if ((count % PLAYLIST_DISPLAY_COUNT) == 0)
3450 display_playlist_count(count, ID2P(LANG_PLAYLIST_SAVE_COUNT),
3451 false);
3453 yield();
3456 index = (index+1)%playlist->amount;
3459 display_playlist_count(count, ID2P(LANG_PLAYLIST_SAVE_COUNT), true);
3461 close(fd);
3463 if (overwrite_current && result >= 0)
3465 result = -1;
3467 mutex_lock(&playlist->control_mutex);
3469 /* Replace the current playlist with the new one and update indices */
3470 close(playlist->fd);
3471 if (remove(playlist->filename) >= 0)
3473 if (rename(path, playlist->filename) >= 0)
3475 playlist->fd = open(playlist->filename, O_RDONLY);
3476 if (playlist->fd >= 0)
3478 index = playlist->first_index;
3479 for (i=0, count=0; i<playlist->amount; i++)
3481 if (!(playlist->indices[index] & PLAYLIST_QUEUE_MASK))
3483 playlist->indices[index] = index_buf[count];
3484 count++;
3486 index = (index+1)%playlist->amount;
3489 /* we need to recreate control because inserted tracks are
3490 now part of the playlist and shuffle has been
3491 invalidated */
3492 result = recreate_control(playlist);
3497 mutex_unlock(&playlist->control_mutex);
3501 cpu_boost(false);
3503 return result;
3507 * Search specified directory for tracks and notify via callback. May be
3508 * called recursively.
3510 int playlist_directory_tracksearch(const char* dirname, bool recurse,
3511 int (*callback)(char*, void*),
3512 void* context)
3514 char buf[MAX_PATH+1];
3515 int result = 0;
3516 int num_files = 0;
3517 int i;
3518 struct entry *files;
3519 struct tree_context* tc = tree_get_context();
3520 int old_dirfilter = *(tc->dirfilter);
3522 if (!callback)
3523 return -1;
3525 /* use the tree browser dircache to load files */
3526 *(tc->dirfilter) = SHOW_ALL;
3528 if (ft_load(tc, dirname) < 0)
3530 gui_syncsplash(HZ*2, ID2P(LANG_PLAYLIST_DIRECTORY_ACCESS_ERROR));
3531 *(tc->dirfilter) = old_dirfilter;
3532 return -1;
3535 files = (struct entry*) tc->dircache;
3536 num_files = tc->filesindir;
3538 /* we've overwritten the dircache so tree browser will need to be
3539 reloaded */
3540 reload_directory();
3542 for (i=0; i<num_files; i++)
3544 /* user abort */
3545 if (action_userabort(TIMEOUT_NOBLOCK))
3547 result = -1;
3548 break;
3551 if (files[i].attr & ATTR_DIRECTORY)
3553 if (recurse)
3555 /* recursively add directories */
3556 snprintf(buf, sizeof(buf), "%s/%s", dirname, files[i].name);
3557 result = playlist_directory_tracksearch(buf, recurse,
3558 callback, context);
3559 if (result < 0)
3560 break;
3562 /* we now need to reload our current directory */
3563 if(ft_load(tc, dirname) < 0)
3565 result = -1;
3566 break;
3569 files = (struct entry*) tc->dircache;
3570 num_files = tc->filesindir;
3571 if (!num_files)
3573 result = -1;
3574 break;
3577 else
3578 continue;
3580 else if ((files[i].attr & FILE_ATTR_MASK) == FILE_ATTR_AUDIO)
3582 snprintf(buf, sizeof(buf), "%s/%s", dirname, files[i].name);
3584 if (callback(buf, context) != 0)
3586 result = -1;
3587 break;
3590 /* let the other threads work */
3591 yield();
3595 /* restore dirfilter */
3596 *(tc->dirfilter) = old_dirfilter;
3598 return result;