Make TTS options actually work (fix variable name).
[kugel-rb.git] / apps / playlist.c
blob133820ebc4a923deef9c3ec8d3f52c5285066f37
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
99 #include "system.h"
101 #include "lang.h"
102 #include "talk.h"
103 #include "splash.h"
104 #include "rbunicode.h"
105 #include "root_menu.h"
107 #define PLAYLIST_CONTROL_FILE ROCKBOX_DIR "/.playlist_control"
108 #define PLAYLIST_CONTROL_FILE_VERSION 2
111 Each playlist index has a flag associated with it which identifies what
112 type of track it is. These flags are stored in the 4 high order bits of
113 the index.
115 NOTE: This limits the playlist file size to a max of 256M.
117 Bits 31-30:
118 00 = Playlist track
119 01 = Track was prepended into playlist
120 10 = Track was inserted into playlist
121 11 = Track was appended into playlist
122 Bit 29:
123 0 = Added track
124 1 = Queued track
125 Bit 28:
126 0 = Track entry is valid
127 1 = Track does not exist on disk and should be skipped
129 #define PLAYLIST_SEEK_MASK 0x0FFFFFFF
130 #define PLAYLIST_INSERT_TYPE_MASK 0xC0000000
131 #define PLAYLIST_QUEUE_MASK 0x20000000
133 #define PLAYLIST_INSERT_TYPE_PREPEND 0x40000000
134 #define PLAYLIST_INSERT_TYPE_INSERT 0x80000000
135 #define PLAYLIST_INSERT_TYPE_APPEND 0xC0000000
137 #define PLAYLIST_QUEUED 0x20000000
138 #define PLAYLIST_SKIPPED 0x10000000
140 struct directory_search_context {
141 struct playlist_info* playlist;
142 int position;
143 bool queue;
144 int count;
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, const char *subdir, bool recurse);
182 static int format_track_path(char *dest, char *src, int buf_length, int max,
183 const 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
202 /* Check if the filename suggests M3U or M3U8 format. */
203 static bool is_m3u8(const char* filename)
205 int len = strlen(filename);
207 /* Default to M3U8 unless explicitly told otherwise. */
208 return !(len > 4 && strcasecmp(&filename[len - 4], ".m3u") == 0);
212 /* Convert a filename in an M3U playlist to UTF-8.
214 * buf - the filename to convert; can contain more than one line from the
215 * playlist.
216 * buf_len - amount of buf that is used.
217 * buf_max - total size of buf.
218 * temp - temporary conversion buffer, at least buf_max bytes.
220 * Returns the length of the converted filename.
222 static int convert_m3u(char* buf, int buf_len, int buf_max, char* temp)
224 int i = 0;
225 char* dest;
227 /* Locate EOL. */
228 while ((buf[i] != '\n') && (buf[i] != '\r') && (i < buf_len))
230 i++;
233 /* Work back killing white space. */
234 while ((i > 0) && isspace(buf[i - 1]))
236 i--;
239 buf_len = i;
240 dest = temp;
242 /* Convert char by char, so as to not overflow temp (iso_decode should
243 * preferably handle this). No more than 4 bytes should be generated for
244 * each input char.
246 for (i = 0; i < buf_len && dest < (temp + buf_max - 4); i++)
248 dest = iso_decode(&buf[i], dest, -1, 1);
251 *dest = 0;
252 strcpy(buf, temp);
253 return dest - temp;
257 * remove any files and indices associated with the playlist
259 static void empty_playlist(struct playlist_info* playlist, bool resume)
261 playlist->filename[0] = '\0';
262 playlist->utf8 = true;
264 if(playlist->fd >= 0)
265 /* If there is an already open playlist, close it. */
266 close(playlist->fd);
267 playlist->fd = -1;
269 if(playlist->control_fd >= 0)
270 close(playlist->control_fd);
271 playlist->control_fd = -1;
272 playlist->control_created = false;
274 playlist->in_ram = false;
276 if (playlist->buffer)
277 playlist->buffer[0] = 0;
279 playlist->buffer_end_pos = 0;
281 playlist->index = 0;
282 playlist->first_index = 0;
283 playlist->amount = 0;
284 playlist->last_insert_pos = -1;
285 playlist->seed = 0;
286 playlist->shuffle_modified = false;
287 playlist->deleted = false;
288 playlist->num_inserted_tracks = 0;
289 playlist->started = false;
291 playlist->num_cached = 0;
292 playlist->pending_control_sync = false;
294 if (!resume && playlist->current)
296 /* start with fresh playlist control file when starting new
297 playlist */
298 create_control(playlist);
303 * Initialize a new playlist for viewing/editing/playing. dir is the
304 * directory where the playlist is located and file is the filename.
306 static void new_playlist(struct playlist_info* playlist, const char *dir,
307 const char *file)
309 const char *fileused = file;
310 const char *dirused = dir;
311 empty_playlist(playlist, false);
313 if (!fileused)
315 fileused = "";
317 if (dirused && playlist->current) /* !current cannot be in_ram */
318 playlist->in_ram = true;
319 else
320 dirused = ""; /* empty playlist */
323 update_playlist_filename(playlist, dirused, fileused);
325 if (playlist->control_fd >= 0)
327 update_control(playlist, PLAYLIST_COMMAND_PLAYLIST,
328 PLAYLIST_CONTROL_FILE_VERSION, -1, dirused, fileused, NULL);
329 sync_control(playlist, false);
334 * create control file for playlist
336 static void create_control(struct playlist_info* playlist)
338 playlist->control_fd = open(playlist->control_filename,
339 O_CREAT|O_RDWR|O_TRUNC);
340 if (playlist->control_fd < 0)
342 if (check_rockboxdir())
344 cond_talk_ids_fq(LANG_PLAYLIST_CONTROL_ACCESS_ERROR);
345 splashf(HZ*2, (unsigned char *)"%s (%d)",
346 str(LANG_PLAYLIST_CONTROL_ACCESS_ERROR),
347 playlist->control_fd);
349 playlist->control_created = false;
351 else
353 playlist->control_created = true;
358 * validate the control file. This may include creating/initializing it if
359 * necessary;
361 static int check_control(struct playlist_info* playlist)
363 if (!playlist->control_created)
365 create_control(playlist);
367 if (playlist->control_fd >= 0)
369 char* dir = playlist->filename;
370 char* file = playlist->filename+playlist->dirlen;
371 char c = playlist->filename[playlist->dirlen-1];
373 playlist->filename[playlist->dirlen-1] = '\0';
375 update_control(playlist, PLAYLIST_COMMAND_PLAYLIST,
376 PLAYLIST_CONTROL_FILE_VERSION, -1, dir, file, NULL);
377 sync_control(playlist, false);
378 playlist->filename[playlist->dirlen-1] = c;
382 if (playlist->control_fd < 0)
383 return -1;
385 return 0;
389 * recreate the control file based on current playlist entries
391 static int recreate_control(struct playlist_info* playlist)
393 char temp_file[MAX_PATH+1];
394 int temp_fd = -1;
395 int i;
396 int result = 0;
398 if(playlist->control_fd >= 0)
400 char* dir = playlist->filename;
401 char* file = playlist->filename+playlist->dirlen;
402 char c = playlist->filename[playlist->dirlen-1];
404 close(playlist->control_fd);
406 snprintf(temp_file, sizeof(temp_file), "%s_temp",
407 playlist->control_filename);
409 if (rename(playlist->control_filename, temp_file) < 0)
410 return -1;
412 temp_fd = open(temp_file, O_RDONLY);
413 if (temp_fd < 0)
414 return -1;
416 playlist->control_fd = open(playlist->control_filename,
417 O_CREAT|O_RDWR|O_TRUNC);
418 if (playlist->control_fd < 0)
419 return -1;
421 playlist->filename[playlist->dirlen-1] = '\0';
423 /* cannot call update_control() because of mutex */
424 result = fdprintf(playlist->control_fd, "P:%d:%s:%s\n",
425 PLAYLIST_CONTROL_FILE_VERSION, dir, file);
427 playlist->filename[playlist->dirlen-1] = c;
429 if (result < 0)
431 close(temp_fd);
432 return result;
436 playlist->seed = 0;
437 playlist->shuffle_modified = false;
438 playlist->deleted = false;
439 playlist->num_inserted_tracks = 0;
441 for (i=0; i<playlist->amount; i++)
443 if (playlist->indices[i] & PLAYLIST_INSERT_TYPE_MASK)
445 bool queue = playlist->indices[i] & PLAYLIST_QUEUE_MASK;
446 char inserted_file[MAX_PATH+1];
448 lseek(temp_fd, playlist->indices[i] & PLAYLIST_SEEK_MASK,
449 SEEK_SET);
450 read_line(temp_fd, inserted_file, sizeof(inserted_file));
452 result = fdprintf(playlist->control_fd, "%c:%d:%d:",
453 queue?'Q':'A', i, playlist->last_insert_pos);
454 if (result > 0)
456 /* save the position in file where name is written */
457 int seek_pos = lseek(playlist->control_fd, 0, SEEK_CUR);
459 result = fdprintf(playlist->control_fd, "%s\n",
460 inserted_file);
462 playlist->indices[i] =
463 (playlist->indices[i] & ~PLAYLIST_SEEK_MASK) | seek_pos;
466 if (result < 0)
467 break;
469 playlist->num_inserted_tracks++;
473 close(temp_fd);
474 remove(temp_file);
475 fsync(playlist->control_fd);
477 if (result < 0)
478 return result;
480 return 0;
484 * store directory and name of playlist file
486 static void update_playlist_filename(struct playlist_info* playlist,
487 const char *dir, const char *file)
489 char *sep="";
490 int dirlen = strlen(dir);
492 playlist->utf8 = is_m3u8(file);
494 /* If the dir does not end in trailing slash, we use a separator.
495 Otherwise we don't. */
496 if('/' != dir[dirlen-1])
498 sep="/";
499 dirlen++;
502 playlist->dirlen = dirlen;
504 snprintf(playlist->filename, sizeof(playlist->filename),
505 "%s%s%s", dir, sep, file);
509 * calculate track offsets within a playlist file
511 static int add_indices_to_playlist(struct playlist_info* playlist,
512 char* buffer, size_t buflen)
514 unsigned int nread;
515 unsigned int i = 0;
516 unsigned int count = 0;
517 bool store_index;
518 unsigned char *p;
519 int result = 0;
521 if(-1 == playlist->fd)
522 playlist->fd = open_utf8(playlist->filename, O_RDONLY);
523 if(playlist->fd < 0)
524 return -1; /* failure */
525 if((i = lseek(playlist->fd, 0, SEEK_CUR)) > 0)
526 playlist->utf8 = true; /* Override any earlier indication. */
528 splash(0, ID2P(LANG_WAIT));
530 if (!buffer)
532 /* use mp3 buffer for maximum load speed */
533 audio_stop();
534 #if CONFIG_CODEC != SWCODEC
535 talk_buffer_steal(); /* we use the mp3 buffer, need to tell */
536 buflen = (audiobufend - audiobuf);
537 buffer = (char *)audiobuf;
538 #else
539 buffer = (char *)audio_get_buffer(false, &buflen);
540 #endif
543 store_index = true;
545 while(1)
547 nread = read(playlist->fd, buffer, buflen);
548 /* Terminate on EOF */
549 if(nread <= 0)
550 break;
552 p = (unsigned char *)buffer;
554 for(count=0; count < nread; count++,p++) {
556 /* Are we on a new line? */
557 if((*p == '\n') || (*p == '\r'))
559 store_index = true;
561 else if(store_index)
563 store_index = false;
565 if(*p != '#')
567 if ( playlist->amount >= playlist->max_playlist_size ) {
568 display_buffer_full();
569 result = -1;
570 goto exit;
573 /* Store a new entry */
574 playlist->indices[ playlist->amount ] = i+count;
575 #ifdef HAVE_DIRCACHE
576 if (playlist->filenames)
577 playlist->filenames[ playlist->amount ] = NULL;
578 #endif
579 playlist->amount++;
584 i+= count;
587 exit:
588 #ifdef HAVE_DIRCACHE
589 queue_post(&playlist_queue, PLAYLIST_LOAD_POINTERS, 0);
590 #endif
592 return result;
596 * Utility function to create a new playlist, fill it with the next or
597 * previous directory, shuffle it if needed, and start playback.
598 * If play_last is true and direction zero or negative, start playing
599 * the last file in the directory, otherwise start playing the first.
601 static int create_and_play_dir(int direction, bool play_last)
603 char dir[MAX_PATH + 1];
604 int res;
605 int index = -1;
607 if(direction > 0)
608 res = get_next_directory(dir);
609 else
610 res = get_previous_directory(dir);
612 if (!res)
614 if (playlist_create(dir, NULL) != -1)
616 ft_build_playlist(tree_get_context(), 0);
618 if (global_settings.playlist_shuffle)
619 playlist_shuffle(current_tick, -1);
621 if (play_last && direction <= 0)
622 index = current_playlist.amount - 1;
623 else
624 index = 0;
626 #if (CONFIG_CODEC != SWCODEC)
627 playlist_start(index, 0);
628 #endif
631 /* we've overwritten the dircache when getting the next/previous dir,
632 so the tree browser context will need to be reloaded */
633 reload_directory();
636 return index;
640 * Removes all tracks, from the playlist, leaving the presently playing
641 * track queued.
643 int playlist_remove_all_tracks(struct playlist_info *playlist)
645 int result;
647 if (playlist == NULL)
648 playlist = &current_playlist;
650 while (playlist->index > 0)
651 if ((result = remove_track_from_playlist(playlist, 0, true)) < 0)
652 return result;
654 while (playlist->amount > 1)
655 if ((result = remove_track_from_playlist(playlist, 1, true)) < 0)
656 return result;
658 if (playlist->amount == 1) {
659 playlist->indices[0] |= PLAYLIST_QUEUED;
662 return 0;
667 * Add track to playlist at specified position. There are seven special
668 * positions that can be specified:
669 * PLAYLIST_PREPEND - Add track at beginning of playlist
670 * PLAYLIST_INSERT - Add track after current song. NOTE: If
671 * there are already inserted tracks then track
672 * is added to the end of the insertion list
673 * PLAYLIST_INSERT_FIRST - Add track immediately after current song, no
674 * matter what other tracks have been inserted
675 * PLAYLIST_INSERT_LAST - Add track to end of playlist
676 * PLAYLIST_INSERT_SHUFFLED - Add track at some random point between the
677 * current playing track and end of playlist
678 * PLAYLIST_INSERT_LAST_SHUFFLED - Add tracks in random order to the end of
679 * the playlist.
680 * PLAYLIST_REPLACE - Erase current playlist, Cue the current track
681 * and inster this track at the end.
683 static int add_track_to_playlist(struct playlist_info* playlist,
684 const char *filename, int position,
685 bool queue, int seek_pos)
687 int insert_position, orig_position;
688 unsigned long flags = PLAYLIST_INSERT_TYPE_INSERT;
689 int i;
691 insert_position = orig_position = position;
693 if (playlist->amount >= playlist->max_playlist_size)
695 display_buffer_full();
696 return -1;
699 switch (position)
701 case PLAYLIST_PREPEND:
702 position = insert_position = playlist->first_index;
703 break;
704 case PLAYLIST_INSERT:
705 /* if there are already inserted tracks then add track to end of
706 insertion list else add after current playing track */
707 if (playlist->last_insert_pos >= 0 &&
708 playlist->last_insert_pos < playlist->amount &&
709 (playlist->indices[playlist->last_insert_pos]&
710 PLAYLIST_INSERT_TYPE_MASK) == PLAYLIST_INSERT_TYPE_INSERT)
711 position = insert_position = playlist->last_insert_pos+1;
712 else if (playlist->amount > 0)
713 position = insert_position = playlist->index + 1;
714 else
715 position = insert_position = 0;
717 playlist->last_insert_pos = position;
718 break;
719 case PLAYLIST_INSERT_FIRST:
720 if (playlist->amount > 0)
721 position = insert_position = playlist->index + 1;
722 else
723 position = insert_position = 0;
725 playlist->last_insert_pos = position;
726 break;
727 case PLAYLIST_INSERT_LAST:
728 if (playlist->first_index > 0)
729 position = insert_position = playlist->first_index;
730 else
731 position = insert_position = playlist->amount;
733 playlist->last_insert_pos = position;
734 break;
735 case PLAYLIST_INSERT_SHUFFLED:
737 if (playlist->started)
739 int offset;
740 int n = playlist->amount -
741 rotate_index(playlist, playlist->index);
743 if (n > 0)
744 offset = rand() % n;
745 else
746 offset = 0;
748 position = playlist->index + offset + 1;
749 if (position >= playlist->amount)
750 position -= playlist->amount;
752 insert_position = position;
754 else
755 position = insert_position = (rand() % (playlist->amount+1));
756 break;
758 case PLAYLIST_INSERT_LAST_SHUFFLED:
760 position = insert_position = playlist->last_shuffled_start +
761 rand() % (playlist->amount - playlist->last_shuffled_start + 1);
762 break;
764 case PLAYLIST_REPLACE:
765 if (playlist_remove_all_tracks(playlist) < 0)
766 return -1;
768 playlist->last_insert_pos = position = insert_position = playlist->index + 1;
769 break;
772 if (queue)
773 flags |= PLAYLIST_QUEUED;
775 /* shift indices so that track can be added */
776 for (i=playlist->amount; i>insert_position; i--)
778 playlist->indices[i] = playlist->indices[i-1];
779 #ifdef HAVE_DIRCACHE
780 if (playlist->filenames)
781 playlist->filenames[i] = playlist->filenames[i-1];
782 #endif
785 /* update stored indices if needed */
786 if (playlist->amount > 0 && insert_position <= playlist->index &&
787 playlist->started)
788 playlist->index++;
790 if (playlist->amount > 0 && insert_position <= playlist->first_index &&
791 orig_position != PLAYLIST_PREPEND && playlist->started)
793 playlist->first_index++;
797 if (insert_position < playlist->last_insert_pos ||
798 (insert_position == playlist->last_insert_pos && position < 0))
799 playlist->last_insert_pos++;
801 if (seek_pos < 0 && playlist->control_fd >= 0)
803 int result = update_control(playlist,
804 (queue?PLAYLIST_COMMAND_QUEUE:PLAYLIST_COMMAND_ADD), position,
805 playlist->last_insert_pos, filename, NULL, &seek_pos);
807 if (result < 0)
808 return result;
811 playlist->indices[insert_position] = flags | seek_pos;
813 #ifdef HAVE_DIRCACHE
814 if (playlist->filenames)
815 playlist->filenames[insert_position] = NULL;
816 #endif
818 playlist->amount++;
819 playlist->num_inserted_tracks++;
821 return insert_position;
825 * Callback for playlist_directory_tracksearch to insert track into
826 * playlist.
828 static int directory_search_callback(char* filename, void* context)
830 struct directory_search_context* c =
831 (struct directory_search_context*) context;
832 int insert_pos;
834 insert_pos = add_track_to_playlist(c->playlist, filename, c->position,
835 c->queue, -1);
837 if (insert_pos < 0)
838 return -1;
840 (c->count)++;
842 /* Make sure tracks are inserted in correct order if user requests
843 INSERT_FIRST */
844 if (c->position == PLAYLIST_INSERT_FIRST || c->position >= 0)
845 c->position = insert_pos + 1;
847 if (((c->count)%PLAYLIST_DISPLAY_COUNT) == 0)
849 unsigned char* count_str;
851 if (c->queue)
852 count_str = ID2P(LANG_PLAYLIST_QUEUE_COUNT);
853 else
854 count_str = ID2P(LANG_PLAYLIST_INSERT_COUNT);
856 display_playlist_count(c->count, count_str, false);
858 if ((c->count) == PLAYLIST_DISPLAY_COUNT &&
859 (audio_status() & AUDIO_STATUS_PLAY) &&
860 c->playlist->started)
861 audio_flush_and_reload_tracks();
864 return 0;
868 * remove track at specified position
870 static int remove_track_from_playlist(struct playlist_info* playlist,
871 int position, bool write)
873 int i;
874 bool inserted;
876 if (playlist->amount <= 0)
877 return -1;
879 inserted = playlist->indices[position] & PLAYLIST_INSERT_TYPE_MASK;
881 /* shift indices now that track has been removed */
882 for (i=position; i<playlist->amount; i++)
884 playlist->indices[i] = playlist->indices[i+1];
885 #ifdef HAVE_DIRCACHE
886 if (playlist->filenames)
887 playlist->filenames[i] = playlist->filenames[i+1];
888 #endif
891 playlist->amount--;
893 if (inserted)
894 playlist->num_inserted_tracks--;
895 else
896 playlist->deleted = true;
898 /* update stored indices if needed */
899 if (position < playlist->index)
900 playlist->index--;
902 if (position < playlist->first_index)
904 playlist->first_index--;
907 if (position <= playlist->last_insert_pos)
908 playlist->last_insert_pos--;
910 if (write && playlist->control_fd >= 0)
912 int result = update_control(playlist, PLAYLIST_COMMAND_DELETE,
913 position, -1, NULL, NULL, NULL);
915 if (result < 0)
916 return result;
918 sync_control(playlist, false);
921 return 0;
925 * randomly rearrange the array of indices for the playlist. If start_current
926 * is true then update the index to the new index of the current playing track
928 static int randomise_playlist(struct playlist_info* playlist,
929 unsigned int seed, bool start_current,
930 bool write)
932 int count;
933 int candidate;
934 long store;
935 unsigned int current = playlist->indices[playlist->index];
937 /* seed 0 is used to identify sorted playlist for resume purposes */
938 if (seed == 0)
939 seed = 1;
941 /* seed with the given seed */
942 srand(seed);
944 /* randomise entire indices list */
945 for(count = playlist->amount - 1; count >= 0; count--)
947 /* the rand is from 0 to RAND_MAX, so adjust to our value range */
948 candidate = rand() % (count + 1);
950 /* now swap the values at the 'count' and 'candidate' positions */
951 store = playlist->indices[candidate];
952 playlist->indices[candidate] = playlist->indices[count];
953 playlist->indices[count] = store;
954 #ifdef HAVE_DIRCACHE
955 if (playlist->filenames)
957 store = (long)playlist->filenames[candidate];
958 playlist->filenames[candidate] = playlist->filenames[count];
959 playlist->filenames[count] = (struct dircache_entry *)store;
961 #endif
964 if (start_current)
965 find_and_set_playlist_index(playlist, current);
967 /* indices have been moved so last insert position is no longer valid */
968 playlist->last_insert_pos = -1;
970 playlist->seed = seed;
971 if (playlist->num_inserted_tracks > 0 || playlist->deleted)
972 playlist->shuffle_modified = true;
974 if (write)
976 update_control(playlist, PLAYLIST_COMMAND_SHUFFLE, seed,
977 playlist->first_index, NULL, NULL, NULL);
980 return 0;
984 * Sort the array of indices for the playlist. If start_current is true then
985 * set the index to the new index of the current song.
987 static int sort_playlist(struct playlist_info* playlist, bool start_current,
988 bool write)
990 unsigned int current = playlist->indices[playlist->index];
992 if (playlist->amount > 0)
993 qsort(playlist->indices, playlist->amount,
994 sizeof(playlist->indices[0]), compare);
996 #ifdef HAVE_DIRCACHE
997 /** We need to re-check the song names from disk because qsort can't
998 * sort two arrays at once :/
999 * FIXME: Please implement a better way to do this. */
1000 memset(playlist->filenames, 0, playlist->max_playlist_size * sizeof(int));
1001 queue_post(&playlist_queue, PLAYLIST_LOAD_POINTERS, 0);
1002 #endif
1004 if (start_current)
1005 find_and_set_playlist_index(playlist, current);
1007 /* indices have been moved so last insert position is no longer valid */
1008 playlist->last_insert_pos = -1;
1010 if (!playlist->num_inserted_tracks && !playlist->deleted)
1011 playlist->shuffle_modified = false;
1012 if (write && playlist->control_fd >= 0)
1014 update_control(playlist, PLAYLIST_COMMAND_UNSHUFFLE,
1015 playlist->first_index, -1, NULL, NULL, NULL);
1018 return 0;
1021 /* Calculate how many steps we have to really step when skipping entries
1022 * marked as bad.
1024 static int calculate_step_count(const struct playlist_info *playlist, int steps)
1026 int i, count, direction;
1027 int index;
1028 int stepped_count = 0;
1030 if (steps < 0)
1032 direction = -1;
1033 count = -steps;
1035 else
1037 direction = 1;
1038 count = steps;
1041 index = playlist->index;
1042 i = 0;
1043 do {
1044 /* Boundary check */
1045 if (index < 0)
1046 index += playlist->amount;
1047 if (index >= playlist->amount)
1048 index -= playlist->amount;
1050 /* Check if we found a bad entry. */
1051 if (playlist->indices[index] & PLAYLIST_SKIPPED)
1053 steps += direction;
1054 /* Are all entries bad? */
1055 if (stepped_count++ > playlist->amount)
1056 break ;
1058 else
1059 i++;
1061 index += direction;
1062 } while (i <= count);
1064 return steps;
1067 /* Marks the index of the track to be skipped that is "steps" away from
1068 * current playing track.
1070 void playlist_skip_entry(struct playlist_info *playlist, int steps)
1072 int index;
1074 if (playlist == NULL)
1075 playlist = &current_playlist;
1077 /* need to account for already skipped tracks */
1078 steps = calculate_step_count(playlist, steps);
1080 index = playlist->index + steps;
1081 if (index < 0)
1082 index += playlist->amount;
1083 else if (index >= playlist->amount)
1084 index -= playlist->amount;
1086 playlist->indices[index] |= PLAYLIST_SKIPPED;
1090 * returns the index of the track that is "steps" away from current playing
1091 * track.
1093 static int get_next_index(const struct playlist_info* playlist, int steps,
1094 int repeat_mode)
1096 int current_index = playlist->index;
1097 int next_index = -1;
1099 if (playlist->amount <= 0)
1100 return -1;
1102 if (repeat_mode == -1)
1103 repeat_mode = global_settings.repeat_mode;
1105 if (repeat_mode == REPEAT_SHUFFLE && playlist->amount <= 1)
1106 repeat_mode = REPEAT_ALL;
1108 steps = calculate_step_count(playlist, steps);
1109 switch (repeat_mode)
1111 case REPEAT_SHUFFLE:
1112 /* Treat repeat shuffle just like repeat off. At end of playlist,
1113 play will be resumed in playlist_next() */
1114 case REPEAT_OFF:
1116 current_index = rotate_index(playlist, current_index);
1117 next_index = current_index+steps;
1118 if ((next_index < 0) || (next_index >= playlist->amount))
1119 next_index = -1;
1120 else
1121 next_index = (next_index+playlist->first_index) %
1122 playlist->amount;
1124 break;
1127 case REPEAT_ONE:
1128 #ifdef AB_REPEAT_ENABLE
1129 case REPEAT_AB:
1130 #endif
1131 next_index = current_index;
1132 break;
1134 case REPEAT_ALL:
1135 default:
1137 next_index = (current_index+steps) % playlist->amount;
1138 while (next_index < 0)
1139 next_index += playlist->amount;
1141 if (steps >= playlist->amount)
1143 int i, index;
1145 index = next_index;
1146 next_index = -1;
1148 /* second time around so skip the queued files */
1149 for (i=0; i<playlist->amount; i++)
1151 if (playlist->indices[index] & PLAYLIST_QUEUE_MASK)
1152 index = (index+1) % playlist->amount;
1153 else
1155 next_index = index;
1156 break;
1160 break;
1164 /* No luck if the whole playlist was bad. */
1165 if (playlist->indices[next_index] & PLAYLIST_SKIPPED)
1166 return -1;
1168 return next_index;
1172 * Search for the seek track and set appropriate indices. Used after shuffle
1173 * to make sure the current index is still pointing to correct track.
1175 static void find_and_set_playlist_index(struct playlist_info* playlist,
1176 unsigned int seek)
1178 int i;
1180 /* Set the index to the current song */
1181 for (i=0; i<playlist->amount; i++)
1183 if (playlist->indices[i] == seek)
1185 playlist->index = playlist->first_index = i;
1187 break;
1193 * used to sort track indices. Sort order is as follows:
1194 * 1. Prepended tracks (in prepend order)
1195 * 2. Playlist/directory tracks (in playlist order)
1196 * 3. Inserted/Appended tracks (in insert order)
1198 static int compare(const void* p1, const void* p2)
1200 unsigned long* e1 = (unsigned long*) p1;
1201 unsigned long* e2 = (unsigned long*) p2;
1202 unsigned long flags1 = *e1 & PLAYLIST_INSERT_TYPE_MASK;
1203 unsigned long flags2 = *e2 & PLAYLIST_INSERT_TYPE_MASK;
1205 if (flags1 == flags2)
1206 return (*e1 & PLAYLIST_SEEK_MASK) - (*e2 & PLAYLIST_SEEK_MASK);
1207 else if (flags1 == PLAYLIST_INSERT_TYPE_PREPEND ||
1208 flags2 == PLAYLIST_INSERT_TYPE_APPEND)
1209 return -1;
1210 else if (flags1 == PLAYLIST_INSERT_TYPE_APPEND ||
1211 flags2 == PLAYLIST_INSERT_TYPE_PREPEND)
1212 return 1;
1213 else if (flags1 && flags2)
1214 return (*e1 & PLAYLIST_SEEK_MASK) - (*e2 & PLAYLIST_SEEK_MASK);
1215 else
1216 return *e1 - *e2;
1219 #ifdef HAVE_DIRCACHE
1221 * Thread to update filename pointers to dircache on background
1222 * without affecting playlist load up performance. This thread also flushes
1223 * any pending control commands when the disk spins up.
1225 static void playlist_flush_callback(void *param)
1227 (void)param;
1228 struct playlist_info *playlist;
1229 playlist = &current_playlist;
1230 if (playlist->control_fd >= 0)
1232 if (playlist->num_cached > 0)
1234 mutex_lock(&playlist->control_mutex);
1235 flush_cached_control(playlist);
1236 mutex_unlock(&playlist->control_mutex);
1238 sync_control(playlist, true);
1242 static void playlist_thread(void)
1244 struct queue_event ev;
1245 bool dirty_pointers = false;
1246 static char tmp[MAX_PATH+1];
1248 struct playlist_info *playlist;
1249 int index;
1250 int seek;
1251 bool control_file;
1253 int sleep_time = 5;
1255 #ifdef HAVE_DISK_STORAGE
1256 if (global_settings.disk_spindown > 1 &&
1257 global_settings.disk_spindown <= 5)
1258 sleep_time = global_settings.disk_spindown - 1;
1259 #endif
1261 while (1)
1263 queue_wait_w_tmo(&playlist_queue, &ev, HZ*sleep_time);
1265 switch (ev.id)
1267 case PLAYLIST_LOAD_POINTERS:
1268 dirty_pointers = true;
1269 break ;
1271 /* Start the background scanning after either the disk spindown
1272 timeout or 5s, whichever is less */
1273 case SYS_TIMEOUT:
1274 playlist = &current_playlist;
1275 if (playlist->control_fd >= 0)
1277 if (playlist->num_cached > 0)
1278 register_storage_idle_func(playlist_flush_callback);
1281 if (!dirty_pointers)
1282 break ;
1284 if (!dircache_is_enabled() || !playlist->filenames
1285 || playlist->amount <= 0)
1286 break ;
1288 #ifdef HAVE_ADJUSTABLE_CPU_FREQ
1289 cpu_boost(true);
1290 #endif
1291 for (index = 0; index < playlist->amount
1292 && queue_empty(&playlist_queue); index++)
1294 /* Process only pointers that are not already loaded. */
1295 if (playlist->filenames[index])
1296 continue ;
1298 control_file = playlist->indices[index] & PLAYLIST_INSERT_TYPE_MASK;
1299 seek = playlist->indices[index] & PLAYLIST_SEEK_MASK;
1301 /* Load the filename from playlist file. */
1302 if (get_filename(playlist, index, seek, control_file, tmp,
1303 sizeof(tmp)) < 0)
1304 break ;
1306 /* Set the dircache entry pointer. */
1307 playlist->filenames[index] = dircache_get_entry_ptr(tmp);
1309 /* And be on background so user doesn't notice any delays. */
1310 yield();
1313 #ifdef HAVE_ADJUSTABLE_CPU_FREQ
1314 cpu_boost(false);
1315 #endif
1316 dirty_pointers = false;
1317 break ;
1319 #ifndef SIMULATOR
1320 case SYS_USB_CONNECTED:
1321 usb_acknowledge(SYS_USB_CONNECTED_ACK);
1322 usb_wait_for_disconnect(&playlist_queue);
1323 break ;
1324 #endif
1328 #endif
1331 * gets pathname for track at seek index
1333 static int get_filename(struct playlist_info* playlist, int index, int seek,
1334 bool control_file, char *buf, int buf_length)
1336 int fd;
1337 int max = -1;
1338 char tmp_buf[MAX_PATH+1];
1339 char dir_buf[MAX_PATH+1];
1340 bool utf8 = playlist->utf8;
1342 if (buf_length > MAX_PATH+1)
1343 buf_length = MAX_PATH+1;
1345 #ifdef HAVE_DIRCACHE
1346 if (dircache_is_enabled() && playlist->filenames)
1348 if (playlist->filenames[index] != NULL)
1350 dircache_copy_path(playlist->filenames[index], tmp_buf, sizeof(tmp_buf)-1);
1351 max = strlen(tmp_buf);
1354 #else
1355 (void)index;
1356 #endif
1358 if (playlist->in_ram && !control_file && max < 0)
1360 max = strlcpy(tmp_buf, &playlist->buffer[seek], sizeof(tmp_buf));
1362 else if (max < 0)
1364 mutex_lock(&playlist->control_mutex);
1366 if (control_file)
1368 fd = playlist->control_fd;
1369 utf8 = true;
1371 else
1373 if(-1 == playlist->fd)
1374 playlist->fd = open(playlist->filename, O_RDONLY);
1376 fd = playlist->fd;
1379 if(-1 != fd)
1382 if (lseek(fd, seek, SEEK_SET) != seek)
1383 max = -1;
1384 else
1386 max = read(fd, tmp_buf, MIN((size_t) buf_length, sizeof(tmp_buf)));
1388 if ((max > 0) && !utf8)
1390 /* Use dir_buf as a temporary buffer. Note that dir_buf must
1391 * be as large as tmp_buf.
1393 max = convert_m3u(tmp_buf, max, sizeof(tmp_buf), dir_buf);
1398 mutex_unlock(&playlist->control_mutex);
1400 if (max < 0)
1402 if (control_file)
1403 splash(HZ*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR));
1404 else
1405 splash(HZ*2, ID2P(LANG_PLAYLIST_ACCESS_ERROR));
1407 return max;
1411 strlcpy(dir_buf, playlist->filename, playlist->dirlen);
1413 return (format_track_path(buf, tmp_buf, buf_length, max, dir_buf));
1416 static int get_next_directory(char *dir){
1417 return get_next_dir(dir,true,false);
1420 static int get_previous_directory(char *dir){
1421 return get_next_dir(dir,false,false);
1425 * search through all the directories (starting with the current) to find
1426 * one that has tracks to play
1428 static int get_next_dir(char *dir, bool is_forward, bool recursion)
1430 struct playlist_info* playlist = &current_playlist;
1431 int result = -1;
1432 char *start_dir = NULL;
1433 bool exit = false;
1434 int i;
1435 struct tree_context* tc = tree_get_context();
1436 int saved_dirfilter = *(tc->dirfilter);
1438 /* process random folder advance */
1439 if (global_settings.next_folder == FOLDER_ADVANCE_RANDOM)
1441 int fd = open(ROCKBOX_DIR "/folder_advance_list.dat", O_RDONLY);
1442 if (fd >= 0)
1444 char buffer[MAX_PATH];
1445 int folder_count = 0;
1446 srand(current_tick);
1447 *(tc->dirfilter) = SHOW_MUSIC;
1448 tc->sort_dir = global_settings.sort_dir;
1449 read(fd,&folder_count,sizeof(int));
1450 if (!folder_count)
1451 exit = true;
1452 while (!exit)
1454 i = rand()%folder_count;
1455 lseek(fd,sizeof(int) + (MAX_PATH*i),SEEK_SET);
1456 read(fd,buffer,MAX_PATH);
1457 if (check_subdir_for_music(buffer, "", false) ==0)
1458 exit = true;
1460 if (folder_count)
1461 strcpy(dir,buffer);
1462 close(fd);
1463 *(tc->dirfilter) = saved_dirfilter;
1464 tc->sort_dir = global_settings.sort_dir;
1465 reload_directory();
1466 return 0;
1470 /* not random folder advance (or random folder advance unavailable) */
1471 if (recursion)
1473 /* start with root */
1474 dir[0] = '\0';
1476 else
1478 /* start with current directory */
1479 strlcpy(dir, playlist->filename, playlist->dirlen);
1482 /* use the tree browser dircache to load files */
1483 *(tc->dirfilter) = SHOW_ALL;
1485 /* set up sorting/direction */
1486 tc->sort_dir = global_settings.sort_dir;
1487 if (!is_forward)
1489 static const char sortpairs[] =
1491 [SORT_ALPHA] = SORT_ALPHA_REVERSED,
1492 [SORT_DATE] = SORT_DATE_REVERSED,
1493 [SORT_TYPE] = SORT_TYPE_REVERSED,
1494 [SORT_ALPHA_REVERSED] = SORT_ALPHA,
1495 [SORT_DATE_REVERSED] = SORT_DATE,
1496 [SORT_TYPE_REVERSED] = SORT_TYPE,
1499 if ((unsigned)tc->sort_dir < sizeof(sortpairs))
1500 tc->sort_dir = sortpairs[tc->sort_dir];
1503 while (!exit)
1505 struct entry *files;
1506 int num_files = 0;
1507 int i;
1509 if (ft_load(tc, (dir[0]=='\0')?"/":dir) < 0)
1511 exit = true;
1512 result = -1;
1513 break;
1516 files = (struct entry*) tc->dircache;
1517 num_files = tc->filesindir;
1519 for (i=0; i<num_files; i++)
1521 /* user abort */
1522 if (action_userabort(TIMEOUT_NOBLOCK))
1524 result = -1;
1525 exit = true;
1526 break;
1529 if (files[i].attr & ATTR_DIRECTORY)
1531 if (!start_dir)
1533 result = check_subdir_for_music(dir, files[i].name, true);
1534 if (result != -1)
1536 exit = true;
1537 break;
1540 else if (!strcmp(start_dir, files[i].name))
1541 start_dir = NULL;
1545 if (!exit)
1547 /* move down to parent directory. current directory name is
1548 stored as the starting point for the search in parent */
1549 start_dir = strrchr(dir, '/');
1550 if (start_dir)
1552 *start_dir = '\0';
1553 start_dir++;
1555 else
1556 break;
1560 /* restore dirfilter */
1561 *(tc->dirfilter) = saved_dirfilter;
1562 tc->sort_dir = global_settings.sort_dir;
1564 /* special case if nothing found: try start searching again from root */
1565 if (result == -1 && !recursion){
1566 result = get_next_dir(dir, is_forward, true);
1569 return result;
1573 * Checks if there are any music files in the dir or any of its
1574 * subdirectories. May be called recursively.
1576 static int check_subdir_for_music(char *dir, const char *subdir, bool recurse)
1578 int result = -1;
1579 int dirlen = strlen(dir);
1580 int num_files = 0;
1581 int i;
1582 struct entry *files;
1583 bool has_music = false;
1584 bool has_subdir = false;
1585 struct tree_context* tc = tree_get_context();
1587 snprintf(dir+dirlen, MAX_PATH-dirlen, "/%s", subdir);
1589 if (ft_load(tc, dir) < 0)
1591 return -2;
1594 files = (struct entry*) tc->dircache;
1595 num_files = tc->filesindir;
1597 for (i=0; i<num_files; i++)
1599 if (files[i].attr & ATTR_DIRECTORY)
1600 has_subdir = true;
1601 else if ((files[i].attr & FILE_ATTR_MASK) == FILE_ATTR_AUDIO)
1603 has_music = true;
1604 break;
1608 if (has_music)
1609 return 0;
1611 if (has_subdir && recurse)
1613 for (i=0; i<num_files; i++)
1615 if (action_userabort(TIMEOUT_NOBLOCK))
1617 result = -2;
1618 break;
1621 if (files[i].attr & ATTR_DIRECTORY)
1623 result = check_subdir_for_music(dir, files[i].name, true);
1624 if (!result)
1625 break;
1630 if (result < 0)
1632 if (dirlen)
1634 dir[dirlen] = '\0';
1636 else
1638 strcpy(dir, "/");
1641 /* we now need to reload our current directory */
1642 if(ft_load(tc, dir) < 0)
1643 splash(HZ*2, ID2P(LANG_PLAYLIST_DIRECTORY_ACCESS_ERROR));
1645 return result;
1649 * Returns absolute path of track
1651 static int format_track_path(char *dest, char *src, int buf_length, int max,
1652 const char *dir)
1654 int i = 0;
1655 int j;
1656 char *temp_ptr;
1658 /* Zero-terminate the file name */
1659 while((src[i] != '\n') &&
1660 (src[i] != '\r') &&
1661 (i < max))
1662 i++;
1664 /* Now work back killing white space */
1665 while((src[i-1] == ' ') ||
1666 (src[i-1] == '\t'))
1667 i--;
1669 src[i]=0;
1671 /* replace backslashes with forward slashes */
1672 for ( j=0; j<i; j++ )
1673 if ( src[j] == '\\' )
1674 src[j] = '/';
1676 if('/' == src[0])
1678 strlcpy(dest, src, buf_length);
1680 else
1682 /* handle dos style drive letter */
1683 if (':' == src[1])
1684 strlcpy(dest, &src[2], buf_length);
1685 else if (!strncmp(src, "../", 3))
1687 /* handle relative paths */
1688 i=3;
1689 while(!strncmp(&src[i], "../", 3))
1690 i += 3;
1691 for (j=0; j<i/3; j++) {
1692 temp_ptr = strrchr(dir, '/');
1693 if (temp_ptr)
1694 *temp_ptr = '\0';
1695 else
1696 break;
1698 snprintf(dest, buf_length, "%s/%s", dir, &src[i]);
1700 else if ( '.' == src[0] && '/' == src[1] ) {
1701 snprintf(dest, buf_length, "%s/%s", dir, &src[2]);
1703 else {
1704 snprintf(dest, buf_length, "%s/%s", dir, src);
1708 return 0;
1712 * Display splash message showing progress of playlist/directory insertion or
1713 * save.
1715 static void display_playlist_count(int count, const unsigned char *fmt,
1716 bool final)
1718 static long talked_tick = 0;
1719 long id = P2ID(fmt);
1720 if(global_settings.talk_menu && id>=0)
1722 if(final || (count && (talked_tick == 0
1723 || TIME_AFTER(current_tick, talked_tick+5*HZ))))
1725 talked_tick = current_tick;
1726 talk_number(count, false);
1727 talk_id(id, true);
1730 fmt = P2STR(fmt);
1732 splashf(0, fmt, count, str(LANG_OFF_ABORT));
1736 * Display buffer full message
1738 static void display_buffer_full(void)
1740 splash(HZ*2, ID2P(LANG_PLAYLIST_BUFFER_FULL));
1744 * Flush any cached control commands to disk. Called when playlist is being
1745 * modified. Returns 0 on success and -1 on failure.
1747 static int flush_cached_control(struct playlist_info* playlist)
1749 int result = 0;
1750 int i;
1752 if (!playlist->num_cached)
1753 return 0;
1755 lseek(playlist->control_fd, 0, SEEK_END);
1757 for (i=0; i<playlist->num_cached; i++)
1759 struct playlist_control_cache* cache =
1760 &(playlist->control_cache[i]);
1762 switch (cache->command)
1764 case PLAYLIST_COMMAND_PLAYLIST:
1765 result = fdprintf(playlist->control_fd, "P:%d:%s:%s\n",
1766 cache->i1, cache->s1, cache->s2);
1767 break;
1768 case PLAYLIST_COMMAND_ADD:
1769 case PLAYLIST_COMMAND_QUEUE:
1770 result = fdprintf(playlist->control_fd, "%c:%d:%d:",
1771 (cache->command == PLAYLIST_COMMAND_ADD)?'A':'Q',
1772 cache->i1, cache->i2);
1773 if (result > 0)
1775 /* save the position in file where name is written */
1776 int* seek_pos = (int *)cache->data;
1777 *seek_pos = lseek(playlist->control_fd, 0, SEEK_CUR);
1778 result = fdprintf(playlist->control_fd, "%s\n",
1779 cache->s1);
1781 break;
1782 case PLAYLIST_COMMAND_DELETE:
1783 result = fdprintf(playlist->control_fd, "D:%d\n", cache->i1);
1784 break;
1785 case PLAYLIST_COMMAND_SHUFFLE:
1786 result = fdprintf(playlist->control_fd, "S:%d:%d\n",
1787 cache->i1, cache->i2);
1788 break;
1789 case PLAYLIST_COMMAND_UNSHUFFLE:
1790 result = fdprintf(playlist->control_fd, "U:%d\n", cache->i1);
1791 break;
1792 case PLAYLIST_COMMAND_RESET:
1793 result = fdprintf(playlist->control_fd, "R\n");
1794 break;
1795 default:
1796 break;
1799 if (result <= 0)
1800 break;
1803 if (result > 0)
1805 playlist->num_cached = 0;
1806 playlist->pending_control_sync = true;
1808 result = 0;
1810 else
1812 result = -1;
1813 splash(HZ*2, ID2P(LANG_PLAYLIST_CONTROL_UPDATE_ERROR));
1816 return result;
1820 * Update control data with new command. Depending on the command, it may be
1821 * cached or flushed to disk.
1823 static int update_control(struct playlist_info* playlist,
1824 enum playlist_command command, int i1, int i2,
1825 const char* s1, const char* s2, void* data)
1827 int result = 0;
1828 struct playlist_control_cache* cache;
1829 bool flush = false;
1831 mutex_lock(&playlist->control_mutex);
1833 cache = &(playlist->control_cache[playlist->num_cached++]);
1835 cache->command = command;
1836 cache->i1 = i1;
1837 cache->i2 = i2;
1838 cache->s1 = s1;
1839 cache->s2 = s2;
1840 cache->data = data;
1842 switch (command)
1844 case PLAYLIST_COMMAND_PLAYLIST:
1845 case PLAYLIST_COMMAND_ADD:
1846 case PLAYLIST_COMMAND_QUEUE:
1847 #ifndef HAVE_DIRCACHE
1848 case PLAYLIST_COMMAND_DELETE:
1849 case PLAYLIST_COMMAND_RESET:
1850 #endif
1851 flush = true;
1852 break;
1853 case PLAYLIST_COMMAND_SHUFFLE:
1854 case PLAYLIST_COMMAND_UNSHUFFLE:
1855 default:
1856 /* only flush when needed */
1857 break;
1860 if (flush || playlist->num_cached == PLAYLIST_MAX_CACHE)
1861 result = flush_cached_control(playlist);
1863 mutex_unlock(&playlist->control_mutex);
1865 return result;
1869 * sync control file to disk
1871 static void sync_control(struct playlist_info* playlist, bool force)
1873 #ifdef HAVE_DIRCACHE
1874 if (playlist->started && force)
1875 #else
1876 (void) force;
1878 if (playlist->started)
1879 #endif
1881 if (playlist->pending_control_sync)
1883 mutex_lock(&playlist->control_mutex);
1884 fsync(playlist->control_fd);
1885 playlist->pending_control_sync = false;
1886 mutex_unlock(&playlist->control_mutex);
1892 * Rotate indices such that first_index is index 0
1894 static int rotate_index(const struct playlist_info* playlist, int index)
1896 index -= playlist->first_index;
1897 if (index < 0)
1898 index += playlist->amount;
1900 return index;
1904 * Initialize playlist entries at startup
1906 void playlist_init(void)
1908 struct playlist_info* playlist = &current_playlist;
1910 playlist->current = true;
1911 strlcpy(playlist->control_filename, PLAYLIST_CONTROL_FILE,
1912 sizeof(playlist->control_filename));
1913 playlist->fd = -1;
1914 playlist->control_fd = -1;
1915 playlist->max_playlist_size = global_settings.max_files_in_playlist;
1916 playlist->indices = buffer_alloc(
1917 playlist->max_playlist_size * sizeof(int));
1918 playlist->buffer_size =
1919 AVERAGE_FILENAME_LENGTH * global_settings.max_files_in_dir;
1920 playlist->buffer = buffer_alloc(playlist->buffer_size);
1921 mutex_init(&playlist->control_mutex);
1922 empty_playlist(playlist, true);
1924 #ifdef HAVE_DIRCACHE
1925 playlist->filenames = buffer_alloc(
1926 playlist->max_playlist_size * sizeof(int));
1927 memset(playlist->filenames, 0,
1928 playlist->max_playlist_size * sizeof(int));
1929 create_thread(playlist_thread, playlist_stack, sizeof(playlist_stack),
1930 0, playlist_thread_name IF_PRIO(, PRIORITY_BACKGROUND)
1931 IF_COP(, CPU));
1932 queue_init(&playlist_queue, true);
1933 #endif
1937 * Clean playlist at shutdown
1939 void playlist_shutdown(void)
1941 struct playlist_info* playlist = &current_playlist;
1943 if (playlist->control_fd >= 0)
1945 mutex_lock(&playlist->control_mutex);
1947 if (playlist->num_cached > 0)
1948 flush_cached_control(playlist);
1950 close(playlist->control_fd);
1952 mutex_unlock(&playlist->control_mutex);
1957 * Create new playlist
1959 int playlist_create(const char *dir, const char *file)
1961 struct playlist_info* playlist = &current_playlist;
1963 new_playlist(playlist, dir, file);
1965 if (file)
1966 /* load the playlist file */
1967 add_indices_to_playlist(playlist, NULL, 0);
1969 return 0;
1972 #define PLAYLIST_COMMAND_SIZE (MAX_PATH+12)
1975 * Restore the playlist state based on control file commands. Called to
1976 * resume playback after shutdown.
1978 int playlist_resume(void)
1980 struct playlist_info* playlist = &current_playlist;
1981 char *buffer;
1982 size_t buflen;
1983 int nread;
1984 int total_read = 0;
1985 int control_file_size = 0;
1986 bool first = true;
1987 bool sorted = true;
1989 /* use mp3 buffer for maximum load speed */
1990 #if CONFIG_CODEC != SWCODEC
1991 talk_buffer_steal(); /* we use the mp3 buffer, need to tell */
1992 buflen = (audiobufend - audiobuf);
1993 buffer = (char *)audiobuf;
1994 #else
1995 buffer = (char *)audio_get_buffer(false, &buflen);
1996 #endif
1998 empty_playlist(playlist, true);
2000 splash(0, ID2P(LANG_WAIT));
2001 playlist->control_fd = open(playlist->control_filename, O_RDWR);
2002 if (playlist->control_fd < 0)
2004 splash(HZ*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR));
2005 return -1;
2007 playlist->control_created = true;
2009 control_file_size = filesize(playlist->control_fd);
2010 if (control_file_size <= 0)
2012 splash(HZ*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR));
2013 return -1;
2016 /* read a small amount first to get the header */
2017 nread = read(playlist->control_fd, buffer,
2018 PLAYLIST_COMMAND_SIZE<buflen?PLAYLIST_COMMAND_SIZE:buflen);
2019 if(nread <= 0)
2021 splash(HZ*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR));
2022 return -1;
2025 playlist->started = true;
2027 while (1)
2029 int result = 0;
2030 int count;
2031 enum playlist_command current_command = PLAYLIST_COMMAND_COMMENT;
2032 int last_newline = 0;
2033 int str_count = -1;
2034 bool newline = true;
2035 bool exit_loop = false;
2036 char *p = buffer;
2037 char *str1 = NULL;
2038 char *str2 = NULL;
2039 char *str3 = NULL;
2040 unsigned long last_tick = current_tick;
2041 bool useraborted = false;
2043 for(count=0; count<nread && !exit_loop && !useraborted; count++,p++)
2045 /* So a splash while we are loading. */
2046 if (TIME_AFTER(current_tick, last_tick + HZ/4))
2048 splashf(0, str(LANG_LOADING_PERCENT),
2049 (total_read+count)*100/control_file_size,
2050 str(LANG_OFF_ABORT));
2051 if (action_userabort(TIMEOUT_NOBLOCK))
2053 useraborted = true;
2054 break;
2056 last_tick = current_tick;
2059 /* Are we on a new line? */
2060 if((*p == '\n') || (*p == '\r'))
2062 *p = '\0';
2064 /* save last_newline in case we need to load more data */
2065 last_newline = count;
2067 switch (current_command)
2069 case PLAYLIST_COMMAND_PLAYLIST:
2071 /* str1=version str2=dir str3=file */
2072 int version;
2074 if (!str1)
2076 result = -1;
2077 exit_loop = true;
2078 break;
2081 if (!str2)
2082 str2 = "";
2084 if (!str3)
2085 str3 = "";
2087 version = atoi(str1);
2089 if (version != PLAYLIST_CONTROL_FILE_VERSION)
2090 return -1;
2092 update_playlist_filename(playlist, str2, str3);
2094 if (str3[0] != '\0')
2096 /* NOTE: add_indices_to_playlist() overwrites the
2097 audiobuf so we need to reload control file
2098 data */
2099 add_indices_to_playlist(playlist, NULL, 0);
2101 else if (str2[0] != '\0')
2103 playlist->in_ram = true;
2104 resume_directory(str2);
2107 /* load the rest of the data */
2108 first = false;
2109 exit_loop = true;
2111 break;
2113 case PLAYLIST_COMMAND_ADD:
2114 case PLAYLIST_COMMAND_QUEUE:
2116 /* str1=position str2=last_position str3=file */
2117 int position, last_position;
2118 bool queue;
2120 if (!str1 || !str2 || !str3)
2122 result = -1;
2123 exit_loop = true;
2124 break;
2127 position = atoi(str1);
2128 last_position = atoi(str2);
2130 queue = (current_command == PLAYLIST_COMMAND_ADD)?
2131 false:true;
2133 /* seek position is based on str3's position in
2134 buffer */
2135 if (add_track_to_playlist(playlist, str3, position,
2136 queue, total_read+(str3-buffer)) < 0)
2137 return -1;
2139 playlist->last_insert_pos = last_position;
2141 break;
2143 case PLAYLIST_COMMAND_DELETE:
2145 /* str1=position */
2146 int position;
2148 if (!str1)
2150 result = -1;
2151 exit_loop = true;
2152 break;
2155 position = atoi(str1);
2157 if (remove_track_from_playlist(playlist, position,
2158 false) < 0)
2159 return -1;
2161 break;
2163 case PLAYLIST_COMMAND_SHUFFLE:
2165 /* str1=seed str2=first_index */
2166 int seed;
2168 if (!str1 || !str2)
2170 result = -1;
2171 exit_loop = true;
2172 break;
2175 if (!sorted)
2177 /* Always sort list before shuffling */
2178 sort_playlist(playlist, false, false);
2181 seed = atoi(str1);
2182 playlist->first_index = atoi(str2);
2184 if (randomise_playlist(playlist, seed, false,
2185 false) < 0)
2186 return -1;
2187 sorted = false;
2188 break;
2190 case PLAYLIST_COMMAND_UNSHUFFLE:
2192 /* str1=first_index */
2193 if (!str1)
2195 result = -1;
2196 exit_loop = true;
2197 break;
2200 playlist->first_index = atoi(str1);
2202 if (sort_playlist(playlist, false, false) < 0)
2203 return -1;
2205 sorted = true;
2206 break;
2208 case PLAYLIST_COMMAND_RESET:
2210 playlist->last_insert_pos = -1;
2211 break;
2213 case PLAYLIST_COMMAND_COMMENT:
2214 default:
2215 break;
2218 newline = true;
2220 /* to ignore any extra newlines */
2221 current_command = PLAYLIST_COMMAND_COMMENT;
2223 else if(newline)
2225 newline = false;
2227 /* first non-comment line must always specify playlist */
2228 if (first && *p != 'P' && *p != '#')
2230 result = -1;
2231 exit_loop = true;
2232 break;
2235 switch (*p)
2237 case 'P':
2238 /* playlist can only be specified once */
2239 if (!first)
2241 result = -1;
2242 exit_loop = true;
2243 break;
2246 current_command = PLAYLIST_COMMAND_PLAYLIST;
2247 break;
2248 case 'A':
2249 current_command = PLAYLIST_COMMAND_ADD;
2250 break;
2251 case 'Q':
2252 current_command = PLAYLIST_COMMAND_QUEUE;
2253 break;
2254 case 'D':
2255 current_command = PLAYLIST_COMMAND_DELETE;
2256 break;
2257 case 'S':
2258 current_command = PLAYLIST_COMMAND_SHUFFLE;
2259 break;
2260 case 'U':
2261 current_command = PLAYLIST_COMMAND_UNSHUFFLE;
2262 break;
2263 case 'R':
2264 current_command = PLAYLIST_COMMAND_RESET;
2265 break;
2266 case '#':
2267 current_command = PLAYLIST_COMMAND_COMMENT;
2268 break;
2269 default:
2270 result = -1;
2271 exit_loop = true;
2272 break;
2275 str_count = -1;
2276 str1 = NULL;
2277 str2 = NULL;
2278 str3 = NULL;
2280 else if(current_command != PLAYLIST_COMMAND_COMMENT)
2282 /* all control file strings are separated with a colon.
2283 Replace the colon with 0 to get proper strings that can be
2284 used by commands above */
2285 if (*p == ':')
2287 *p = '\0';
2288 str_count++;
2290 if ((count+1) < nread)
2292 switch (str_count)
2294 case 0:
2295 str1 = p+1;
2296 break;
2297 case 1:
2298 str2 = p+1;
2299 break;
2300 case 2:
2301 str3 = p+1;
2302 break;
2303 default:
2304 /* allow last string to contain colons */
2305 *p = ':';
2306 break;
2313 if (result < 0)
2315 splash(HZ*2, ID2P(LANG_PLAYLIST_CONTROL_INVALID));
2316 return result;
2319 if (useraborted)
2321 splash(HZ*2, ID2P(LANG_CANCEL));
2322 return -1;
2324 if (!newline || (exit_loop && count<nread))
2326 if ((total_read + count) >= control_file_size)
2328 /* no newline at end of control file */
2329 splash(HZ*2, ID2P(LANG_PLAYLIST_CONTROL_INVALID));
2330 return -1;
2333 /* We didn't end on a newline or we exited loop prematurely.
2334 Either way, re-read the remainder. */
2335 count = last_newline;
2336 lseek(playlist->control_fd, total_read+count, SEEK_SET);
2339 total_read += count;
2341 if (first)
2342 /* still looking for header */
2343 nread = read(playlist->control_fd, buffer,
2344 PLAYLIST_COMMAND_SIZE<buflen?PLAYLIST_COMMAND_SIZE:buflen);
2345 else
2346 nread = read(playlist->control_fd, buffer, buflen);
2348 /* Terminate on EOF */
2349 if(nread <= 0)
2351 break;
2355 #ifdef HAVE_DIRCACHE
2356 queue_post(&playlist_queue, PLAYLIST_LOAD_POINTERS, 0);
2357 #endif
2359 return 0;
2363 * Add track to in_ram playlist. Used when playing directories.
2365 int playlist_add(const char *filename)
2367 struct playlist_info* playlist = &current_playlist;
2368 int len = strlen(filename);
2370 if((len+1 > playlist->buffer_size - playlist->buffer_end_pos) ||
2371 (playlist->amount >= playlist->max_playlist_size))
2373 display_buffer_full();
2374 return -1;
2377 playlist->indices[playlist->amount] = playlist->buffer_end_pos;
2378 #ifdef HAVE_DIRCACHE
2379 playlist->filenames[playlist->amount] = NULL;
2380 #endif
2381 playlist->amount++;
2383 strcpy(&playlist->buffer[playlist->buffer_end_pos], filename);
2384 playlist->buffer_end_pos += len;
2385 playlist->buffer[playlist->buffer_end_pos++] = '\0';
2387 return 0;
2390 /* shuffle newly created playlist using random seed. */
2391 int playlist_shuffle(int random_seed, int start_index)
2393 struct playlist_info* playlist = &current_playlist;
2395 unsigned int seek_pos = 0;
2396 bool start_current = false;
2398 if (start_index >= 0 && global_settings.play_selected)
2400 /* store the seek position before the shuffle */
2401 seek_pos = playlist->indices[start_index];
2402 playlist->index = playlist->first_index = start_index;
2403 start_current = true;
2406 randomise_playlist(playlist, random_seed, start_current, true);
2408 return playlist->index;
2411 /* start playing current playlist at specified index/offset */
2412 void playlist_start(int start_index, int offset)
2414 struct playlist_info* playlist = &current_playlist;
2416 /* Cancel FM radio selection as previous music. For cases where we start
2417 playback without going to the WPS, such as playlist insert.. or
2418 playlist catalog. */
2419 previous_music_is_wps();
2421 playlist->index = start_index;
2423 #if CONFIG_CODEC != SWCODEC
2424 talk_buffer_steal(); /* will use the mp3 buffer */
2425 #endif
2427 playlist->started = true;
2428 sync_control(playlist, false);
2429 audio_play(offset);
2432 /* Returns false if 'steps' is out of bounds, else true */
2433 bool playlist_check(int steps)
2435 struct playlist_info* playlist = &current_playlist;
2437 /* always allow folder navigation */
2438 if (global_settings.next_folder && playlist->in_ram)
2439 return true;
2441 int index = get_next_index(playlist, steps, -1);
2443 if (index < 0 && steps >= 0 && global_settings.repeat_mode == REPEAT_SHUFFLE)
2444 index = get_next_index(playlist, steps, REPEAT_ALL);
2446 return (index >= 0);
2449 /* get trackname of track that is "steps" away from current playing track.
2450 NULL is used to identify end of playlist */
2451 char* playlist_peek(int steps)
2453 struct playlist_info* playlist = &current_playlist;
2454 int seek;
2455 char *temp_ptr;
2456 int index;
2457 bool control_file;
2459 index = get_next_index(playlist, steps, -1);
2460 if (index < 0)
2461 return NULL;
2463 control_file = playlist->indices[index] & PLAYLIST_INSERT_TYPE_MASK;
2464 seek = playlist->indices[index] & PLAYLIST_SEEK_MASK;
2466 if (get_filename(playlist, index, seek, control_file, now_playing,
2467 MAX_PATH+1) < 0)
2468 return NULL;
2470 temp_ptr = now_playing;
2472 if (!playlist->in_ram || control_file)
2474 /* remove bogus dirs from beginning of path
2475 (workaround for buggy playlist creation tools) */
2476 while (temp_ptr)
2478 if (file_exists(temp_ptr))
2479 break;
2481 temp_ptr = strchr(temp_ptr+1, '/');
2484 if (!temp_ptr)
2486 /* Even though this is an invalid file, we still need to pass a
2487 file name to the caller because NULL is used to indicate end
2488 of playlist */
2489 return now_playing;
2493 return temp_ptr;
2497 * Update indices as track has changed
2499 int playlist_next(int steps)
2501 struct playlist_info* playlist = &current_playlist;
2502 int index;
2504 if ( (steps > 0)
2505 #ifdef AB_REPEAT_ENABLE
2506 && (global_settings.repeat_mode != REPEAT_AB)
2507 #endif
2508 && (global_settings.repeat_mode != REPEAT_ONE) )
2510 int i, j;
2512 /* We need to delete all the queued songs */
2513 for (i=0, j=steps; i<j; i++)
2515 index = get_next_index(playlist, i, -1);
2517 if (playlist->indices[index] & PLAYLIST_QUEUE_MASK)
2519 remove_track_from_playlist(playlist, index, true);
2520 steps--; /* one less track */
2525 index = get_next_index(playlist, steps, -1);
2527 if (index < 0)
2529 /* end of playlist... or is it */
2530 if (global_settings.repeat_mode == REPEAT_SHUFFLE &&
2531 playlist->amount > 1)
2533 /* Repeat shuffle mode. Re-shuffle playlist and resume play */
2534 playlist->first_index = 0;
2535 sort_playlist(playlist, false, false);
2536 randomise_playlist(playlist, current_tick, false, true);
2537 #if CONFIG_CODEC != SWCODEC
2538 playlist_start(0, 0);
2539 #endif
2540 playlist->index = 0;
2541 index = 0;
2543 else if (playlist->in_ram && global_settings.next_folder)
2545 index = create_and_play_dir(steps, true);
2547 if (index >= 0)
2549 playlist->index = index;
2553 return index;
2556 playlist->index = index;
2558 if (playlist->last_insert_pos >= 0 && steps > 0)
2560 /* check to see if we've gone beyond the last inserted track */
2561 int cur = rotate_index(playlist, index);
2562 int last_pos = rotate_index(playlist, playlist->last_insert_pos);
2564 if (cur > last_pos)
2566 /* reset last inserted track */
2567 playlist->last_insert_pos = -1;
2569 if (playlist->control_fd >= 0)
2571 int result = update_control(playlist, PLAYLIST_COMMAND_RESET,
2572 -1, -1, NULL, NULL, NULL);
2574 if (result < 0)
2575 return result;
2577 sync_control(playlist, false);
2582 return index;
2585 /* try playing next or previous folder */
2586 bool playlist_next_dir(int direction)
2588 /* not to mess up real playlists */
2589 if(!current_playlist.in_ram)
2590 return false;
2592 return create_and_play_dir(direction, false) >= 0;
2595 /* Get resume info for current playing song. If return value is -1 then
2596 settings shouldn't be saved. */
2597 int playlist_get_resume_info(int *resume_index)
2599 struct playlist_info* playlist = &current_playlist;
2601 *resume_index = playlist->index;
2603 return 0;
2606 /* Update resume info for current playing song. Returns -1 on error. */
2607 int playlist_update_resume_info(const struct mp3entry* id3)
2609 struct playlist_info* playlist = &current_playlist;
2611 if (id3)
2613 if (global_status.resume_index != playlist->index ||
2614 global_status.resume_offset != id3->offset)
2616 global_status.resume_index = playlist->index;
2617 global_status.resume_offset = id3->offset;
2618 status_save();
2621 else
2623 global_status.resume_index = -1;
2624 global_status.resume_offset = -1;
2625 status_save();
2628 return 0;
2631 /* Returns index of current playing track for display purposes. This value
2632 should not be used for resume purposes as it doesn't represent the actual
2633 index into the playlist */
2634 int playlist_get_display_index(void)
2636 struct playlist_info* playlist = &current_playlist;
2638 /* first_index should always be index 0 for display purposes */
2639 int index = rotate_index(playlist, playlist->index);
2641 return (index+1);
2644 /* returns number of tracks in current playlist */
2645 int playlist_amount(void)
2647 return playlist_amount_ex(NULL);
2649 /* set playlist->last_shuffle_start to playlist->amount for
2650 PLAYLIST_INSERT_LAST_SHUFFLED command purposes*/
2651 void playlist_set_last_shuffled_start(void)
2653 struct playlist_info* playlist = &current_playlist;
2654 playlist->last_shuffled_start = playlist->amount;
2657 * Create a new playlist If playlist is not NULL then we're loading a
2658 * playlist off disk for viewing/editing. The index_buffer is used to store
2659 * playlist indices (required for and only used if !current playlist). The
2660 * temp_buffer (if not NULL) is used as a scratchpad when loading indices.
2662 int playlist_create_ex(struct playlist_info* playlist,
2663 const char* dir, const char* file,
2664 void* index_buffer, int index_buffer_size,
2665 void* temp_buffer, int temp_buffer_size)
2667 if (!playlist)
2668 playlist = &current_playlist;
2669 else
2671 /* Initialize playlist structure */
2672 int r = rand() % 10;
2673 playlist->current = false;
2675 /* Use random name for control file */
2676 snprintf(playlist->control_filename, sizeof(playlist->control_filename),
2677 "%s.%d", PLAYLIST_CONTROL_FILE, r);
2678 playlist->fd = -1;
2679 playlist->control_fd = -1;
2681 if (index_buffer)
2683 int num_indices = index_buffer_size / sizeof(int);
2685 #ifdef HAVE_DIRCACHE
2686 num_indices /= 2;
2687 #endif
2688 if (num_indices > global_settings.max_files_in_playlist)
2689 num_indices = global_settings.max_files_in_playlist;
2691 playlist->max_playlist_size = num_indices;
2692 playlist->indices = index_buffer;
2693 #ifdef HAVE_DIRCACHE
2694 playlist->filenames = (const struct dircache_entry **)
2695 &playlist->indices[num_indices];
2696 #endif
2698 else
2700 playlist->max_playlist_size = current_playlist.max_playlist_size;
2701 playlist->indices = current_playlist.indices;
2702 #ifdef HAVE_DIRCACHE
2703 playlist->filenames = current_playlist.filenames;
2704 #endif
2707 playlist->buffer_size = 0;
2708 playlist->buffer = NULL;
2709 mutex_init(&playlist->control_mutex);
2712 new_playlist(playlist, dir, file);
2714 if (file)
2715 /* load the playlist file */
2716 add_indices_to_playlist(playlist, temp_buffer, temp_buffer_size);
2718 return 0;
2722 * Set the specified playlist as the current.
2723 * NOTE: You will get undefined behaviour if something is already playing so
2724 * remember to stop before calling this. Also, this call will
2725 * effectively close your playlist, making it unusable.
2727 int playlist_set_current(struct playlist_info* playlist)
2729 if (!playlist || (check_control(playlist) < 0))
2730 return -1;
2732 empty_playlist(&current_playlist, false);
2734 strlcpy(current_playlist.filename, playlist->filename,
2735 sizeof(current_playlist.filename));
2737 current_playlist.utf8 = playlist->utf8;
2738 current_playlist.fd = playlist->fd;
2740 close(playlist->control_fd);
2741 close(current_playlist.control_fd);
2742 remove(current_playlist.control_filename);
2743 if (rename(playlist->control_filename,
2744 current_playlist.control_filename) < 0)
2745 return -1;
2746 current_playlist.control_fd = open(current_playlist.control_filename,
2747 O_RDWR);
2748 if (current_playlist.control_fd < 0)
2749 return -1;
2750 current_playlist.control_created = true;
2752 current_playlist.dirlen = playlist->dirlen;
2754 if (playlist->indices && playlist->indices != current_playlist.indices)
2756 memcpy(current_playlist.indices, playlist->indices,
2757 playlist->max_playlist_size*sizeof(int));
2758 #ifdef HAVE_DIRCACHE
2759 memcpy(current_playlist.filenames, playlist->filenames,
2760 playlist->max_playlist_size*sizeof(int));
2761 #endif
2764 current_playlist.first_index = playlist->first_index;
2765 current_playlist.amount = playlist->amount;
2766 current_playlist.last_insert_pos = playlist->last_insert_pos;
2767 current_playlist.seed = playlist->seed;
2768 current_playlist.shuffle_modified = playlist->shuffle_modified;
2769 current_playlist.deleted = playlist->deleted;
2770 current_playlist.num_inserted_tracks = playlist->num_inserted_tracks;
2772 memcpy(current_playlist.control_cache, playlist->control_cache,
2773 sizeof(current_playlist.control_cache));
2774 current_playlist.num_cached = playlist->num_cached;
2775 current_playlist.pending_control_sync = playlist->pending_control_sync;
2777 return 0;
2779 struct playlist_info *playlist_get_current(void)
2781 return &current_playlist;
2784 * Close files and delete control file for non-current playlist.
2786 void playlist_close(struct playlist_info* playlist)
2788 if (!playlist)
2789 return;
2791 if (playlist->fd >= 0)
2792 close(playlist->fd);
2794 if (playlist->control_fd >= 0)
2795 close(playlist->control_fd);
2797 if (playlist->control_created)
2798 remove(playlist->control_filename);
2801 void playlist_sync(struct playlist_info* playlist)
2803 if (!playlist)
2804 playlist = &current_playlist;
2806 sync_control(playlist, false);
2807 if ((audio_status() & AUDIO_STATUS_PLAY) && playlist->started)
2808 audio_flush_and_reload_tracks();
2810 #ifdef HAVE_DIRCACHE
2811 queue_post(&playlist_queue, PLAYLIST_LOAD_POINTERS, 0);
2812 #endif
2816 * Insert track into playlist at specified position (or one of the special
2817 * positions). Returns position where track was inserted or -1 if error.
2819 int playlist_insert_track(struct playlist_info* playlist, const char *filename,
2820 int position, bool queue, bool sync)
2822 int result;
2824 if (!playlist)
2825 playlist = &current_playlist;
2827 if (check_control(playlist) < 0)
2829 splash(HZ*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR));
2830 return -1;
2833 result = add_track_to_playlist(playlist, filename, position, queue, -1);
2835 /* Check if we want manually sync later. For example when adding
2836 * bunch of files from tagcache, syncing after every file wouldn't be
2837 * a good thing to do. */
2838 if (sync && result >= 0)
2839 playlist_sync(playlist);
2841 return result;
2845 * Insert all tracks from specified directory into playlist.
2847 int playlist_insert_directory(struct playlist_info* playlist,
2848 const char *dirname, int position, bool queue,
2849 bool recurse)
2851 int result;
2852 unsigned char *count_str;
2853 struct directory_search_context context;
2855 if (!playlist)
2856 playlist = &current_playlist;
2858 if (check_control(playlist) < 0)
2860 splash(HZ*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR));
2861 return -1;
2864 if (position == PLAYLIST_REPLACE)
2866 if (playlist_remove_all_tracks(playlist) == 0)
2867 position = PLAYLIST_INSERT_LAST;
2868 else
2869 return -1;
2872 if (queue)
2873 count_str = ID2P(LANG_PLAYLIST_QUEUE_COUNT);
2874 else
2875 count_str = ID2P(LANG_PLAYLIST_INSERT_COUNT);
2877 display_playlist_count(0, count_str, false);
2879 context.playlist = playlist;
2880 context.position = position;
2881 context.queue = queue;
2882 context.count = 0;
2884 cpu_boost(true);
2886 result = playlist_directory_tracksearch(dirname, recurse,
2887 directory_search_callback, &context);
2889 sync_control(playlist, false);
2891 cpu_boost(false);
2893 display_playlist_count(context.count, count_str, true);
2895 if ((audio_status() & AUDIO_STATUS_PLAY) && playlist->started)
2896 audio_flush_and_reload_tracks();
2898 #ifdef HAVE_DIRCACHE
2899 queue_post(&playlist_queue, PLAYLIST_LOAD_POINTERS, 0);
2900 #endif
2902 return result;
2906 * Insert all tracks from specified playlist into dynamic playlist.
2908 int playlist_insert_playlist(struct playlist_info* playlist, const char *filename,
2909 int position, bool queue)
2911 int fd;
2912 int max;
2913 char *temp_ptr;
2914 const char *dir;
2915 unsigned char *count_str;
2916 char temp_buf[MAX_PATH+1];
2917 char trackname[MAX_PATH+1];
2918 int count = 0;
2919 int result = 0;
2920 bool utf8 = is_m3u8(filename);
2922 if (!playlist)
2923 playlist = &current_playlist;
2925 if (check_control(playlist) < 0)
2927 splash(HZ*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR));
2928 return -1;
2931 fd = open_utf8(filename, O_RDONLY);
2932 if (fd < 0)
2934 splash(HZ*2, ID2P(LANG_PLAYLIST_ACCESS_ERROR));
2935 return -1;
2938 /* we need the directory name for formatting purposes */
2939 dir = filename;
2941 temp_ptr = strrchr(filename+1,'/');
2942 if (temp_ptr)
2943 *temp_ptr = 0;
2944 else
2945 dir = "/";
2947 if (queue)
2948 count_str = ID2P(LANG_PLAYLIST_QUEUE_COUNT);
2949 else
2950 count_str = ID2P(LANG_PLAYLIST_INSERT_COUNT);
2952 display_playlist_count(count, count_str, false);
2954 if (position == PLAYLIST_REPLACE)
2956 if (playlist_remove_all_tracks(playlist) == 0)
2957 position = PLAYLIST_INSERT_LAST;
2958 else return -1;
2961 cpu_boost(true);
2963 while ((max = read_line(fd, temp_buf, sizeof(temp_buf))) > 0)
2965 /* user abort */
2966 if (action_userabort(TIMEOUT_NOBLOCK))
2967 break;
2969 if (temp_buf[0] != '#' && temp_buf[0] != '\0')
2971 int insert_pos;
2973 if (!utf8)
2975 /* Use trackname as a temporay buffer. Note that trackname must
2976 * be as large as temp_buf.
2978 max = convert_m3u(temp_buf, max, sizeof(temp_buf), trackname);
2981 /* we need to format so that relative paths are correctly
2982 handled */
2983 if (format_track_path(trackname, temp_buf, sizeof(trackname), max,
2984 dir) < 0)
2986 result = -1;
2987 break;
2990 insert_pos = add_track_to_playlist(playlist, trackname, position,
2991 queue, -1);
2993 if (insert_pos < 0)
2995 result = -1;
2996 break;
2999 /* Make sure tracks are inserted in correct order if user
3000 requests INSERT_FIRST */
3001 if (position == PLAYLIST_INSERT_FIRST || position >= 0)
3002 position = insert_pos + 1;
3004 count++;
3006 if ((count%PLAYLIST_DISPLAY_COUNT) == 0)
3008 display_playlist_count(count, count_str, false);
3010 if (count == PLAYLIST_DISPLAY_COUNT &&
3011 (audio_status() & AUDIO_STATUS_PLAY) &&
3012 playlist->started)
3013 audio_flush_and_reload_tracks();
3017 /* let the other threads work */
3018 yield();
3021 close(fd);
3023 if (temp_ptr)
3024 *temp_ptr = '/';
3026 sync_control(playlist, false);
3028 cpu_boost(false);
3030 display_playlist_count(count, count_str, true);
3032 if ((audio_status() & AUDIO_STATUS_PLAY) && playlist->started)
3033 audio_flush_and_reload_tracks();
3035 #ifdef HAVE_DIRCACHE
3036 queue_post(&playlist_queue, PLAYLIST_LOAD_POINTERS, 0);
3037 #endif
3039 return result;
3043 * Delete track at specified index. If index is PLAYLIST_DELETE_CURRENT then
3044 * we want to delete the current playing track.
3046 int playlist_delete(struct playlist_info* playlist, int index)
3048 int result = 0;
3050 if (!playlist)
3051 playlist = &current_playlist;
3053 if (check_control(playlist) < 0)
3055 splash(HZ*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR));
3056 return -1;
3059 if (index == PLAYLIST_DELETE_CURRENT)
3060 index = playlist->index;
3062 result = remove_track_from_playlist(playlist, index, true);
3064 if (result != -1 && (audio_status() & AUDIO_STATUS_PLAY) &&
3065 playlist->started)
3066 audio_flush_and_reload_tracks();
3068 return result;
3072 * Move track at index to new_index. Tracks between the two are shifted
3073 * appropriately. Returns 0 on success and -1 on failure.
3075 int playlist_move(struct playlist_info* playlist, int index, int new_index)
3077 int result;
3078 int seek;
3079 bool control_file;
3080 bool queue;
3081 bool current = false;
3082 int r;
3083 char filename[MAX_PATH];
3085 if (!playlist)
3086 playlist = &current_playlist;
3088 if (check_control(playlist) < 0)
3090 splash(HZ*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR));
3091 return -1;
3094 if (index == new_index)
3095 return -1;
3097 if (index == playlist->index)
3098 /* Moving the current track */
3099 current = true;
3101 control_file = playlist->indices[index] & PLAYLIST_INSERT_TYPE_MASK;
3102 queue = playlist->indices[index] & PLAYLIST_QUEUE_MASK;
3103 seek = playlist->indices[index] & PLAYLIST_SEEK_MASK;
3105 if (get_filename(playlist, index, seek, control_file, filename,
3106 sizeof(filename)) < 0)
3107 return -1;
3109 /* We want to insert the track at the position that was specified by
3110 new_index. This may be different then new_index because of the
3111 shifting that will occur after the delete.
3112 We calculate this before we do the remove as it depends on the
3113 size of the playlist before the track removal */
3114 r = rotate_index(playlist, new_index);
3116 /* Delete track from original position */
3117 result = remove_track_from_playlist(playlist, index, true);
3119 if (result != -1)
3121 if (r == 0)
3122 /* First index */
3123 new_index = PLAYLIST_PREPEND;
3124 else if (r == playlist->amount)
3125 /* Append */
3126 new_index = PLAYLIST_INSERT_LAST;
3127 else
3128 /* Calculate index of desired position */
3129 new_index = (r+playlist->first_index)%playlist->amount;
3131 result = add_track_to_playlist(playlist, filename, new_index, queue,
3132 -1);
3134 if (result != -1)
3136 if (current)
3138 /* Moved the current track */
3139 switch (new_index)
3141 case PLAYLIST_PREPEND:
3142 playlist->index = playlist->first_index;
3143 break;
3144 case PLAYLIST_INSERT_LAST:
3145 playlist->index = playlist->first_index - 1;
3146 if (playlist->index < 0)
3147 playlist->index += playlist->amount;
3148 break;
3149 default:
3150 playlist->index = new_index;
3151 break;
3155 if ((audio_status() & AUDIO_STATUS_PLAY) && playlist->started)
3156 audio_flush_and_reload_tracks();
3160 #ifdef HAVE_DIRCACHE
3161 queue_post(&playlist_queue, PLAYLIST_LOAD_POINTERS, 0);
3162 #endif
3164 return result;
3167 /* shuffle currently playing playlist */
3168 int playlist_randomise(struct playlist_info* playlist, unsigned int seed,
3169 bool start_current)
3171 int result;
3173 if (!playlist)
3174 playlist = &current_playlist;
3176 check_control(playlist);
3178 result = randomise_playlist(playlist, seed, start_current, true);
3180 if (result != -1 && (audio_status() & AUDIO_STATUS_PLAY) &&
3181 playlist->started)
3182 audio_flush_and_reload_tracks();
3184 return result;
3187 /* sort currently playing playlist */
3188 int playlist_sort(struct playlist_info* playlist, bool start_current)
3190 int result;
3192 if (!playlist)
3193 playlist = &current_playlist;
3195 check_control(playlist);
3197 result = sort_playlist(playlist, start_current, true);
3199 if (result != -1 && (audio_status() & AUDIO_STATUS_PLAY) &&
3200 playlist->started)
3201 audio_flush_and_reload_tracks();
3203 return result;
3206 /* returns true if playlist has been modified */
3207 bool playlist_modified(const struct playlist_info* playlist)
3209 if (!playlist)
3210 playlist = &current_playlist;
3212 if (playlist->shuffle_modified ||
3213 playlist->deleted ||
3214 playlist->num_inserted_tracks > 0)
3215 return true;
3217 return false;
3220 /* returns index of first track in playlist */
3221 int playlist_get_first_index(const struct playlist_info* playlist)
3223 if (!playlist)
3224 playlist = &current_playlist;
3226 return playlist->first_index;
3229 /* returns shuffle seed of playlist */
3230 int playlist_get_seed(const struct playlist_info* playlist)
3232 if (!playlist)
3233 playlist = &current_playlist;
3235 return playlist->seed;
3238 /* returns number of tracks in playlist (includes queued/inserted tracks) */
3239 int playlist_amount_ex(const struct playlist_info* playlist)
3241 if (!playlist)
3242 playlist = &current_playlist;
3244 return playlist->amount;
3247 /* returns full path of playlist (minus extension) */
3248 char *playlist_name(const struct playlist_info* playlist, char *buf,
3249 int buf_size)
3251 char *sep;
3253 if (!playlist)
3254 playlist = &current_playlist;
3256 strlcpy(buf, playlist->filename+playlist->dirlen, buf_size);
3258 if (!buf[0])
3259 return NULL;
3261 /* Remove extension */
3262 sep = strrchr(buf, '.');
3263 if (sep)
3264 *sep = 0;
3266 return buf;
3269 /* returns the playlist filename */
3270 char *playlist_get_name(const struct playlist_info* playlist, char *buf,
3271 int buf_size)
3273 if (!playlist)
3274 playlist = &current_playlist;
3276 strlcpy(buf, playlist->filename, buf_size);
3278 if (!buf[0])
3279 return NULL;
3281 return buf;
3284 /* Fills info structure with information about track at specified index.
3285 Returns 0 on success and -1 on failure */
3286 int playlist_get_track_info(struct playlist_info* playlist, int index,
3287 struct playlist_track_info* info)
3289 int seek;
3290 bool control_file;
3292 if (!playlist)
3293 playlist = &current_playlist;
3295 if (index < 0 || index >= playlist->amount)
3296 return -1;
3298 control_file = playlist->indices[index] & PLAYLIST_INSERT_TYPE_MASK;
3299 seek = playlist->indices[index] & PLAYLIST_SEEK_MASK;
3301 if (get_filename(playlist, index, seek, control_file, info->filename,
3302 sizeof(info->filename)) < 0)
3303 return -1;
3305 info->attr = 0;
3307 if (control_file)
3309 if (playlist->indices[index] & PLAYLIST_QUEUE_MASK)
3310 info->attr |= PLAYLIST_ATTR_QUEUED;
3311 else
3312 info->attr |= PLAYLIST_ATTR_INSERTED;
3316 if (playlist->indices[index] & PLAYLIST_SKIPPED)
3317 info->attr |= PLAYLIST_ATTR_SKIPPED;
3319 info->index = index;
3320 info->display_index = rotate_index(playlist, index) + 1;
3322 return 0;
3325 /* save the current dynamic playlist to specified file */
3326 int playlist_save(struct playlist_info* playlist, char *filename)
3328 int fd;
3329 int i, index;
3330 int count = 0;
3331 char path[MAX_PATH+1];
3332 char tmp_buf[MAX_PATH+1];
3333 int result = 0;
3334 bool overwrite_current = false;
3335 int* index_buf = NULL;
3337 if (!playlist)
3338 playlist = &current_playlist;
3340 if (playlist->amount <= 0)
3341 return -1;
3343 /* use current working directory as base for pathname */
3344 if (format_track_path(path, filename, sizeof(tmp_buf),
3345 strlen(filename)+1, getcwd(NULL, -1)) < 0)
3346 return -1;
3348 if (!strncmp(playlist->filename, path, strlen(path)))
3350 /* Attempting to overwrite current playlist file.*/
3352 if (playlist->buffer_size < (int)(playlist->amount * sizeof(int)))
3354 /* not enough buffer space to store updated indices */
3355 splash(HZ*2, ID2P(LANG_PLAYLIST_ACCESS_ERROR));
3356 return -1;
3359 /* in_ram buffer is unused for m3u files so we'll use for storing
3360 updated indices */
3361 index_buf = (int*)playlist->buffer;
3363 /* use temporary pathname */
3364 snprintf(path, sizeof(path), "%s_temp", playlist->filename);
3365 overwrite_current = true;
3368 if (is_m3u8(path))
3370 fd = open_utf8(path, O_CREAT|O_WRONLY|O_TRUNC);
3372 else
3374 /* some applications require a BOM to read the file properly */
3375 fd = open(path, O_CREAT|O_WRONLY|O_TRUNC);
3377 if (fd < 0)
3379 splash(HZ*2, ID2P(LANG_PLAYLIST_ACCESS_ERROR));
3380 return -1;
3383 display_playlist_count(count, ID2P(LANG_PLAYLIST_SAVE_COUNT), false);
3385 cpu_boost(true);
3387 index = playlist->first_index;
3388 for (i=0; i<playlist->amount; i++)
3390 bool control_file;
3391 bool queue;
3392 int seek;
3394 /* user abort */
3395 if (action_userabort(TIMEOUT_NOBLOCK))
3397 result = -1;
3398 break;
3401 control_file = playlist->indices[index] & PLAYLIST_INSERT_TYPE_MASK;
3402 queue = playlist->indices[index] & PLAYLIST_QUEUE_MASK;
3403 seek = playlist->indices[index] & PLAYLIST_SEEK_MASK;
3405 /* Don't save queued files */
3406 if (!queue)
3408 if (get_filename(playlist, index, seek, control_file, tmp_buf,
3409 MAX_PATH+1) < 0)
3411 result = -1;
3412 break;
3415 if (overwrite_current)
3416 index_buf[count] = lseek(fd, 0, SEEK_CUR);
3418 if (fdprintf(fd, "%s\n", tmp_buf) < 0)
3420 splash(HZ*2, ID2P(LANG_PLAYLIST_ACCESS_ERROR));
3421 result = -1;
3422 break;
3425 count++;
3427 if ((count % PLAYLIST_DISPLAY_COUNT) == 0)
3428 display_playlist_count(count, ID2P(LANG_PLAYLIST_SAVE_COUNT),
3429 false);
3431 yield();
3434 index = (index+1)%playlist->amount;
3437 display_playlist_count(count, ID2P(LANG_PLAYLIST_SAVE_COUNT), true);
3439 close(fd);
3441 if (overwrite_current && result >= 0)
3443 result = -1;
3445 mutex_lock(&playlist->control_mutex);
3447 /* Replace the current playlist with the new one and update indices */
3448 close(playlist->fd);
3449 if (remove(playlist->filename) >= 0)
3451 if (rename(path, playlist->filename) >= 0)
3453 playlist->fd = open_utf8(playlist->filename, O_RDONLY);
3454 if (playlist->fd >= 0)
3456 index = playlist->first_index;
3457 for (i=0, count=0; i<playlist->amount; i++)
3459 if (!(playlist->indices[index] & PLAYLIST_QUEUE_MASK))
3461 playlist->indices[index] = index_buf[count];
3462 count++;
3464 index = (index+1)%playlist->amount;
3467 /* we need to recreate control because inserted tracks are
3468 now part of the playlist and shuffle has been
3469 invalidated */
3470 result = recreate_control(playlist);
3475 mutex_unlock(&playlist->control_mutex);
3479 cpu_boost(false);
3481 return result;
3485 * Search specified directory for tracks and notify via callback. May be
3486 * called recursively.
3488 int playlist_directory_tracksearch(const char* dirname, bool recurse,
3489 int (*callback)(char*, void*),
3490 void* context)
3492 char buf[MAX_PATH+1];
3493 int result = 0;
3494 int num_files = 0;
3495 int i;
3496 struct entry *files;
3497 struct tree_context* tc = tree_get_context();
3498 int old_dirfilter = *(tc->dirfilter);
3500 if (!callback)
3501 return -1;
3503 /* use the tree browser dircache to load files */
3504 *(tc->dirfilter) = SHOW_ALL;
3506 if (ft_load(tc, dirname) < 0)
3508 splash(HZ*2, ID2P(LANG_PLAYLIST_DIRECTORY_ACCESS_ERROR));
3509 *(tc->dirfilter) = old_dirfilter;
3510 return -1;
3513 files = (struct entry*) tc->dircache;
3514 num_files = tc->filesindir;
3516 /* we've overwritten the dircache so tree browser will need to be
3517 reloaded */
3518 reload_directory();
3520 for (i=0; i<num_files; i++)
3522 /* user abort */
3523 if (action_userabort(TIMEOUT_NOBLOCK))
3525 result = -1;
3526 break;
3529 if (files[i].attr & ATTR_DIRECTORY)
3531 if (recurse)
3533 /* recursively add directories */
3534 snprintf(buf, sizeof(buf), "%s/%s", dirname, files[i].name);
3535 result = playlist_directory_tracksearch(buf, recurse,
3536 callback, context);
3537 if (result < 0)
3538 break;
3540 /* we now need to reload our current directory */
3541 if(ft_load(tc, dirname) < 0)
3543 result = -1;
3544 break;
3547 files = (struct entry*) tc->dircache;
3548 num_files = tc->filesindir;
3549 if (!num_files)
3551 result = -1;
3552 break;
3555 else
3556 continue;
3558 else if ((files[i].attr & FILE_ATTR_MASK) == FILE_ATTR_AUDIO)
3560 snprintf(buf, sizeof(buf), "%s/%s", dirname, files[i].name);
3562 if (callback(buf, context) != 0)
3564 result = -1;
3565 break;
3568 /* let the other threads work */
3569 yield();
3573 /* restore dirfilter */
3574 *(tc->dirfilter) = old_dirfilter;
3576 return result;