WpsEditor: add linenumbrs WpsEditor: Add linenumbers to the WPS Code, and highlight...
[kugel-rb.git] / apps / playlist.c
blob9c494778a38bece853071141ac85468663120903
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
10 * Copyright (C) 2002 by wavey@wavey.org
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
20 ****************************************************************************/
23 Dynamic playlist design (based on design originally proposed by ricII)
25 There are two files associated with a dynamic playlist:
26 1. Playlist file : This file contains the initial songs in the playlist.
27 The file is created by the user and stored on the hard
28 drive. NOTE: If we are playing the contents of a
29 directory, there will be no playlist file.
30 2. Control file : This file is automatically created when a playlist is
31 started and contains all the commands done to it.
33 The first non-comment line in a control file must begin with
34 "P:VERSION:DIR:FILE" where VERSION is the playlist control file version,
35 DIR is the directory where the playlist is located and FILE is the
36 playlist filename. For dirplay, FILE will be empty. An empty playlist
37 will have both entries as null.
39 Control file commands:
40 a. Add track (A:<position>:<last position>:<path to track>)
41 - Insert a track at the specified position in the current
42 playlist. Last position is used to specify where last insertion
43 occurred.
44 b. Queue track (Q:<position>:<last position>:<path to track>)
45 - Queue a track at the specified position in the current
46 playlist. Queued tracks differ from added tracks in that they
47 are deleted from the playlist as soon as they are played and
48 they are not saved to disk as part of the playlist.
49 c. Delete track (D:<position>)
50 - Delete track from specified position in the current playlist.
51 d. Shuffle playlist (S:<seed>:<index>)
52 - Shuffle entire playlist with specified seed. The index
53 identifies the first index in the newly shuffled playlist
54 (needed for repeat mode).
55 e. Unshuffle playlist (U:<index>)
56 - Unshuffle entire playlist. The index identifies the first index
57 in the newly unshuffled playlist.
58 f. Reset last insert position (R)
59 - Needed so that insertions work properly after resume
61 Resume:
62 The only resume info that needs to be saved is the current index in the
63 playlist and the position in the track. When resuming, all the commands
64 in the control file will be reapplied so that the playlist indices are
65 exactly the same as before shutdown. To avoid unnecessary disk
66 accesses, the shuffle mode settings are also saved in settings and only
67 flushed to disk when required.
70 #include <stdio.h>
71 #include <stdlib.h>
72 #include <string.h>
73 #include <ctype.h>
74 #include "playlist.h"
75 #include "ata_idle_notify.h"
76 #include "file.h"
77 #include "action.h"
78 #include "dir.h"
79 #include "sprintf.h"
80 #include "debug.h"
81 #include "audio.h"
82 #include "lcd.h"
83 #include "kernel.h"
84 #include "settings.h"
85 #include "status.h"
86 #include "applimits.h"
87 #include "screens.h"
88 #include "buffer.h"
89 #include "misc.h"
90 #include "button.h"
91 #include "filetree.h"
92 #include "abrepeat.h"
93 #include "thread.h"
94 #include "usb.h"
95 #include "filetypes.h"
96 #ifdef HAVE_LCD_BITMAP
97 #include "icons.h"
98 #endif
100 #include "lang.h"
101 #include "talk.h"
102 #include "splash.h"
103 #include "rbunicode.h"
104 #include "root_menu.h"
106 #define PLAYLIST_CONTROL_FILE ROCKBOX_DIR "/.playlist_control"
107 #define PLAYLIST_CONTROL_FILE_VERSION 2
110 Each playlist index has a flag associated with it which identifies what
111 type of track it is. These flags are stored in the 4 high order bits of
112 the index.
114 NOTE: This limits the playlist file size to a max of 256M.
116 Bits 31-30:
117 00 = Playlist track
118 01 = Track was prepended into playlist
119 10 = Track was inserted into playlist
120 11 = Track was appended into playlist
121 Bit 29:
122 0 = Added track
123 1 = Queued track
124 Bit 28:
125 0 = Track entry is valid
126 1 = Track does not exist on disk and should be skipped
128 #define PLAYLIST_SEEK_MASK 0x0FFFFFFF
129 #define PLAYLIST_INSERT_TYPE_MASK 0xC0000000
130 #define PLAYLIST_QUEUE_MASK 0x20000000
132 #define PLAYLIST_INSERT_TYPE_PREPEND 0x40000000
133 #define PLAYLIST_INSERT_TYPE_INSERT 0x80000000
134 #define PLAYLIST_INSERT_TYPE_APPEND 0xC0000000
136 #define PLAYLIST_QUEUED 0x20000000
137 #define PLAYLIST_SKIPPED 0x10000000
139 #define PLAYLIST_DISPLAY_COUNT 10
141 struct directory_search_context {
142 struct playlist_info* playlist;
143 int position;
144 bool queue;
145 int count;
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, bool recurse);
183 static int format_track_path(char *dest, char *src, int buf_length, int max,
184 const 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
203 /* Check if the filename suggests M3U or M3U8 format. */
204 static bool is_m3u8(const char* filename)
206 int len = strlen(filename);
208 /* Default to M3U8 unless explicitly told otherwise. */
209 return !(len > 4 && strcasecmp(&filename[len - 4], ".m3u") == 0);
213 /* Convert a filename in an M3U playlist to UTF-8.
215 * buf - the filename to convert; can contain more than one line from the
216 * playlist.
217 * buf_len - amount of buf that is used.
218 * buf_max - total size of buf.
219 * temp - temporary conversion buffer, at least buf_max bytes.
221 * Returns the length of the converted filename.
223 static int convert_m3u(char* buf, int buf_len, int buf_max, char* temp)
225 int i = 0;
226 char* dest;
228 /* Locate EOL. */
229 while ((buf[i] != '\n') && (buf[i] != '\r') && (i < buf_len))
231 i++;
234 /* Work back killing white space. */
235 while ((i > 0) && isspace(buf[i - 1]))
237 i--;
240 buf_len = i;
241 dest = temp;
243 /* Convert char by char, so as to not overflow temp (iso_decode should
244 * preferably handle this). No more than 4 bytes should be generated for
245 * each input char.
247 for (i = 0; i < buf_len && dest < (temp + buf_max - 4); i++)
249 dest = iso_decode(&buf[i], dest, -1, 1);
252 *dest = 0;
253 strcpy(buf, temp);
254 return dest - temp;
258 * remove any files and indices associated with the playlist
260 static void empty_playlist(struct playlist_info* playlist, bool resume)
262 playlist->filename[0] = '\0';
263 playlist->utf8 = true;
265 if(playlist->fd >= 0)
266 /* If there is an already open playlist, close it. */
267 close(playlist->fd);
268 playlist->fd = -1;
270 if(playlist->control_fd >= 0)
271 close(playlist->control_fd);
272 playlist->control_fd = -1;
273 playlist->control_created = false;
275 playlist->in_ram = false;
277 if (playlist->buffer)
278 playlist->buffer[0] = 0;
280 playlist->buffer_end_pos = 0;
282 playlist->index = 0;
283 playlist->first_index = 0;
284 playlist->amount = 0;
285 playlist->last_insert_pos = -1;
286 playlist->seed = 0;
287 playlist->shuffle_modified = false;
288 playlist->deleted = false;
289 playlist->num_inserted_tracks = 0;
290 playlist->started = false;
292 playlist->num_cached = 0;
293 playlist->pending_control_sync = false;
295 if (!resume && playlist->current)
297 /* start with fresh playlist control file when starting new
298 playlist */
299 create_control(playlist);
301 /* Reset resume settings */
302 global_status.resume_first_index = 0;
303 global_status.resume_seed = -1;
308 * Initialize a new playlist for viewing/editing/playing. dir is the
309 * directory where the playlist is located and file is the filename.
311 static void new_playlist(struct playlist_info* playlist, const char *dir,
312 const char *file)
314 const char *fileused = file;
315 const char *dirused = dir;
316 empty_playlist(playlist, false);
318 if (!fileused)
320 fileused = "";
322 if (dirused && playlist->current) /* !current cannot be in_ram */
323 playlist->in_ram = true;
324 else
325 dirused = ""; /* empty playlist */
328 update_playlist_filename(playlist, dirused, fileused);
330 if (playlist->control_fd >= 0)
332 update_control(playlist, PLAYLIST_COMMAND_PLAYLIST,
333 PLAYLIST_CONTROL_FILE_VERSION, -1, dirused, fileused, NULL);
334 sync_control(playlist, false);
339 * create control file for playlist
341 static void create_control(struct playlist_info* playlist)
343 playlist->control_fd = open(playlist->control_filename,
344 O_CREAT|O_RDWR|O_TRUNC);
345 if (playlist->control_fd < 0)
347 if (check_rockboxdir())
349 cond_talk_ids_fq(LANG_PLAYLIST_CONTROL_ACCESS_ERROR);
350 splashf(HZ*2, (unsigned char *)"%s (%d)",
351 str(LANG_PLAYLIST_CONTROL_ACCESS_ERROR),
352 playlist->control_fd);
354 playlist->control_created = false;
356 else
358 playlist->control_created = true;
363 * validate the control file. This may include creating/initializing it if
364 * necessary;
366 static int check_control(struct playlist_info* playlist)
368 if (!playlist->control_created)
370 create_control(playlist);
372 if (playlist->control_fd >= 0)
374 char* dir = playlist->filename;
375 char* file = playlist->filename+playlist->dirlen;
376 char c = playlist->filename[playlist->dirlen-1];
378 playlist->filename[playlist->dirlen-1] = '\0';
380 update_control(playlist, PLAYLIST_COMMAND_PLAYLIST,
381 PLAYLIST_CONTROL_FILE_VERSION, -1, dir, file, NULL);
382 sync_control(playlist, false);
383 playlist->filename[playlist->dirlen-1] = c;
387 if (playlist->control_fd < 0)
388 return -1;
390 return 0;
394 * recreate the control file based on current playlist entries
396 static int recreate_control(struct playlist_info* playlist)
398 char temp_file[MAX_PATH+1];
399 int temp_fd = -1;
400 int i;
401 int result = 0;
403 if(playlist->control_fd >= 0)
405 char* dir = playlist->filename;
406 char* file = playlist->filename+playlist->dirlen;
407 char c = playlist->filename[playlist->dirlen-1];
409 close(playlist->control_fd);
411 snprintf(temp_file, sizeof(temp_file), "%s_temp",
412 playlist->control_filename);
414 if (rename(playlist->control_filename, temp_file) < 0)
415 return -1;
417 temp_fd = open(temp_file, O_RDONLY);
418 if (temp_fd < 0)
419 return -1;
421 playlist->control_fd = open(playlist->control_filename,
422 O_CREAT|O_RDWR|O_TRUNC);
423 if (playlist->control_fd < 0)
424 return -1;
426 playlist->filename[playlist->dirlen-1] = '\0';
428 /* cannot call update_control() because of mutex */
429 result = fdprintf(playlist->control_fd, "P:%d:%s:%s\n",
430 PLAYLIST_CONTROL_FILE_VERSION, dir, file);
432 playlist->filename[playlist->dirlen-1] = c;
434 if (result < 0)
436 close(temp_fd);
437 return result;
441 playlist->seed = 0;
442 playlist->shuffle_modified = false;
443 playlist->deleted = false;
444 playlist->num_inserted_tracks = 0;
446 if (playlist->current)
448 global_status.resume_seed = -1;
449 status_save();
452 for (i=0; i<playlist->amount; i++)
454 if (playlist->indices[i] & PLAYLIST_INSERT_TYPE_MASK)
456 bool queue = playlist->indices[i] & PLAYLIST_QUEUE_MASK;
457 char inserted_file[MAX_PATH+1];
459 lseek(temp_fd, playlist->indices[i] & PLAYLIST_SEEK_MASK,
460 SEEK_SET);
461 read_line(temp_fd, inserted_file, sizeof(inserted_file));
463 result = fdprintf(playlist->control_fd, "%c:%d:%d:",
464 queue?'Q':'A', i, playlist->last_insert_pos);
465 if (result > 0)
467 /* save the position in file where name is written */
468 int seek_pos = lseek(playlist->control_fd, 0, SEEK_CUR);
470 result = fdprintf(playlist->control_fd, "%s\n",
471 inserted_file);
473 playlist->indices[i] =
474 (playlist->indices[i] & ~PLAYLIST_SEEK_MASK) | seek_pos;
477 if (result < 0)
478 break;
480 playlist->num_inserted_tracks++;
484 close(temp_fd);
485 remove(temp_file);
486 fsync(playlist->control_fd);
488 if (result < 0)
489 return result;
491 return 0;
495 * store directory and name of playlist file
497 static void update_playlist_filename(struct playlist_info* playlist,
498 const char *dir, const char *file)
500 char *sep="";
501 int dirlen = strlen(dir);
503 playlist->utf8 = is_m3u8(file);
505 /* If the dir does not end in trailing slash, we use a separator.
506 Otherwise we don't. */
507 if('/' != dir[dirlen-1])
509 sep="/";
510 dirlen++;
513 playlist->dirlen = dirlen;
515 snprintf(playlist->filename, sizeof(playlist->filename),
516 "%s%s%s", dir, sep, file);
520 * calculate track offsets within a playlist file
522 static int add_indices_to_playlist(struct playlist_info* playlist,
523 char* buffer, size_t buflen)
525 unsigned int nread;
526 unsigned int i = 0;
527 unsigned int count = 0;
528 bool store_index;
529 unsigned char *p;
530 int result = 0;
532 if(-1 == playlist->fd)
533 playlist->fd = open_utf8(playlist->filename, O_RDONLY);
534 if(playlist->fd < 0)
535 return -1; /* failure */
536 if((i = lseek(playlist->fd, 0, SEEK_CUR)) > 0)
537 playlist->utf8 = true; /* Override any earlier indication. */
539 splash(0, ID2P(LANG_WAIT));
541 if (!buffer)
543 /* use mp3 buffer for maximum load speed */
544 audio_stop();
545 #if CONFIG_CODEC != SWCODEC
546 talk_buffer_steal(); /* we use the mp3 buffer, need to tell */
547 buflen = (audiobufend - audiobuf);
548 buffer = (char *)audiobuf;
549 #else
550 buffer = (char *)audio_get_buffer(false, &buflen);
551 #endif
554 store_index = true;
556 while(1)
558 nread = read(playlist->fd, buffer, buflen);
559 /* Terminate on EOF */
560 if(nread <= 0)
561 break;
563 p = (unsigned char *)buffer;
565 for(count=0; count < nread; count++,p++) {
567 /* Are we on a new line? */
568 if((*p == '\n') || (*p == '\r'))
570 store_index = true;
572 else if(store_index)
574 store_index = false;
576 if(*p != '#')
578 if ( playlist->amount >= playlist->max_playlist_size ) {
579 display_buffer_full();
580 result = -1;
581 goto exit;
584 /* Store a new entry */
585 playlist->indices[ playlist->amount ] = i+count;
586 #ifdef HAVE_DIRCACHE
587 if (playlist->filenames)
588 playlist->filenames[ playlist->amount ] = NULL;
589 #endif
590 playlist->amount++;
595 i+= count;
598 exit:
599 #ifdef HAVE_DIRCACHE
600 queue_post(&playlist_queue, PLAYLIST_LOAD_POINTERS, 0);
601 #endif
603 return result;
607 * Utility function to create a new playlist, fill it with the next or
608 * previous directory, shuffle it if needed, and start playback.
609 * If play_last is true and direction zero or negative, start playing
610 * the last file in the directory, otherwise start playing the first.
612 static int create_and_play_dir(int direction, bool play_last)
614 char dir[MAX_PATH + 1];
615 int res;
616 int index = -1;
618 if(direction > 0)
619 res = get_next_directory(dir);
620 else
621 res = get_previous_directory(dir);
623 if (!res)
625 if (playlist_create(dir, NULL) != -1)
627 ft_build_playlist(tree_get_context(), 0);
629 if (global_settings.playlist_shuffle)
630 playlist_shuffle(current_tick, -1);
632 if (play_last && direction <= 0)
633 index = current_playlist.amount - 1;
634 else
635 index = 0;
637 #if (CONFIG_CODEC != SWCODEC)
638 playlist_start(index, 0);
639 #endif
642 /* we've overwritten the dircache when getting the next/previous dir,
643 so the tree browser context will need to be reloaded */
644 reload_directory();
647 return index;
651 * Removes all tracks, from the playlist, leaving the presently playing
652 * track queued.
654 int playlist_remove_all_tracks(struct playlist_info *playlist)
656 int result;
658 if (playlist == NULL)
659 playlist = &current_playlist;
661 while (playlist->index > 0)
662 if ((result = remove_track_from_playlist(playlist, 0, true)) < 0)
663 return result;
665 while (playlist->amount > 1)
666 if ((result = remove_track_from_playlist(playlist, 1, true)) < 0)
667 return result;
669 if (playlist->amount == 1) {
670 playlist->indices[0] |= PLAYLIST_QUEUED;
673 return 0;
678 * Add track to playlist at specified position. There are five special
679 * positions that can be specified:
680 * PLAYLIST_PREPEND - Add track at beginning of playlist
681 * PLAYLIST_INSERT - Add track after current song. NOTE: If
682 * there are already inserted tracks then track
683 * is added to the end of the insertion list
684 * PLAYLIST_INSERT_FIRST - Add track immediately after current song, no
685 * matter what other tracks have been inserted
686 * PLAYLIST_INSERT_LAST - Add track to end of playlist
687 * PLAYLIST_INSERT_SHUFFLED - Add track at some random point between the
688 * current playing track and end of playlist
689 * PLAYLIST_REPLACE - Erase current playlist, Cue the current track
690 * and inster this track at the end.
692 static int add_track_to_playlist(struct playlist_info* playlist,
693 const char *filename, int position,
694 bool queue, int seek_pos)
696 int insert_position, orig_position;
697 unsigned long flags = PLAYLIST_INSERT_TYPE_INSERT;
698 int i;
700 insert_position = orig_position = position;
702 if (playlist->amount >= playlist->max_playlist_size)
704 display_buffer_full();
705 return -1;
708 switch (position)
710 case PLAYLIST_PREPEND:
711 position = insert_position = playlist->first_index;
712 break;
713 case PLAYLIST_INSERT:
714 /* if there are already inserted tracks then add track to end of
715 insertion list else add after current playing track */
716 if (playlist->last_insert_pos >= 0 &&
717 playlist->last_insert_pos < playlist->amount &&
718 (playlist->indices[playlist->last_insert_pos]&
719 PLAYLIST_INSERT_TYPE_MASK) == PLAYLIST_INSERT_TYPE_INSERT)
720 position = insert_position = playlist->last_insert_pos+1;
721 else if (playlist->amount > 0)
722 position = insert_position = playlist->index + 1;
723 else
724 position = insert_position = 0;
726 if (playlist->started)
727 playlist->last_insert_pos = position;
728 break;
729 case PLAYLIST_INSERT_FIRST:
730 if (playlist->amount > 0)
731 position = insert_position = playlist->index + 1;
732 else
733 position = insert_position = 0;
735 if (playlist->last_insert_pos < 0 && playlist->started)
736 playlist->last_insert_pos = position;
737 break;
738 case PLAYLIST_INSERT_LAST:
739 if (playlist->first_index > 0)
740 position = insert_position = playlist->first_index;
741 else
742 position = insert_position = playlist->amount;
743 break;
744 case PLAYLIST_INSERT_SHUFFLED:
746 if (playlist->started)
748 int offset;
749 int n = playlist->amount -
750 rotate_index(playlist, playlist->index);
752 if (n > 0)
753 offset = rand() % n;
754 else
755 offset = 0;
757 position = playlist->index + offset + 1;
758 if (position >= playlist->amount)
759 position -= playlist->amount;
761 insert_position = position;
763 else
764 position = insert_position = (rand() % (playlist->amount+1));
765 break;
767 case PLAYLIST_REPLACE:
768 if (playlist_remove_all_tracks(playlist) < 0)
769 return -1;
771 position = insert_position = playlist->index + 1;
772 break;
775 if (queue)
776 flags |= PLAYLIST_QUEUED;
778 /* shift indices so that track can be added */
779 for (i=playlist->amount; i>insert_position; i--)
781 playlist->indices[i] = playlist->indices[i-1];
782 #ifdef HAVE_DIRCACHE
783 if (playlist->filenames)
784 playlist->filenames[i] = playlist->filenames[i-1];
785 #endif
788 /* update stored indices if needed */
789 if (playlist->amount > 0 && insert_position <= playlist->index &&
790 playlist->started)
791 playlist->index++;
793 if (playlist->amount > 0 && insert_position <= playlist->first_index &&
794 orig_position != PLAYLIST_PREPEND && playlist->started)
796 playlist->first_index++;
798 if (seek_pos < 0 && playlist->current)
800 global_status.resume_first_index = playlist->first_index;
801 status_save();
805 if (insert_position < playlist->last_insert_pos ||
806 (insert_position == playlist->last_insert_pos && position < 0))
807 playlist->last_insert_pos++;
809 if (seek_pos < 0 && playlist->control_fd >= 0)
811 int result = update_control(playlist,
812 (queue?PLAYLIST_COMMAND_QUEUE:PLAYLIST_COMMAND_ADD), position,
813 playlist->last_insert_pos, filename, NULL, &seek_pos);
815 if (result < 0)
816 return result;
819 playlist->indices[insert_position] = flags | seek_pos;
821 #ifdef HAVE_DIRCACHE
822 if (playlist->filenames)
823 playlist->filenames[insert_position] = NULL;
824 #endif
826 playlist->amount++;
827 playlist->num_inserted_tracks++;
829 return insert_position;
833 * Callback for playlist_directory_tracksearch to insert track into
834 * playlist.
836 static int directory_search_callback(char* filename, void* context)
838 struct directory_search_context* c =
839 (struct directory_search_context*) context;
840 int insert_pos;
842 insert_pos = add_track_to_playlist(c->playlist, filename, c->position,
843 c->queue, -1);
845 if (insert_pos < 0)
846 return -1;
848 (c->count)++;
850 /* Make sure tracks are inserted in correct order if user requests
851 INSERT_FIRST */
852 if (c->position == PLAYLIST_INSERT_FIRST || c->position >= 0)
853 c->position = insert_pos + 1;
855 if (((c->count)%PLAYLIST_DISPLAY_COUNT) == 0)
857 unsigned char* count_str;
859 if (c->queue)
860 count_str = ID2P(LANG_PLAYLIST_QUEUE_COUNT);
861 else
862 count_str = ID2P(LANG_PLAYLIST_INSERT_COUNT);
864 display_playlist_count(c->count, count_str, false);
866 if ((c->count) == PLAYLIST_DISPLAY_COUNT &&
867 (audio_status() & AUDIO_STATUS_PLAY) &&
868 c->playlist->started)
869 audio_flush_and_reload_tracks();
872 return 0;
876 * remove track at specified position
878 static int remove_track_from_playlist(struct playlist_info* playlist,
879 int position, bool write)
881 int i;
882 bool inserted;
884 if (playlist->amount <= 0)
885 return -1;
887 inserted = playlist->indices[position] & PLAYLIST_INSERT_TYPE_MASK;
889 /* shift indices now that track has been removed */
890 for (i=position; i<playlist->amount; i++)
892 playlist->indices[i] = playlist->indices[i+1];
893 #ifdef HAVE_DIRCACHE
894 if (playlist->filenames)
895 playlist->filenames[i] = playlist->filenames[i+1];
896 #endif
899 playlist->amount--;
901 if (inserted)
902 playlist->num_inserted_tracks--;
903 else
904 playlist->deleted = true;
906 /* update stored indices if needed */
907 if (position < playlist->index)
908 playlist->index--;
910 if (position < playlist->first_index)
912 playlist->first_index--;
914 if (write)
916 global_status.resume_first_index = playlist->first_index;
917 status_save();
921 if (position <= playlist->last_insert_pos)
922 playlist->last_insert_pos--;
924 if (write && playlist->control_fd >= 0)
926 int result = update_control(playlist, PLAYLIST_COMMAND_DELETE,
927 position, -1, NULL, NULL, NULL);
929 if (result < 0)
930 return result;
932 sync_control(playlist, false);
935 return 0;
939 * randomly rearrange the array of indices for the playlist. If start_current
940 * is true then update the index to the new index of the current playing track
942 static int randomise_playlist(struct playlist_info* playlist,
943 unsigned int seed, bool start_current,
944 bool write)
946 int count;
947 int candidate;
948 long store;
949 unsigned int current = playlist->indices[playlist->index];
951 /* seed 0 is used to identify sorted playlist for resume purposes */
952 if (seed == 0)
953 seed = 1;
955 /* seed with the given seed */
956 srand(seed);
958 /* randomise entire indices list */
959 for(count = playlist->amount - 1; count >= 0; count--)
961 /* the rand is from 0 to RAND_MAX, so adjust to our value range */
962 candidate = rand() % (count + 1);
964 /* now swap the values at the 'count' and 'candidate' positions */
965 store = playlist->indices[candidate];
966 playlist->indices[candidate] = playlist->indices[count];
967 playlist->indices[count] = store;
968 #ifdef HAVE_DIRCACHE
969 if (playlist->filenames)
971 store = (long)playlist->filenames[candidate];
972 playlist->filenames[candidate] = playlist->filenames[count];
973 playlist->filenames[count] = (struct dircache_entry *)store;
975 #endif
978 if (start_current)
979 find_and_set_playlist_index(playlist, current);
981 /* indices have been moved so last insert position is no longer valid */
982 playlist->last_insert_pos = -1;
984 playlist->seed = seed;
985 if (playlist->num_inserted_tracks > 0 || playlist->deleted)
986 playlist->shuffle_modified = true;
988 if (write)
990 update_control(playlist, PLAYLIST_COMMAND_SHUFFLE, seed,
991 playlist->first_index, NULL, NULL, NULL);
992 global_status.resume_seed = seed;
993 status_save();
996 return 0;
1000 * Sort the array of indices for the playlist. If start_current is true then
1001 * set the index to the new index of the current song.
1003 static int sort_playlist(struct playlist_info* playlist, bool start_current,
1004 bool write)
1006 unsigned int current = playlist->indices[playlist->index];
1008 if (playlist->amount > 0)
1009 qsort(playlist->indices, playlist->amount,
1010 sizeof(playlist->indices[0]), compare);
1012 #ifdef HAVE_DIRCACHE
1013 /** We need to re-check the song names from disk because qsort can't
1014 * sort two arrays at once :/
1015 * FIXME: Please implement a better way to do this. */
1016 memset(playlist->filenames, 0, playlist->max_playlist_size * sizeof(int));
1017 queue_post(&playlist_queue, PLAYLIST_LOAD_POINTERS, 0);
1018 #endif
1020 if (start_current)
1021 find_and_set_playlist_index(playlist, current);
1023 /* indices have been moved so last insert position is no longer valid */
1024 playlist->last_insert_pos = -1;
1026 if (!playlist->num_inserted_tracks && !playlist->deleted)
1027 playlist->shuffle_modified = false;
1028 if (write && playlist->control_fd >= 0)
1030 update_control(playlist, PLAYLIST_COMMAND_UNSHUFFLE,
1031 playlist->first_index, -1, NULL, NULL, NULL);
1032 global_status.resume_seed = 0;
1033 status_save();
1036 return 0;
1039 /* Calculate how many steps we have to really step when skipping entries
1040 * marked as bad.
1042 static int calculate_step_count(const struct playlist_info *playlist, int steps)
1044 int i, count, direction;
1045 int index;
1046 int stepped_count = 0;
1048 if (steps < 0)
1050 direction = -1;
1051 count = -steps;
1053 else
1055 direction = 1;
1056 count = steps;
1059 index = playlist->index;
1060 i = 0;
1061 do {
1062 /* Boundary check */
1063 if (index < 0)
1064 index += playlist->amount;
1065 if (index >= playlist->amount)
1066 index -= playlist->amount;
1068 /* Check if we found a bad entry. */
1069 if (playlist->indices[index] & PLAYLIST_SKIPPED)
1071 steps += direction;
1072 /* Are all entries bad? */
1073 if (stepped_count++ > playlist->amount)
1074 break ;
1076 else
1077 i++;
1079 index += direction;
1080 } while (i <= count);
1082 return steps;
1085 /* Marks the index of the track to be skipped that is "steps" away from
1086 * current playing track.
1088 void playlist_skip_entry(struct playlist_info *playlist, int steps)
1090 int index;
1092 if (playlist == NULL)
1093 playlist = &current_playlist;
1095 /* need to account for already skipped tracks */
1096 steps = calculate_step_count(playlist, steps);
1098 index = playlist->index + steps;
1099 if (index < 0)
1100 index += playlist->amount;
1101 else if (index >= playlist->amount)
1102 index -= playlist->amount;
1104 playlist->indices[index] |= PLAYLIST_SKIPPED;
1108 * returns the index of the track that is "steps" away from current playing
1109 * track.
1111 static int get_next_index(const struct playlist_info* playlist, int steps,
1112 int repeat_mode)
1114 int current_index = playlist->index;
1115 int next_index = -1;
1117 if (playlist->amount <= 0)
1118 return -1;
1120 if (repeat_mode == -1)
1121 repeat_mode = global_settings.repeat_mode;
1123 if (repeat_mode == REPEAT_SHUFFLE && playlist->amount <= 1)
1124 repeat_mode = REPEAT_ALL;
1126 steps = calculate_step_count(playlist, steps);
1127 switch (repeat_mode)
1129 case REPEAT_SHUFFLE:
1130 /* Treat repeat shuffle just like repeat off. At end of playlist,
1131 play will be resumed in playlist_next() */
1132 case REPEAT_OFF:
1134 current_index = rotate_index(playlist, current_index);
1135 next_index = current_index+steps;
1136 if ((next_index < 0) || (next_index >= playlist->amount))
1137 next_index = -1;
1138 else
1139 next_index = (next_index+playlist->first_index) %
1140 playlist->amount;
1142 break;
1145 case REPEAT_ONE:
1146 #ifdef AB_REPEAT_ENABLE
1147 case REPEAT_AB:
1148 #endif
1149 next_index = current_index;
1150 break;
1152 case REPEAT_ALL:
1153 default:
1155 next_index = (current_index+steps) % playlist->amount;
1156 while (next_index < 0)
1157 next_index += playlist->amount;
1159 if (steps >= playlist->amount)
1161 int i, index;
1163 index = next_index;
1164 next_index = -1;
1166 /* second time around so skip the queued files */
1167 for (i=0; i<playlist->amount; i++)
1169 if (playlist->indices[index] & PLAYLIST_QUEUE_MASK)
1170 index = (index+1) % playlist->amount;
1171 else
1173 next_index = index;
1174 break;
1178 break;
1182 /* No luck if the whole playlist was bad. */
1183 if (playlist->indices[next_index] & PLAYLIST_SKIPPED)
1184 return -1;
1186 return next_index;
1190 * Search for the seek track and set appropriate indices. Used after shuffle
1191 * to make sure the current index is still pointing to correct track.
1193 static void find_and_set_playlist_index(struct playlist_info* playlist,
1194 unsigned int seek)
1196 int i;
1198 /* Set the index to the current song */
1199 for (i=0; i<playlist->amount; i++)
1201 if (playlist->indices[i] == seek)
1203 playlist->index = playlist->first_index = i;
1205 if (playlist->current)
1207 global_status.resume_first_index = i;
1208 status_save();
1211 break;
1217 * used to sort track indices. Sort order is as follows:
1218 * 1. Prepended tracks (in prepend order)
1219 * 2. Playlist/directory tracks (in playlist order)
1220 * 3. Inserted/Appended tracks (in insert order)
1222 static int compare(const void* p1, const void* p2)
1224 unsigned long* e1 = (unsigned long*) p1;
1225 unsigned long* e2 = (unsigned long*) p2;
1226 unsigned long flags1 = *e1 & PLAYLIST_INSERT_TYPE_MASK;
1227 unsigned long flags2 = *e2 & PLAYLIST_INSERT_TYPE_MASK;
1229 if (flags1 == flags2)
1230 return (*e1 & PLAYLIST_SEEK_MASK) - (*e2 & PLAYLIST_SEEK_MASK);
1231 else if (flags1 == PLAYLIST_INSERT_TYPE_PREPEND ||
1232 flags2 == PLAYLIST_INSERT_TYPE_APPEND)
1233 return -1;
1234 else if (flags1 == PLAYLIST_INSERT_TYPE_APPEND ||
1235 flags2 == PLAYLIST_INSERT_TYPE_PREPEND)
1236 return 1;
1237 else if (flags1 && flags2)
1238 return (*e1 & PLAYLIST_SEEK_MASK) - (*e2 & PLAYLIST_SEEK_MASK);
1239 else
1240 return *e1 - *e2;
1243 #ifdef HAVE_DIRCACHE
1245 * Thread to update filename pointers to dircache on background
1246 * without affecting playlist load up performance. This thread also flushes
1247 * any pending control commands when the disk spins up.
1249 static bool playlist_flush_callback(void)
1251 struct playlist_info *playlist;
1252 playlist = &current_playlist;
1253 if (playlist->control_fd >= 0)
1255 if (playlist->num_cached > 0)
1257 mutex_lock(&playlist->control_mutex);
1258 flush_cached_control(playlist);
1259 mutex_unlock(&playlist->control_mutex);
1261 sync_control(playlist, true);
1263 return true;
1266 static void playlist_thread(void)
1268 struct queue_event ev;
1269 bool dirty_pointers = false;
1270 static char tmp[MAX_PATH+1];
1272 struct playlist_info *playlist;
1273 int index;
1274 int seek;
1275 bool control_file;
1277 int sleep_time = 5;
1279 #ifndef HAVE_FLASH_STORAGE
1280 if (global_settings.disk_spindown > 1 &&
1281 global_settings.disk_spindown <= 5)
1282 sleep_time = global_settings.disk_spindown - 1;
1283 #endif
1285 while (1)
1287 queue_wait_w_tmo(&playlist_queue, &ev, HZ*sleep_time);
1289 switch (ev.id)
1291 case PLAYLIST_LOAD_POINTERS:
1292 dirty_pointers = true;
1293 break ;
1295 /* Start the background scanning after either the disk spindown
1296 timeout or 5s, whichever is less */
1297 case SYS_TIMEOUT:
1298 playlist = &current_playlist;
1299 if (playlist->control_fd >= 0)
1301 if (playlist->num_cached > 0)
1302 register_ata_idle_func(playlist_flush_callback);
1305 if (!dirty_pointers)
1306 break ;
1308 if (!dircache_is_enabled() || !playlist->filenames
1309 || playlist->amount <= 0)
1310 break ;
1312 #ifdef HAVE_ADJUSTABLE_CPU_FREQ
1313 cpu_boost(true);
1314 #endif
1315 for (index = 0; index < playlist->amount
1316 && queue_empty(&playlist_queue); index++)
1318 /* Process only pointers that are not already loaded. */
1319 if (playlist->filenames[index])
1320 continue ;
1322 control_file = playlist->indices[index] & PLAYLIST_INSERT_TYPE_MASK;
1323 seek = playlist->indices[index] & PLAYLIST_SEEK_MASK;
1325 /* Load the filename from playlist file. */
1326 if (get_filename(playlist, index, seek, control_file, tmp,
1327 sizeof(tmp)) < 0)
1328 break ;
1330 /* Set the dircache entry pointer. */
1331 playlist->filenames[index] = dircache_get_entry_ptr(tmp);
1333 /* And be on background so user doesn't notice any delays. */
1334 yield();
1337 #ifdef HAVE_ADJUSTABLE_CPU_FREQ
1338 cpu_boost(false);
1339 #endif
1340 dirty_pointers = false;
1341 break ;
1343 #ifndef SIMULATOR
1344 case SYS_USB_CONNECTED:
1345 usb_acknowledge(SYS_USB_CONNECTED_ACK);
1346 usb_wait_for_disconnect(&playlist_queue);
1347 break ;
1348 #endif
1352 #endif
1355 * gets pathname for track at seek index
1357 static int get_filename(struct playlist_info* playlist, int index, int seek,
1358 bool control_file, char *buf, int buf_length)
1360 int fd;
1361 int max = -1;
1362 char tmp_buf[MAX_PATH+1];
1363 char dir_buf[MAX_PATH+1];
1364 bool utf8 = playlist->utf8;
1366 if (buf_length > MAX_PATH+1)
1367 buf_length = MAX_PATH+1;
1369 #ifdef HAVE_DIRCACHE
1370 if (dircache_is_enabled() && playlist->filenames)
1372 if (playlist->filenames[index] != NULL)
1374 dircache_copy_path(playlist->filenames[index], tmp_buf, sizeof(tmp_buf)-1);
1375 max = strlen(tmp_buf) + 1;
1378 #else
1379 (void)index;
1380 #endif
1382 if (playlist->in_ram && !control_file && max < 0)
1384 strncpy(tmp_buf, &playlist->buffer[seek], sizeof(tmp_buf));
1385 tmp_buf[MAX_PATH] = '\0';
1386 max = strlen(tmp_buf) + 1;
1388 else if (max < 0)
1390 mutex_lock(&playlist->control_mutex);
1392 if (control_file)
1394 fd = playlist->control_fd;
1395 utf8 = true;
1397 else
1399 if(-1 == playlist->fd)
1400 playlist->fd = open(playlist->filename, O_RDONLY);
1402 fd = playlist->fd;
1405 if(-1 != fd)
1408 if (lseek(fd, seek, SEEK_SET) != seek)
1409 max = -1;
1410 else
1412 max = read(fd, tmp_buf, MIN((size_t) buf_length, sizeof(tmp_buf)));
1414 if ((max > 0) && !utf8)
1416 /* Use dir_buf as a temporary buffer. Note that dir_buf must
1417 * be as large as tmp_buf.
1419 max = convert_m3u(tmp_buf, max, sizeof(tmp_buf), dir_buf);
1424 mutex_unlock(&playlist->control_mutex);
1426 if (max < 0)
1428 if (control_file)
1429 splash(HZ*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR));
1430 else
1431 splash(HZ*2, ID2P(LANG_PLAYLIST_ACCESS_ERROR));
1433 return max;
1437 strncpy(dir_buf, playlist->filename, playlist->dirlen-1);
1438 dir_buf[playlist->dirlen-1] = 0;
1440 return (format_track_path(buf, tmp_buf, buf_length, max, dir_buf));
1443 static int get_next_directory(char *dir){
1444 return get_next_dir(dir,true,false);
1447 static int get_previous_directory(char *dir){
1448 return get_next_dir(dir,false,false);
1452 * search through all the directories (starting with the current) to find
1453 * one that has tracks to play
1455 static int get_next_dir(char *dir, bool is_forward, bool recursion)
1457 struct playlist_info* playlist = &current_playlist;
1458 int result = -1;
1459 int sort_dir = global_settings.sort_dir;
1460 char *start_dir = NULL;
1461 bool exit = false;
1462 struct tree_context* tc = tree_get_context();
1463 int dirfilter = *(tc->dirfilter);
1464 if (global_settings.next_folder == FOLDER_ADVANCE_RANDOM)
1466 int fd = open(ROCKBOX_DIR "/folder_advance_list.dat",O_RDONLY);
1467 char buffer[MAX_PATH];
1468 int folder_count = 0,i;
1469 srand(current_tick);
1470 *(tc->dirfilter) = SHOW_MUSIC;
1471 if (fd >= 0)
1473 read(fd,&folder_count,sizeof(int));
1474 if (!folder_count)
1475 exit = true;
1476 while (!exit)
1478 i = rand()%folder_count;
1479 lseek(fd,sizeof(int) + (MAX_PATH*i),SEEK_SET);
1480 read(fd,buffer,MAX_PATH);
1481 if (check_subdir_for_music(buffer, "", false) ==0)
1482 exit = true;
1484 if (folder_count)
1485 strcpy(dir,buffer);
1486 close(fd);
1487 *(tc->dirfilter) = dirfilter;
1488 reload_directory();
1489 return 0;
1492 /* not random folder advance */
1493 if (recursion){
1494 /* start with root */
1495 dir[0] = '\0';
1497 else{
1498 /* start with current directory */
1499 strncpy(dir, playlist->filename, playlist->dirlen-1);
1500 dir[playlist->dirlen-1] = '\0';
1503 /* use the tree browser dircache to load files */
1504 *(tc->dirfilter) = SHOW_ALL;
1506 /* sort in another direction if previous dir is requested */
1507 if(!is_forward){
1508 if ((global_settings.sort_dir == 0) || (global_settings.sort_dir == 3))
1509 global_settings.sort_dir = 4;
1510 else if (global_settings.sort_dir == 1)
1511 global_settings.sort_dir = 2;
1512 else if (global_settings.sort_dir == 2)
1513 global_settings.sort_dir = 1;
1514 else if (global_settings.sort_dir == 4)
1515 global_settings.sort_dir = 0;
1518 while (!exit)
1520 struct entry *files;
1521 int num_files = 0;
1522 int i;
1524 if (ft_load(tc, (dir[0]=='\0')?"/":dir) < 0)
1526 splash(HZ*2, ID2P(LANG_PLAYLIST_DIRECTORY_ACCESS_ERROR));
1527 exit = true;
1528 result = -1;
1529 break;
1532 files = (struct entry*) tc->dircache;
1533 num_files = tc->filesindir;
1535 for (i=0; i<num_files; i++)
1537 /* user abort */
1538 if (action_userabort(TIMEOUT_NOBLOCK))
1540 result = -1;
1541 exit = true;
1542 break;
1545 if (files[i].attr & ATTR_DIRECTORY)
1547 if (!start_dir)
1549 result = check_subdir_for_music(dir, files[i].name, true);
1550 if (result != -1)
1552 exit = true;
1553 break;
1556 else if (!strcmp(start_dir, files[i].name))
1557 start_dir = NULL;
1561 if (!exit)
1563 /* move down to parent directory. current directory name is
1564 stored as the starting point for the search in parent */
1565 start_dir = strrchr(dir, '/');
1566 if (start_dir)
1568 *start_dir = '\0';
1569 start_dir++;
1571 else
1572 break;
1576 /* restore dirfilter & sort_dir */
1577 *(tc->dirfilter) = dirfilter;
1578 global_settings.sort_dir = sort_dir;
1580 /* special case if nothing found: try start searching again from root */
1581 if (result == -1 && !recursion){
1582 result = get_next_dir(dir,is_forward, true);
1585 return result;
1589 * Checks if there are any music files in the dir or any of its
1590 * subdirectories. May be called recursively.
1592 static int check_subdir_for_music(char *dir, char *subdir, bool recurse)
1594 int result = -1;
1595 int dirlen = strlen(dir);
1596 int num_files = 0;
1597 int i;
1598 struct entry *files;
1599 bool has_music = false;
1600 bool has_subdir = false;
1601 struct tree_context* tc = tree_get_context();
1603 snprintf(dir+dirlen, MAX_PATH-dirlen, "/%s", subdir);
1605 if (ft_load(tc, dir) < 0)
1607 splash(HZ*2, ID2P(LANG_PLAYLIST_DIRECTORY_ACCESS_ERROR));
1608 return -2;
1611 files = (struct entry*) tc->dircache;
1612 num_files = tc->filesindir;
1614 for (i=0; i<num_files; i++)
1616 if (files[i].attr & ATTR_DIRECTORY)
1617 has_subdir = true;
1618 else if ((files[i].attr & FILE_ATTR_MASK) == FILE_ATTR_AUDIO)
1620 has_music = true;
1621 break;
1625 if (has_music)
1626 return 0;
1628 if (has_subdir && recurse)
1630 for (i=0; i<num_files; i++)
1632 if (action_userabort(TIMEOUT_NOBLOCK))
1634 result = -2;
1635 break;
1638 if (files[i].attr & ATTR_DIRECTORY)
1640 result = check_subdir_for_music(dir, files[i].name, true);
1641 if (!result)
1642 break;
1647 if (result < 0)
1649 if (dirlen)
1651 dir[dirlen] = '\0';
1653 else
1655 strcpy(dir, "/");
1658 /* we now need to reload our current directory */
1659 if(ft_load(tc, dir) < 0)
1660 splash(HZ*2, ID2P(LANG_PLAYLIST_DIRECTORY_ACCESS_ERROR));
1662 return result;
1666 * Returns absolute path of track
1668 static int format_track_path(char *dest, char *src, int buf_length, int max,
1669 const char *dir)
1671 int i = 0;
1672 int j;
1673 char *temp_ptr;
1675 /* Zero-terminate the file name */
1676 while((src[i] != '\n') &&
1677 (src[i] != '\r') &&
1678 (i < max))
1679 i++;
1681 /* Now work back killing white space */
1682 while((src[i-1] == ' ') ||
1683 (src[i-1] == '\t'))
1684 i--;
1686 src[i]=0;
1688 /* replace backslashes with forward slashes */
1689 for ( j=0; j<i; j++ )
1690 if ( src[j] == '\\' )
1691 src[j] = '/';
1693 if('/' == src[0])
1695 strncpy(dest, src, buf_length);
1697 else
1699 /* handle dos style drive letter */
1700 if (':' == src[1])
1701 strncpy(dest, &src[2], buf_length);
1702 else if (!strncmp(src, "../", 3))
1704 /* handle relative paths */
1705 i=3;
1706 while(!strncmp(&src[i], "../", 3))
1707 i += 3;
1708 for (j=0; j<i/3; j++) {
1709 temp_ptr = strrchr(dir, '/');
1710 if (temp_ptr)
1711 *temp_ptr = '\0';
1712 else
1713 break;
1715 snprintf(dest, buf_length, "%s/%s", dir, &src[i]);
1717 else if ( '.' == src[0] && '/' == src[1] ) {
1718 snprintf(dest, buf_length, "%s/%s", dir, &src[2]);
1720 else {
1721 snprintf(dest, buf_length, "%s/%s", dir, src);
1725 return 0;
1729 * Display splash message showing progress of playlist/directory insertion or
1730 * save.
1732 static void display_playlist_count(int count, const unsigned char *fmt,
1733 bool final)
1735 static long talked_tick = 0;
1736 long id = P2ID(fmt);
1737 if(global_settings.talk_menu && id>=0)
1739 if(final || (count && (talked_tick == 0
1740 || TIME_AFTER(current_tick, talked_tick+5*HZ))))
1742 talked_tick = current_tick;
1743 talk_number(count, false);
1744 talk_id(id, true);
1747 fmt = P2STR(fmt);
1749 splashf(0, fmt, count, str(LANG_OFF_ABORT));
1753 * Display buffer full message
1755 static void display_buffer_full(void)
1757 splash(HZ*2, ID2P(LANG_PLAYLIST_BUFFER_FULL));
1761 * Flush any cached control commands to disk. Called when playlist is being
1762 * modified. Returns 0 on success and -1 on failure.
1764 static int flush_cached_control(struct playlist_info* playlist)
1766 int result = 0;
1767 int i;
1769 if (!playlist->num_cached)
1770 return 0;
1772 lseek(playlist->control_fd, 0, SEEK_END);
1774 for (i=0; i<playlist->num_cached; i++)
1776 struct playlist_control_cache* cache =
1777 &(playlist->control_cache[i]);
1779 switch (cache->command)
1781 case PLAYLIST_COMMAND_PLAYLIST:
1782 result = fdprintf(playlist->control_fd, "P:%d:%s:%s\n",
1783 cache->i1, cache->s1, cache->s2);
1784 break;
1785 case PLAYLIST_COMMAND_ADD:
1786 case PLAYLIST_COMMAND_QUEUE:
1787 result = fdprintf(playlist->control_fd, "%c:%d:%d:",
1788 (cache->command == PLAYLIST_COMMAND_ADD)?'A':'Q',
1789 cache->i1, cache->i2);
1790 if (result > 0)
1792 /* save the position in file where name is written */
1793 int* seek_pos = (int *)cache->data;
1794 *seek_pos = lseek(playlist->control_fd, 0, SEEK_CUR);
1795 result = fdprintf(playlist->control_fd, "%s\n",
1796 cache->s1);
1798 break;
1799 case PLAYLIST_COMMAND_DELETE:
1800 result = fdprintf(playlist->control_fd, "D:%d\n", cache->i1);
1801 break;
1802 case PLAYLIST_COMMAND_SHUFFLE:
1803 result = fdprintf(playlist->control_fd, "S:%d:%d\n",
1804 cache->i1, cache->i2);
1805 break;
1806 case PLAYLIST_COMMAND_UNSHUFFLE:
1807 result = fdprintf(playlist->control_fd, "U:%d\n", cache->i1);
1808 break;
1809 case PLAYLIST_COMMAND_RESET:
1810 result = fdprintf(playlist->control_fd, "R\n");
1811 break;
1812 default:
1813 break;
1816 if (result <= 0)
1817 break;
1820 if (result > 0)
1822 if (global_status.resume_seed >= 0)
1824 global_status.resume_seed = -1;
1825 status_save();
1828 playlist->num_cached = 0;
1829 playlist->pending_control_sync = true;
1831 result = 0;
1833 else
1835 result = -1;
1836 splash(HZ*2, ID2P(LANG_PLAYLIST_CONTROL_UPDATE_ERROR));
1839 return result;
1843 * Update control data with new command. Depending on the command, it may be
1844 * cached or flushed to disk.
1846 static int update_control(struct playlist_info* playlist,
1847 enum playlist_command command, int i1, int i2,
1848 const char* s1, const char* s2, void* data)
1850 int result = 0;
1851 struct playlist_control_cache* cache;
1852 bool flush = false;
1854 mutex_lock(&playlist->control_mutex);
1856 cache = &(playlist->control_cache[playlist->num_cached++]);
1858 cache->command = command;
1859 cache->i1 = i1;
1860 cache->i2 = i2;
1861 cache->s1 = s1;
1862 cache->s2 = s2;
1863 cache->data = data;
1865 switch (command)
1867 case PLAYLIST_COMMAND_PLAYLIST:
1868 case PLAYLIST_COMMAND_ADD:
1869 case PLAYLIST_COMMAND_QUEUE:
1870 #ifndef HAVE_DIRCACHE
1871 case PLAYLIST_COMMAND_DELETE:
1872 case PLAYLIST_COMMAND_RESET:
1873 #endif
1874 flush = true;
1875 break;
1876 case PLAYLIST_COMMAND_SHUFFLE:
1877 case PLAYLIST_COMMAND_UNSHUFFLE:
1878 default:
1879 /* only flush when needed */
1880 break;
1883 if (flush || playlist->num_cached == PLAYLIST_MAX_CACHE)
1884 result = flush_cached_control(playlist);
1886 mutex_unlock(&playlist->control_mutex);
1888 return result;
1892 * sync control file to disk
1894 static void sync_control(struct playlist_info* playlist, bool force)
1896 #ifdef HAVE_DIRCACHE
1897 if (playlist->started && force)
1898 #else
1899 (void) force;
1901 if (playlist->started)
1902 #endif
1904 if (playlist->pending_control_sync)
1906 mutex_lock(&playlist->control_mutex);
1907 fsync(playlist->control_fd);
1908 playlist->pending_control_sync = false;
1909 mutex_unlock(&playlist->control_mutex);
1915 * Rotate indices such that first_index is index 0
1917 static int rotate_index(const struct playlist_info* playlist, int index)
1919 index -= playlist->first_index;
1920 if (index < 0)
1921 index += playlist->amount;
1923 return index;
1927 * Initialize playlist entries at startup
1929 void playlist_init(void)
1931 struct playlist_info* playlist = &current_playlist;
1933 playlist->current = true;
1934 snprintf(playlist->control_filename, sizeof(playlist->control_filename),
1935 "%s", PLAYLIST_CONTROL_FILE);
1936 playlist->fd = -1;
1937 playlist->control_fd = -1;
1938 playlist->max_playlist_size = global_settings.max_files_in_playlist;
1939 playlist->indices = buffer_alloc(
1940 playlist->max_playlist_size * sizeof(int));
1941 playlist->buffer_size =
1942 AVERAGE_FILENAME_LENGTH * global_settings.max_files_in_dir;
1943 playlist->buffer = buffer_alloc(playlist->buffer_size);
1944 mutex_init(&playlist->control_mutex);
1945 empty_playlist(playlist, true);
1947 #ifdef HAVE_DIRCACHE
1948 playlist->filenames = buffer_alloc(
1949 playlist->max_playlist_size * sizeof(int));
1950 memset(playlist->filenames, 0,
1951 playlist->max_playlist_size * sizeof(int));
1952 create_thread(playlist_thread, playlist_stack, sizeof(playlist_stack),
1953 0, playlist_thread_name IF_PRIO(, PRIORITY_BACKGROUND)
1954 IF_COP(, CPU));
1955 queue_init(&playlist_queue, true);
1956 #endif
1960 * Clean playlist at shutdown
1962 void playlist_shutdown(void)
1964 struct playlist_info* playlist = &current_playlist;
1966 if (playlist->control_fd >= 0)
1968 mutex_lock(&playlist->control_mutex);
1970 if (playlist->num_cached > 0)
1971 flush_cached_control(playlist);
1973 close(playlist->control_fd);
1975 mutex_unlock(&playlist->control_mutex);
1980 * Create new playlist
1982 int playlist_create(const char *dir, const char *file)
1984 struct playlist_info* playlist = &current_playlist;
1986 new_playlist(playlist, dir, file);
1988 if (file)
1989 /* load the playlist file */
1990 add_indices_to_playlist(playlist, NULL, 0);
1992 return 0;
1995 #define PLAYLIST_COMMAND_SIZE (MAX_PATH+12)
1998 * Restore the playlist state based on control file commands. Called to
1999 * resume playback after shutdown.
2001 int playlist_resume(void)
2003 struct playlist_info* playlist = &current_playlist;
2004 char *buffer;
2005 size_t buflen;
2006 int nread;
2007 int total_read = 0;
2008 int control_file_size = 0;
2009 bool first = true;
2010 bool sorted = true;
2012 /* use mp3 buffer for maximum load speed */
2013 #if CONFIG_CODEC != SWCODEC
2014 talk_buffer_steal(); /* we use the mp3 buffer, need to tell */
2015 buflen = (audiobufend - audiobuf);
2016 buffer = (char *)audiobuf;
2017 #else
2018 buffer = (char *)audio_get_buffer(false, &buflen);
2019 #endif
2021 empty_playlist(playlist, true);
2023 splash(0, ID2P(LANG_WAIT));
2024 playlist->control_fd = open(playlist->control_filename, O_RDWR);
2025 if (playlist->control_fd < 0)
2027 splash(HZ*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR));
2028 return -1;
2030 playlist->control_created = true;
2032 control_file_size = filesize(playlist->control_fd);
2033 if (control_file_size <= 0)
2035 splash(HZ*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR));
2036 return -1;
2039 /* read a small amount first to get the header */
2040 nread = read(playlist->control_fd, buffer,
2041 PLAYLIST_COMMAND_SIZE<buflen?PLAYLIST_COMMAND_SIZE:buflen);
2042 if(nread <= 0)
2044 splash(HZ*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR));
2045 return -1;
2048 playlist->started = true;
2050 while (1)
2052 int result = 0;
2053 int count;
2054 enum playlist_command current_command = PLAYLIST_COMMAND_COMMENT;
2055 int last_newline = 0;
2056 int str_count = -1;
2057 bool newline = true;
2058 bool exit_loop = false;
2059 char *p = buffer;
2060 char *str1 = NULL;
2061 char *str2 = NULL;
2062 char *str3 = NULL;
2063 unsigned long last_tick = current_tick;
2064 bool useraborted = false;
2066 for(count=0; count<nread && !exit_loop && !useraborted; count++,p++)
2068 /* So a splash while we are loading. */
2069 if (current_tick - last_tick > HZ/4)
2071 splashf(0, str(LANG_LOADING_PERCENT),
2072 (total_read+count)*100/control_file_size,
2073 str(LANG_OFF_ABORT));
2074 if (action_userabort(TIMEOUT_NOBLOCK))
2076 useraborted = true;
2077 break;
2079 last_tick = current_tick;
2082 /* Are we on a new line? */
2083 if((*p == '\n') || (*p == '\r'))
2085 *p = '\0';
2087 /* save last_newline in case we need to load more data */
2088 last_newline = count;
2090 switch (current_command)
2092 case PLAYLIST_COMMAND_PLAYLIST:
2094 /* str1=version str2=dir str3=file */
2095 int version;
2097 if (!str1)
2099 result = -1;
2100 exit_loop = true;
2101 break;
2104 if (!str2)
2105 str2 = "";
2107 if (!str3)
2108 str3 = "";
2110 version = atoi(str1);
2112 if (version != PLAYLIST_CONTROL_FILE_VERSION)
2113 return -1;
2115 update_playlist_filename(playlist, str2, str3);
2117 if (str3[0] != '\0')
2119 /* NOTE: add_indices_to_playlist() overwrites the
2120 audiobuf so we need to reload control file
2121 data */
2122 add_indices_to_playlist(playlist, NULL, 0);
2124 else if (str2[0] != '\0')
2126 playlist->in_ram = true;
2127 resume_directory(str2);
2130 /* load the rest of the data */
2131 first = false;
2132 exit_loop = true;
2134 break;
2136 case PLAYLIST_COMMAND_ADD:
2137 case PLAYLIST_COMMAND_QUEUE:
2139 /* str1=position str2=last_position str3=file */
2140 int position, last_position;
2141 bool queue;
2143 if (!str1 || !str2 || !str3)
2145 result = -1;
2146 exit_loop = true;
2147 break;
2150 position = atoi(str1);
2151 last_position = atoi(str2);
2153 queue = (current_command == PLAYLIST_COMMAND_ADD)?
2154 false:true;
2156 /* seek position is based on str3's position in
2157 buffer */
2158 if (add_track_to_playlist(playlist, str3, position,
2159 queue, total_read+(str3-buffer)) < 0)
2160 return -1;
2162 playlist->last_insert_pos = last_position;
2164 break;
2166 case PLAYLIST_COMMAND_DELETE:
2168 /* str1=position */
2169 int position;
2171 if (!str1)
2173 result = -1;
2174 exit_loop = true;
2175 break;
2178 position = atoi(str1);
2180 if (remove_track_from_playlist(playlist, position,
2181 false) < 0)
2182 return -1;
2184 break;
2186 case PLAYLIST_COMMAND_SHUFFLE:
2188 /* str1=seed str2=first_index */
2189 int seed;
2191 if (!str1 || !str2)
2193 result = -1;
2194 exit_loop = true;
2195 break;
2198 if (!sorted)
2200 /* Always sort list before shuffling */
2201 sort_playlist(playlist, false, false);
2204 seed = atoi(str1);
2205 playlist->first_index = atoi(str2);
2207 if (randomise_playlist(playlist, seed, false,
2208 false) < 0)
2209 return -1;
2211 sorted = false;
2212 break;
2214 case PLAYLIST_COMMAND_UNSHUFFLE:
2216 /* str1=first_index */
2217 if (!str1)
2219 result = -1;
2220 exit_loop = true;
2221 break;
2224 playlist->first_index = atoi(str1);
2226 if (sort_playlist(playlist, false, false) < 0)
2227 return -1;
2229 sorted = true;
2230 break;
2232 case PLAYLIST_COMMAND_RESET:
2234 playlist->last_insert_pos = -1;
2235 break;
2237 case PLAYLIST_COMMAND_COMMENT:
2238 default:
2239 break;
2242 newline = true;
2244 /* to ignore any extra newlines */
2245 current_command = PLAYLIST_COMMAND_COMMENT;
2247 else if(newline)
2249 newline = false;
2251 /* first non-comment line must always specify playlist */
2252 if (first && *p != 'P' && *p != '#')
2254 result = -1;
2255 exit_loop = true;
2256 break;
2259 switch (*p)
2261 case 'P':
2262 /* playlist can only be specified once */
2263 if (!first)
2265 result = -1;
2266 exit_loop = true;
2267 break;
2270 current_command = PLAYLIST_COMMAND_PLAYLIST;
2271 break;
2272 case 'A':
2273 current_command = PLAYLIST_COMMAND_ADD;
2274 break;
2275 case 'Q':
2276 current_command = PLAYLIST_COMMAND_QUEUE;
2277 break;
2278 case 'D':
2279 current_command = PLAYLIST_COMMAND_DELETE;
2280 break;
2281 case 'S':
2282 current_command = PLAYLIST_COMMAND_SHUFFLE;
2283 break;
2284 case 'U':
2285 current_command = PLAYLIST_COMMAND_UNSHUFFLE;
2286 break;
2287 case 'R':
2288 current_command = PLAYLIST_COMMAND_RESET;
2289 break;
2290 case '#':
2291 current_command = PLAYLIST_COMMAND_COMMENT;
2292 break;
2293 default:
2294 result = -1;
2295 exit_loop = true;
2296 break;
2299 str_count = -1;
2300 str1 = NULL;
2301 str2 = NULL;
2302 str3 = NULL;
2304 else if(current_command != PLAYLIST_COMMAND_COMMENT)
2306 /* all control file strings are separated with a colon.
2307 Replace the colon with 0 to get proper strings that can be
2308 used by commands above */
2309 if (*p == ':')
2311 *p = '\0';
2312 str_count++;
2314 if ((count+1) < nread)
2316 switch (str_count)
2318 case 0:
2319 str1 = p+1;
2320 break;
2321 case 1:
2322 str2 = p+1;
2323 break;
2324 case 2:
2325 str3 = p+1;
2326 break;
2327 default:
2328 /* allow last string to contain colons */
2329 *p = ':';
2330 break;
2337 if (result < 0)
2339 splash(HZ*2, ID2P(LANG_PLAYLIST_CONTROL_INVALID));
2340 return result;
2343 if (useraborted)
2345 splash(HZ*2, ID2P(LANG_CANCEL));
2346 return -1;
2348 if (!newline || (exit_loop && count<nread))
2350 if ((total_read + count) >= control_file_size)
2352 /* no newline at end of control file */
2353 splash(HZ*2, ID2P(LANG_PLAYLIST_CONTROL_INVALID));
2354 return -1;
2357 /* We didn't end on a newline or we exited loop prematurely.
2358 Either way, re-read the remainder. */
2359 count = last_newline;
2360 lseek(playlist->control_fd, total_read+count, SEEK_SET);
2363 total_read += count;
2365 if (first)
2366 /* still looking for header */
2367 nread = read(playlist->control_fd, buffer,
2368 PLAYLIST_COMMAND_SIZE<buflen?PLAYLIST_COMMAND_SIZE:buflen);
2369 else
2370 nread = read(playlist->control_fd, buffer, buflen);
2372 /* Terminate on EOF */
2373 if(nread <= 0)
2375 if (global_status.resume_seed >= 0)
2377 /* Apply shuffle command saved in settings */
2378 if (global_status.resume_seed == 0)
2379 sort_playlist(playlist, false, true);
2380 else
2382 if (!sorted)
2383 sort_playlist(playlist, false, false);
2385 randomise_playlist(playlist, global_status.resume_seed,
2386 false, true);
2390 playlist->first_index = global_status.resume_first_index;
2391 break;
2395 #ifdef HAVE_DIRCACHE
2396 queue_post(&playlist_queue, PLAYLIST_LOAD_POINTERS, 0);
2397 #endif
2399 return 0;
2403 * Add track to in_ram playlist. Used when playing directories.
2405 int playlist_add(const char *filename)
2407 struct playlist_info* playlist = &current_playlist;
2408 int len = strlen(filename);
2410 if((len+1 > playlist->buffer_size - playlist->buffer_end_pos) ||
2411 (playlist->amount >= playlist->max_playlist_size))
2413 display_buffer_full();
2414 return -1;
2417 playlist->indices[playlist->amount] = playlist->buffer_end_pos;
2418 #ifdef HAVE_DIRCACHE
2419 playlist->filenames[playlist->amount] = NULL;
2420 #endif
2421 playlist->amount++;
2423 strcpy(&playlist->buffer[playlist->buffer_end_pos], filename);
2424 playlist->buffer_end_pos += len;
2425 playlist->buffer[playlist->buffer_end_pos++] = '\0';
2427 return 0;
2430 /* shuffle newly created playlist using random seed. */
2431 int playlist_shuffle(int random_seed, int start_index)
2433 struct playlist_info* playlist = &current_playlist;
2435 unsigned int seek_pos = 0;
2436 bool start_current = false;
2438 if (start_index >= 0 && global_settings.play_selected)
2440 /* store the seek position before the shuffle */
2441 seek_pos = playlist->indices[start_index];
2442 playlist->index = global_status.resume_first_index =
2443 playlist->first_index = start_index;
2444 start_current = true;
2447 randomise_playlist(playlist, random_seed, start_current, true);
2449 return playlist->index;
2452 /* start playing current playlist at specified index/offset */
2453 int playlist_start(int start_index, int offset)
2455 struct playlist_info* playlist = &current_playlist;
2457 /* Cancel FM radio selection as previous music. For cases where we start
2458 playback without going to the WPS, such as playlist insert.. or
2459 playlist catalog. */
2460 previous_music_is_wps();
2462 playlist->index = start_index;
2464 #if CONFIG_CODEC != SWCODEC
2465 talk_buffer_steal(); /* will use the mp3 buffer */
2466 #endif
2468 playlist->started = true;
2469 sync_control(playlist, false);
2470 audio_play(offset);
2472 return 0;
2475 /* Returns false if 'steps' is out of bounds, else true */
2476 bool playlist_check(int steps)
2478 struct playlist_info* playlist = &current_playlist;
2480 /* always allow folder navigation */
2481 if (global_settings.next_folder && playlist->in_ram)
2482 return true;
2484 int index = get_next_index(playlist, steps, -1);
2486 if (index < 0 && steps >= 0 && global_settings.repeat_mode == REPEAT_SHUFFLE)
2487 index = get_next_index(playlist, steps, REPEAT_ALL);
2489 return (index >= 0);
2492 /* get trackname of track that is "steps" away from current playing track.
2493 NULL is used to identify end of playlist */
2494 char* playlist_peek(int steps)
2496 struct playlist_info* playlist = &current_playlist;
2497 int seek;
2498 char *temp_ptr;
2499 int index;
2500 bool control_file;
2502 index = get_next_index(playlist, steps, -1);
2503 if (index < 0)
2504 return NULL;
2506 control_file = playlist->indices[index] & PLAYLIST_INSERT_TYPE_MASK;
2507 seek = playlist->indices[index] & PLAYLIST_SEEK_MASK;
2509 if (get_filename(playlist, index, seek, control_file, now_playing,
2510 MAX_PATH+1) < 0)
2511 return NULL;
2513 temp_ptr = now_playing;
2515 if (!playlist->in_ram || control_file)
2517 /* remove bogus dirs from beginning of path
2518 (workaround for buggy playlist creation tools) */
2519 while (temp_ptr)
2521 if (file_exists(temp_ptr))
2522 break;
2524 temp_ptr = strchr(temp_ptr+1, '/');
2527 if (!temp_ptr)
2529 /* Even though this is an invalid file, we still need to pass a
2530 file name to the caller because NULL is used to indicate end
2531 of playlist */
2532 return now_playing;
2536 return temp_ptr;
2540 * Update indices as track has changed
2542 int playlist_next(int steps)
2544 struct playlist_info* playlist = &current_playlist;
2545 int index;
2547 if ( (steps > 0)
2548 #ifdef AB_REPEAT_ENABLE
2549 && (global_settings.repeat_mode != REPEAT_AB)
2550 #endif
2551 && (global_settings.repeat_mode != REPEAT_ONE) )
2553 int i, j;
2555 /* We need to delete all the queued songs */
2556 for (i=0, j=steps; i<j; i++)
2558 index = get_next_index(playlist, i, -1);
2560 if (playlist->indices[index] & PLAYLIST_QUEUE_MASK)
2562 remove_track_from_playlist(playlist, index, true);
2563 steps--; /* one less track */
2568 index = get_next_index(playlist, steps, -1);
2570 if (index < 0)
2572 /* end of playlist... or is it */
2573 if (global_settings.repeat_mode == REPEAT_SHUFFLE &&
2574 playlist->amount > 1)
2576 /* Repeat shuffle mode. Re-shuffle playlist and resume play */
2577 playlist->first_index = global_status.resume_first_index = 0;
2578 sort_playlist(playlist, false, false);
2579 randomise_playlist(playlist, current_tick, false, true);
2580 #if CONFIG_CODEC != SWCODEC
2581 playlist_start(0, 0);
2582 #endif
2583 playlist->index = 0;
2584 index = 0;
2586 else if (playlist->in_ram && global_settings.next_folder)
2588 index = create_and_play_dir(steps, true);
2590 if (index >= 0)
2592 playlist->index = index;
2596 return index;
2599 playlist->index = index;
2601 if (playlist->last_insert_pos >= 0 && steps > 0)
2603 /* check to see if we've gone beyond the last inserted track */
2604 int cur = rotate_index(playlist, index);
2605 int last_pos = rotate_index(playlist, playlist->last_insert_pos);
2607 if (cur > last_pos)
2609 /* reset last inserted track */
2610 playlist->last_insert_pos = -1;
2612 if (playlist->control_fd >= 0)
2614 int result = update_control(playlist, PLAYLIST_COMMAND_RESET,
2615 -1, -1, NULL, NULL, NULL);
2617 if (result < 0)
2618 return result;
2620 sync_control(playlist, false);
2625 return index;
2628 /* try playing next or previous folder */
2629 bool playlist_next_dir(int direction)
2631 /* not to mess up real playlists */
2632 if(!current_playlist.in_ram)
2633 return false;
2635 return create_and_play_dir(direction, false) >= 0;
2638 /* Get resume info for current playing song. If return value is -1 then
2639 settings shouldn't be saved. */
2640 int playlist_get_resume_info(int *resume_index)
2642 struct playlist_info* playlist = &current_playlist;
2644 *resume_index = playlist->index;
2646 return 0;
2649 /* Update resume info for current playing song. Returns -1 on error. */
2650 int playlist_update_resume_info(const struct mp3entry* id3)
2652 struct playlist_info* playlist = &current_playlist;
2654 if (id3)
2656 if (global_status.resume_index != playlist->index ||
2657 global_status.resume_offset != id3->offset)
2659 global_status.resume_index = playlist->index;
2660 global_status.resume_offset = id3->offset;
2661 status_save();
2664 else
2666 global_status.resume_index = -1;
2667 global_status.resume_offset = -1;
2668 status_save();
2671 return 0;
2674 /* Returns index of current playing track for display purposes. This value
2675 should not be used for resume purposes as it doesn't represent the actual
2676 index into the playlist */
2677 int playlist_get_display_index(void)
2679 struct playlist_info* playlist = &current_playlist;
2681 /* first_index should always be index 0 for display purposes */
2682 int index = rotate_index(playlist, playlist->index);
2684 return (index+1);
2687 /* returns number of tracks in current playlist */
2688 int playlist_amount(void)
2690 return playlist_amount_ex(NULL);
2694 * Create a new playlist If playlist is not NULL then we're loading a
2695 * playlist off disk for viewing/editing. The index_buffer is used to store
2696 * playlist indices (required for and only used if !current playlist). The
2697 * temp_buffer (if not NULL) is used as a scratchpad when loading indices.
2699 int playlist_create_ex(struct playlist_info* playlist,
2700 const char* dir, const char* file,
2701 void* index_buffer, int index_buffer_size,
2702 void* temp_buffer, int temp_buffer_size)
2704 if (!playlist)
2705 playlist = &current_playlist;
2706 else
2708 /* Initialize playlist structure */
2709 int r = rand() % 10;
2710 playlist->current = false;
2712 /* Use random name for control file */
2713 snprintf(playlist->control_filename, sizeof(playlist->control_filename),
2714 "%s.%d", PLAYLIST_CONTROL_FILE, r);
2715 playlist->fd = -1;
2716 playlist->control_fd = -1;
2718 if (index_buffer)
2720 int num_indices = index_buffer_size / sizeof(int);
2722 #ifdef HAVE_DIRCACHE
2723 num_indices /= 2;
2724 #endif
2725 if (num_indices > global_settings.max_files_in_playlist)
2726 num_indices = global_settings.max_files_in_playlist;
2728 playlist->max_playlist_size = num_indices;
2729 playlist->indices = index_buffer;
2730 #ifdef HAVE_DIRCACHE
2731 playlist->filenames = (const struct dircache_entry **)
2732 &playlist->indices[num_indices];
2733 #endif
2735 else
2737 playlist->max_playlist_size = current_playlist.max_playlist_size;
2738 playlist->indices = current_playlist.indices;
2739 #ifdef HAVE_DIRCACHE
2740 playlist->filenames = current_playlist.filenames;
2741 #endif
2744 playlist->buffer_size = 0;
2745 playlist->buffer = NULL;
2746 mutex_init(&playlist->control_mutex);
2749 new_playlist(playlist, dir, file);
2751 if (file)
2752 /* load the playlist file */
2753 add_indices_to_playlist(playlist, temp_buffer, temp_buffer_size);
2755 return 0;
2759 * Set the specified playlist as the current.
2760 * NOTE: You will get undefined behaviour if something is already playing so
2761 * remember to stop before calling this. Also, this call will
2762 * effectively close your playlist, making it unusable.
2764 int playlist_set_current(struct playlist_info* playlist)
2766 if (!playlist || (check_control(playlist) < 0))
2767 return -1;
2769 empty_playlist(&current_playlist, false);
2771 strncpy(current_playlist.filename, playlist->filename,
2772 sizeof(current_playlist.filename));
2774 current_playlist.utf8 = playlist->utf8;
2775 current_playlist.fd = playlist->fd;
2777 close(playlist->control_fd);
2778 close(current_playlist.control_fd);
2779 remove(current_playlist.control_filename);
2780 if (rename(playlist->control_filename,
2781 current_playlist.control_filename) < 0)
2782 return -1;
2783 current_playlist.control_fd = open(current_playlist.control_filename,
2784 O_RDWR);
2785 if (current_playlist.control_fd < 0)
2786 return -1;
2787 current_playlist.control_created = true;
2789 current_playlist.dirlen = playlist->dirlen;
2791 if (playlist->indices && playlist->indices != current_playlist.indices)
2793 memcpy(current_playlist.indices, playlist->indices,
2794 playlist->max_playlist_size*sizeof(int));
2795 #ifdef HAVE_DIRCACHE
2796 memcpy(current_playlist.filenames, playlist->filenames,
2797 playlist->max_playlist_size*sizeof(int));
2798 #endif
2801 current_playlist.first_index = playlist->first_index;
2802 current_playlist.amount = playlist->amount;
2803 current_playlist.last_insert_pos = playlist->last_insert_pos;
2804 current_playlist.seed = playlist->seed;
2805 current_playlist.shuffle_modified = playlist->shuffle_modified;
2806 current_playlist.deleted = playlist->deleted;
2807 current_playlist.num_inserted_tracks = playlist->num_inserted_tracks;
2809 memcpy(current_playlist.control_cache, playlist->control_cache,
2810 sizeof(current_playlist.control_cache));
2811 current_playlist.num_cached = playlist->num_cached;
2812 current_playlist.pending_control_sync = playlist->pending_control_sync;
2814 return 0;
2818 * Close files and delete control file for non-current playlist.
2820 void playlist_close(struct playlist_info* playlist)
2822 if (!playlist)
2823 return;
2825 if (playlist->fd >= 0)
2826 close(playlist->fd);
2828 if (playlist->control_fd >= 0)
2829 close(playlist->control_fd);
2831 if (playlist->control_created)
2832 remove(playlist->control_filename);
2835 void playlist_sync(struct playlist_info* playlist)
2837 if (!playlist)
2838 playlist = &current_playlist;
2840 sync_control(playlist, false);
2841 if ((audio_status() & AUDIO_STATUS_PLAY) && playlist->started)
2842 audio_flush_and_reload_tracks();
2844 #ifdef HAVE_DIRCACHE
2845 queue_post(&playlist_queue, PLAYLIST_LOAD_POINTERS, 0);
2846 #endif
2850 * Insert track into playlist at specified position (or one of the special
2851 * positions). Returns position where track was inserted or -1 if error.
2853 int playlist_insert_track(struct playlist_info* playlist, const char *filename,
2854 int position, bool queue, bool sync)
2856 int result;
2858 if (!playlist)
2859 playlist = &current_playlist;
2861 if (check_control(playlist) < 0)
2863 splash(HZ*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR));
2864 return -1;
2867 result = add_track_to_playlist(playlist, filename, position, queue, -1);
2869 /* Check if we want manually sync later. For example when adding
2870 * bunch of files from tagcache, syncing after every file wouldn't be
2871 * a good thing to do. */
2872 if (sync && result >= 0)
2873 playlist_sync(playlist);
2875 return result;
2879 * Insert all tracks from specified directory into playlist.
2881 int playlist_insert_directory(struct playlist_info* playlist,
2882 const char *dirname, int position, bool queue,
2883 bool recurse)
2885 int result;
2886 unsigned char *count_str;
2887 struct directory_search_context context;
2889 if (!playlist)
2890 playlist = &current_playlist;
2892 if (check_control(playlist) < 0)
2894 splash(HZ*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR));
2895 return -1;
2898 if (position == PLAYLIST_REPLACE)
2900 if (playlist_remove_all_tracks(playlist) == 0)
2901 position = PLAYLIST_INSERT_LAST;
2902 else
2903 return -1;
2906 if (queue)
2907 count_str = ID2P(LANG_PLAYLIST_QUEUE_COUNT);
2908 else
2909 count_str = ID2P(LANG_PLAYLIST_INSERT_COUNT);
2911 display_playlist_count(0, count_str, false);
2913 context.playlist = playlist;
2914 context.position = position;
2915 context.queue = queue;
2916 context.count = 0;
2918 cpu_boost(true);
2920 result = playlist_directory_tracksearch(dirname, recurse,
2921 directory_search_callback, &context);
2923 sync_control(playlist, false);
2925 cpu_boost(false);
2927 display_playlist_count(context.count, count_str, true);
2929 if ((audio_status() & AUDIO_STATUS_PLAY) && playlist->started)
2930 audio_flush_and_reload_tracks();
2932 #ifdef HAVE_DIRCACHE
2933 queue_post(&playlist_queue, PLAYLIST_LOAD_POINTERS, 0);
2934 #endif
2936 return result;
2940 * Insert all tracks from specified playlist into dynamic playlist.
2942 int playlist_insert_playlist(struct playlist_info* playlist, const char *filename,
2943 int position, bool queue)
2945 int fd;
2946 int max;
2947 char *temp_ptr;
2948 const char *dir;
2949 unsigned char *count_str;
2950 char temp_buf[MAX_PATH+1];
2951 char trackname[MAX_PATH+1];
2952 int count = 0;
2953 int result = 0;
2954 bool utf8 = is_m3u8(filename);
2956 if (!playlist)
2957 playlist = &current_playlist;
2959 if (check_control(playlist) < 0)
2961 splash(HZ*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR));
2962 return -1;
2965 fd = open_utf8(filename, O_RDONLY);
2966 if (fd < 0)
2968 splash(HZ*2, ID2P(LANG_PLAYLIST_ACCESS_ERROR));
2969 return -1;
2972 /* we need the directory name for formatting purposes */
2973 dir = filename;
2975 temp_ptr = strrchr(filename+1,'/');
2976 if (temp_ptr)
2977 *temp_ptr = 0;
2978 else
2979 dir = "/";
2981 if (queue)
2982 count_str = ID2P(LANG_PLAYLIST_QUEUE_COUNT);
2983 else
2984 count_str = ID2P(LANG_PLAYLIST_INSERT_COUNT);
2986 display_playlist_count(count, count_str, false);
2988 if (position == PLAYLIST_REPLACE)
2990 if (playlist_remove_all_tracks(playlist) == 0)
2991 position = PLAYLIST_INSERT_LAST;
2992 else return -1;
2995 cpu_boost(true);
2997 while ((max = read_line(fd, temp_buf, sizeof(temp_buf))) > 0)
2999 /* user abort */
3000 if (action_userabort(TIMEOUT_NOBLOCK))
3001 break;
3003 if (temp_buf[0] != '#' && temp_buf[0] != '\0')
3005 int insert_pos;
3007 if (!utf8)
3009 /* Use trackname as a temporay buffer. Note that trackname must
3010 * be as large as temp_buf.
3012 max = convert_m3u(temp_buf, max, sizeof(temp_buf), trackname);
3015 /* we need to format so that relative paths are correctly
3016 handled */
3017 if (format_track_path(trackname, temp_buf, sizeof(trackname), max,
3018 dir) < 0)
3020 result = -1;
3021 break;
3024 insert_pos = add_track_to_playlist(playlist, trackname, position,
3025 queue, -1);
3027 if (insert_pos < 0)
3029 result = -1;
3030 break;
3033 /* Make sure tracks are inserted in correct order if user
3034 requests INSERT_FIRST */
3035 if (position == PLAYLIST_INSERT_FIRST || position >= 0)
3036 position = insert_pos + 1;
3038 count++;
3040 if ((count%PLAYLIST_DISPLAY_COUNT) == 0)
3042 display_playlist_count(count, count_str, false);
3044 if (count == PLAYLIST_DISPLAY_COUNT &&
3045 (audio_status() & AUDIO_STATUS_PLAY) &&
3046 playlist->started)
3047 audio_flush_and_reload_tracks();
3051 /* let the other threads work */
3052 yield();
3055 close(fd);
3057 if (temp_ptr)
3058 *temp_ptr = '/';
3060 sync_control(playlist, false);
3062 cpu_boost(false);
3064 display_playlist_count(count, count_str, true);
3066 if ((audio_status() & AUDIO_STATUS_PLAY) && playlist->started)
3067 audio_flush_and_reload_tracks();
3069 #ifdef HAVE_DIRCACHE
3070 queue_post(&playlist_queue, PLAYLIST_LOAD_POINTERS, 0);
3071 #endif
3073 return result;
3077 * Delete track at specified index. If index is PLAYLIST_DELETE_CURRENT then
3078 * we want to delete the current playing track.
3080 int playlist_delete(struct playlist_info* playlist, int index)
3082 int result = 0;
3084 if (!playlist)
3085 playlist = &current_playlist;
3087 if (check_control(playlist) < 0)
3089 splash(HZ*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR));
3090 return -1;
3093 if (index == PLAYLIST_DELETE_CURRENT)
3094 index = playlist->index;
3096 result = remove_track_from_playlist(playlist, index, true);
3098 if (result != -1 && (audio_status() & AUDIO_STATUS_PLAY) &&
3099 playlist->started)
3100 audio_flush_and_reload_tracks();
3102 return result;
3106 * Move track at index to new_index. Tracks between the two are shifted
3107 * appropriately. Returns 0 on success and -1 on failure.
3109 int playlist_move(struct playlist_info* playlist, int index, int new_index)
3111 int result;
3112 int seek;
3113 bool control_file;
3114 bool queue;
3115 bool current = false;
3116 int r;
3117 char filename[MAX_PATH];
3119 if (!playlist)
3120 playlist = &current_playlist;
3122 if (check_control(playlist) < 0)
3124 splash(HZ*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR));
3125 return -1;
3128 if (index == new_index)
3129 return -1;
3131 if (index == playlist->index)
3132 /* Moving the current track */
3133 current = true;
3135 control_file = playlist->indices[index] & PLAYLIST_INSERT_TYPE_MASK;
3136 queue = playlist->indices[index] & PLAYLIST_QUEUE_MASK;
3137 seek = playlist->indices[index] & PLAYLIST_SEEK_MASK;
3139 if (get_filename(playlist, index, seek, control_file, filename,
3140 sizeof(filename)) < 0)
3141 return -1;
3143 /* Delete track from original position */
3144 result = remove_track_from_playlist(playlist, index, true);
3146 if (result != -1)
3148 /* We want to insert the track at the position that was specified by
3149 new_index. This may be different then new_index because of the
3150 shifting that occurred after the delete */
3151 r = rotate_index(playlist, new_index);
3153 if (r == 0)
3154 /* First index */
3155 new_index = PLAYLIST_PREPEND;
3156 else if (r == playlist->amount)
3157 /* Append */
3158 new_index = PLAYLIST_INSERT_LAST;
3159 else
3160 /* Calculate index of desired position */
3161 new_index = (r+playlist->first_index)%playlist->amount;
3163 result = add_track_to_playlist(playlist, filename, new_index, queue,
3164 -1);
3166 if (result != -1)
3168 if (current)
3170 /* Moved the current track */
3171 switch (new_index)
3173 case PLAYLIST_PREPEND:
3174 playlist->index = playlist->first_index;
3175 break;
3176 case PLAYLIST_INSERT_LAST:
3177 playlist->index = playlist->first_index - 1;
3178 if (playlist->index < 0)
3179 playlist->index += playlist->amount;
3180 break;
3181 default:
3182 playlist->index = new_index;
3183 break;
3187 if ((audio_status() & AUDIO_STATUS_PLAY) && playlist->started)
3188 audio_flush_and_reload_tracks();
3192 #ifdef HAVE_DIRCACHE
3193 queue_post(&playlist_queue, PLAYLIST_LOAD_POINTERS, 0);
3194 #endif
3196 return result;
3199 /* shuffle currently playing playlist */
3200 int playlist_randomise(struct playlist_info* playlist, unsigned int seed,
3201 bool start_current)
3203 int result;
3205 if (!playlist)
3206 playlist = &current_playlist;
3208 check_control(playlist);
3210 result = randomise_playlist(playlist, seed, start_current, true);
3212 if (result != -1 && (audio_status() & AUDIO_STATUS_PLAY) &&
3213 playlist->started)
3214 audio_flush_and_reload_tracks();
3216 return result;
3219 /* sort currently playing playlist */
3220 int playlist_sort(struct playlist_info* playlist, bool start_current)
3222 int result;
3224 if (!playlist)
3225 playlist = &current_playlist;
3227 check_control(playlist);
3229 result = sort_playlist(playlist, start_current, true);
3231 if (result != -1 && (audio_status() & AUDIO_STATUS_PLAY) &&
3232 playlist->started)
3233 audio_flush_and_reload_tracks();
3235 return result;
3238 /* returns true if playlist has been modified */
3239 bool playlist_modified(const struct playlist_info* playlist)
3241 if (!playlist)
3242 playlist = &current_playlist;
3244 if (playlist->shuffle_modified ||
3245 playlist->deleted ||
3246 playlist->num_inserted_tracks > 0)
3247 return true;
3249 return false;
3252 /* returns index of first track in playlist */
3253 int playlist_get_first_index(const struct playlist_info* playlist)
3255 if (!playlist)
3256 playlist = &current_playlist;
3258 return playlist->first_index;
3261 /* returns shuffle seed of playlist */
3262 int playlist_get_seed(const struct playlist_info* playlist)
3264 if (!playlist)
3265 playlist = &current_playlist;
3267 return playlist->seed;
3270 /* returns number of tracks in playlist (includes queued/inserted tracks) */
3271 int playlist_amount_ex(const struct playlist_info* playlist)
3273 if (!playlist)
3274 playlist = &current_playlist;
3276 return playlist->amount;
3279 /* returns full path of playlist (minus extension) */
3280 char *playlist_name(const struct playlist_info* playlist, char *buf,
3281 int buf_size)
3283 char *sep;
3285 if (!playlist)
3286 playlist = &current_playlist;
3288 snprintf(buf, buf_size, "%s", playlist->filename+playlist->dirlen);
3290 if (!buf[0])
3291 return NULL;
3293 /* Remove extension */
3294 sep = strrchr(buf, '.');
3295 if (sep)
3296 *sep = 0;
3298 return buf;
3301 /* returns the playlist filename */
3302 char *playlist_get_name(const struct playlist_info* playlist, char *buf,
3303 int buf_size)
3305 if (!playlist)
3306 playlist = &current_playlist;
3308 snprintf(buf, buf_size, "%s", playlist->filename);
3310 if (!buf[0])
3311 return NULL;
3313 return buf;
3316 /* Fills info structure with information about track at specified index.
3317 Returns 0 on success and -1 on failure */
3318 int playlist_get_track_info(struct playlist_info* playlist, int index,
3319 struct playlist_track_info* info)
3321 int seek;
3322 bool control_file;
3324 if (!playlist)
3325 playlist = &current_playlist;
3327 if (index < 0 || index >= playlist->amount)
3328 return -1;
3330 control_file = playlist->indices[index] & PLAYLIST_INSERT_TYPE_MASK;
3331 seek = playlist->indices[index] & PLAYLIST_SEEK_MASK;
3333 if (get_filename(playlist, index, seek, control_file, info->filename,
3334 sizeof(info->filename)) < 0)
3335 return -1;
3337 info->attr = 0;
3339 if (control_file)
3341 if (playlist->indices[index] & PLAYLIST_QUEUE_MASK)
3342 info->attr |= PLAYLIST_ATTR_QUEUED;
3343 else
3344 info->attr |= PLAYLIST_ATTR_INSERTED;
3348 if (playlist->indices[index] & PLAYLIST_SKIPPED)
3349 info->attr |= PLAYLIST_ATTR_SKIPPED;
3351 info->index = index;
3352 info->display_index = rotate_index(playlist, index) + 1;
3354 return 0;
3357 /* save the current dynamic playlist to specified file */
3358 int playlist_save(struct playlist_info* playlist, char *filename)
3360 int fd;
3361 int i, index;
3362 int count = 0;
3363 char path[MAX_PATH+1];
3364 char tmp_buf[MAX_PATH+1];
3365 int result = 0;
3366 bool overwrite_current = false;
3367 int* index_buf = NULL;
3369 if (!playlist)
3370 playlist = &current_playlist;
3372 if (playlist->amount <= 0)
3373 return -1;
3375 /* use current working directory as base for pathname */
3376 if (format_track_path(path, filename, sizeof(tmp_buf),
3377 strlen(filename)+1, getcwd(NULL, -1)) < 0)
3378 return -1;
3380 if (!strncmp(playlist->filename, path, strlen(path)))
3382 /* Attempting to overwrite current playlist file.*/
3384 if (playlist->buffer_size < (int)(playlist->amount * sizeof(int)))
3386 /* not enough buffer space to store updated indices */
3387 splash(HZ*2, ID2P(LANG_PLAYLIST_ACCESS_ERROR));
3388 return -1;
3391 /* in_ram buffer is unused for m3u files so we'll use for storing
3392 updated indices */
3393 index_buf = (int*)playlist->buffer;
3395 /* use temporary pathname */
3396 snprintf(path, sizeof(path), "%s_temp", playlist->filename);
3397 overwrite_current = true;
3400 if (is_m3u8(path))
3402 fd = open_utf8(path, O_CREAT|O_WRONLY|O_TRUNC);
3404 else
3406 /* some applications require a BOM to read the file properly */
3407 fd = open(path, O_CREAT|O_WRONLY|O_TRUNC);
3409 if (fd < 0)
3411 splash(HZ*2, ID2P(LANG_PLAYLIST_ACCESS_ERROR));
3412 return -1;
3415 display_playlist_count(count, ID2P(LANG_PLAYLIST_SAVE_COUNT), false);
3417 cpu_boost(true);
3419 index = playlist->first_index;
3420 for (i=0; i<playlist->amount; i++)
3422 bool control_file;
3423 bool queue;
3424 int seek;
3426 /* user abort */
3427 if (action_userabort(TIMEOUT_NOBLOCK))
3429 result = -1;
3430 break;
3433 control_file = playlist->indices[index] & PLAYLIST_INSERT_TYPE_MASK;
3434 queue = playlist->indices[index] & PLAYLIST_QUEUE_MASK;
3435 seek = playlist->indices[index] & PLAYLIST_SEEK_MASK;
3437 /* Don't save queued files */
3438 if (!queue)
3440 if (get_filename(playlist, index, seek, control_file, tmp_buf,
3441 MAX_PATH+1) < 0)
3443 result = -1;
3444 break;
3447 if (overwrite_current)
3448 index_buf[count] = lseek(fd, 0, SEEK_CUR);
3450 if (fdprintf(fd, "%s\n", tmp_buf) < 0)
3452 splash(HZ*2, ID2P(LANG_PLAYLIST_ACCESS_ERROR));
3453 result = -1;
3454 break;
3457 count++;
3459 if ((count % PLAYLIST_DISPLAY_COUNT) == 0)
3460 display_playlist_count(count, ID2P(LANG_PLAYLIST_SAVE_COUNT),
3461 false);
3463 yield();
3466 index = (index+1)%playlist->amount;
3469 display_playlist_count(count, ID2P(LANG_PLAYLIST_SAVE_COUNT), true);
3471 close(fd);
3473 if (overwrite_current && result >= 0)
3475 result = -1;
3477 mutex_lock(&playlist->control_mutex);
3479 /* Replace the current playlist with the new one and update indices */
3480 close(playlist->fd);
3481 if (remove(playlist->filename) >= 0)
3483 if (rename(path, playlist->filename) >= 0)
3485 playlist->fd = open_utf8(playlist->filename, O_RDONLY);
3486 if (playlist->fd >= 0)
3488 index = playlist->first_index;
3489 for (i=0, count=0; i<playlist->amount; i++)
3491 if (!(playlist->indices[index] & PLAYLIST_QUEUE_MASK))
3493 playlist->indices[index] = index_buf[count];
3494 count++;
3496 index = (index+1)%playlist->amount;
3499 /* we need to recreate control because inserted tracks are
3500 now part of the playlist and shuffle has been
3501 invalidated */
3502 result = recreate_control(playlist);
3507 mutex_unlock(&playlist->control_mutex);
3511 cpu_boost(false);
3513 return result;
3517 * Search specified directory for tracks and notify via callback. May be
3518 * called recursively.
3520 int playlist_directory_tracksearch(const char* dirname, bool recurse,
3521 int (*callback)(char*, void*),
3522 void* context)
3524 char buf[MAX_PATH+1];
3525 int result = 0;
3526 int num_files = 0;
3527 int i;
3528 struct entry *files;
3529 struct tree_context* tc = tree_get_context();
3530 int old_dirfilter = *(tc->dirfilter);
3532 if (!callback)
3533 return -1;
3535 /* use the tree browser dircache to load files */
3536 *(tc->dirfilter) = SHOW_ALL;
3538 if (ft_load(tc, dirname) < 0)
3540 splash(HZ*2, ID2P(LANG_PLAYLIST_DIRECTORY_ACCESS_ERROR));
3541 *(tc->dirfilter) = old_dirfilter;
3542 return -1;
3545 files = (struct entry*) tc->dircache;
3546 num_files = tc->filesindir;
3548 /* we've overwritten the dircache so tree browser will need to be
3549 reloaded */
3550 reload_directory();
3552 for (i=0; i<num_files; i++)
3554 /* user abort */
3555 if (action_userabort(TIMEOUT_NOBLOCK))
3557 result = -1;
3558 break;
3561 if (files[i].attr & ATTR_DIRECTORY)
3563 if (recurse)
3565 /* recursively add directories */
3566 snprintf(buf, sizeof(buf), "%s/%s", dirname, files[i].name);
3567 result = playlist_directory_tracksearch(buf, recurse,
3568 callback, context);
3569 if (result < 0)
3570 break;
3572 /* we now need to reload our current directory */
3573 if(ft_load(tc, dirname) < 0)
3575 result = -1;
3576 break;
3579 files = (struct entry*) tc->dircache;
3580 num_files = tc->filesindir;
3581 if (!num_files)
3583 result = -1;
3584 break;
3587 else
3588 continue;
3590 else if ((files[i].attr & FILE_ATTR_MASK) == FILE_ATTR_AUDIO)
3592 snprintf(buf, sizeof(buf), "%s/%s", dirname, files[i].name);
3594 if (callback(buf, context) != 0)
3596 result = -1;
3597 break;
3600 /* let the other threads work */
3601 yield();
3605 /* restore dirfilter */
3606 *(tc->dirfilter) = old_dirfilter;
3608 return result;