summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorWilliam Wilgus <wilgus.william@gmail.com>2022-12-03 06:21:52 -0500
committerWilliam Wilgus <me.theuser@yahoo.com>2022-12-03 06:28:48 -0500
commit03c225fe54a2ae4699f92473ea21da11ba043148 (patch)
treeef9513da39eb6a75905516d6633223be5116cbb0
parent90dc64da322f428b058740a9e1847cf469a3bdea (diff)
downloadrockbox-03c225fe54.tar.gz
rockbox-03c225fe54.zip
playlist.c clean-up and organize
No functional changes Change-Id: I5c7a4a63c54bc867b7d6c2ca6cbc8ef135e01a90
-rw-r--r--apps/playlist.c3753
-rw-r--r--apps/playlist.h37
2 files changed, 1881 insertions, 1909 deletions
diff --git a/apps/playlist.c b/apps/playlist.c
index d71257a515..23195b4417 100644
--- a/apps/playlist.c
+++ b/apps/playlist.c
@@ -29,7 +29,7 @@
directory, there will be no playlist file.
2. Control file : This file is automatically created when a playlist is
started and contains all the commands done to it.
-
+
The first non-comment line in a control file must begin with
"P:VERSION:DIR:FILE" where VERSION is the playlist control file version,
DIR is the directory where the playlist is located and FILE is the
@@ -68,8 +68,8 @@
*/
#include <stdio.h>
-#include <stdlib.h>
-#include <ctype.h>
+#include <stdlib.h>
+#include <ctype.h>
#include "string-extra.h"
#include "playlist.h"
#include "ata_idle_notify.h"
@@ -127,14 +127,15 @@
/* default load buffer size (should be at least 1 KiB) */
#define PLAYLIST_LOAD_BUFLEN (32*1024)
-
#define PLAYLIST_CONTROL_FILE_VERSION 2
+#define PLAYLIST_COMMAND_SIZE (MAX_PATH+12)
+
/*
Each playlist index has a flag associated with it which identifies what
type of track it is. These flags are stored in the 4 high order bits of
the index.
-
+
NOTE: This limits the playlist file size to a max of 256M.
Bits 31-30:
@@ -169,56 +170,12 @@ struct directory_search_context {
static struct playlist_info current_playlist;
-static void empty_playlist(struct playlist_info* playlist, bool resume);
-static void new_playlist(struct playlist_info* playlist, const char *dir,
- const char *file);
-static void create_control(struct playlist_info* playlist);
-static int check_control(struct playlist_info* playlist);
-static int recreate_control(struct playlist_info* playlist);
-static void update_playlist_filename(struct playlist_info* playlist,
- const char *dir, const char *file);
-static int add_indices_to_playlist(struct playlist_info* playlist,
- char* buffer, size_t buflen);
-static int add_track_to_playlist(struct playlist_info* playlist,
- const char *filename, int position,
- bool queue, int seek_pos);
-static int directory_search_callback(char* filename, void* context);
-static int remove_track_from_playlist(struct playlist_info* playlist,
- int position, bool write);
-static int randomise_playlist(struct playlist_info* playlist,
- unsigned int seed, bool start_current,
- bool write);
-static int sort_playlist(struct playlist_info* playlist, bool start_current,
- bool write);
-static int get_next_index(const struct playlist_info* playlist, int steps,
- int repeat_mode);
-static void find_and_set_playlist_index(struct playlist_info* playlist,
- unsigned int seek);
-static int compare(const void* p1, const void* p2);
-static int get_filename(struct playlist_info* playlist, int index, int seek,
- bool control_file, char *buf, int buf_length);
-static int get_next_directory(char *dir);
-static int get_next_dir(char *dir, bool is_forward);
-static int get_previous_directory(char *dir);
-static int check_subdir_for_music(char *dir, const char *subdir, bool recurse);
-static ssize_t format_track_path(char *dest, char *src, int buf_length,
- const char *dir);
-static void display_playlist_count(int count, const unsigned char *fmt,
- bool final);
-static void display_buffer_full(void);
-static int flush_cached_control(struct playlist_info* playlist);
-static int update_control(struct playlist_info* playlist,
- enum playlist_command command, int i1, int i2,
- const char* s1, const char* s2, void* data);
-static void sync_control(struct playlist_info* playlist, bool force);
-static int rotate_index(const struct playlist_info* playlist, int index);
-
#ifdef HAVE_DIRCACHE
#define PLAYLIST_LOAD_POINTERS 1
static struct event_queue playlist_queue SHAREDBSS_ATTR;
static long playlist_stack[(DEFAULT_STACK_SIZE + 0x800)/sizeof(long)];
-static const char playlist_thread_name[] = "playlist cachectrl";
+static const char thread_playlist_name[] = "playlist cachectrl";
#endif
static struct mutex current_playlist_mutex SHAREDBSS_ATTR;
@@ -253,14 +210,14 @@ static bool is_m3u8(const char* filename)
}
-/* Convert a filename in an M3U playlist to UTF-8.
+/* Convert a filename in an M3U playlist to UTF-8.
*
* buf - the filename to convert; can contain more than one line from the
* playlist.
* buf_len - amount of buf that is used.
* buf_max - total size of buf.
* temp - temporary conversion buffer, at least buf_max bytes.
- *
+ *
* Returns the length of the converted filename.
*/
static int convert_m3u(char* buf, int buf_len, int buf_max, char* temp)
@@ -279,10 +236,10 @@ static int convert_m3u(char* buf, int buf_len, int buf_max, char* temp)
{
i--;
}
-
+
buf_len = i;
dest = temp;
-
+
/* Convert char by char, so as to not overflow temp (iso_decode should
* preferably handle this). No more than 4 bytes should be generated for
* each input char.
@@ -291,13 +248,222 @@ static int convert_m3u(char* buf, int buf_len, int buf_max, char* temp)
{
dest = iso_decode(&buf[i], dest, -1, 1);
}
-
+
*dest = 0;
strcpy(buf, temp);
return dest - temp;
}
/*
+ * create control file for playlist
+ */
+static void create_control(struct playlist_info* playlist)
+{
+ playlist->control_fd = open(playlist->control_filename,
+ O_CREAT|O_RDWR|O_TRUNC, 0666);
+ if (playlist->control_fd < 0)
+ {
+ if (check_rockboxdir())
+ {
+ cond_talk_ids_fq(LANG_PLAYLIST_CONTROL_ACCESS_ERROR);
+ splashf(HZ*2, (unsigned char *)"%s (%d)",
+ str(LANG_PLAYLIST_CONTROL_ACCESS_ERROR),
+ playlist->control_fd);
+ }
+ playlist->control_created = false;
+ }
+ else
+ {
+ playlist->control_created = true;
+ }
+}
+
+/*
+ * Rotate indices such that first_index is index 0
+ */
+static int rotate_index(const struct playlist_info* playlist, int index)
+{
+ index -= playlist->first_index;
+ if (index < 0)
+ index += playlist->amount;
+
+ return index;
+}
+
+/*
+ * sync control file to disk
+ */
+static void sync_control(struct playlist_info* playlist, bool force)
+{
+#ifdef HAVE_DIRCACHE
+ if (playlist->started && force)
+#else
+ (void) force;
+
+ if (playlist->started)
+#endif
+ {
+ if (playlist->pending_control_sync)
+ {
+ mutex_lock(playlist->control_mutex);
+ fsync(playlist->control_fd);
+ playlist->pending_control_sync = false;
+ mutex_unlock(playlist->control_mutex);
+ }
+ }
+}
+
+/*
+ * Flush any cached control commands to disk. Called when playlist is being
+ * modified. Returns 0 on success and -1 on failure.
+ */
+static int flush_cached_control(struct playlist_info* playlist)
+{
+ int result = 0;
+ int i;
+
+ if (!playlist->num_cached)
+ return 0;
+
+ lseek(playlist->control_fd, 0, SEEK_END);
+
+ for (i=0; i<playlist->num_cached; i++)
+ {
+ struct playlist_control_cache* cache =
+ &(playlist->control_cache[i]);
+
+ switch (cache->command)
+ {
+ case PLAYLIST_COMMAND_PLAYLIST:
+ result = fdprintf(playlist->control_fd, "P:%d:%s:%s\n",
+ cache->i1, cache->s1, cache->s2);
+ break;
+ case PLAYLIST_COMMAND_ADD:
+ case PLAYLIST_COMMAND_QUEUE:
+ result = fdprintf(playlist->control_fd, "%c:%d:%d:",
+ (cache->command == PLAYLIST_COMMAND_ADD)?'A':'Q',
+ cache->i1, cache->i2);
+ if (result > 0)
+ {
+ /* save the position in file where name is written */
+ int* seek_pos = (int *)cache->data;
+ *seek_pos = lseek(playlist->control_fd, 0, SEEK_CUR);
+ result = fdprintf(playlist->control_fd, "%s\n",
+ cache->s1);
+ }
+ break;
+ case PLAYLIST_COMMAND_DELETE:
+ result = fdprintf(playlist->control_fd, "D:%d\n", cache->i1);
+ break;
+ case PLAYLIST_COMMAND_SHUFFLE:
+ result = fdprintf(playlist->control_fd, "S:%d:%d\n",
+ cache->i1, cache->i2);
+ break;
+ case PLAYLIST_COMMAND_UNSHUFFLE:
+ result = fdprintf(playlist->control_fd, "U:%d\n", cache->i1);
+ break;
+ case PLAYLIST_COMMAND_RESET:
+ result = fdprintf(playlist->control_fd, "R\n");
+ break;
+ default:
+ break;
+ }
+
+ if (result <= 0)
+ break;
+ }
+
+ if (result > 0)
+ {
+ playlist->num_cached = 0;
+ playlist->pending_control_sync = true;
+
+ result = 0;
+ }
+ else
+ {
+ result = -1;
+ splash(HZ*2, ID2P(LANG_PLAYLIST_CONTROL_UPDATE_ERROR));
+ }
+
+ return result;
+}
+
+/*
+ * Update control data with new command. Depending on the command, it may be
+ * cached or flushed to disk.
+ */
+static int update_control(struct playlist_info* playlist,
+ enum playlist_command command, int i1, int i2,
+ const char* s1, const char* s2, void* data)
+{
+ int result = 0;
+ struct playlist_control_cache* cache;
+ bool flush = false;
+
+ mutex_lock(playlist->control_mutex);
+
+ cache = &(playlist->control_cache[playlist->num_cached++]);
+
+ cache->command = command;
+ cache->i1 = i1;
+ cache->i2 = i2;
+ cache->s1 = s1;
+ cache->s2 = s2;
+ cache->data = data;
+
+ switch (command)
+ {
+ case PLAYLIST_COMMAND_PLAYLIST:
+ case PLAYLIST_COMMAND_ADD:
+ case PLAYLIST_COMMAND_QUEUE:
+#ifndef HAVE_DIRCACHE
+ case PLAYLIST_COMMAND_DELETE:
+ case PLAYLIST_COMMAND_RESET:
+#endif
+ flush = true;
+ break;
+ case PLAYLIST_COMMAND_SHUFFLE:
+ case PLAYLIST_COMMAND_UNSHUFFLE:
+ default:
+ /* only flush when needed */
+ break;
+ }
+
+ if (flush || playlist->num_cached == PLAYLIST_MAX_CACHE)
+ result = flush_cached_control(playlist);
+
+ mutex_unlock(playlist->control_mutex);
+
+ return result;
+}
+
+/*
+ * store directory and name of playlist file
+ */
+static void update_playlist_filename(struct playlist_info* playlist,
+ const char *dir, const char *file)
+{
+ char *sep="";
+ int dirlen = strlen(dir);
+
+ playlist->utf8 = is_m3u8(file);
+
+ /* If the dir does not end in trailing slash, we use a separator.
+ Otherwise we don't. */
+ if(!dirlen || '/' != dir[dirlen-1])
+ {
+ sep="/";
+ dirlen++;
+ }
+
+ playlist->dirlen = dirlen;
+
+ snprintf(playlist->filename, sizeof(playlist->filename),
+ "%s%s%s", dir, sep, file);
+}
+
+/*
* remove any files and indices associated with the playlist
*/
static void empty_playlist(struct playlist_info* playlist, bool resume)
@@ -344,6 +510,73 @@ static void empty_playlist(struct playlist_info* playlist, bool resume)
}
/*
+ * Returns absolute path of track
+ *
+ * dest: output buffer
+ * src: the file name from the playlist
+ * dir: the absolute path to the directory where the playlist resides
+ *
+ * The type of path in "src" determines what will be written to "dest":
+ *
+ * 1. UNIX-style absolute paths (/foo/bar) remain unaltered
+ * 2. Windows-style absolute paths (C:/foo/bar) will be converted into an
+ * absolute path by replacing the drive letter with the volume that the
+ * *playlist* resides on, ie. the volume in "dir"
+ * 3. Relative paths are converted to absolute paths by prepending "dir".
+ * This also applies to Windows-style relative paths "C:foo/bar" where
+ * the drive letter is accepted but ignored.
+ */
+static ssize_t format_track_path(char *dest, char *src, int buf_length,
+ const char *dir)
+{
+ size_t len = 0;
+
+ /* Look for the end of the string */
+ while (1)
+ {
+ int c = src[len];
+ if (c == '\n' || c == '\r' || c == '\0')
+ break;
+ len++;
+ }
+
+ /* Now work back killing white space */
+ while (len > 0)
+ {
+ int c = src[len - 1];
+ if (c != '\t' && c != ' ')
+ break;
+ len--;
+ }
+
+ src[len] = '\0';
+
+ /* Replace backslashes with forward slashes */
+ path_correct_separators(src, src);
+
+ /* Handle Windows-style absolute paths */
+ if (path_strip_drive(src, (const char **)&src, true) >= 0 &&
+ src[-1] == PATH_SEPCH)
+ {
+ #ifdef HAVE_MULTIVOLUME
+ const char *p;
+ path_strip_last_volume(dir, &p, false);
+ dir = strmemdupa(dir, p - dir); /* empty if no volspec on dir */
+ #else
+ dir = ""; /* only volume is root */
+ #endif
+ }
+
+ len = path_append(dest, *dir ? dir : PATH_ROOTSTR, src, buf_length);
+ if (len >= (size_t)buf_length)
+ return -1; /* buffer too small */
+
+ path_remove_dot_segments (dest, dest);
+
+ return strlen (dest);
+}
+
+/*
* Initialize a new playlist for viewing/editing/playing. dir is the
* directory where the playlist is located and file is the filename.
*/
@@ -363,7 +596,7 @@ static void new_playlist(struct playlist_info* playlist, const char *dir,
else
dirused = ""; /* empty playlist */
}
-
+
update_playlist_filename(playlist, dirused, fileused);
if (playlist->control_fd >= 0)
@@ -375,30 +608,6 @@ static void new_playlist(struct playlist_info* playlist, const char *dir,
}
/*
- * create control file for playlist
- */
-static void create_control(struct playlist_info* playlist)
-{
- playlist->control_fd = open(playlist->control_filename,
- O_CREAT|O_RDWR|O_TRUNC, 0666);
- if (playlist->control_fd < 0)
- {
- if (check_rockboxdir())
- {
- cond_talk_ids_fq(LANG_PLAYLIST_CONTROL_ACCESS_ERROR);
- splashf(HZ*2, (unsigned char *)"%s (%d)",
- str(LANG_PLAYLIST_CONTROL_ACCESS_ERROR),
- playlist->control_fd);
- }
- playlist->control_created = false;
- }
- else
- {
- playlist->control_created = true;
- }
-}
-
-/*
* validate the control file. This may include creating/initializing it if
* necessary;
*/
@@ -429,6 +638,40 @@ static int check_control(struct playlist_info* playlist)
return 0;
}
+
+/*
+ * Display buffer full message
+ */
+static void display_buffer_full(void)
+{
+ splash(HZ*2, ID2P(LANG_PLAYLIST_BUFFER_FULL));
+}
+
+/*
+ * Display splash message showing progress of playlist/directory insertion or
+ * save.
+ */
+static void display_playlist_count(int count, const unsigned char *fmt,
+ bool final)
+{
+ static long talked_tick = 0;
+ long id = P2ID(fmt);
+ if(global_settings.talk_menu && id>=0)
+ {
+ if(final || (count && (talked_tick == 0
+ || TIME_AFTER(current_tick, talked_tick+5*HZ))))
+ {
+ talked_tick = current_tick;
+ talk_number(count, false);
+ talk_id(id, true);
+ }
+ }
+ fmt = P2STR(fmt);
+
+ splashf(0, fmt, count, str(LANG_OFF_ABORT));
+}
+
+
/*
* recreate the control file based on current playlist entries
*/
@@ -532,31 +775,6 @@ static int recreate_control(struct playlist_info* playlist)
}
/*
- * store directory and name of playlist file
- */
-static void update_playlist_filename(struct playlist_info* playlist,
- const char *dir, const char *file)
-{
- char *sep="";
- int dirlen = strlen(dir);
-
- playlist->utf8 = is_m3u8(file);
-
- /* If the dir does not end in trailing slash, we use a separator.
- Otherwise we don't. */
- if(!dirlen || '/' != dir[dirlen-1])
- {
- sep="/";
- dirlen++;
- }
-
- playlist->dirlen = dirlen;
-
- snprintf(playlist->filename, sizeof(playlist->filename),
- "%s%s%s", dir, sep, file);
-}
-
-/*
* calculate track offsets within a playlist file
*/
static int add_indices_to_playlist(struct playlist_info* playlist,
@@ -588,7 +806,7 @@ static int add_indices_to_playlist(struct playlist_info* playlist,
i = lseek(playlist->fd, playlist->utf8 ? BOM_UTF_8_SIZE : 0, SEEK_SET);
playlist_fd = playlist->fd;
- playlist->fd = -2; /* DEBUGGING Remove me! */
+ playlist->fd = -2; /* DEBUGGING Remove me! */
splash(0, ID2P(LANG_WAIT));
@@ -642,11 +860,11 @@ static int add_indices_to_playlist(struct playlist_info* playlist,
}
exit:
-/* v DEBUGGING Remove me! */
- if (playlist->fd != -2)
- splashf(HZ, "Error playlist fd");
- playlist->fd = playlist_fd;
-/* ^ DEBUGGING Remove me! */
+/* v DEBUGGING Remove me! */
+ if (playlist->fd != -2)
+ splashf(HZ, "Error playlist fd");
+ playlist->fd = playlist_fd;
+/* ^ DEBUGGING Remove me! */
playlist->amount = amount;
playlist->max_playlist_size = max_playlist_size;
mutex_unlock(playlist->control_mutex);
@@ -657,6 +875,372 @@ exit:
return result;
}
+
+/*
+ * Checks if there are any music files in the dir or any of its
+ * subdirectories. May be called recursively.
+ */
+static int check_subdir_for_music(char *dir, const char *subdir, bool recurse)
+{
+ int result = -1;
+ size_t dirlen = strlen(dir);
+ int num_files = 0;
+ int i;
+ struct entry *files;
+ bool has_music = false;
+ bool has_subdir = false;
+ struct tree_context* tc = tree_get_context();
+
+ if (path_append(dir + dirlen, PA_SEP_HARD, subdir, MAX_PATH - dirlen) >=
+ MAX_PATH - dirlen)
+ {
+ return 0;
+ }
+
+ if (ft_load(tc, dir) < 0)
+ {
+ return -2;
+ }
+
+ tree_lock_cache(tc);
+ files = tree_get_entries(tc);
+ num_files = tc->filesindir;
+
+ for (i=0; i<num_files; i++)
+ {
+ if (files[i].attr & ATTR_DIRECTORY)
+ has_subdir = true;
+ else if ((files[i].attr & FILE_ATTR_MASK) == FILE_ATTR_AUDIO)
+ {
+ has_music = true;
+ break;
+ }
+ }
+
+ if (has_music)
+ {
+ tree_unlock_cache(tc);
+ return 0;
+ }
+
+ if (has_subdir && recurse)
+ {
+ for (i=0; i<num_files; i++)
+ {
+ if (action_userabort(TIMEOUT_NOBLOCK))
+ {
+ result = -2;
+ break;
+ }
+
+ if (files[i].attr & ATTR_DIRECTORY)
+ {
+ result = check_subdir_for_music(dir, files[i].name, true);
+ if (!result)
+ break;
+ }
+ }
+ }
+ tree_unlock_cache(tc);
+
+ if (result < 0)
+ {
+ if (dirlen)
+ {
+ dir[dirlen] = '\0';
+ }
+ else
+ {
+ strcpy(dir, PATH_ROOTSTR);
+ }
+
+ /* we now need to reload our current directory */
+ if(ft_load(tc, dir) < 0)
+ splash(HZ*2, ID2P(LANG_PLAYLIST_DIRECTORY_ACCESS_ERROR));
+ }
+ return result;
+}
+
+/*
+ * search through all the directories (starting with the current) to find
+ * one that has tracks to play
+ */
+static int get_next_dir(char *dir, bool is_forward)
+{
+ struct playlist_info* playlist = &current_playlist;
+ int result = -1;
+ char *start_dir = NULL;
+ bool exit = false;
+ struct tree_context* tc = tree_get_context();
+ int saved_dirfilter = *(tc->dirfilter);
+ unsigned int base_len;
+
+ if (global_settings.constrain_next_folder)
+ {
+ /* constrain results to directories below user's start directory */
+ strcpy(dir, global_settings.start_directory);
+ base_len = strlen(dir);
+
+ /* strip any trailing slash from base directory */
+ if (base_len > 0 && dir[base_len - 1] == '/')
+ {
+ base_len--;
+ dir[base_len] = '\0';
+ }
+ }
+ else
+ {
+ /* start from root directory */
+ dir[0] = '\0';
+ base_len = 0;
+ }
+
+ /* process random folder advance */
+ if (global_settings.next_folder == FOLDER_ADVANCE_RANDOM)
+ {
+ int fd = open(ROCKBOX_DIR "/folder_advance_list.dat", O_RDONLY);
+ if (fd >= 0)
+ {
+ int folder_count = 0;
+ ssize_t nread = read(fd,&folder_count,sizeof(int));
+ if ((nread == sizeof(int)) && folder_count)
+ {
+ char buffer[MAX_PATH];
+ /* give up looking for a directory after we've had four
+ times as many tries as there are directories. */
+ unsigned long allowed_tries = folder_count * 4;
+ int i;
+ srand(current_tick);
+ *(tc->dirfilter) = SHOW_MUSIC;
+ tc->sort_dir = global_settings.sort_dir;
+ while (!exit && allowed_tries--)
+ {
+ i = rand() % folder_count;
+ lseek(fd, sizeof(int) + (MAX_PATH * i), SEEK_SET);
+ read(fd, buffer, MAX_PATH);
+ /* is the current dir within our base dir and has music? */
+ if ((base_len == 0 || !strncmp(buffer, dir, base_len))
+ && check_subdir_for_music(buffer, "", false) == 0)
+ exit = true;
+ }
+ close(fd);
+ *(tc->dirfilter) = saved_dirfilter;
+ tc->sort_dir = global_settings.sort_dir;
+ reload_directory();
+ if (exit)
+ {
+ strcpy(dir,buffer);
+ return 0;
+ }
+ }
+ else
+ close(fd);
+ }
+ }
+
+ /* if the current file is within our base dir, use its dir instead */
+ if (base_len == 0 || !strncmp(playlist->filename, dir, base_len))
+ strmemccpy(dir, playlist->filename, playlist->dirlen);
+
+ /* use the tree browser dircache to load files */
+ *(tc->dirfilter) = SHOW_ALL;
+
+ /* set up sorting/direction */
+ tc->sort_dir = global_settings.sort_dir;
+ if (!is_forward)
+ {
+ static const char sortpairs[] =
+ {
+ [SORT_ALPHA] = SORT_ALPHA_REVERSED,
+ [SORT_DATE] = SORT_DATE_REVERSED,
+ [SORT_TYPE] = SORT_TYPE_REVERSED,
+ [SORT_ALPHA_REVERSED] = SORT_ALPHA,
+ [SORT_DATE_REVERSED] = SORT_DATE,
+ [SORT_TYPE_REVERSED] = SORT_TYPE,
+ };
+
+ if ((unsigned)tc->sort_dir < sizeof(sortpairs))
+ tc->sort_dir = sortpairs[tc->sort_dir];
+ }
+
+ while (!exit)
+ {
+ struct entry *files;
+ int num_files = 0;
+ int i;
+
+ if (ft_load(tc, (dir[0]=='\0')?"/":dir) < 0)
+ {
+ exit = true;
+ result = -1;
+ break;
+ }
+
+ tree_lock_cache(tc);
+ files = tree_get_entries(tc);
+ num_files = tc->filesindir;
+
+ for (i=0; i<num_files; i++)
+ {
+ /* user abort */
+ if (action_userabort(TIMEOUT_NOBLOCK))
+ {
+ result = -1;
+ exit = true;
+ break;
+ }
+
+ if (files[i].attr & ATTR_DIRECTORY)
+ {
+ if (!start_dir)
+ {
+ result = check_subdir_for_music(dir, files[i].name, true);
+ if (result != -1)
+ {
+ exit = true;
+ break;
+ }
+ }
+ else if (!strcmp(start_dir, files[i].name))
+ start_dir = NULL;
+ }
+ }
+ tree_unlock_cache(tc);
+
+ if (!exit)
+ {
+ /* we've already descended to the base dir with nothing found,
+ check whether that contains music */
+ if (strlen(dir) <= base_len)
+ {
+ result = check_subdir_for_music(dir, "", true);
+ if (result == -1)
+ /* there's no music files in the base directory,
+ treat as a fatal error */
+ result = -2;
+ break;
+ }
+ else
+ {
+ /* move down to parent directory. current directory name is
+ stored as the starting point for the search in parent */
+ start_dir = strrchr(dir, '/');
+ if (start_dir)
+ {
+ *start_dir = '\0';
+ start_dir++;
+ }
+ else
+ break;
+ }
+ }
+ }
+
+ /* restore dirfilter */
+ *(tc->dirfilter) = saved_dirfilter;
+ tc->sort_dir = global_settings.sort_dir;
+
+ return result;
+}
+
+static int get_next_directory(char *dir){
+ return get_next_dir(dir, true);
+}
+
+static int get_previous_directory(char *dir){
+ return get_next_dir(dir, false);
+}
+
+
+/*
+ * gets pathname for track at seek index
+ */
+static int get_filename(struct playlist_info* playlist, int index, int seek,
+ bool control_file, char *buf, int buf_length)
+{
+ int fd;
+ int max = -1;
+ char tmp_buf[MAX_PATH+1];
+ char dir_buf[MAX_PATH+1];
+ bool utf8 = playlist->utf8;
+
+ if (buf_length > MAX_PATH+1)
+ buf_length = MAX_PATH+1;
+
+#ifdef HAVE_DIRCACHE
+ if (playlist->dcfrefs)
+ {
+ max = dircache_get_fileref_path(&playlist->dcfrefs[index],
+ tmp_buf, sizeof(tmp_buf));
+ }
+#endif /* HAVE_DIRCACHE */
+
+ if (playlist->in_ram && !control_file && max < 0)
+ {
+ strmemccpy(tmp_buf, (char*)&playlist->buffer[seek], sizeof(tmp_buf));
+ }
+ else if (max < 0)
+ {
+ mutex_lock(playlist->control_mutex);
+
+ if (control_file)
+ {
+ fd = playlist->control_fd;
+ utf8 = true;
+ }
+ else
+ {
+ if(-1 == playlist->fd)
+ playlist->fd = open(playlist->filename, O_RDONLY);
+
+ fd = playlist->fd;
+ }
+
+ if(-1 != fd)
+ {
+
+ if (lseek(fd, seek, SEEK_SET) != seek)
+ max = -1;
+ else
+ {
+ max = read(fd, tmp_buf, MIN((size_t) buf_length, sizeof(tmp_buf)));
+
+ if (max > 0)
+ {
+ /* playlist file may end without a new line - terminate buffer */
+ tmp_buf[MIN(max, (int)sizeof(tmp_buf) - 1)] = '\0';
+
+ /* Use dir_buf as a temporary buffer. Note that dir_buf must
+ * be as large as tmp_buf.
+ */
+ if (!utf8)
+ max = convert_m3u(tmp_buf, max, sizeof(tmp_buf), dir_buf);
+ }
+ }
+ }
+
+ mutex_unlock(playlist->control_mutex);
+
+ if (max < 0)
+ {
+ if (usb_detect() == USB_INSERTED)
+ ; /* ignore error on usb plug */
+ else if (control_file)
+ splash(HZ*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR));
+ else
+ splash(HZ*2, ID2P(LANG_PLAYLIST_ACCESS_ERROR));
+
+ return max;
+ }
+ }
+
+ strmemccpy(dir_buf, playlist->filename, playlist->dirlen);
+
+ return format_track_path(buf, tmp_buf, buf_length, dir_buf);
+
+ (void)index;
+}
+
/*
* Utility function to create a new playlist, fill it with the next or
* previous directory, shuffle it if needed, and start playback.
@@ -701,33 +1285,6 @@ static int create_and_play_dir(int direction, bool play_last)
}
/*
- * Removes all tracks, from the playlist, leaving the presently playing
- * track queued.
- */
-int playlist_remove_all_tracks(struct playlist_info *playlist)
-{
- int result;
-
- if (playlist == NULL)
- playlist = &current_playlist;
-
- while (playlist->index > 0)
- if ((result = remove_track_from_playlist(playlist, 0, true)) < 0)
- return result;
-
- while (playlist->amount > 1)
- if ((result = remove_track_from_playlist(playlist, 1, true)) < 0)
- return result;
-
- if (playlist->amount == 1) {
- playlist->indices[0] |= PLAYLIST_QUEUED;
- }
-
- return 0;
-}
-
-
-/*
* Add track to playlist at specified position. There are seven special
* positions that can be specified:
* PLAYLIST_PREPEND - Add track at beginning of playlist
@@ -803,16 +1360,16 @@ static int add_track_to_playlist(struct playlist_info* playlist,
int offset;
int n = playlist->amount -
rotate_index(playlist, playlist->index);
-
+
if (n > 0)
offset = rand() % n;
else
offset = 0;
-
+
position = playlist->index + offset + 1;
if (position >= playlist->amount)
position -= playlist->amount;
-
+
insert_position = position;
}
else
@@ -828,11 +1385,11 @@ static int add_track_to_playlist(struct playlist_info* playlist,
case PLAYLIST_REPLACE:
if (playlist_remove_all_tracks(playlist) < 0)
return -1;
-
+
playlist->last_insert_pos = position = insert_position = playlist->index + 1;
break;
}
-
+
if (queue)
flags |= PLAYLIST_QUEUED;
@@ -845,7 +1402,7 @@ static int add_track_to_playlist(struct playlist_info* playlist,
playlist->dcfrefs[i] = playlist->dcfrefs[i-1];
#endif
}
-
+
/* update stored indices if needed */
if (orig_position < 0)
@@ -881,7 +1438,7 @@ static int add_track_to_playlist(struct playlist_info* playlist,
playlist->amount++;
playlist->num_inserted_tracks++;
-
+
return insert_position;
}
@@ -918,7 +1475,7 @@ static int directory_search_callback(char* filename, void* context)
count_str = ID2P(LANG_PLAYLIST_INSERT_COUNT);
display_playlist_count(c->count, count_str, false);
-
+
if ((c->count) == PLAYLIST_DISPLAY_COUNT &&
(audio_status() & AUDIO_STATUS_PLAY) &&
c->playlist->started)
@@ -981,11 +1538,32 @@ static int remove_track_from_playlist(struct playlist_info* playlist,
sync_control(playlist, false);
}
-
+
return 0;
}
/*
+ * Search for the seek track and set appropriate indices. Used after shuffle
+ * to make sure the current index is still pointing to correct track.
+ */
+static void find_and_set_playlist_index(struct playlist_info* playlist,
+ unsigned int seek)
+{
+ int i;
+
+ /* Set the index to the current song */
+ for (i=0; i<playlist->amount; i++)
+ {
+ if (playlist->indices[i] == seek)
+ {
+ playlist->index = playlist->first_index = i;
+
+ break;
+ }
+ }
+}
+
+/*
* randomly rearrange the array of indices for the playlist. If start_current
* is true then update the index to the new index of the current playing track
*/
@@ -996,7 +1574,7 @@ static int randomise_playlist(struct playlist_info* playlist,
int count;
int candidate;
unsigned int current = playlist->indices[playlist->index];
-
+
/* seed 0 is used to identify sorted playlist for resume purposes */
if (seed == 0)
seed = 1;
@@ -1039,11 +1617,38 @@ static int randomise_playlist(struct playlist_info* playlist,
update_control(playlist, PLAYLIST_COMMAND_SHUFFLE, seed,
playlist->first_index, NULL, NULL, NULL);
}
-
+
return 0;
}
/*
+ * used to sort track indices. Sort order is as follows:
+ * 1. Prepended tracks (in prepend order)
+ * 2. Playlist/directory tracks (in playlist order)
+ * 3. Inserted/Appended tracks (in insert order)
+ */
+static int compare(const void* p1, const void* p2)
+{
+ unsigned long* e1 = (unsigned long*) p1;
+ unsigned long* e2 = (unsigned long*) p2;
+ unsigned long flags1 = *e1 & PLAYLIST_INSERT_TYPE_MASK;
+ unsigned long flags2 = *e2 & PLAYLIST_INSERT_TYPE_MASK;
+
+ if (flags1 == flags2)
+ return (*e1 & PLAYLIST_SEEK_MASK) - (*e2 & PLAYLIST_SEEK_MASK);
+ else if (flags1 == PLAYLIST_INSERT_TYPE_PREPEND ||
+ flags2 == PLAYLIST_INSERT_TYPE_APPEND)
+ return -1;
+ else if (flags1 == PLAYLIST_INSERT_TYPE_APPEND ||
+ flags2 == PLAYLIST_INSERT_TYPE_PREPEND)
+ return 1;
+ else if (flags1 && flags2)
+ return (*e1 & PLAYLIST_SEEK_MASK) - (*e2 & PLAYLIST_SEEK_MASK);
+ else
+ return *e1 - *e2;
+}
+
+/*
* Sort the array of indices for the playlist. If start_current is true then
* set the index to the new index of the current song.
* Also while going to unshuffled mode set the first_index to 0.
@@ -1079,7 +1684,7 @@ static int sort_playlist(struct playlist_info* playlist, bool start_current,
update_control(playlist, PLAYLIST_COMMAND_UNSHUFFLE,
playlist->first_index, -1, NULL, NULL, NULL);
}
-
+
return 0;
}
@@ -1091,7 +1696,7 @@ static int calculate_step_count(const struct playlist_info *playlist, int steps)
int i, count, direction;
int index;
int stepped_count = 0;
-
+
if (steps < 0)
{
direction = -1;
@@ -1129,28 +1734,6 @@ static int calculate_step_count(const struct playlist_info *playlist, int steps)
return steps;
}
-/* Marks the index of the track to be skipped that is "steps" away from
- * current playing track.
- */
-void playlist_skip_entry(struct playlist_info *playlist, int steps)
-{
- int index;
-
- if (playlist == NULL)
- playlist = &current_playlist;
-
- /* need to account for already skipped tracks */
- steps = calculate_step_count(playlist, steps);
-
- index = playlist->index + steps;
- if (index < 0)
- index += playlist->amount;
- else if (index >= playlist->amount)
- index -= playlist->amount;
-
- playlist->indices[index] |= PLAYLIST_SKIPPED;
-}
-
/*
* returns the index of the track that is "steps" away from current playing
* track.
@@ -1229,56 +1812,8 @@ static int get_next_index(const struct playlist_info* playlist, int steps,
/* No luck if the whole playlist was bad. */
if (playlist->indices[next_index] & PLAYLIST_SKIPPED)
return -1;
-
- return next_index;
-}
-
-/*
- * Search for the seek track and set appropriate indices. Used after shuffle
- * to make sure the current index is still pointing to correct track.
- */
-static void find_and_set_playlist_index(struct playlist_info* playlist,
- unsigned int seek)
-{
- int i;
-
- /* Set the index to the current song */
- for (i=0; i<playlist->amount; i++)
- {
- if (playlist->indices[i] == seek)
- {
- playlist->index = playlist->first_index = i;
-
- break;
- }
- }
-}
-
-/*
- * used to sort track indices. Sort order is as follows:
- * 1. Prepended tracks (in prepend order)
- * 2. Playlist/directory tracks (in playlist order)
- * 3. Inserted/Appended tracks (in insert order)
- */
-static int compare(const void* p1, const void* p2)
-{
- unsigned long* e1 = (unsigned long*) p1;
- unsigned long* e2 = (unsigned long*) p2;
- unsigned long flags1 = *e1 & PLAYLIST_INSERT_TYPE_MASK;
- unsigned long flags2 = *e2 & PLAYLIST_INSERT_TYPE_MASK;
- if (flags1 == flags2)
- return (*e1 & PLAYLIST_SEEK_MASK) - (*e2 & PLAYLIST_SEEK_MASK);
- else if (flags1 == PLAYLIST_INSERT_TYPE_PREPEND ||
- flags2 == PLAYLIST_INSERT_TYPE_APPEND)
- return -1;
- else if (flags1 == PLAYLIST_INSERT_TYPE_APPEND ||
- flags2 == PLAYLIST_INSERT_TYPE_PREPEND)
- return 1;
- else if (flags1 && flags2)
- return (*e1 & PLAYLIST_SEEK_MASK) - (*e2 & PLAYLIST_SEEK_MASK);
- else
- return *e1 - *e2;
+ return next_index;
}
#ifdef HAVE_DIRCACHE
@@ -1287,7 +1822,7 @@ static int compare(const void* p1, const void* p2)
* without affecting playlist load up performance. This thread also flushes
* any pending control commands when the disk spins up.
*/
-static void playlist_flush_callback(void)
+static void flush_playlist_callback(void)
{
struct playlist_info *playlist;
playlist = &current_playlist;
@@ -1303,7 +1838,7 @@ static void playlist_flush_callback(void)
}
}
-static void playlist_thread(void)
+static void thread_playlist(void)
{
struct queue_event ev;
bool dirty_pointers = false;
@@ -1340,12 +1875,12 @@ static void playlist_thread(void)
if (playlist->control_fd >= 0)
{
if (playlist->num_cached > 0)
- register_storage_idle_func(playlist_flush_callback);
+ register_storage_idle_func(flush_playlist_callback);
}
if (!playlist->dcfrefs || playlist->amount <= 0)
break ;
-
+
/* Check if previously loaded pointers are intact. */
if (!dirty_pointers)
break ;
@@ -1364,7 +1899,7 @@ static void playlist_thread(void)
/* Process only pointers that are superficially stale. */
if (dircache_search(DCS_FILEREF, &playlist->dcfrefs[index], NULL) > 0)
continue ;
-
+
control_file = playlist->indices[index] & PLAYLIST_INSERT_TYPE_MASK;
seek = playlist->indices[index] & PLAYLIST_SEEK_MASK;
@@ -1387,10 +1922,10 @@ static void playlist_thread(void)
if (index == playlist->amount)
dirty_pointers = false;
-
+
break ;
}
-
+
case SYS_USB_CONNECTED:
usb_acknowledge(SYS_USB_CONNECTED_ACK);
usb_wait_for_disconnect(&playlist_queue);
@@ -1401,629 +1936,6 @@ static void playlist_thread(void)
#endif
/*
- * gets pathname for track at seek index
- */
-static int get_filename(struct playlist_info* playlist, int index, int seek,
- bool control_file, char *buf, int buf_length)
-{
- int fd;
- int max = -1;
- char tmp_buf[MAX_PATH+1];
- char dir_buf[MAX_PATH+1];
- bool utf8 = playlist->utf8;
-
- if (buf_length > MAX_PATH+1)
- buf_length = MAX_PATH+1;
-
-#ifdef HAVE_DIRCACHE
- if (playlist->dcfrefs)
- {
- max = dircache_get_fileref_path(&playlist->dcfrefs[index],
- tmp_buf, sizeof(tmp_buf));
- }
-#endif /* HAVE_DIRCACHE */
-
- if (playlist->in_ram && !control_file && max < 0)
- {
- strmemccpy(tmp_buf, (char*)&playlist->buffer[seek], sizeof(tmp_buf));
- }
- else if (max < 0)
- {
- mutex_lock(playlist->control_mutex);
-
- if (control_file)
- {
- fd = playlist->control_fd;
- utf8 = true;
- }
- else
- {
- if(-1 == playlist->fd)
- playlist->fd = open(playlist->filename, O_RDONLY);
-
- fd = playlist->fd;
- }
-
- if(-1 != fd)
- {
-
- if (lseek(fd, seek, SEEK_SET) != seek)
- max = -1;
- else
- {
- max = read(fd, tmp_buf, MIN((size_t) buf_length, sizeof(tmp_buf)));
-
- if (max > 0)
- {
- /* playlist file may end without a new line - terminate buffer */
- tmp_buf[MIN(max, (int)sizeof(tmp_buf) - 1)] = '\0';
-
- /* Use dir_buf as a temporary buffer. Note that dir_buf must
- * be as large as tmp_buf.
- */
- if (!utf8)
- max = convert_m3u(tmp_buf, max, sizeof(tmp_buf), dir_buf);
- }
- }
- }
-
- mutex_unlock(playlist->control_mutex);
-
- if (max < 0)
- {
- if (usb_detect() == USB_INSERTED)
- ; /* ignore error on usb plug */
- else if (control_file)
- splash(HZ*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR));
- else
- splash(HZ*2, ID2P(LANG_PLAYLIST_ACCESS_ERROR));
-
- return max;
- }
- }
-
- strmemccpy(dir_buf, playlist->filename, playlist->dirlen);
-
- return format_track_path(buf, tmp_buf, buf_length, dir_buf);
-
- (void)index;
-}
-
-static int get_next_directory(char *dir){
- return get_next_dir(dir, true);
-}
-
-static int get_previous_directory(char *dir){
- return get_next_dir(dir, false);
-}
-
-/*
- * search through all the directories (starting with the current) to find
- * one that has tracks to play
- */
-static int get_next_dir(char *dir, bool is_forward)
-{
- struct playlist_info* playlist = &current_playlist;
- int result = -1;
- char *start_dir = NULL;
- bool exit = false;
- struct tree_context* tc = tree_get_context();
- int saved_dirfilter = *(tc->dirfilter);
- unsigned int base_len;
-
- if (global_settings.constrain_next_folder)
- {
- /* constrain results to directories below user's start directory */
- strcpy(dir, global_settings.start_directory);
- base_len = strlen(dir);
-
- /* strip any trailing slash from base directory */
- if (base_len > 0 && dir[base_len - 1] == '/')
- {
- base_len--;
- dir[base_len] = '\0';
- }
- }
- else
- {
- /* start from root directory */
- dir[0] = '\0';
- base_len = 0;
- }
-
- /* process random folder advance */
- if (global_settings.next_folder == FOLDER_ADVANCE_RANDOM)
- {
- int fd = open(ROCKBOX_DIR "/folder_advance_list.dat", O_RDONLY);
- if (fd >= 0)
- {
- int folder_count = 0;
- ssize_t nread = read(fd,&folder_count,sizeof(int));
- if ((nread == sizeof(int)) && folder_count)
- {
- char buffer[MAX_PATH];
- /* give up looking for a directory after we've had four
- times as many tries as there are directories. */
- unsigned long allowed_tries = folder_count * 4;
- int i;
- srand(current_tick);
- *(tc->dirfilter) = SHOW_MUSIC;
- tc->sort_dir = global_settings.sort_dir;
- while (!exit && allowed_tries--)
- {
- i = rand() % folder_count;
- lseek(fd, sizeof(int) + (MAX_PATH * i), SEEK_SET);
- read(fd, buffer, MAX_PATH);
- /* is the current dir within our base dir and has music? */
- if ((base_len == 0 || !strncmp(buffer, dir, base_len))
- && check_subdir_for_music(buffer, "", false) == 0)
- exit = true;
- }
- close(fd);
- *(tc->dirfilter) = saved_dirfilter;
- tc->sort_dir = global_settings.sort_dir;
- reload_directory();
- if (exit)
- {
- strcpy(dir,buffer);
- return 0;
- }
- }
- else
- close(fd);
- }
- }
-
- /* if the current file is within our base dir, use its dir instead */
- if (base_len == 0 || !strncmp(playlist->filename, dir, base_len))
- strmemccpy(dir, playlist->filename, playlist->dirlen);
-
- /* use the tree browser dircache to load files */
- *(tc->dirfilter) = SHOW_ALL;
-
- /* set up sorting/direction */
- tc->sort_dir = global_settings.sort_dir;
- if (!is_forward)
- {
- static const char sortpairs[] =
- {
- [SORT_ALPHA] = SORT_ALPHA_REVERSED,
- [SORT_DATE] = SORT_DATE_REVERSED,
- [SORT_TYPE] = SORT_TYPE_REVERSED,
- [SORT_ALPHA_REVERSED] = SORT_ALPHA,
- [SORT_DATE_REVERSED] = SORT_DATE,
- [SORT_TYPE_REVERSED] = SORT_TYPE,
- };
-
- if ((unsigned)tc->sort_dir < sizeof(sortpairs))
- tc->sort_dir = sortpairs[tc->sort_dir];
- }
-
- while (!exit)
- {
- struct entry *files;
- int num_files = 0;
- int i;
-
- if (ft_load(tc, (dir[0]=='\0')?"/":dir) < 0)
- {
- exit = true;
- result = -1;
- break;
- }
-
- tree_lock_cache(tc);
- files = tree_get_entries(tc);
- num_files = tc->filesindir;
-
- for (i=0; i<num_files; i++)
- {
- /* user abort */
- if (action_userabort(TIMEOUT_NOBLOCK))
- {
- result = -1;
- exit = true;
- break;
- }
-
- if (files[i].attr & ATTR_DIRECTORY)
- {
- if (!start_dir)
- {
- result = check_subdir_for_music(dir, files[i].name, true);
- if (result != -1)
- {
- exit = true;
- break;
- }
- }
- else if (!strcmp(start_dir, files[i].name))
- start_dir = NULL;
- }
- }
- tree_unlock_cache(tc);
-
- if (!exit)
- {
- /* we've already descended to the base dir with nothing found,
- check whether that contains music */
- if (strlen(dir) <= base_len)
- {
- result = check_subdir_for_music(dir, "", true);
- if (result == -1)
- /* there's no music files in the base directory,
- treat as a fatal error */
- result = -2;
- break;
- }
- else
- {
- /* move down to parent directory. current directory name is
- stored as the starting point for the search in parent */
- start_dir = strrchr(dir, '/');
- if (start_dir)
- {
- *start_dir = '\0';
- start_dir++;
- }
- else
- break;
- }
- }
- }
-
- /* restore dirfilter */
- *(tc->dirfilter) = saved_dirfilter;
- tc->sort_dir = global_settings.sort_dir;
-
- return result;
-}
-
-/*
- * Checks if there are any music files in the dir or any of its
- * subdirectories. May be called recursively.
- */
-static int check_subdir_for_music(char *dir, const char *subdir, bool recurse)
-{
- int result = -1;
- size_t dirlen = strlen(dir);
- int num_files = 0;
- int i;
- struct entry *files;
- bool has_music = false;
- bool has_subdir = false;
- struct tree_context* tc = tree_get_context();
-
- if (path_append(dir + dirlen, PA_SEP_HARD, subdir, MAX_PATH - dirlen) >=
- MAX_PATH - dirlen)
- {
- return 0;
- }
-
- if (ft_load(tc, dir) < 0)
- {
- return -2;
- }
-
- tree_lock_cache(tc);
- files = tree_get_entries(tc);
- num_files = tc->filesindir;
-
- for (i=0; i<num_files; i++)
- {
- if (files[i].attr & ATTR_DIRECTORY)
- has_subdir = true;
- else if ((files[i].attr & FILE_ATTR_MASK) == FILE_ATTR_AUDIO)
- {
- has_music = true;
- break;
- }
- }
-
- if (has_music)
- {
- tree_unlock_cache(tc);
- return 0;
- }
-
- if (has_subdir && recurse)
- {
- for (i=0; i<num_files; i++)
- {
- if (action_userabort(TIMEOUT_NOBLOCK))
- {
- result = -2;
- break;
- }
-
- if (files[i].attr & ATTR_DIRECTORY)
- {
- result = check_subdir_for_music(dir, files[i].name, true);
- if (!result)
- break;
- }
- }
- }
- tree_unlock_cache(tc);
-
- if (result < 0)
- {
- if (dirlen)
- {
- dir[dirlen] = '\0';
- }
- else
- {
- strcpy(dir, PATH_ROOTSTR);
- }
-
- /* we now need to reload our current directory */
- if(ft_load(tc, dir) < 0)
- splash(HZ*2, ID2P(LANG_PLAYLIST_DIRECTORY_ACCESS_ERROR));
- }
- return result;
-}
-
-/*
- * Returns absolute path of track
- *
- * dest: output buffer
- * src: the file name from the playlist
- * dir: the absolute path to the directory where the playlist resides
- *
- * The type of path in "src" determines what will be written to "dest":
- *
- * 1. UNIX-style absolute paths (/foo/bar) remain unaltered
- * 2. Windows-style absolute paths (C:/foo/bar) will be converted into an
- * absolute path by replacing the drive letter with the volume that the
- * *playlist* resides on, ie. the volume in "dir"
- * 3. Relative paths are converted to absolute paths by prepending "dir".
- * This also applies to Windows-style relative paths "C:foo/bar" where
- * the drive letter is accepted but ignored.
- */
-static ssize_t format_track_path(char *dest, char *src, int buf_length,
- const char *dir)
-{
- size_t len = 0;
-
- /* Look for the end of the string */
- while (1)
- {
- int c = src[len];
- if (c == '\n' || c == '\r' || c == '\0')
- break;
- len++;
- }
-
- /* Now work back killing white space */
- while (len > 0)
- {
- int c = src[len - 1];
- if (c != '\t' && c != ' ')
- break;
- len--;
- }
-
- src[len] = '\0';
-
- /* Replace backslashes with forward slashes */
- path_correct_separators(src, src);
-
- /* Handle Windows-style absolute paths */
- if (path_strip_drive(src, (const char **)&src, true) >= 0 &&
- src[-1] == PATH_SEPCH)
- {
- #ifdef HAVE_MULTIVOLUME
- const char *p;
- path_strip_last_volume(dir, &p, false);
- dir = strmemdupa(dir, p - dir); /* empty if no volspec on dir */
- #else
- dir = ""; /* only volume is root */
- #endif
- }
-
- len = path_append(dest, *dir ? dir : PATH_ROOTSTR, src, buf_length);
- if (len >= (size_t)buf_length)
- return -1; /* buffer too small */
-
- path_remove_dot_segments (dest, dest);
-
- return strlen (dest);
-}
-
-/*
- * Display splash message showing progress of playlist/directory insertion or
- * save.
- */
-static void display_playlist_count(int count, const unsigned char *fmt,
- bool final)
-{
- static long talked_tick = 0;
- long id = P2ID(fmt);
- if(global_settings.talk_menu && id>=0)
- {
- if(final || (count && (talked_tick == 0
- || TIME_AFTER(current_tick, talked_tick+5*HZ))))
- {
- talked_tick = current_tick;
- talk_number(count, false);
- talk_id(id, true);
- }
- }
- fmt = P2STR(fmt);
-
- splashf(0, fmt, count, str(LANG_OFF_ABORT));
-}
-
-/*
- * Display buffer full message
- */
-static void display_buffer_full(void)
-{
- splash(HZ*2, ID2P(LANG_PLAYLIST_BUFFER_FULL));
-}
-
-/*
- * Flush any cached control commands to disk. Called when playlist is being
- * modified. Returns 0 on success and -1 on failure.
- */
-static int flush_cached_control(struct playlist_info* playlist)
-{
- int result = 0;
- int i;
-
- if (!playlist->num_cached)
- return 0;
-
- lseek(playlist->control_fd, 0, SEEK_END);
-
- for (i=0; i<playlist->num_cached; i++)
- {
- struct playlist_control_cache* cache =
- &(playlist->control_cache[i]);
-
- switch (cache->command)
- {
- case PLAYLIST_COMMAND_PLAYLIST:
- result = fdprintf(playlist->control_fd, "P:%d:%s:%s\n",
- cache->i1, cache->s1, cache->s2);
- break;
- case PLAYLIST_COMMAND_ADD:
- case PLAYLIST_COMMAND_QUEUE:
- result = fdprintf(playlist->control_fd, "%c:%d:%d:",
- (cache->command == PLAYLIST_COMMAND_ADD)?'A':'Q',
- cache->i1, cache->i2);
- if (result > 0)
- {
- /* save the position in file where name is written */
- int* seek_pos = (int *)cache->data;
- *seek_pos = lseek(playlist->control_fd, 0, SEEK_CUR);
- result = fdprintf(playlist->control_fd, "%s\n",
- cache->s1);
- }
- break;
- case PLAYLIST_COMMAND_DELETE:
- result = fdprintf(playlist->control_fd, "D:%d\n", cache->i1);
- break;
- case PLAYLIST_COMMAND_SHUFFLE:
- result = fdprintf(playlist->control_fd, "S:%d:%d\n",
- cache->i1, cache->i2);
- break;
- case PLAYLIST_COMMAND_UNSHUFFLE:
- result = fdprintf(playlist->control_fd, "U:%d\n", cache->i1);
- break;
- case PLAYLIST_COMMAND_RESET:
- result = fdprintf(playlist->control_fd, "R\n");
- break;
- default:
- break;
- }
-
- if (result <= 0)
- break;
- }
-
- if (result > 0)
- {
- playlist->num_cached = 0;
- playlist->pending_control_sync = true;
-
- result = 0;
- }
- else
- {
- result = -1;
- splash(HZ*2, ID2P(LANG_PLAYLIST_CONTROL_UPDATE_ERROR));
- }
-
- return result;
-}
-
-/*
- * Update control data with new command. Depending on the command, it may be
- * cached or flushed to disk.
- */
-static int update_control(struct playlist_info* playlist,
- enum playlist_command command, int i1, int i2,
- const char* s1, const char* s2, void* data)
-{
- int result = 0;
- struct playlist_control_cache* cache;
- bool flush = false;
-
- mutex_lock(playlist->control_mutex);
-
- cache = &(playlist->control_cache[playlist->num_cached++]);
-
- cache->command = command;
- cache->i1 = i1;
- cache->i2 = i2;
- cache->s1 = s1;
- cache->s2 = s2;
- cache->data = data;
-
- switch (command)
- {
- case PLAYLIST_COMMAND_PLAYLIST:
- case PLAYLIST_COMMAND_ADD:
- case PLAYLIST_COMMAND_QUEUE:
-#ifndef HAVE_DIRCACHE
- case PLAYLIST_COMMAND_DELETE:
- case PLAYLIST_COMMAND_RESET:
-#endif
- flush = true;
- break;
- case PLAYLIST_COMMAND_SHUFFLE:
- case PLAYLIST_COMMAND_UNSHUFFLE:
- default:
- /* only flush when needed */
- break;
- }
-
- if (flush || playlist->num_cached == PLAYLIST_MAX_CACHE)
- result = flush_cached_control(playlist);
-
- mutex_unlock(playlist->control_mutex);
-
- return result;
-}
-
-/*
- * sync control file to disk
- */
-static void sync_control(struct playlist_info* playlist, bool force)
-{
-#ifdef HAVE_DIRCACHE
- if (playlist->started && force)
-#else
- (void) force;
-
- if (playlist->started)
-#endif
- {
- if (playlist->pending_control_sync)
- {
- mutex_lock(playlist->control_mutex);
- fsync(playlist->control_fd);
- playlist->pending_control_sync = false;
- mutex_unlock(playlist->control_mutex);
- }
- }
-}
-
-/*
- * Rotate indices such that first_index is index 0
- */
-static int rotate_index(const struct playlist_info* playlist, int index)
-{
- index -= playlist->first_index;
- if (index < 0)
- index += playlist->amount;
-
- return index;
-}
-
-/*
* Allocate a temporary buffer for loading playlists
*/
static int alloc_tempbuf(size_t* buflen)
@@ -2064,6 +1976,14 @@ static struct buflib_callbacks ops = {
.move_callback = move_callback,
.shrink_callback = NULL,
};
+
+/******************************************************************************/
+/******************************************************************************/
+/* ************************************************************************** */
+/* * PUBLIC INTERFACE FUNCTIONS * *********************************************/
+/* ************************************************************************** */
+/******************************************************************************/
+/******************************************************************************/
/*
* Initialize playlist entries at startup
*/
@@ -2099,8 +2019,8 @@ void playlist_init(void)
playlist->max_playlist_size * sizeof(*playlist->dcfrefs), &ops);
playlist->dcfrefs = core_get_data(handle);
copy_filerefs(playlist->dcfrefs, NULL, playlist->max_playlist_size);
- create_thread(playlist_thread, playlist_stack, sizeof(playlist_stack),
- 0, playlist_thread_name IF_PRIO(, PRIORITY_BACKGROUND)
+ create_thread(thread_playlist, playlist_stack, sizeof(playlist_stack),
+ 0, thread_playlist_name IF_PRIO(, PRIORITY_BACKGROUND)
IF_COP(, CPU));
queue_init(&playlist_queue, true);
@@ -2129,6 +2049,113 @@ void playlist_shutdown(void)
}
/*
+ * Add track to in_ram playlist. Used when playing directories.
+ */
+int playlist_add(const char *filename)
+{
+ struct playlist_info* playlist = &current_playlist;
+ int len = strlen(filename);
+
+ if((len+1 > playlist->buffer_size - playlist->buffer_end_pos) ||
+ (playlist->amount >= playlist->max_playlist_size))
+ {
+ display_buffer_full();
+ return -1;
+ }
+
+ playlist->indices[playlist->amount] = playlist->buffer_end_pos;
+#ifdef HAVE_DIRCACHE
+ copy_filerefs(&playlist->dcfrefs[playlist->amount], NULL, 1);
+#endif
+
+ playlist->amount++;
+
+ strcpy((char*)&playlist->buffer[playlist->buffer_end_pos], filename);
+ playlist->buffer_end_pos += len;
+ playlist->buffer[playlist->buffer_end_pos++] = '\0';
+
+ return 0;
+}
+
+/* returns number of tracks in playlist (includes queued/inserted tracks) */
+int playlist_amount_ex(const struct playlist_info* playlist)
+{
+ if (!playlist)
+ playlist = &current_playlist;
+
+ return playlist->amount;
+}
+
+/* returns number of tracks in current playlist */
+int playlist_amount(void)
+{
+ return playlist_amount_ex(NULL);
+}
+
+/*
+ * Create a new playlist If playlist is not NULL then we're loading a
+ * playlist off disk for viewing/editing. The index_buffer is used to store
+ * playlist indices (required for and only used if !current playlist). The
+ * temp_buffer (if not NULL) is used as a scratchpad when loading indices.
+ */
+int playlist_create_ex(struct playlist_info* playlist,
+ const char* dir, const char* file,
+ void* index_buffer, int index_buffer_size,
+ void* temp_buffer, int temp_buffer_size)
+{
+ if (!playlist)
+ playlist = &current_playlist;
+ else
+ {
+ /* Initialize playlist structure */
+ int r = rand() % 10;
+ playlist->current = false;
+
+ /* Use random name for control file */
+ snprintf(playlist->control_filename, sizeof(playlist->control_filename),
+ "%s.%d", PLAYLIST_CONTROL_FILE, r);
+ playlist->fd = -1;
+ playlist->control_fd = -1;
+
+ if (index_buffer)
+ {
+ int num_indices = index_buffer_size /
+ playlist_get_required_bufsz(playlist, false, 1);
+
+ if (num_indices > global_settings.max_files_in_playlist)
+ num_indices = global_settings.max_files_in_playlist;
+
+ playlist->max_playlist_size = num_indices;
+ playlist->indices = index_buffer;
+#ifdef HAVE_DIRCACHE
+ playlist->dcfrefs = (void *)&playlist->indices[num_indices];
+#endif
+ }
+ else
+ {
+ playlist->max_playlist_size = current_playlist.max_playlist_size;
+ playlist->indices = current_playlist.indices;
+#ifdef HAVE_DIRCACHE
+ playlist->dcfrefs = current_playlist.dcfrefs;
+#endif
+ }
+
+ playlist->buffer_size = 0;
+ playlist->buffer_handle = -1;
+ playlist->buffer = NULL;
+ playlist->control_mutex = &created_playlist_mutex;
+ }
+
+ new_playlist(playlist, dir, file);
+
+ if (file)
+ /* load the playlist file */
+ add_indices_to_playlist(playlist, temp_buffer, temp_buffer_size);
+
+ return 0;
+}
+
+/*
* Create new playlist
*/
int playlist_create(const char *dir, const char *file)
@@ -2162,474 +2189,196 @@ int playlist_create(const char *dir, const char *file)
return 0;
}
-#define PLAYLIST_COMMAND_SIZE (MAX_PATH+12)
-
-/*
- * Restore the playlist state based on control file commands. Called to
- * resume playback after shutdown.
- */
-int playlist_resume(void)
+/* Returns false if 'steps' is out of bounds, else true */
+bool playlist_check(int steps)
{
struct playlist_info* playlist = &current_playlist;
- char *buffer;
- size_t buflen;
- int handle;
- int nread;
- int total_read = 0;
- int control_file_size = 0;
- bool first = true;
- bool sorted = true;
- int result = -1;
- splash(0, ID2P(LANG_WAIT));
- if (core_allocatable() < (1 << 10))
- talk_buffer_set_policy(TALK_BUFFER_LOOSE); /* back off voice buffer */
+ /* always allow folder navigation */
+ if (global_settings.next_folder && playlist->in_ram)
+ return true;
-#ifdef HAVE_DIRCACHE
- dircache_wait(); /* we need the dircache to use the files in the playlist */
-#endif
+ int index = get_next_index(playlist, steps, -1);
- handle = alloc_tempbuf(&buflen);
- if (handle < 0)
- {
- splashf(HZ * 2, "%s(): OOM", __func__);
- return -1;
- }
+ if (index < 0 && steps >= 0 && global_settings.repeat_mode == REPEAT_SHUFFLE)
+ index = get_next_index(playlist, steps, REPEAT_ALL);
- /* align buffer for faster load times */
- buffer = core_get_data(handle);
- STORAGE_ALIGN_BUFFER(buffer, buflen);
- buflen = ALIGN_DOWN(buflen, 512); /* to avoid partial sector I/O */
+ return (index >= 0);
+}
- playlist_shutdown(); /* flush any cached control commands to disk */
- empty_playlist(playlist, true);
+/*
+ * Close files and delete control file for non-current playlist.
+ */
+void playlist_close(struct playlist_info* playlist)
+{
+ if (!playlist)
+ return;
- playlist->control_fd = open(playlist->control_filename, O_RDWR);
- if (playlist->control_fd < 0)
- {
- splash(HZ*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR));
- goto out;
+ if (playlist->fd >= 0) {
+ close(playlist->fd);
+ playlist->fd = -1;
}
- playlist->control_created = true;
- control_file_size = filesize(playlist->control_fd);
- if (control_file_size <= 0)
- {
- splash(HZ*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR));
- goto out;
+ if (playlist->control_fd >= 0) {
+ close(playlist->control_fd);
+ playlist->control_fd = -1;
}
- /* read a small amount first to get the header */
- nread = read(playlist->control_fd, buffer,
- PLAYLIST_COMMAND_SIZE<buflen?PLAYLIST_COMMAND_SIZE:buflen);
- if(nread <= 0)
- {
- splash(HZ*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR));
- goto out;
+ if (playlist->control_created) {
+ remove(playlist->control_filename);
+ playlist->control_created = false;
}
+}
- playlist->started = true;
+/*
+ * Delete track at specified index. If index is PLAYLIST_DELETE_CURRENT then
+ * we want to delete the current playing track.
+ */
+int playlist_delete(struct playlist_info* playlist, int index)
+{
+ int result = 0;
- while (1)
+ if (!playlist)
+ playlist = &current_playlist;
+
+ if (check_control(playlist) < 0)
{
- result = 0;
- int count;
- enum playlist_command current_command = PLAYLIST_COMMAND_COMMENT;
- int last_newline = 0;
- int str_count = -1;
- bool newline = true;
- bool exit_loop = false;
- char *p = buffer;
- char *str1 = NULL;
- char *str2 = NULL;
- char *str3 = NULL;
- unsigned long last_tick = current_tick;
- splash_progress_set_delay(HZ / 2); /* wait 1/2 sec before progress */
- bool useraborted = false;
-
- for(count=0; count<nread && !exit_loop && !useraborted; count++,p++)
- {
- /* Show a splash while we are loading. */
- splash_progress((total_read + count), control_file_size,
- "%s (%s)", str(LANG_WAIT), str(LANG_OFF_ABORT));
- if (TIME_AFTER(current_tick, last_tick + HZ/4))
- {
- if (action_userabort(TIMEOUT_NOBLOCK))
- {
- useraborted = true;
- break;
- }
- last_tick = current_tick;
- }
- /* Are we on a new line? */
- if((*p == '\n') || (*p == '\r'))
- {
- *p = '\0';
+ splash(HZ*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR));
+ return -1;
+ }
- /* save last_newline in case we need to load more data */
- last_newline = count;
+ if (index == PLAYLIST_DELETE_CURRENT)
+ index = playlist->index;
- switch (current_command)
- {
- case PLAYLIST_COMMAND_PLAYLIST:
- {
- /* str1=version str2=dir str3=file */
- int version;
+ result = remove_track_from_playlist(playlist, index, true);
- if (!str1)
- {
- result = -2;
- exit_loop = true;
- break;
- }
-
- if (!str2)
- str2 = "";
-
- if (!str3)
- str3 = "";
-
- version = atoi(str1);
-
- if (version != PLAYLIST_CONTROL_FILE_VERSION)
- {
- result = -3;
- goto out;
- }
-
- update_playlist_filename(playlist, str2, str3);
-
- if (str3[0] != '\0')
- {
- /* NOTE: add_indices_to_playlist() overwrites the
- audiobuf so we need to reload control file
- data */
- add_indices_to_playlist(playlist, buffer, buflen);
- }
- else if (str2[0] != '\0')
- {
- playlist->in_ram = true;
- resume_directory(str2);
- }
-
- /* load the rest of the data */
- first = false;
- exit_loop = true;
+ if (result != -1 && (audio_status() & AUDIO_STATUS_PLAY) &&
+ playlist->started)
+ audio_flush_and_reload_tracks();
- break;
- }
- case PLAYLIST_COMMAND_ADD:
- case PLAYLIST_COMMAND_QUEUE:
- {
- /* str1=position str2=last_position str3=file */
- int position, last_position;
- bool queue;
-
- if (!str1 || !str2 || !str3)
- {
- result = -4;
- exit_loop = true;
- break;
- }
-
- position = atoi(str1);
- last_position = atoi(str2);
-
- queue = (current_command == PLAYLIST_COMMAND_ADD)?
- false:true;
+ return result;
+}
- /* seek position is based on str3's position in
- buffer */
- if (add_track_to_playlist(playlist, str3, position,
- queue, total_read+(str3-buffer)) < 0)
- {
- result = -5;
- goto out;
- }
-
- playlist->last_insert_pos = last_position;
+/*
+ * Search specified directory for tracks and notify via callback. May be
+ * called recursively.
+ */
+int playlist_directory_tracksearch(const char* dirname, bool recurse,
+ int (*callback)(char*, void*),
+ void* context)
+{
+ char buf[MAX_PATH+1];
+ int result = 0;
+ int num_files = 0;
+ int i;;
+ struct tree_context* tc = tree_get_context();
+ struct tree_cache* cache = &tc->cache;
+ int old_dirfilter = *(tc->dirfilter);
- break;
- }
- case PLAYLIST_COMMAND_DELETE:
- {
- /* str1=position */
- int position;
-
- if (!str1)
- {
- result = -6;
- exit_loop = true;
- break;
- }
-
- position = atoi(str1);
-
- if (remove_track_from_playlist(playlist, position,
- false) < 0)
- {
- result = -7;
- goto out;
- }
+ if (!callback)
+ return -1;
- break;
- }
- case PLAYLIST_COMMAND_SHUFFLE:
- {
- /* str1=seed str2=first_index */
- int seed;
-
- if (!str1 || !str2)
- {
- result = -8;
- exit_loop = true;
- break;
- }
-
- if (!sorted)
- {
- /* Always sort list before shuffling */
- sort_playlist(playlist, false, false);
- }
+ /* use the tree browser dircache to load files */
+ *(tc->dirfilter) = SHOW_ALL;
- seed = atoi(str1);
- playlist->first_index = atoi(str2);
-
- if (randomise_playlist(playlist, seed, false,
- false) < 0)
- {
- result = -9;
- goto out;
- }
- sorted = false;
- break;
- }
- case PLAYLIST_COMMAND_UNSHUFFLE:
- {
- /* str1=first_index */
- if (!str1)
- {
- result = -10;
- exit_loop = true;
- break;
- }
-
- playlist->first_index = atoi(str1);
-
- if (sort_playlist(playlist, false, false) < 0)
- {
- result = -11;
- goto out;
- }
+ if (ft_load(tc, dirname) < 0)
+ {
+ splash(HZ*2, ID2P(LANG_PLAYLIST_DIRECTORY_ACCESS_ERROR));
+ *(tc->dirfilter) = old_dirfilter;
+ return -1;
+ }
- sorted = true;
- break;
- }
- case PLAYLIST_COMMAND_RESET:
- {
- playlist->last_insert_pos = -1;
- break;
- }
- case PLAYLIST_COMMAND_COMMENT:
- default:
- break;
- }
+ num_files = tc->filesindir;
- newline = true;
+ /* we've overwritten the dircache so tree browser will need to be
+ reloaded */
+ reload_directory();
- /* to ignore any extra newlines */
- current_command = PLAYLIST_COMMAND_COMMENT;
- }
- else if(newline)
- {
- newline = false;
+ for (i=0; i<num_files; i++)
+ {
+ /* user abort */
+ if (action_userabort(TIMEOUT_NOBLOCK))
+ {
+ result = -1;
+ break;
+ }
- /* first non-comment line must always specify playlist */
- if (first && *p != 'P' && *p != '#')
+ struct entry *files = core_get_data(cache->entries_handle);
+ if (files[i].attr & ATTR_DIRECTORY)
+ {
+ if (recurse)
+ {
+ /* recursively add directories */
+ if (path_append(buf, dirname, files[i].name, sizeof(buf))
+ >= sizeof(buf))
{
- result = -12;
- exit_loop = true;
- break;
+ continue;
}
- switch (*p)
- {
- case 'P':
- /* playlist can only be specified once */
- if (!first)
- {
- result = -13;
- exit_loop = true;
- break;
- }
+ result = playlist_directory_tracksearch(buf, recurse,
+ callback, context);
+ if (result < 0)
+ break;
- current_command = PLAYLIST_COMMAND_PLAYLIST;
- break;
- case 'A':
- current_command = PLAYLIST_COMMAND_ADD;
- break;
- case 'Q':
- current_command = PLAYLIST_COMMAND_QUEUE;
- break;
- case 'D':
- current_command = PLAYLIST_COMMAND_DELETE;
- break;
- case 'S':
- current_command = PLAYLIST_COMMAND_SHUFFLE;
- break;
- case 'U':
- current_command = PLAYLIST_COMMAND_UNSHUFFLE;
- break;
- case 'R':
- current_command = PLAYLIST_COMMAND_RESET;
- break;
- case '#':
- current_command = PLAYLIST_COMMAND_COMMENT;
- break;
- default:
- result = -14;
- exit_loop = true;
- break;
+ /* we now need to reload our current directory */
+ if(ft_load(tc, dirname) < 0)
+ {
+ result = -1;
+ break;
}
- str_count = -1;
- str1 = NULL;
- str2 = NULL;
- str3 = NULL;
- }
- else if(current_command != PLAYLIST_COMMAND_COMMENT)
- {
- /* all control file strings are separated with a colon.
- Replace the colon with 0 to get proper strings that can be
- used by commands above */
- if (*p == ':')
+ num_files = tc->filesindir;
+ if (!num_files)
{
- *p = '\0';
- str_count++;
-
- if ((count+1) < nread)
- {
- switch (str_count)
- {
- case 0:
- str1 = p+1;
- break;
- case 1:
- str2 = p+1;
- break;
- case 2:
- str3 = p+1;
- break;
- default:
- /* allow last string to contain colons */
- *p = ':';
- break;
- }
- }
+ result = -1;
+ break;
}
}
+ else
+ continue;
}
-
- if (result < 0)
- {
- splashf(HZ*2, "Err: %d, %s", result, str(LANG_PLAYLIST_CONTROL_INVALID));
- goto out;
- }
-
- if (useraborted)
- {
- splash(HZ*2, ID2P(LANG_CANCEL));
- result = -1;
- goto out;
- }
- if (!newline || (exit_loop && count<nread))
+ else if ((files[i].attr & FILE_ATTR_MASK) == FILE_ATTR_AUDIO)
{
- if ((total_read + count) >= control_file_size)
+ if (path_append(buf, dirname, files[i].name, sizeof(buf))
+ >= sizeof(buf))
{
- /* no newline at end of control file */
- splashf(HZ*2, "Err: EOF, %s", str(LANG_PLAYLIST_CONTROL_INVALID));
- result = -15;
- goto out;
+ continue;
}
- /* We didn't end on a newline or we exited loop prematurely.
- Either way, re-read the remainder. */
- count = last_newline;
- lseek(playlist->control_fd, total_read+count, SEEK_SET);
- }
-
- total_read += count;
-
- if (first)
- /* still looking for header */
- nread = read(playlist->control_fd, buffer,
- PLAYLIST_COMMAND_SIZE<buflen?PLAYLIST_COMMAND_SIZE:buflen);
- else
- nread = read(playlist->control_fd, buffer, buflen);
+ if (callback(buf, context) != 0)
+ {
+ result = -1;
+ break;
+ }
- /* Terminate on EOF */
- if(nread <= 0)
- {
- break;
+ /* let the other threads work */
+ yield();
}
}
-#ifdef HAVE_DIRCACHE
- queue_post(&playlist_queue, PLAYLIST_LOAD_POINTERS, 0);
-#endif
+ /* restore dirfilter */
+ *(tc->dirfilter) = old_dirfilter;
-out:
- talk_buffer_set_policy(TALK_BUFFER_DEFAULT);
- core_free(handle);
return result;
}
-/*
- * Add track to in_ram playlist. Used when playing directories.
- */
-int playlist_add(const char *filename)
-{
- struct playlist_info* playlist = &current_playlist;
- int len = strlen(filename);
-
- if((len+1 > playlist->buffer_size - playlist->buffer_end_pos) ||
- (playlist->amount >= playlist->max_playlist_size))
- {
- display_buffer_full();
- return -1;
- }
-
- playlist->indices[playlist->amount] = playlist->buffer_end_pos;
-#ifdef HAVE_DIRCACHE
- copy_filerefs(&playlist->dcfrefs[playlist->amount], NULL, 1);
-#endif
-
- playlist->amount++;
-
- strcpy((char*)&playlist->buffer[playlist->buffer_end_pos], filename);
- playlist->buffer_end_pos += len;
- playlist->buffer[playlist->buffer_end_pos++] = '\0';
- return 0;
+struct playlist_info *playlist_get_current(void)
+{
+ return &current_playlist;
}
-/* shuffle newly created playlist using random seed. */
-int playlist_shuffle(int random_seed, int start_index)
+/* Returns index of current playing track for display purposes. This value
+ should not be used for resume purposes as it doesn't represent the actual
+ index into the playlist */
+int playlist_get_display_index(void)
{
struct playlist_info* playlist = &current_playlist;
- bool start_current = false;
-
- if (start_index >= 0 && global_settings.play_selected)
- {
- /* store the seek position before the shuffle */
- playlist->index = playlist->first_index = start_index;
- start_current = true;
- }
-
- randomise_playlist(playlist, random_seed, start_current, true);
+ /* first_index should always be index 0 for display purposes */
+ int index = rotate_index(playlist, playlist->index);
- return playlist->index;
+ return (index+1);
}
/* returns the crc32 of the filename of the track at the specified index */
@@ -2650,212 +2399,48 @@ unsigned int playlist_get_filename_crc32(struct playlist_info *playlist,
return crc_32(basename, strlen(basename), -1);
}
-/* resume a playlist track with the given crc_32 of the track name. */
-void playlist_resume_track(int start_index, unsigned int crc,
- unsigned long elapsed, unsigned long offset)
-{
- int i;
- unsigned int tmp_crc;
- struct playlist_info* playlist = &current_playlist;
- tmp_crc = playlist_get_filename_crc32(playlist, start_index);
- if (tmp_crc == crc)
- {
- playlist_start(start_index, elapsed, offset);
- return;
- }
-
- for (i = 0 ; i < playlist->amount; i++)
- {
- tmp_crc = playlist_get_filename_crc32(playlist, i);
- if (tmp_crc == crc)
- {
- playlist_start(i, elapsed, offset);
- return;
- }
- }
-
- /* If we got here the file wasnt found, so start from the beginning */
- playlist_start(0, 0, 0);
-}
-
-/* start playing current playlist at specified index/offset */
-void playlist_start(int start_index, unsigned long elapsed,
- unsigned long offset)
-{
- struct playlist_info* playlist = &current_playlist;
-
- playlist->index = start_index;
-
- playlist->started = true;
- sync_control(playlist, false);
- audio_play(elapsed, offset);
- audio_resume();
-}
-
-/* Returns false if 'steps' is out of bounds, else true */
-bool playlist_check(int steps)
+/* returns index of first track in playlist */
+int playlist_get_first_index(const struct playlist_info* playlist)
{
- struct playlist_info* playlist = &current_playlist;
-
- /* always allow folder navigation */
- if (global_settings.next_folder && playlist->in_ram)
- return true;
-
- int index = get_next_index(playlist, steps, -1);
-
- if (index < 0 && steps >= 0 && global_settings.repeat_mode == REPEAT_SHUFFLE)
- index = get_next_index(playlist, steps, REPEAT_ALL);
+ if (!playlist)
+ playlist = &current_playlist;
- return (index >= 0);
+ return playlist->first_index;
}
-/* get trackname of track that is "steps" away from current playing track.
- NULL is used to identify end of playlist */
-const char* playlist_peek(int steps, char* buf, size_t buf_size)
+/* returns the playlist filename */
+char *playlist_get_name(const struct playlist_info* playlist, char *buf,
+ int buf_size)
{
- struct playlist_info* playlist = &current_playlist;
- int seek;
- char *temp_ptr;
- int index;
- bool control_file;
-
- index = get_next_index(playlist, steps, -1);
- if (index < 0)
- return NULL;
-
- /* Just testing - don't care about the file name */
- if (!buf || !buf_size)
- return "";
+ if (!playlist)
+ playlist = &current_playlist;
- control_file = playlist->indices[index] & PLAYLIST_INSERT_TYPE_MASK;
- seek = playlist->indices[index] & PLAYLIST_SEEK_MASK;
+ strmemccpy(buf, playlist->filename, buf_size);
- if (get_filename(playlist, index, seek, control_file, buf,
- buf_size) < 0)
+ if (!buf[0])
return NULL;
- temp_ptr = buf;
-
- if (!playlist->in_ram || control_file)
- {
- /* remove bogus dirs from beginning of path
- (workaround for buggy playlist creation tools) */
- while (temp_ptr)
- {
- if (file_exists(temp_ptr))
- break;
-
- temp_ptr = strchr(temp_ptr+1, '/');
- }
-
- if (!temp_ptr)
- {
- /* Even though this is an invalid file, we still need to pass a
- file name to the caller because NULL is used to indicate end
- of playlist */
- return buf;
- }
- }
-
- return temp_ptr;
+ return buf;
}
-/*
- * Update indices as track has changed
- */
-int playlist_next(int steps)
+/* return size of buffer needed for playlist to initialize num_indices entries */
+size_t playlist_get_required_bufsz(struct playlist_info* playlist,
+ bool include_namebuf,
+ int num_indices)
{
- struct playlist_info* playlist = &current_playlist;
- int index;
-
- if ( (steps > 0)
-#ifdef AB_REPEAT_ENABLE
- && (global_settings.repeat_mode != REPEAT_AB)
-#endif
- && (global_settings.repeat_mode != REPEAT_ONE) )
- {
- int i, j;
-
- /* We need to delete all the queued songs */
- for (i=0, j=steps; i<j; i++)
- {
- index = get_next_index(playlist, i, -1);
-
- if (index >= 0 && playlist->indices[index] & PLAYLIST_QUEUE_MASK)
- {
- remove_track_from_playlist(playlist, index, true);
- steps--; /* one less track */
- }
- }
- }
-
- index = get_next_index(playlist, steps, -1);
-
- if (index < 0)
- {
- /* end of playlist... or is it */
- if (global_settings.repeat_mode == REPEAT_SHUFFLE &&
- playlist->amount > 1)
- {
- /* Repeat shuffle mode. Re-shuffle playlist and resume play */
- playlist->first_index = 0;
- sort_playlist(playlist, false, false);
- randomise_playlist(playlist, current_tick, false, true);
-
- playlist->started = true;
- playlist->index = 0;
- index = 0;
- }
- else if (playlist->in_ram && global_settings.next_folder)
- {
- index = create_and_play_dir(steps, true);
-
- if (index >= 0)
- {
- playlist->index = index;
- }
- }
-
- return index;
- }
-
- playlist->index = index;
-
- if (playlist->last_insert_pos >= 0 && steps > 0)
- {
- /* check to see if we've gone beyond the last inserted track */
- int cur = rotate_index(playlist, index);
- int last_pos = rotate_index(playlist, playlist->last_insert_pos);
-
- if (cur > last_pos)
- {
- /* reset last inserted track */
- playlist->last_insert_pos = -1;
-
- if (playlist->control_fd >= 0)
- {
- int result = update_control(playlist, PLAYLIST_COMMAND_RESET,
- -1, -1, NULL, NULL, NULL);
-
- if (result < 0)
- return result;
-
- sync_control(playlist, false);
- }
- }
- }
+ size_t namebuf = 0;
- return index;
-}
+ if (!playlist)
+ playlist = &current_playlist;
-/* try playing next or previous folder */
-bool playlist_next_dir(int direction)
-{
- /* not to mess up real playlists */
- if(!current_playlist.in_ram)
- return false;
+ size_t unit_size = sizeof (*playlist->indices);
+ #ifdef HAVE_DIRCACHE
+ unit_size += sizeof (*playlist->dcfrefs);
+ #endif
+ if (include_namebuf)
+ namebuf = AVERAGE_FILENAME_LENGTH * global_settings.max_files_in_dir;
- return create_and_play_dir(direction, false) >= 0;
+ return (num_indices * unit_size) + namebuf;
}
/* Get resume info for current playing song. If return value is -1 then
@@ -2869,256 +2454,54 @@ int playlist_get_resume_info(int *resume_index)
return 0;
}
-/* Update resume info for current playing song. Returns -1 on error. */
-int playlist_update_resume_info(const struct mp3entry* id3)
+/* returns shuffle seed of playlist */
+int playlist_get_seed(const struct playlist_info* playlist)
{
- struct playlist_info* playlist = &current_playlist;
-
- if (id3)
- {
- if (global_status.resume_index != playlist->index ||
- global_status.resume_elapsed != id3->elapsed ||
- global_status.resume_offset != id3->offset)
- {
- unsigned int crc = playlist_get_filename_crc32(playlist,
- playlist->index);
- global_status.resume_index = playlist->index;
- global_status.resume_crc32 = crc;
- global_status.resume_elapsed = id3->elapsed;
- global_status.resume_offset = id3->offset;
- status_save();
- }
- }
- else
- {
- global_status.resume_index = -1;
- global_status.resume_crc32 = -1;
- global_status.resume_elapsed = -1;
- global_status.resume_offset = -1;
- status_save();
- }
+ if (!playlist)
+ playlist = &current_playlist;
- return 0;
+ return playlist->seed;
}
-/* Returns index of current playing track for display purposes. This value
- should not be used for resume purposes as it doesn't represent the actual
- index into the playlist */
-int playlist_get_display_index(void)
+/* Fills info structure with information about track at specified index.
+ Returns 0 on success and -1 on failure */
+int playlist_get_track_info(struct playlist_info* playlist, int index,
+ struct playlist_track_info* info)
{
- struct playlist_info* playlist = &current_playlist;
-
- /* first_index should always be index 0 for display purposes */
- int index = rotate_index(playlist, playlist->index);
-
- return (index+1);
-}
+ int seek;
+ bool control_file;
-/* returns number of tracks in current playlist */
-int playlist_amount(void)
-{
- return playlist_amount_ex(NULL);
-}
-/* set playlist->last_shuffle_start to playlist->amount for
- PLAYLIST_INSERT_LAST_SHUFFLED command purposes*/
-void playlist_set_last_shuffled_start(void)
-{
- struct playlist_info* playlist = &current_playlist;
- playlist->last_shuffled_start = playlist->amount;
-}
-/*
- * Create a new playlist If playlist is not NULL then we're loading a
- * playlist off disk for viewing/editing. The index_buffer is used to store
- * playlist indices (required for and only used if !current playlist). The
- * temp_buffer (if not NULL) is used as a scratchpad when loading indices.
- */
-int playlist_create_ex(struct playlist_info* playlist,
- const char* dir, const char* file,
- void* index_buffer, int index_buffer_size,
- void* temp_buffer, int temp_buffer_size)
-{
if (!playlist)
playlist = &current_playlist;
- else
- {
- /* Initialize playlist structure */
- int r = rand() % 10;
- playlist->current = false;
-
- /* Use random name for control file */
- snprintf(playlist->control_filename, sizeof(playlist->control_filename),
- "%s.%d", PLAYLIST_CONTROL_FILE, r);
- playlist->fd = -1;
- playlist->control_fd = -1;
-
- if (index_buffer)
- {
- int num_indices = index_buffer_size /
- playlist_get_required_bufsz(playlist, false, 1);
-
- if (num_indices > global_settings.max_files_in_playlist)
- num_indices = global_settings.max_files_in_playlist;
-
- playlist->max_playlist_size = num_indices;
- playlist->indices = index_buffer;
-#ifdef HAVE_DIRCACHE
- playlist->dcfrefs = (void *)&playlist->indices[num_indices];
-#endif
- }
- else
- {
- playlist->max_playlist_size = current_playlist.max_playlist_size;
- playlist->indices = current_playlist.indices;
-#ifdef HAVE_DIRCACHE
- playlist->dcfrefs = current_playlist.dcfrefs;
-#endif
- }
-
- playlist->buffer_size = 0;
- playlist->buffer_handle = -1;
- playlist->buffer = NULL;
- playlist->control_mutex = &created_playlist_mutex;
- }
-
- new_playlist(playlist, dir, file);
- if (file)
- /* load the playlist file */
- add_indices_to_playlist(playlist, temp_buffer, temp_buffer_size);
-
- return 0;
-}
-
-/*
- * Set the specified playlist as the current.
- * NOTE: You will get undefined behaviour if something is already playing so
- * remember to stop before calling this. Also, this call will
- * effectively close your playlist, making it unusable.
- */
-int playlist_set_current(struct playlist_info* playlist)
-{
- if (!playlist || (check_control(playlist) < 0))
+ if (index < 0 || index >= playlist->amount)
return -1;
- empty_playlist(&current_playlist, false);
-
- strmemccpy(current_playlist.filename, playlist->filename,
- sizeof(current_playlist.filename));
-
- current_playlist.utf8 = playlist->utf8;
- current_playlist.fd = playlist->fd;
+ control_file = playlist->indices[index] & PLAYLIST_INSERT_TYPE_MASK;
+ seek = playlist->indices[index] & PLAYLIST_SEEK_MASK;
- close(playlist->control_fd);
- playlist->control_fd = -1;
- close(current_playlist.control_fd);
- current_playlist.control_fd = -1;
- remove(current_playlist.control_filename);
- current_playlist.control_created = false;
- if (rename(playlist->control_filename,
- current_playlist.control_filename) < 0)
- return -1;
- current_playlist.control_fd = open(current_playlist.control_filename,
- O_RDWR);
- if (current_playlist.control_fd < 0)
+ if (get_filename(playlist, index, seek, control_file, info->filename,
+ sizeof(info->filename)) < 0)
return -1;
- current_playlist.control_created = true;
- current_playlist.dirlen = playlist->dirlen;
+ info->attr = 0;
- if (playlist->indices && playlist->indices != current_playlist.indices)
+ if (control_file)
{
- memcpy((void*)current_playlist.indices, (void*)playlist->indices,
- playlist->max_playlist_size*sizeof(*playlist->indices));
-#ifdef HAVE_DIRCACHE
- copy_filerefs(current_playlist.dcfrefs, playlist->dcfrefs,
- playlist->max_playlist_size);
-#endif
- }
-
- current_playlist.first_index = playlist->first_index;
- current_playlist.amount = playlist->amount;
- current_playlist.last_insert_pos = playlist->last_insert_pos;
- current_playlist.seed = playlist->seed;
- current_playlist.shuffle_modified = playlist->shuffle_modified;
- current_playlist.deleted = playlist->deleted;
- current_playlist.num_inserted_tracks = playlist->num_inserted_tracks;
-
- memcpy(current_playlist.control_cache, playlist->control_cache,
- sizeof(current_playlist.control_cache));
- current_playlist.num_cached = playlist->num_cached;
- current_playlist.pending_control_sync = playlist->pending_control_sync;
-
- return 0;
-}
-struct playlist_info *playlist_get_current(void)
-{
- return &current_playlist;
-}
-/*
- * Close files and delete control file for non-current playlist.
- */
-void playlist_close(struct playlist_info* playlist)
-{
- if (!playlist)
- return;
-
- if (playlist->fd >= 0) {
- close(playlist->fd);
- playlist->fd = -1;
- }
-
- if (playlist->control_fd >= 0) {
- close(playlist->control_fd);
- playlist->control_fd = -1;
- }
-
- if (playlist->control_created) {
- remove(playlist->control_filename);
- playlist->control_created = false;
- }
-}
-
-void playlist_sync(struct playlist_info* playlist)
-{
- if (!playlist)
- playlist = &current_playlist;
-
- sync_control(playlist, false);
- if ((audio_status() & AUDIO_STATUS_PLAY) && playlist->started)
- audio_flush_and_reload_tracks();
-
-#ifdef HAVE_DIRCACHE
- queue_post(&playlist_queue, PLAYLIST_LOAD_POINTERS, 0);
-#endif
-}
-
-/*
- * Insert track into playlist at specified position (or one of the special
- * positions). Returns position where track was inserted or -1 if error.
- */
-int playlist_insert_track(struct playlist_info* playlist, const char *filename,
- int position, bool queue, bool sync)
-{
- int result;
-
- if (!playlist)
- playlist = &current_playlist;
+ if (playlist->indices[index] & PLAYLIST_QUEUE_MASK)
+ info->attr |= PLAYLIST_ATTR_QUEUED;
+ else
+ info->attr |= PLAYLIST_ATTR_INSERTED;
- if (check_control(playlist) < 0)
- {
- splash(HZ*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR));
- return -1;
}
- result = add_track_to_playlist(playlist, filename, position, queue, -1);
+ if (playlist->indices[index] & PLAYLIST_SKIPPED)
+ info->attr |= PLAYLIST_ATTR_SKIPPED;
- /* Check if we want manually sync later. For example when adding
- * bunch of files from tagcache, syncing after every file wouldn't be
- * a good thing to do. */
- if (sync && result >= 0)
- playlist_sync(playlist);
+ info->index = index;
+ info->display_index = rotate_index(playlist, index) + 1;
- return result;
+ return 0;
}
/*
@@ -3160,7 +2543,7 @@ int playlist_insert_directory(struct playlist_info* playlist,
context.position = position;
context.queue = queue;
context.count = 0;
-
+
cpu_boost(true);
result = playlist_directory_tracksearch(dirname, recurse,
@@ -3243,11 +2626,11 @@ int playlist_insert_playlist(struct playlist_info* playlist, const char *filenam
/* user abort */
if (action_userabort(TIMEOUT_NOBLOCK))
break;
-
+
if (temp_buf[0] != '#' && temp_buf[0] != '\0')
{
int insert_pos;
-
+
if (!utf8)
{
/* Use trackname as a temporay buffer. Note that trackname must
@@ -3315,12 +2698,13 @@ int playlist_insert_playlist(struct playlist_info* playlist, const char *filenam
}
/*
- * Delete track at specified index. If index is PLAYLIST_DELETE_CURRENT then
- * we want to delete the current playing track.
+ * Insert track into playlist at specified position (or one of the special
+ * positions). Returns position where track was inserted or -1 if error.
*/
-int playlist_delete(struct playlist_info* playlist, int index)
+int playlist_insert_track(struct playlist_info* playlist, const char *filename,
+ int position, bool queue, bool sync)
{
- int result = 0;
+ int result;
if (!playlist)
playlist = &current_playlist;
@@ -3331,18 +2715,32 @@ int playlist_delete(struct playlist_info* playlist, int index)
return -1;
}
- if (index == PLAYLIST_DELETE_CURRENT)
- index = playlist->index;
+ result = add_track_to_playlist(playlist, filename, position, queue, -1);
- result = remove_track_from_playlist(playlist, index, true);
-
- if (result != -1 && (audio_status() & AUDIO_STATUS_PLAY) &&
- playlist->started)
- audio_flush_and_reload_tracks();
+ /* Check if we want manually sync later. For example when adding
+ * bunch of files from tagcache, syncing after every file wouldn't be
+ * a good thing to do. */
+ if (sync && result >= 0)
+ playlist_sync(playlist);
return result;
}
+
+/* returns true if playlist has been modified */
+bool playlist_modified(const struct playlist_info* playlist)
+{
+ if (!playlist)
+ playlist = &current_playlist;
+
+ if (playlist->shuffle_modified ||
+ playlist->deleted ||
+ playlist->num_inserted_tracks > 0)
+ return true;
+
+ return false;
+}
+
/*
* Move track at index to new_index. Tracks between the two are shifted
* appropriately. Returns 0 on success and -1 on failure.
@@ -3475,182 +2873,669 @@ int playlist_move(struct playlist_info* playlist, int index, int new_index)
return result;
}
-/* shuffle currently playing playlist */
-int playlist_randomise(struct playlist_info* playlist, unsigned int seed,
- bool start_current)
+/* returns full path of playlist (minus extension) */
+char *playlist_name(const struct playlist_info* playlist, char *buf,
+ int buf_size)
{
- int result;
+ char *sep;
if (!playlist)
playlist = &current_playlist;
- check_control(playlist);
+ strmemccpy(buf, playlist->filename+playlist->dirlen, buf_size);
- result = randomise_playlist(playlist, seed, start_current, true);
+ if (!buf[0])
+ return NULL;
- if (result != -1 && (audio_status() & AUDIO_STATUS_PLAY) &&
- playlist->started)
- audio_flush_and_reload_tracks();
+ /* Remove extension */
+ sep = strrchr(buf, '.');
+ if (sep)
+ *sep = 0;
- return result;
+ return buf;
}
-/* sort currently playing playlist */
-int playlist_sort(struct playlist_info* playlist, bool start_current)
+/*
+ * Update indices as track has changed
+ */
+int playlist_next(int steps)
{
- int result;
+ struct playlist_info* playlist = &current_playlist;
+ int index;
- if (!playlist)
- playlist = &current_playlist;
+ if ( (steps > 0)
+#ifdef AB_REPEAT_ENABLE
+ && (global_settings.repeat_mode != REPEAT_AB)
+#endif
+ && (global_settings.repeat_mode != REPEAT_ONE) )
+ {
+ int i, j;
- check_control(playlist);
+ /* We need to delete all the queued songs */
+ for (i=0, j=steps; i<j; i++)
+ {
+ index = get_next_index(playlist, i, -1);
- result = sort_playlist(playlist, start_current, true);
+ if (index >= 0 && playlist->indices[index] & PLAYLIST_QUEUE_MASK)
+ {
+ remove_track_from_playlist(playlist, index, true);
+ steps--; /* one less track */
+ }
+ }
+ }
- if (result != -1 && (audio_status() & AUDIO_STATUS_PLAY) &&
- playlist->started)
- audio_flush_and_reload_tracks();
+ index = get_next_index(playlist, steps, -1);
- return result;
-}
+ if (index < 0)
+ {
+ /* end of playlist... or is it */
+ if (global_settings.repeat_mode == REPEAT_SHUFFLE &&
+ playlist->amount > 1)
+ {
+ /* Repeat shuffle mode. Re-shuffle playlist and resume play */
+ playlist->first_index = 0;
+ sort_playlist(playlist, false, false);
+ randomise_playlist(playlist, current_tick, false, true);
-/* returns true if playlist has been modified */
-bool playlist_modified(const struct playlist_info* playlist)
-{
- if (!playlist)
- playlist = &current_playlist;
+ playlist->started = true;
+ playlist->index = 0;
+ index = 0;
+ }
+ else if (playlist->in_ram && global_settings.next_folder)
+ {
+ index = create_and_play_dir(steps, true);
- if (playlist->shuffle_modified ||
- playlist->deleted ||
- playlist->num_inserted_tracks > 0)
- return true;
+ if (index >= 0)
+ {
+ playlist->index = index;
+ }
+ }
- return false;
-}
+ return index;
+ }
-/* returns index of first track in playlist */
-int playlist_get_first_index(const struct playlist_info* playlist)
-{
- if (!playlist)
- playlist = &current_playlist;
+ playlist->index = index;
- return playlist->first_index;
-}
+ if (playlist->last_insert_pos >= 0 && steps > 0)
+ {
+ /* check to see if we've gone beyond the last inserted track */
+ int cur = rotate_index(playlist, index);
+ int last_pos = rotate_index(playlist, playlist->last_insert_pos);
-/* returns shuffle seed of playlist */
-int playlist_get_seed(const struct playlist_info* playlist)
-{
- if (!playlist)
- playlist = &current_playlist;
+ if (cur > last_pos)
+ {
+ /* reset last inserted track */
+ playlist->last_insert_pos = -1;
- return playlist->seed;
+ if (playlist->control_fd >= 0)
+ {
+ int result = update_control(playlist, PLAYLIST_COMMAND_RESET,
+ -1, -1, NULL, NULL, NULL);
+
+ if (result < 0)
+ return result;
+
+ sync_control(playlist, false);
+ }
+ }
+ }
+
+ return index;
}
-/* returns number of tracks in playlist (includes queued/inserted tracks) */
-int playlist_amount_ex(const struct playlist_info* playlist)
+/* try playing next or previous folder */
+bool playlist_next_dir(int direction)
{
- if (!playlist)
- playlist = &current_playlist;
+ /* not to mess up real playlists */
+ if(!current_playlist.in_ram)
+ return false;
- return playlist->amount;
+ return create_and_play_dir(direction, false) >= 0;
}
-/* returns full path of playlist (minus extension) */
-char *playlist_name(const struct playlist_info* playlist, char *buf,
- int buf_size)
+/* get trackname of track that is "steps" away from current playing track.
+ NULL is used to identify end of playlist */
+const char* playlist_peek(int steps, char* buf, size_t buf_size)
{
- char *sep;
+ struct playlist_info* playlist = &current_playlist;
+ int seek;
+ char *temp_ptr;
+ int index;
+ bool control_file;
- if (!playlist)
- playlist = &current_playlist;
+ index = get_next_index(playlist, steps, -1);
+ if (index < 0)
+ return NULL;
- strmemccpy(buf, playlist->filename+playlist->dirlen, buf_size);
-
- if (!buf[0])
+ /* Just testing - don't care about the file name */
+ if (!buf || !buf_size)
+ return "";
+
+ control_file = playlist->indices[index] & PLAYLIST_INSERT_TYPE_MASK;
+ seek = playlist->indices[index] & PLAYLIST_SEEK_MASK;
+
+ if (get_filename(playlist, index, seek, control_file, buf,
+ buf_size) < 0)
return NULL;
- /* Remove extension */
- sep = strrchr(buf, '.');
- if (sep)
- *sep = 0;
+ temp_ptr = buf;
- return buf;
+ if (!playlist->in_ram || control_file)
+ {
+ /* remove bogus dirs from beginning of path
+ (workaround for buggy playlist creation tools) */
+ while (temp_ptr)
+ {
+ if (file_exists(temp_ptr))
+ break;
+
+ temp_ptr = strchr(temp_ptr+1, '/');
+ }
+
+ if (!temp_ptr)
+ {
+ /* Even though this is an invalid file, we still need to pass a
+ file name to the caller because NULL is used to indicate end
+ of playlist */
+ return buf;
+ }
+ }
+
+ return temp_ptr;
}
-/* returns the playlist filename */
-char *playlist_get_name(const struct playlist_info* playlist, char *buf,
- int buf_size)
+/* shuffle currently playing playlist */
+int playlist_randomise(struct playlist_info* playlist, unsigned int seed,
+ bool start_current)
{
+ int result;
+
if (!playlist)
playlist = &current_playlist;
- strmemccpy(buf, playlist->filename, buf_size);
+ check_control(playlist);
- if (!buf[0])
- return NULL;
+ result = randomise_playlist(playlist, seed, start_current, true);
- return buf;
+ if (result != -1 && (audio_status() & AUDIO_STATUS_PLAY) &&
+ playlist->started)
+ audio_flush_and_reload_tracks();
+
+ return result;
}
-/* return size of buffer needed for playlist to initialize num_indices entries */
-size_t playlist_get_required_bufsz(struct playlist_info* playlist,
- bool include_namebuf,
- int num_indices)
+/*
+ * Removes all tracks, from the playlist, leaving the presently playing
+ * track queued.
+ */
+int playlist_remove_all_tracks(struct playlist_info *playlist)
{
- size_t namebuf = 0;
+ int result;
- if (!playlist)
+ if (playlist == NULL)
playlist = &current_playlist;
- size_t unit_size = sizeof (*playlist->indices);
- #ifdef HAVE_DIRCACHE
- unit_size += sizeof (*playlist->dcfrefs);
- #endif
- if (include_namebuf)
- namebuf = AVERAGE_FILENAME_LENGTH * global_settings.max_files_in_dir;
+ while (playlist->index > 0)
+ if ((result = remove_track_from_playlist(playlist, 0, true)) < 0)
+ return result;
- return (num_indices * unit_size) + namebuf;
+ while (playlist->amount > 1)
+ if ((result = remove_track_from_playlist(playlist, 1, true)) < 0)
+ return result;
+
+ if (playlist->amount == 1) {
+ playlist->indices[0] |= PLAYLIST_QUEUED;
+ }
+
+ return 0;
}
-/* Fills info structure with information about track at specified index.
- Returns 0 on success and -1 on failure */
-int playlist_get_track_info(struct playlist_info* playlist, int index,
- struct playlist_track_info* info)
+/*
+ * Restore the playlist state based on control file commands. Called to
+ * resume playback after shutdown.
+ */
+int playlist_resume(void)
{
- int seek;
- bool control_file;
+ struct playlist_info* playlist = &current_playlist;
+ char *buffer;
+ size_t buflen;
+ int handle;
+ int nread;
+ int total_read = 0;
+ int control_file_size = 0;
+ bool first = true;
+ bool sorted = true;
+ int result = -1;
- if (!playlist)
- playlist = &current_playlist;
+ splash(0, ID2P(LANG_WAIT));
+ if (core_allocatable() < (1 << 10))
+ talk_buffer_set_policy(TALK_BUFFER_LOOSE); /* back off voice buffer */
- if (index < 0 || index >= playlist->amount)
+#ifdef HAVE_DIRCACHE
+ dircache_wait(); /* we need the dircache to use the files in the playlist */
+#endif
+
+ handle = alloc_tempbuf(&buflen);
+ if (handle < 0)
+ {
+ splashf(HZ * 2, "%s(): OOM", __func__);
return -1;
+ }
- control_file = playlist->indices[index] & PLAYLIST_INSERT_TYPE_MASK;
- seek = playlist->indices[index] & PLAYLIST_SEEK_MASK;
+ /* align buffer for faster load times */
+ buffer = core_get_data(handle);
+ STORAGE_ALIGN_BUFFER(buffer, buflen);
+ buflen = ALIGN_DOWN(buflen, 512); /* to avoid partial sector I/O */
- if (get_filename(playlist, index, seek, control_file, info->filename,
- sizeof(info->filename)) < 0)
- return -1;
+ playlist_shutdown(); /* flush any cached control commands to disk */
+ empty_playlist(playlist, true);
- info->attr = 0;
+ playlist->control_fd = open(playlist->control_filename, O_RDWR);
+ if (playlist->control_fd < 0)
+ {
+ splash(HZ*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR));
+ goto out;
+ }
+ playlist->control_created = true;
- if (control_file)
+ control_file_size = filesize(playlist->control_fd);
+ if (control_file_size <= 0)
{
- if (playlist->indices[index] & PLAYLIST_QUEUE_MASK)
- info->attr |= PLAYLIST_ATTR_QUEUED;
+ splash(HZ*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR));
+ goto out;
+ }
+
+ /* read a small amount first to get the header */
+ nread = read(playlist->control_fd, buffer,
+ PLAYLIST_COMMAND_SIZE<buflen?PLAYLIST_COMMAND_SIZE:buflen);
+ if(nread <= 0)
+ {
+ splash(HZ*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR));
+ goto out;
+ }
+
+ playlist->started = true;
+
+ while (1)
+ {
+ result = 0;
+ int count;
+ enum playlist_command current_command = PLAYLIST_COMMAND_COMMENT;
+ int last_newline = 0;
+ int str_count = -1;
+ bool newline = true;
+ bool exit_loop = false;
+ char *p = buffer;
+ char *str1 = NULL;
+ char *str2 = NULL;
+ char *str3 = NULL;
+ unsigned long last_tick = current_tick;
+ splash_progress_set_delay(HZ / 2); /* wait 1/2 sec before progress */
+ bool useraborted = false;
+
+ for(count=0; count<nread && !exit_loop && !useraborted; count++,p++)
+ {
+ /* Show a splash while we are loading. */
+ splash_progress((total_read + count), control_file_size,
+ "%s (%s)", str(LANG_WAIT), str(LANG_OFF_ABORT));
+ if (TIME_AFTER(current_tick, last_tick + HZ/4))
+ {
+ if (action_userabort(TIMEOUT_NOBLOCK))
+ {
+ useraborted = true;
+ break;
+ }
+ last_tick = current_tick;
+ }
+ /* Are we on a new line? */
+ if((*p == '\n') || (*p == '\r'))
+ {
+ *p = '\0';
+
+ /* save last_newline in case we need to load more data */
+ last_newline = count;
+
+ switch (current_command)
+ {
+ case PLAYLIST_COMMAND_PLAYLIST:
+ {
+ /* str1=version str2=dir str3=file */
+ int version;
+
+ if (!str1)
+ {
+ result = -2;
+ exit_loop = true;
+ break;
+ }
+
+ if (!str2)
+ str2 = "";
+
+ if (!str3)
+ str3 = "";
+
+ version = atoi(str1);
+
+ if (version != PLAYLIST_CONTROL_FILE_VERSION)
+ {
+ result = -3;
+ goto out;
+ }
+
+ update_playlist_filename(playlist, str2, str3);
+
+ if (str3[0] != '\0')
+ {
+ /* NOTE: add_indices_to_playlist() overwrites the
+ audiobuf so we need to reload control file
+ data */
+ add_indices_to_playlist(playlist, buffer, buflen);
+ }
+ else if (str2[0] != '\0')
+ {
+ playlist->in_ram = true;
+ resume_directory(str2);
+ }
+
+ /* load the rest of the data */
+ first = false;
+ exit_loop = true;
+
+ break;
+ }
+ case PLAYLIST_COMMAND_ADD:
+ case PLAYLIST_COMMAND_QUEUE:
+ {
+ /* str1=position str2=last_position str3=file */
+ int position, last_position;
+ bool queue;
+
+ if (!str1 || !str2 || !str3)
+ {
+ result = -4;
+ exit_loop = true;
+ break;
+ }
+
+ position = atoi(str1);
+ last_position = atoi(str2);
+
+ queue = (current_command == PLAYLIST_COMMAND_ADD)?
+ false:true;
+
+ /* seek position is based on str3's position in
+ buffer */
+ if (add_track_to_playlist(playlist, str3, position,
+ queue, total_read+(str3-buffer)) < 0)
+ {
+ result = -5;
+ goto out;
+ }
+
+ playlist->last_insert_pos = last_position;
+
+ break;
+ }
+ case PLAYLIST_COMMAND_DELETE:
+ {
+ /* str1=position */
+ int position;
+
+ if (!str1)
+ {
+ result = -6;
+ exit_loop = true;
+ break;
+ }
+
+ position = atoi(str1);
+
+ if (remove_track_from_playlist(playlist, position,
+ false) < 0)
+ {
+ result = -7;
+ goto out;
+ }
+
+ break;
+ }
+ case PLAYLIST_COMMAND_SHUFFLE:
+ {
+ /* str1=seed str2=first_index */
+ int seed;
+
+ if (!str1 || !str2)
+ {
+ result = -8;
+ exit_loop = true;
+ break;
+ }
+
+ if (!sorted)
+ {
+ /* Always sort list before shuffling */
+ sort_playlist(playlist, false, false);
+ }
+
+ seed = atoi(str1);
+ playlist->first_index = atoi(str2);
+
+ if (randomise_playlist(playlist, seed, false,
+ false) < 0)
+ {
+ result = -9;
+ goto out;
+ }
+ sorted = false;
+ break;
+ }
+ case PLAYLIST_COMMAND_UNSHUFFLE:
+ {
+ /* str1=first_index */
+ if (!str1)
+ {
+ result = -10;
+ exit_loop = true;
+ break;
+ }
+
+ playlist->first_index = atoi(str1);
+
+ if (sort_playlist(playlist, false, false) < 0)
+ {
+ result = -11;
+ goto out;
+ }
+
+ sorted = true;
+ break;
+ }
+ case PLAYLIST_COMMAND_RESET:
+ {
+ playlist->last_insert_pos = -1;
+ break;
+ }
+ case PLAYLIST_COMMAND_COMMENT:
+ default:
+ break;
+ }
+
+ newline = true;
+
+ /* to ignore any extra newlines */
+ current_command = PLAYLIST_COMMAND_COMMENT;
+ }
+ else if(newline)
+ {
+ newline = false;
+
+ /* first non-comment line must always specify playlist */
+ if (first && *p != 'P' && *p != '#')
+ {
+ result = -12;
+ exit_loop = true;
+ break;
+ }
+
+ switch (*p)
+ {
+ case 'P':
+ /* playlist can only be specified once */
+ if (!first)
+ {
+ result = -13;
+ exit_loop = true;
+ break;
+ }
+
+ current_command = PLAYLIST_COMMAND_PLAYLIST;
+ break;
+ case 'A':
+ current_command = PLAYLIST_COMMAND_ADD;
+ break;
+ case 'Q':
+ current_command = PLAYLIST_COMMAND_QUEUE;
+ break;
+ case 'D':
+ current_command = PLAYLIST_COMMAND_DELETE;
+ break;
+ case 'S':
+ current_command = PLAYLIST_COMMAND_SHUFFLE;
+ break;
+ case 'U':
+ current_command = PLAYLIST_COMMAND_UNSHUFFLE;
+ break;
+ case 'R':
+ current_command = PLAYLIST_COMMAND_RESET;
+ break;
+ case '#':
+ current_command = PLAYLIST_COMMAND_COMMENT;
+ break;
+ default:
+ result = -14;
+ exit_loop = true;
+ break;
+ }
+
+ str_count = -1;
+ str1 = NULL;
+ str2 = NULL;
+ str3 = NULL;
+ }
+ else if(current_command != PLAYLIST_COMMAND_COMMENT)
+ {
+ /* all control file strings are separated with a colon.
+ Replace the colon with 0 to get proper strings that can be
+ used by commands above */
+ if (*p == ':')
+ {
+ *p = '\0';
+ str_count++;
+
+ if ((count+1) < nread)
+ {
+ switch (str_count)
+ {
+ case 0:
+ str1 = p+1;
+ break;
+ case 1:
+ str2 = p+1;
+ break;
+ case 2:
+ str3 = p+1;
+ break;
+ default:
+ /* allow last string to contain colons */
+ *p = ':';
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ if (result < 0)
+ {
+ splashf(HZ*2, "Err: %d, %s", result, str(LANG_PLAYLIST_CONTROL_INVALID));
+ goto out;
+ }
+
+ if (useraborted)
+ {
+ splash(HZ*2, ID2P(LANG_CANCEL));
+ result = -1;
+ goto out;
+ }
+ if (!newline || (exit_loop && count<nread))
+ {
+ if ((total_read + count) >= control_file_size)
+ {
+ /* no newline at end of control file */
+ splashf(HZ*2, "Err: EOF, %s", str(LANG_PLAYLIST_CONTROL_INVALID));
+ result = -15;
+ goto out;
+ }
+
+ /* We didn't end on a newline or we exited loop prematurely.
+ Either way, re-read the remainder. */
+ count = last_newline;
+ lseek(playlist->control_fd, total_read+count, SEEK_SET);
+ }
+
+ total_read += count;
+
+ if (first)
+ /* still looking for header */
+ nread = read(playlist->control_fd, buffer,
+ PLAYLIST_COMMAND_SIZE<buflen?PLAYLIST_COMMAND_SIZE:buflen);
else
- info->attr |= PLAYLIST_ATTR_INSERTED;
-
+ nread = read(playlist->control_fd, buffer, buflen);
+
+ /* Terminate on EOF */
+ if(nread <= 0)
+ {
+ break;
+ }
}
- if (playlist->indices[index] & PLAYLIST_SKIPPED)
- info->attr |= PLAYLIST_ATTR_SKIPPED;
-
- info->index = index;
- info->display_index = rotate_index(playlist, index) + 1;
+#ifdef HAVE_DIRCACHE
+ queue_post(&playlist_queue, PLAYLIST_LOAD_POINTERS, 0);
+#endif
- return 0;
+out:
+ talk_buffer_set_policy(TALK_BUFFER_DEFAULT);
+ core_free(handle);
+ return result;
+}
+
+/* resume a playlist track with the given crc_32 of the track name. */
+void playlist_resume_track(int start_index, unsigned int crc,
+ unsigned long elapsed, unsigned long offset)
+{
+ int i;
+ unsigned int tmp_crc;
+ struct playlist_info* playlist = &current_playlist;
+ tmp_crc = playlist_get_filename_crc32(playlist, start_index);
+ if (tmp_crc == crc)
+ {
+ playlist_start(start_index, elapsed, offset);
+ return;
+ }
+
+ for (i = 0 ; i < playlist->amount; i++)
+ {
+ tmp_crc = playlist_get_filename_crc32(playlist, i);
+ if (tmp_crc == crc)
+ {
+ playlist_start(i, elapsed, offset);
+ return;
+ }
+ }
+
+ /* If we got here the file wasnt found, so start from the beginning */
+ playlist_start(0, 0, 0);
}
/* save the current dynamic playlist to specified file. The
@@ -3838,104 +3723,194 @@ reset_old_buffer:
}
/*
- * Search specified directory for tracks and notify via callback. May be
- * called recursively.
+ * Set the specified playlist as the current.
+ * NOTE: You will get undefined behaviour if something is already playing so
+ * remember to stop before calling this. Also, this call will
+ * effectively close your playlist, making it unusable.
*/
-int playlist_directory_tracksearch(const char* dirname, bool recurse,
- int (*callback)(char*, void*),
- void* context)
+int playlist_set_current(struct playlist_info* playlist)
{
- char buf[MAX_PATH+1];
- int result = 0;
- int num_files = 0;
- int i;;
- struct tree_context* tc = tree_get_context();
- struct tree_cache* cache = &tc->cache;
- int old_dirfilter = *(tc->dirfilter);
+ if (!playlist || (check_control(playlist) < 0))
+ return -1;
- if (!callback)
+ empty_playlist(&current_playlist, false);
+
+ strmemccpy(current_playlist.filename, playlist->filename,
+ sizeof(current_playlist.filename));
+
+ current_playlist.utf8 = playlist->utf8;
+ current_playlist.fd = playlist->fd;
+
+ close(playlist->control_fd);
+ playlist->control_fd = -1;
+ close(current_playlist.control_fd);
+ current_playlist.control_fd = -1;
+ remove(current_playlist.control_filename);
+ current_playlist.control_created = false;
+ if (rename(playlist->control_filename,
+ current_playlist.control_filename) < 0)
return -1;
+ current_playlist.control_fd = open(current_playlist.control_filename,
+ O_RDWR);
+ if (current_playlist.control_fd < 0)
+ return -1;
+ current_playlist.control_created = true;
- /* use the tree browser dircache to load files */
- *(tc->dirfilter) = SHOW_ALL;
+ current_playlist.dirlen = playlist->dirlen;
- if (ft_load(tc, dirname) < 0)
+ if (playlist->indices && playlist->indices != current_playlist.indices)
{
- splash(HZ*2, ID2P(LANG_PLAYLIST_DIRECTORY_ACCESS_ERROR));
- *(tc->dirfilter) = old_dirfilter;
- return -1;
+ memcpy((void*)current_playlist.indices, (void*)playlist->indices,
+ playlist->max_playlist_size*sizeof(*playlist->indices));
+#ifdef HAVE_DIRCACHE
+ copy_filerefs(current_playlist.dcfrefs, playlist->dcfrefs,
+ playlist->max_playlist_size);
+#endif
}
- num_files = tc->filesindir;
+ current_playlist.first_index = playlist->first_index;
+ current_playlist.amount = playlist->amount;
+ current_playlist.last_insert_pos = playlist->last_insert_pos;
+ current_playlist.seed = playlist->seed;
+ current_playlist.shuffle_modified = playlist->shuffle_modified;
+ current_playlist.deleted = playlist->deleted;
+ current_playlist.num_inserted_tracks = playlist->num_inserted_tracks;
- /* we've overwritten the dircache so tree browser will need to be
- reloaded */
- reload_directory();
+ memcpy(current_playlist.control_cache, playlist->control_cache,
+ sizeof(current_playlist.control_cache));
+ current_playlist.num_cached = playlist->num_cached;
+ current_playlist.pending_control_sync = playlist->pending_control_sync;
- for (i=0; i<num_files; i++)
+ return 0;
+}
+
+/* set playlist->last_shuffle_start to playlist->amount for
+ PLAYLIST_INSERT_LAST_SHUFFLED command purposes*/
+void playlist_set_last_shuffled_start(void)
+{
+ struct playlist_info* playlist = &current_playlist;
+ playlist->last_shuffled_start = playlist->amount;
+}
+
+/* shuffle newly created playlist using random seed. */
+int playlist_shuffle(int random_seed, int start_index)
+{
+ struct playlist_info* playlist = &current_playlist;
+
+ bool start_current = false;
+
+ if (start_index >= 0 && global_settings.play_selected)
{
- /* user abort */
- if (action_userabort(TIMEOUT_NOBLOCK))
- {
- result = -1;
- break;
- }
+ /* store the seek position before the shuffle */
+ playlist->index = playlist->first_index = start_index;
+ start_current = true;
+ }
- struct entry *files = core_get_data(cache->entries_handle);
- if (files[i].attr & ATTR_DIRECTORY)
- {
- if (recurse)
- {
- /* recursively add directories */
- if (path_append(buf, dirname, files[i].name, sizeof(buf))
- >= sizeof(buf))
- {
- continue;
- }
+ randomise_playlist(playlist, random_seed, start_current, true);
- result = playlist_directory_tracksearch(buf, recurse,
- callback, context);
- if (result < 0)
- break;
+ return playlist->index;
+}
- /* we now need to reload our current directory */
- if(ft_load(tc, dirname) < 0)
- {
- result = -1;
- break;
- }
+/* Marks the index of the track to be skipped that is "steps" away from
+ * current playing track.
+ */
+void playlist_skip_entry(struct playlist_info *playlist, int steps)
+{
+ int index;
- num_files = tc->filesindir;
- if (!num_files)
- {
- result = -1;
- break;
- }
- }
- else
- continue;
- }
- else if ((files[i].attr & FILE_ATTR_MASK) == FILE_ATTR_AUDIO)
- {
- if (path_append(buf, dirname, files[i].name, sizeof(buf))
- >= sizeof(buf))
- {
- continue;
- }
+ if (playlist == NULL)
+ playlist = &current_playlist;
- if (callback(buf, context) != 0)
- {
- result = -1;
- break;
- }
+ /* need to account for already skipped tracks */
+ steps = calculate_step_count(playlist, steps);
- /* let the other threads work */
- yield();
- }
- }
+ index = playlist->index + steps;
+ if (index < 0)
+ index += playlist->amount;
+ else if (index >= playlist->amount)
+ index -= playlist->amount;
- /* restore dirfilter */
- *(tc->dirfilter) = old_dirfilter;
+ playlist->indices[index] |= PLAYLIST_SKIPPED;
+}
+
+/* sort currently playing playlist */
+int playlist_sort(struct playlist_info* playlist, bool start_current)
+{
+ int result;
+
+ if (!playlist)
+ playlist = &current_playlist;
+
+ check_control(playlist);
+
+ result = sort_playlist(playlist, start_current, true);
+
+ if (result != -1 && (audio_status() & AUDIO_STATUS_PLAY) &&
+ playlist->started)
+ audio_flush_and_reload_tracks();
return result;
}
+
+/* start playing current playlist at specified index/offset */
+void playlist_start(int start_index, unsigned long elapsed,
+ unsigned long offset)
+{
+ struct playlist_info* playlist = &current_playlist;
+
+ playlist->index = start_index;
+
+ playlist->started = true;
+ sync_control(playlist, false);
+ audio_play(elapsed, offset);
+ audio_resume();
+}
+
+void playlist_sync(struct playlist_info* playlist)
+{
+ if (!playlist)
+ playlist = &current_playlist;
+
+ sync_control(playlist, false);
+ if ((audio_status() & AUDIO_STATUS_PLAY) && playlist->started)
+ audio_flush_and_reload_tracks();
+
+#ifdef HAVE_DIRCACHE
+ queue_post(&playlist_queue, PLAYLIST_LOAD_POINTERS, 0);
+#endif
+}
+
+/* Update resume info for current playing song. Returns -1 on error. */
+int playlist_update_resume_info(const struct mp3entry* id3)
+{
+ struct playlist_info* playlist = &current_playlist;
+
+ if (id3)
+ {
+ if (global_status.resume_index != playlist->index ||
+ global_status.resume_elapsed != id3->elapsed ||
+ global_status.resume_offset != id3->offset)
+ {
+ unsigned int crc = playlist_get_filename_crc32(playlist,
+ playlist->index);
+ global_status.resume_index = playlist->index;
+ global_status.resume_crc32 = crc;
+ global_status.resume_elapsed = id3->elapsed;
+ global_status.resume_offset = id3->offset;
+ status_save();
+ }
+ }
+ else
+ {
+ global_status.resume_index = -1;
+ global_status.resume_crc32 = -1;
+ global_status.resume_elapsed = -1;
+ global_status.resume_offset = -1;
+ status_save();
+ }
+
+ return 0;
+}
+
+
+
diff --git a/apps/playlist.h b/apps/playlist.h
index 2eca7355e4..0ecc7ccf77 100644
--- a/apps/playlist.h
+++ b/apps/playlist.h
@@ -74,22 +74,16 @@ struct playlist_control_cache {
struct playlist_info
{
bool current; /* current playing playlist */
- char filename[MAX_PATH]; /* path name of m3u playlist on disk */
- char control_filename[MAX_PATH]; /* full path of control file */
bool utf8; /* playlist is in .m3u8 format */
+ bool control_created; /* has control file been created? */
+ bool in_ram; /* playlist stored in ram (dirplay) */
int fd; /* descriptor of the open playlist file */
int control_fd; /* descriptor of the open control file */
- bool control_created; /* has control file been created? */
- int dirlen; /* Length of the path to the playlist file */
- volatile unsigned long *indices; /* array of indices */
-#ifdef HAVE_DIRCACHE
- struct dircache_fileref *dcfrefs; /* Dircache entry shortcuts */
-#endif
int max_playlist_size; /* Max number of files in playlist. Mirror of
global_settings.max_files_in_playlist */
- bool in_ram; /* playlist stored in ram (dirplay) */
+ int num_inserted_tracks; /* number of tracks inserted */
+ volatile unsigned long *indices; /* array of indices */
int buffer_handle; /* handle to the below buffer (-1 if non-buflib) */
-
volatile char *buffer;/* buffer for in-ram playlists */
int buffer_size; /* size of buffer */
int buffer_end_pos; /* last position where buffer was written */
@@ -97,22 +91,25 @@ struct playlist_info
int first_index; /* index of first song in playlist */
int amount; /* number of tracks in the index */
int last_insert_pos; /* last position we inserted a track */
- int seed; /* shuffle seed */
- bool shuffle_modified; /* has playlist been shuffled with
- inserted tracks? */
bool deleted; /* have any tracks been deleted? */
- int num_inserted_tracks; /* number of tracks inserted */
bool started; /* has playlist been started? */
-
+ bool pending_control_sync; /* control file needs to be synced */
+ bool shuffle_modified; /* has playlist been shuffled with
+ inserted tracks? */
+ int last_shuffled_start; /* number of tracks when insert last
+ shuffled command start */
+ int seed; /* shuffle seed */
/* cache of playlist control commands waiting to be flushed to
to disk */
struct playlist_control_cache control_cache[PLAYLIST_MAX_CACHE];
int num_cached; /* number of cached entries */
- bool pending_control_sync; /* control file needs to be synced */
-
- struct mutex *control_mutex; /* mutex for control file access */
- int last_shuffled_start; /* number of tracks when insert last
- shuffled command start */
+ struct mutex mutex; /* mutex for control file access */
+#ifdef HAVE_DIRCACHE
+ struct dircache_fileref *dcfrefs; /* Dircache entry shortcuts */
+#endif
+ int dirlen; /* Length of the path to the playlist file */
+ char filename[MAX_PATH]; /* path name of m3u playlist on disk */
+ char control_filename[MAX_PATH]; /* full path of control file */
};
struct playlist_track_info