summaryrefslogtreecommitdiffstats
path: root/apps
diff options
context:
space:
mode:
authorHardeep Sidhu <dyp@pobox.com>2003-07-01 21:05:43 +0000
committerHardeep Sidhu <dyp@pobox.com>2003-07-01 21:05:43 +0000
commit9e4262081b4ab5bad2e2708ea064643cf828685c (patch)
treebd809cc4616a2ed61bdbff217d26a13fd78b6609 /apps
parent928a09e3f464dc62e2863f8d77e766578788ba13 (diff)
downloadrockbox-9e4262081b4ab5bad2e2708ea064643cf828685c.tar.gz
rockbox-9e4262081b4ab5bad2e2708ea064643cf828685c.tar.bz2
rockbox-9e4262081b4ab5bad2e2708ea064643cf828685c.zip
Added dynamic playlists. ON+PLAY->Playlist on a track, directory, or playlist from file browser to see available options.
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@3796 a1c6a512-1295-4272-9138-f99709370657
Diffstat (limited to 'apps')
-rw-r--r--apps/applimits.h1
-rw-r--r--apps/lang/english.lang90
-rw-r--r--apps/main_menu.c3
-rw-r--r--apps/onplay.c116
-rw-r--r--apps/onplay.h6
-rw-r--r--apps/playlist.c2039
-rw-r--r--apps/playlist.h69
-rw-r--r--apps/playlist_menu.c71
-rw-r--r--apps/playlist_menu.h24
-rw-r--r--apps/screens.c4
-rw-r--r--apps/settings.c51
-rw-r--r--apps/settings.h10
-rw-r--r--apps/settings_menu.c4
-rw-r--r--apps/tree.c477
-rw-r--r--apps/tree.h11
-rw-r--r--apps/wps-display.c7
-rw-r--r--apps/wps.c13
17 files changed, 2138 insertions, 858 deletions
diff --git a/apps/applimits.h b/apps/applimits.h
index 6372dac924..a18544e87d 100644
--- a/apps/applimits.h
+++ b/apps/applimits.h
@@ -23,6 +23,5 @@
#define AVERAGE_FILENAME_LENGTH 40
#define MAX_DIR_LEVELS 10
#define MAX_PLAYLIST_SIZE 10000
-#define MAX_QUEUED_FILES 100
#endif
diff --git a/apps/lang/english.lang b/apps/lang/english.lang
index 57bec0c656..dab8fa6bd4 100644
--- a/apps/lang/english.lang
+++ b/apps/lang/english.lang
@@ -1632,3 +1632,93 @@ id: LANG_STAR
desc: in the games menu
eng: "Star"
new:
+
+id: LANG_QUEUE_LAST
+desc: in onplay menu. queue a track/playlist at end of playlist.
+eng: "Queue last"
+new:
+
+id: LANG_INSERT
+desc: in onplay menu. insert a track/playlist into dynamic playlist.
+eng: "Insert"
+new:
+
+id: LANG_INSERT_LAST
+desc: in onplay menu. append a track/playlist into dynamic playlist.
+eng: "Insert last"
+new:
+
+id: LANG_QUEUE_FIRST
+desc: in onplay menu. queue a track/playlist into dynamic playlist.
+eng: "Queue first"
+new:
+
+id: LANG_INSERT_FIRST
+desc: in onplay menu. insert a track/playlist into dynamic playlist.
+eng: "Insert first"
+new:
+
+id: LANG_SAVE_DYNAMIC_PLAYLIST
+desc: in playlist menu.
+eng: "Save Dynamic Playlist"
+new:
+
+id: LANG_PLAYLIST_MENU
+desc: in main menu.
+eng: "Playlist Options"
+new:
+
+id: LANG_PLAYLIST_INSERT_COUNT
+desc: splash number of tracks inserted
+eng: "Inserted %d tracks (%s)"
+new:
+
+id: LANG_PLAYLIST_QUEUE_COUNT
+desc: splash number of tracks queued
+eng: "Queued %d tracks (%s)"
+new:
+
+id: LANG_PLAYLIST_SAVE_COUNT
+desc: splash number of tracks saved
+eng: "Saved %d tracks (%s)"
+new:
+
+id: LANG_OFF_ABORT
+desc: Used on recorder models
+eng: "OFF to abort"
+new:
+
+id: LANG_STOP_ABORT
+desc: Used on player models
+eng: "STOP to abort"
+new:
+
+id: LANG_PLAYLIST_CONTROL_UPDATE_ERROR
+desc: Playlist error
+eng: "Error updating playlist control file"
+new:
+
+id: LANG_PLAYLIST_ACCESS_ERROR
+desc: Playlist error
+eng: "Error accessing playlist file"
+new:
+
+id: LANG_PLAYLIST_CONTROL_ACCESS_ERROR
+desc: Playlist error
+eng: "Error accessing playlist control file"
+new:
+
+id: LANG_PLAYLIST_DIRECTORY_ACCESS_ERROR
+desc: Playlist error
+eng: "Error accessing directory"
+new:
+
+id: LANG_PLAYLIST_CONTROL_INVALID
+desc: Playlist resume error
+eng: "Playlist control file is invalid"
+new:
+
+id: LANG_RECURSE_DIRECTORY
+desc: In playlist menu
+eng: "Recursively Insert Directories"
+new:
diff --git a/apps/main_menu.c b/apps/main_menu.c
index b4d952ea5a..e356603653 100644
--- a/apps/main_menu.c
+++ b/apps/main_menu.c
@@ -43,6 +43,7 @@
#include "wps.h"
#include "buffer.h"
#include "screens.h"
+#include "playlist_menu.h"
#ifdef HAVE_FMRADIO
#include "radio.h"
#endif
@@ -266,7 +267,7 @@ bool main_menu(void)
{ str(LANG_RECORDING), recording_screen },
{ str(LANG_RECORDING_SETTINGS), recording_menu },
#endif
- { str(LANG_CREATE_PLAYLIST), create_playlist },
+ { str(LANG_PLAYLIST_MENU), playlist_menu },
{ str(LANG_MENU_SHOW_ID3_INFO), browse_id3 },
{ str(LANG_SLEEP_TIMER), sleeptimer_screen },
#ifdef HAVE_ALARM_MOD
diff --git a/apps/onplay.c b/apps/onplay.c
index 3e0f5a365e..6c538f8d8a 100644
--- a/apps/onplay.c
+++ b/apps/onplay.c
@@ -38,13 +38,103 @@
#include "screens.h"
#include "tree.h"
#include "buffer.h"
+#include "settings.h"
+#include "status.h"
+#include "onplay.h"
static char* selected_file = NULL;
-static bool reload_dir = false;
+static int selected_file_attr = 0;
+static int onplay_result = ONPLAY_OK;
-static bool queue_file(void)
+/* For playlist options */
+struct playlist_args {
+ int position;
+ bool queue;
+};
+
+static bool add_to_playlist(int position, bool queue)
{
- queue_add(selected_file);
+ bool new_playlist = !(mpeg_status() & MPEG_STATUS_PLAY);
+
+ if (new_playlist)
+ playlist_create(NULL, NULL);
+
+ if (selected_file_attr & TREE_ATTR_MPA)
+ playlist_insert_track(selected_file, position, queue);
+ else if (selected_file_attr & ATTR_DIRECTORY)
+ playlist_insert_directory(selected_file, position, queue);
+ else if (selected_file_attr & TREE_ATTR_M3U)
+ playlist_insert_playlist(selected_file, position, queue);
+
+ if (new_playlist && (playlist_amount() > 0))
+ {
+ /* nothing is currently playing so begin playing what we just
+ inserted */
+ if (global_settings.playlist_shuffle)
+ playlist_shuffle(current_tick, -1);
+ playlist_start(0,0);
+ status_set_playmode(STATUS_PLAY);
+ status_draw(false);
+ onplay_result = ONPLAY_START_PLAY;
+ }
+
+ return false;
+}
+
+/* Sub-menu for playlist options */
+static bool playlist_options(void)
+{
+ struct menu_items menu[6];
+ struct playlist_args args[6]; /* increase these 2 if you add entries! */
+ int m, i=0, result;
+
+ if (mpeg_status() & MPEG_STATUS_PLAY)
+ {
+ menu[i].desc = str(LANG_INSERT);
+ args[i].position = PLAYLIST_INSERT;
+ args[i].queue = false;
+ i++;
+
+ menu[i].desc = str(LANG_INSERT_FIRST);
+ args[i].position = PLAYLIST_INSERT_FIRST;
+ args[i].queue = false;
+ i++;
+
+ menu[i].desc = str(LANG_INSERT_LAST);
+ args[i].position = PLAYLIST_INSERT_LAST;
+ args[i].queue = false;
+ i++;
+
+ menu[i].desc = str(LANG_QUEUE);
+ args[i].position = PLAYLIST_INSERT;
+ args[i].queue = true;
+ i++;
+
+ menu[i].desc = str(LANG_QUEUE_FIRST);
+ args[i].position = PLAYLIST_INSERT_FIRST;
+ args[i].queue = true;
+ i++;
+
+ menu[i].desc = str(LANG_QUEUE_LAST);
+ args[i].position = PLAYLIST_INSERT_LAST;
+ args[i].queue = true;
+ i++;
+ }
+ else if ((selected_file_attr & TREE_ATTR_MPA) ||
+ (selected_file_attr & ATTR_DIRECTORY))
+ {
+ menu[i].desc = str(LANG_INSERT);
+ args[i].position = PLAYLIST_INSERT;
+ args[i].queue = false;
+ i++;
+ }
+
+ m = menu_init( menu, i );
+ result = menu_show(m);
+ if (result >= 0)
+ add_to_playlist(args[result].position, args[result].queue);
+ menu_exit(m);
+
return false;
}
@@ -68,7 +158,7 @@ static bool delete_file(void)
switch (btn) {
case BUTTON_PLAY:
if (!remove(selected_file)) {
- reload_dir = true;
+ onplay_result = ONPLAY_RELOAD_DIR;
lcd_clear_display();
lcd_puts(0,0,str(LANG_DELETED));
lcd_puts_scroll(0,1,selected_file);
@@ -104,7 +194,7 @@ static bool rename_file(void)
sleep(HZ*2);
}
else
- reload_dir = true;
+ onplay_result = ONPLAY_RELOAD_DIR;
}
return false;
@@ -225,7 +315,7 @@ static bool vbr_fix(void)
if(mpeg_status()) {
splash(HZ*2, 0, true, str(LANG_VBRFIX_STOP_PLAY));
- return reload_dir;
+ return onplay_result;
}
lcd_clear_display();
@@ -356,12 +446,16 @@ int onplay(char* file, int attr)
struct menu_items menu[5]; /* increase this if you add entries! */
int m, i=0, result;
+ onplay_result = ONPLAY_OK;
+
selected_file = file;
-
- if ((mpeg_status() & MPEG_STATUS_PLAY) && (attr & TREE_ATTR_MPA))
+ selected_file_attr = attr;
+
+ if ((attr & TREE_ATTR_MPA) || (attr & ATTR_DIRECTORY) ||
+ (attr & TREE_ATTR_M3U))
{
- menu[i].desc = str(LANG_QUEUE);
- menu[i].function = queue_file;
+ menu[i].desc = str(LANG_PLAYINDICES_PLAYLIST);
+ menu[i].function = playlist_options;
i++;
}
@@ -390,5 +484,5 @@ int onplay(char* file, int attr)
menu[result].function();
menu_exit(m);
- return reload_dir;
+ return onplay_result;
}
diff --git a/apps/onplay.h b/apps/onplay.h
index 8540aaf2c7..7b47479e4b 100644
--- a/apps/onplay.h
+++ b/apps/onplay.h
@@ -21,4 +21,10 @@
int onplay(char* file, int attr);
+enum {
+ ONPLAY_OK,
+ ONPLAY_RELOAD_DIR,
+ ONPLAY_START_PLAY
+};
+
#endif
diff --git a/apps/playlist.c b/apps/playlist.c
index 726e8a9ecb..05149d164f 100644
--- a/apps/playlist.c
+++ b/apps/playlist.c
@@ -17,11 +17,58 @@
*
****************************************************************************/
+/*
+ Dynamic playlist design (based on design originally proposed by ricII)
+
+ There are two files associated with a dynamic playlist:
+ 1. Playlist file : This file contains the initial songs in the playlist.
+ The file is created by the user and stored on the hard
+ drive. NOTE: If we are playing the contents of a
+ 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
+ playlist filename. For dirplay, FILE will be empty. An empty playlist
+ will have both entries as null.
+
+ Control file commands:
+ a. Add track (A:<position>:<last position>:<path to track>)
+ - Insert a track at the specified position in the current
+ playlist. Last position is used to specify where last insertion
+ occurred.
+ b. Queue track (Q:<position>:<last position>:<path to track>)
+ - Queue a track at the specified position in the current
+ playlist. Queued tracks differ from added tracks in that they
+ are deleted from the playlist as soon as they are played and
+ they are not saved to disk as part of the playlist.
+ c. Delete track (D:<position>)
+ - Delete track from specified position in the current playlist.
+ d. Shuffle playlist (S:<seed>:<index>)
+ - Shuffle entire playlist with specified seed. The index
+ identifies the first index in the newly shuffled playlist
+ (needed for repeat mode).
+ e. Unshuffle playlist (U:<index>)
+ - Unshuffle entire playlist. The index identifies the first index
+ in the newly unshuffled playlist.
+ f. Reset last insert position (R)
+ - Needed so that insertions work properly after resume
+
+ Resume:
+ The only resume info that needs to be saved is the current index in the
+ playlist and the position in the track. When resuming, all the commands
+ in the control file will be reapplied so that the playlist indices are
+ exactly the same as before shutdown.
+ */
+
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "playlist.h"
#include "file.h"
+#include "dir.h"
#include "sprintf.h"
#include "debug.h"
#include "mpeg.h"
@@ -32,6 +79,10 @@
#include "applimits.h"
#include "screens.h"
#include "buffer.h"
+#include "atoi.h"
+#include "misc.h"
+#include "button.h"
+#include "tree.h"
#ifdef HAVE_LCD_BITMAP
#include "icons.h"
#include "widgets.h"
@@ -41,526 +92,825 @@
static struct playlist_info playlist;
-#define QUEUE_FILE ROCKBOX_DIR "/.queue_file"
+#define PLAYLIST_CONTROL_FILE ROCKBOX_DIR "/.playlist_control"
+#define PLAYLIST_CONTROL_FILE_VERSION 1
+
+/*
+ Each playlist index has a flag associated with it which identifies what
+ type of track it is. These flags are stored in the 3 high order bits of
+ the index.
+
+ NOTE: This limits the playlist file size to a max of 512K.
+
+ Bits 31-30:
+ 00 = Playlist track
+ 01 = Track was prepended into playlist
+ 10 = Track was inserted into playlist
+ 11 = Track was appended into playlist
+ Bit 29:
+ 0 = Added track
+ 1 = Queued track
+ */
+#define PLAYLIST_SEEK_MASK 0x1FFFFFFF
+#define PLAYLIST_INSERT_TYPE_MASK 0xC0000000
+#define PLAYLIST_QUEUE_MASK 0x20000000
+
+#define PLAYLIST_INSERT_TYPE_PREPEND 0x40000000
+#define PLAYLIST_INSERT_TYPE_INSERT 0x80000000
+#define PLAYLIST_INSERT_TYPE_APPEND 0xC0000000
-static unsigned char *playlist_buffer;
+#define PLAYLIST_QUEUED 0x20000000
-static int playlist_end_pos = 0;
+#define PLAYLIST_DISPLAY_COUNT 10
static char now_playing[MAX_PATH+1];
-void playlist_init(void)
-{
- playlist.fd = -1;
- playlist.max_playlist_size = global_settings.max_files_in_playlist;
- playlist.indices = buffer_alloc(playlist.max_playlist_size * sizeof(int));
- playlist.buffer_size =
- AVERAGE_FILENAME_LENGTH * global_settings.max_files_in_dir;
- playlist_buffer = buffer_alloc(playlist.buffer_size);
-}
+static void empty_playlist(bool resume);
+static void update_playlist_filename(char *dir, char *file);
+static int add_indices_to_playlist(void);
+static int add_track_to_playlist(char *filename, int position, bool queue,
+ int seek_pos);
+static int add_directory_to_playlist(char *dirname, int *position, bool queue,
+ int *count);
+static int remove_track_from_playlist(int position, bool write);
+static int randomise_playlist(unsigned int seed, bool start_current,
+ bool write);
+static int sort_playlist(bool start_current, bool write);
+static int get_next_index(int steps);
+static void find_and_set_playlist_index(unsigned int seek);
+static int compare(const void* p1, const void* p2);
+static int get_filename(int seek, bool control_file, char *buf,
+ int buf_length);
+static int format_track_path(char *dest, char *src, int buf_length, int max,
+ char *dir);
+static void display_playlist_count(int count, char *fmt);
+static void display_buffer_full(void);
/*
* remove any files and indices associated with the playlist
*/
-static void empty_playlist(bool queue_resume)
+static void empty_playlist(bool resume)
{
- int fd;
-
playlist.filename[0] = '\0';
+
if(-1 != playlist.fd)
/* If there is an already open playlist, close it. */
close(playlist.fd);
playlist.fd = -1;
+
+ if(-1 != playlist.control_fd)
+ close(playlist.control_fd);
+ playlist.control_fd = -1;
+
+ playlist.in_ram = false;
+ playlist.buffer[0] = 0;
+ playlist.buffer_end_pos = 0;
+
playlist.index = 0;
- playlist.queue_index = 0;
- playlist.last_queue_index = 0;
+ playlist.first_index = 0;
playlist.amount = 0;
- playlist.num_queued = 0;
- playlist.start_queue = 0;
+ playlist.last_insert_pos = -1;
- if (!queue_resume)
+ if (!resume)
{
- /* start with fresh queue file when starting new playlist */
- remove(QUEUE_FILE);
- fd = creat(QUEUE_FILE, 0);
- if (fd > 0)
+ int fd;
+
+ /* start with fresh playlist control file when starting new
+ playlist */
+ fd = creat(PLAYLIST_CONTROL_FILE, 0000200);
+ if (fd >= 0)
close(fd);
}
}
-/* update queue list after resume */
-static void add_indices_to_queuelist(int seek)
+/*
+ * store directory and name of playlist file
+ */
+static void update_playlist_filename(char *dir, char *file)
{
- int nread;
- int fd = -1;
- int i = seek;
- int count = 0;
+ char *sep="";
+ int dirlen = strlen(dir);
+
+ /* If the dir does not end in trailing slash, we use a separator.
+ Otherwise we don't. */
+ if('/' != 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(void)
+{
+ unsigned int nread;
+ unsigned int i = 0;
+ unsigned int count = 0;
+ int buflen;
bool store_index;
- char buf[MAX_PATH];
+ char *buffer;
+ unsigned char *p;
- unsigned char *p = buf;
+ if(-1 == playlist.fd)
+ playlist.fd = open(playlist.filename, O_RDONLY);
+ if(-1 == playlist.fd)
+ return -1; /* failure */
+
+#ifdef HAVE_LCD_BITMAP
+ if(global_settings.statusbar)
+ lcd_setmargins(0, STATUSBAR_HEIGHT);
+ else
+ lcd_setmargins(0, 0);
+#endif
- fd = open(QUEUE_FILE, O_RDONLY);
- if(fd < 0)
- return;
+ splash(0, 0, true, str(LANG_PLAYLIST_LOAD));
- nread = lseek(fd, seek, SEEK_SET);
- if (nread < 0)
- return;
+ /* use mp3 buffer for maximum load speed */
+ buflen = (mp3end - mp3buf);
+ buffer = mp3buf;
store_index = true;
+ mpeg_stop();
+
while(1)
{
- nread = read(fd, buf, MAX_PATH);
+ nread = read(playlist.fd, buffer, buflen);
+ /* Terminate on EOF */
if(nread <= 0)
break;
-
- p = buf;
- for(count=0; count < nread; count++,p++) {
- if(*p == '\n')
+ p = buffer;
+
+ for(count=0; count < nread; count++,p++) {
+
+ /* Are we on a new line? */
+ if((*p == '\n') || (*p == '\r'))
+ {
store_index = true;
+ }
else if(store_index)
{
store_index = false;
-
- playlist.queue_indices[playlist.last_queue_index] = i+count;
- playlist.last_queue_index =
- (playlist.last_queue_index + 1) % MAX_QUEUED_FILES;
- playlist.num_queued++;
+
+ if(*p != '#')
+ {
+ /* Store a new entry */
+ playlist.indices[ playlist.amount ] = i+count;
+ playlist.amount++;
+ if ( playlist.amount >= playlist.max_playlist_size ) {
+ display_buffer_full();
+ return -1;
+ }
+ }
}
}
-
- i += count;
+
+ i+= count;
}
+
+ return 0;
}
-static int get_next_index(int steps, bool *queue)
+/*
+ * Add track to playlist at specified position. There are three special
+ * positions that can be specified:
+ * PLAYLIST_PREPEND - Add track at beginning of playlist
+ * PLAYLIST_INSERT - Add track after current song. NOTE: If there
+ * are already inserted tracks then track is added
+ * to the end of the insertion list.
+ * PLAYLIST_INSERT_FIRST - Add track immediately after current song, no
+ * matter what other tracks have been inserted.
+ * PLAYLIST_INSERT_LAST - Add track to end of playlist
+ */
+static int add_track_to_playlist(char *filename, int position, bool queue,
+ int seek_pos)
{
- int current_index = playlist.index;
- int next_index = -1;
+ int insert_position = position;
+ unsigned int flags = PLAYLIST_INSERT_TYPE_INSERT;
+ int i;
- if (global_settings.repeat_mode == REPEAT_ONE)
+ if (playlist.amount >= playlist.max_playlist_size)
{
- /* this is needed for repeat one to work with queue mode */
- steps = 0;
+ display_buffer_full();
+ return -1;
}
- else if (steps >= 0)
+
+ switch (position)
{
- /* Queue index starts from 0 which needs to be accounted for. Also,
- after resume, this handles case where we want to begin with
- playlist */
- steps -= playlist.start_queue;
+ case PLAYLIST_PREPEND:
+ insert_position = playlist.first_index;
+ flags = PLAYLIST_INSERT_TYPE_PREPEND;
+ break;
+ case PLAYLIST_INSERT:
+ /* if there are already inserted tracks then add track to end of
+ insertion list else add after current playing track */
+ if (playlist.last_insert_pos >= 0 &&
+ playlist.last_insert_pos < playlist.amount &&
+ (playlist.indices[playlist.last_insert_pos]&
+ PLAYLIST_INSERT_TYPE_MASK) == PLAYLIST_INSERT_TYPE_INSERT)
+ position = insert_position = playlist.last_insert_pos+1;
+ else if (playlist.amount > 0)
+ position = insert_position = playlist.index + 1;
+ else
+ position = insert_position = 0;
+
+ playlist.last_insert_pos = position;
+ break;
+ case PLAYLIST_INSERT_FIRST:
+ if (playlist.amount > 0)
+ position = insert_position = playlist.index + 1;
+ else
+ position = insert_position = 0;
+
+ if (playlist.last_insert_pos < 0)
+ playlist.last_insert_pos = position;
+ break;
+ case PLAYLIST_INSERT_LAST:
+ if (playlist.first_index > 0)
+ insert_position = playlist.first_index;
+ else
+ insert_position = playlist.amount;
+
+ flags = PLAYLIST_INSERT_TYPE_APPEND;
+ break;
}
+
+ if (queue)
+ flags |= PLAYLIST_QUEUED;
- if (steps >= 0 && playlist.num_queued > 0 &&
- playlist.num_queued - steps > 0)
- *queue = true;
- else
+ /* shift indices so that track can be added */
+ for (i=playlist.amount; i>insert_position; i--)
+ playlist.indices[i] = playlist.indices[i-1];
+
+ /* update stored indices if needed */
+ if (playlist.amount > 0 && insert_position <= playlist.index)
+ playlist.index++;
+
+ if (playlist.amount > 0 && insert_position <= playlist.first_index &&
+ position != PLAYLIST_PREPEND)
+ playlist.first_index++;
+
+ if (insert_position < playlist.last_insert_pos ||
+ (insert_position == playlist.last_insert_pos && position < 0))
+ playlist.last_insert_pos++;
+
+ if (seek_pos < 0 && playlist.control_fd >= 0)
{
- *queue = false;
- if (playlist.num_queued)
+ int result = -1;
+
+ mutex_lock(&playlist.control_mutex);
+
+ if (lseek(playlist.control_fd, 0, SEEK_END) >= 0)
{
- if (steps >= 0)
- {
- /* skip the queued tracks */
- steps -= (playlist.num_queued - 1);
- }
- else if (!playlist.start_queue)
+ if (fprintf(playlist.control_fd, "%c:%d:%d:", (queue?'Q':'A'),
+ position, playlist.last_insert_pos) > 0)
{
- /* previous from queue list needs to go to current track in
- playlist */
- steps += 1;
+ /* save the position in file where track name is written */
+ seek_pos = lseek(playlist.control_fd, 0, SEEK_CUR);
+
+ if (fprintf(playlist.control_fd, "%s\n", filename) > 0)
+ result = 0;
}
}
+
+ mutex_unlock(&playlist.control_mutex);
+
+ if (result < 0)
+ {
+ splash(HZ*2, 0, true, str(LANG_PLAYLIST_CONTROL_UPDATE_ERROR));
+ return result;
+ }
}
- switch (global_settings.repeat_mode)
+ playlist.indices[insert_position] = flags | seek_pos;
+
+ playlist.amount++;
+
+ return insert_position;
+}
+
+/*
+ * Insert directory into playlist. May be called recursively.
+ */
+static int add_directory_to_playlist(char *dirname, int *position, bool queue,
+ int *count)
+{
+ char buf[MAX_PATH+1];
+ char *count_str;
+ int result = 0;
+ int num_files = 0;
+ bool buffer_full = false;
+ int i;
+ struct entry *files;
+
+ /* use the tree browser dircache to load files */
+ files = load_and_sort_directory(dirname, SHOW_ALL, &num_files,
+ &buffer_full);
+
+ if(!files)
{
- case REPEAT_OFF:
- if (*queue)
- next_index = (playlist.queue_index+steps) % MAX_QUEUED_FILES;
- else
- {
- if (current_index < playlist.first_index)
- current_index += playlist.amount;
- current_index -= playlist.first_index;
-
- next_index = current_index+steps;
- if ((next_index < 0) || (next_index >= playlist.amount))
- next_index = -1;
- else
- next_index = (next_index+playlist.first_index) %
- playlist.amount;
- }
+ splash(HZ*2, 0, true, str(LANG_PLAYLIST_DIRECTORY_ACCESS_ERROR));
+ return 0;
+ }
+
+ /* we've overwritten the dircache so tree browser will need to be
+ reloaded */
+ reload_directory();
+
+ if (queue)
+ count_str = str(LANG_PLAYLIST_QUEUE_COUNT);
+ else
+ count_str = str(LANG_PLAYLIST_INSERT_COUNT);
+
+ for (i=0; i<num_files; i++)
+ {
+ /* user abort */
+#ifdef HAVE_PLAYER_KEYPAD
+ if (button_get(false) == BUTTON_STOP)
+#else
+ if (button_get(false) == BUTTON_OFF)
+#endif
+ {
+ result = -1;
break;
+ }
- case REPEAT_ONE:
- /* if we are still in playlist when repeat one is set, don't go to
- queue list */
- if (*queue && !playlist.start_queue)
- next_index = playlist.queue_index;
+ if (files[i].attr & ATTR_DIRECTORY)
+ {
+ if (global_settings.recursive_dir_insert)
+ {
+ /* recursively add directories */
+ snprintf(buf, sizeof(buf), "%s/%s", dirname, files[i].name);
+ result = add_directory_to_playlist(buf, position, queue,
+ count);
+ if (result < 0)
+ break;
+
+ /* we now need to reload our current directory */
+ files = load_and_sort_directory(dirname, SHOW_ALL, &num_files,
+ &buffer_full);
+ if (!files)
+ {
+ result = -1;
+ break;
+ }
+ }
else
+ continue;
+ }
+ else if (files[i].attr & TREE_ATTR_MPA)
+ {
+ int insert_pos;
+
+ snprintf(buf, sizeof(buf), "%s/%s", dirname, files[i].name);
+
+ insert_pos = add_track_to_playlist(buf, *position, queue, -1);
+ if (insert_pos < 0)
{
- next_index = current_index;
- *queue = false;
+ result = -1;
+ break;
}
- break;
- case REPEAT_ALL:
- default:
- if (*queue)
- next_index = (playlist.queue_index+steps) % MAX_QUEUED_FILES;
- else
+ (*count)++;
+
+ /* Make sure tracks are inserted in correct order if user requests
+ INSERT_FIRST */
+ if (*position == PLAYLIST_INSERT_FIRST || *position >= 0)
+ *position = insert_pos + 1;
+
+ if ((*count%PLAYLIST_DISPLAY_COUNT) == 0)
{
- next_index = (current_index+steps) % playlist.amount;
- while (next_index < 0)
- next_index += playlist.amount;
+ display_playlist_count(*count, count_str);
+
+ if (*count == PLAYLIST_DISPLAY_COUNT)
+ mpeg_flush_and_reload_tracks();
}
- break;
+
+ /* let the other threads work */
+ yield();
+ }
}
- return next_index;
+ return result;
}
-int playlist_amount(void)
+/*
+ * remove track at specified position
+ */
+static int remove_track_from_playlist(int position, bool write)
{
- return playlist.amount + playlist.num_queued;
-}
+ int i;
-int playlist_first_index(void)
-{
- return playlist.first_index;
-}
+ if (playlist.amount <= 0)
+ return -1;
-/* Get resume info for current playing song. If return value is -1 then
- settings shouldn't be saved (eg. when playing queued song and save queue
- disabled) */
-int playlist_get_resume_info(int *resume_index, int *queue_resume,
- int *queue_resume_index)
-{
- int result = 0;
+ /* shift indices now that track has been removed */
+ for (i=position; i<playlist.amount; i++)
+ playlist.indices[i] = playlist.indices[i+1];
- *resume_index = playlist.index;
+ playlist.amount--;
+
+ /* update stored indices if needed */
+ if (position < playlist.index)
+ playlist.index--;
+
+ if (position < playlist.first_index)
+ playlist.first_index--;
- if (playlist.num_queued > 0)
+ if (position <= playlist.last_insert_pos)
+ playlist.last_insert_pos--;
+
+ if (write && playlist.control_fd >= 0)
{
- if (global_settings.save_queue_resume)
+ int result = -1;
+
+ mutex_lock(&playlist.control_mutex);
+
+ if (lseek(playlist.control_fd, 0, SEEK_END) >= 0)
{
- *queue_resume_index =
- playlist.queue_indices[playlist.queue_index];
- /* have we started playing the queue list yet? */
- if (playlist.start_queue)
- *queue_resume = QUEUE_BEGIN_PLAYLIST;
- else
- *queue_resume = QUEUE_BEGIN_QUEUE;
+ if (fprintf(playlist.control_fd, "D:%d\n", position) > 0)
+ {
+ fsync(playlist.control_fd);
+ result = 0;
+ }
}
- else if (!playlist.start_queue)
+
+ mutex_unlock(&playlist.control_mutex);
+
+ if (result < 0)
{
- *queue_resume = QUEUE_OFF;
- result = -1;
+ splash(HZ*2, 0, true, str(LANG_PLAYLIST_CONTROL_UPDATE_ERROR));
+ return result;
}
}
- else
- *queue_resume = QUEUE_OFF;
- return result;
+ return 0;
}
-char *playlist_name(char *buf, int buf_size)
+/*
+ * 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
+ */
+static int randomise_playlist(unsigned int seed, bool start_current, bool write)
{
- char *sep;
+ int count;
+ int candidate;
+ int store;
+ unsigned int current = playlist.indices[playlist.index];
+
+ /* seed with the given seed */
+ srand(seed);
- snprintf(buf, buf_size, "%s", playlist.filename+playlist.dirlen);
+ /* randomise entire indices list */
+ for(count = playlist.amount - 1; count >= 0; count--)
+ {
+ /* the rand is from 0 to RAND_MAX, so adjust to our value range */
+ candidate = rand() % (count + 1);
- if (0 == buf[0])
- return NULL;
+ /* now swap the values at the 'count' and 'candidate' positions */
+ store = playlist.indices[candidate];
+ playlist.indices[candidate] = playlist.indices[count];
+ playlist.indices[count] = store;
+ }
- /* Remove extension */
- sep = strrchr(buf, '.');
- if (NULL != sep)
- *sep = 0;
-
- return buf;
-}
+ if (start_current)
+ find_and_set_playlist_index(current);
-void playlist_clear(void)
-{
- playlist_end_pos = 0;
- playlist_buffer[0] = 0;
-}
+ /* indices have been moved so last insert position is no longer valid */
+ playlist.last_insert_pos = -1;
-int playlist_add(char *filename)
-{
- int len = strlen(filename);
+ if (write && playlist.control_fd >= 0)
+ {
+ int result = -1;
- if(len+2 > playlist.buffer_size - playlist_end_pos)
- return -1;
+ mutex_lock(&playlist.control_mutex);
+
+ if (lseek(playlist.control_fd, 0, SEEK_END) >= 0)
+ {
+ if (fprintf(playlist.control_fd, "S:%d:%d\n", seed,
+ playlist.first_index) > 0)
+ {
+ fsync(playlist.control_fd);
+ result = 0;
+ }
+ }
+
+ mutex_unlock(&playlist.control_mutex);
+
+ if (result < 0)
+ {
+ splash(HZ*2, 0, true, str(LANG_PLAYLIST_CONTROL_UPDATE_ERROR));
+ return result;
+ }
+ }
- strcpy(&playlist_buffer[playlist_end_pos], filename);
- playlist_end_pos += len;
- playlist_buffer[playlist_end_pos++] = '\n';
- playlist_buffer[playlist_end_pos] = '\0';
return 0;
}
-/* Add track to queue file */
-int queue_add(char *filename)
+/*
+ * 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.
+ */
+static int sort_playlist(bool start_current, bool write)
{
- int fd, seek, result;
- int len = strlen(filename);
+ unsigned int current = playlist.indices[playlist.index];
- if(playlist.num_queued >= MAX_QUEUED_FILES)
- return -1;
+ if (playlist.amount > 0)
+ qsort(playlist.indices, playlist.amount, sizeof(playlist.indices[0]),
+ compare);
- fd = open(QUEUE_FILE, O_WRONLY);
- if (fd < 0)
- return -1;
+ if (start_current)
+ find_and_set_playlist_index(current);
- seek = lseek(fd, 0, SEEK_END);
- if (seek < 0)
- {
- close(fd);
- return -1;
- }
+ /* indices have been moved so last insert position is no longer valid */
+ playlist.last_insert_pos = -1;
- /* save the file name with a trailing \n. QUEUE_FILE can be used as a
- playlist if desired */
- filename[len] = '\n';
- result = write(fd, filename, len+1);
- if (result < 0)
+ if (write && playlist.control_fd >= 0)
{
- close(fd);
- return -1;
- }
- filename[len] = '\0';
+ int result = -1;
- close(fd);
+ mutex_lock(&playlist.control_mutex);
- if (playlist.num_queued <= 0)
- playlist.start_queue = 1;
+ if (lseek(playlist.control_fd, 0, SEEK_END) >= 0)
+ {
+ if (fprintf(playlist.control_fd, "U:%d\n", playlist.first_index) >
+ 0)
+ {
+ fsync(playlist.control_fd);
+ result = 0;
+ }
+ }
- playlist.queue_indices[playlist.last_queue_index] = seek;
- playlist.last_queue_index =
- (playlist.last_queue_index + 1) % MAX_QUEUED_FILES;
- playlist.num_queued++;
+ mutex_unlock(&playlist.control_mutex);
- /* the play order has changed */
- mpeg_flush_and_reload_tracks();
+ if (result < 0)
+ {
+ splash(HZ*2, 0, true, str(LANG_PLAYLIST_CONTROL_UPDATE_ERROR));
+ return result;
+ }
+ }
- return playlist.num_queued;
+ return 0;
}
-int playlist_next(int steps)
+/*
+ * returns the index of the track that is "steps" away from current playing
+ * track.
+ */
+static int get_next_index(int steps)
{
- bool queue;
- int index = get_next_index(steps, &queue);
+ int current_index = playlist.index;
+ int next_index = -1;
- if (queue)
- {
- /* queue_diff accounts for bad songs in queue list */
- int queue_diff = index - playlist.queue_index;
- if (queue_diff < 0)
- queue_diff += MAX_QUEUED_FILES;
+ if (playlist.amount <= 0)
+ return -1;
- playlist.num_queued -= queue_diff;
- playlist.queue_index = index;
- playlist.start_queue = 0;
- }
- else
+ switch (global_settings.repeat_mode)
{
- playlist.index = index;
- if (playlist.num_queued > 0 && !playlist.start_queue)
+ case REPEAT_OFF:
{
- if (steps >= 0)
- {
- /* done with queue list */
- playlist.queue_index = playlist.last_queue_index;
- playlist.num_queued = 0;
- }
+ /* Rotate indices such that first_index is considered index 0 to
+ simplify next calculation */
+ current_index -= playlist.first_index;
+ if (current_index < 0)
+ current_index += playlist.amount;
+
+ next_index = current_index+steps;
+ if ((next_index < 0) || (next_index >= playlist.amount))
+ next_index = -1;
else
+ next_index = (next_index+playlist.first_index) %
+ playlist.amount;
+
+ break;
+ }
+
+ case REPEAT_ONE:
+ next_index = current_index;
+ break;
+
+ case REPEAT_ALL:
+ default:
+ {
+ next_index = (current_index+steps) % playlist.amount;
+ while (next_index < 0)
+ next_index += playlist.amount;
+
+ if (steps >= playlist.amount)
{
- /* user requested previous. however, don't forget about queue
- list */
- playlist.start_queue = 1;
+ int i, index;
+
+ index = next_index;
+ next_index = -1;
+
+ /* second time around so skip the queued files */
+ for (i=0; i<playlist.amount; i++)
+ {
+ if (playlist.indices[index] & PLAYLIST_QUEUE_MASK)
+ index = (index+1) % playlist.amount;
+ else
+ {
+ next_index = index;
+ break;
+ }
+ }
}
+ break;
}
}
- return index;
+ return next_index;
}
-/* Returns false if 'steps' is out of bounds, else true */
-bool playlist_check(int steps)
+/*
+ * 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(unsigned int seek)
{
- bool queue;
- int index = get_next_index(steps, &queue);
- return (index >= 0);
+ 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;
+ }
+ }
}
-char* playlist_peek(int steps)
+/*
+ * 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 int* e1 = (unsigned int*) p1;
+ unsigned int* e2 = (unsigned int*) p2;
+ unsigned int flags1 = *e1 & PLAYLIST_INSERT_TYPE_MASK;
+ unsigned int 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;
+}
+
+/*
+ * gets pathname for track at seek index
+ */
+static int get_filename(int seek, bool control_file, char *buf,
+ int buf_length)
{
- int seek;
- int max;
- int i;
int fd;
- char *buf;
+ int max = -1;
+ char tmp_buf[MAX_PATH+1];
char dir_buf[MAX_PATH+1];
- char *dir_end;
- int index;
- bool queue;
- index = get_next_index(steps, &queue);
- if (index < 0)
- return NULL;
+ if (buf_length > MAX_PATH+1)
+ buf_length = MAX_PATH+1;
- if (queue)
+ if (playlist.in_ram && !control_file)
{
- seek = playlist.queue_indices[index];
- fd = open(QUEUE_FILE, O_RDONLY);
- if(-1 != fd)
- {
- buf = dir_buf;
- lseek(fd, seek, SEEK_SET);
- max = read(fd, buf, MAX_PATH);
- close(fd);
- }
- else
- return NULL;
+ strncpy(tmp_buf, &playlist.buffer[seek], sizeof(tmp_buf));
+ tmp_buf[MAX_PATH] = '\0';
+ max = strlen(tmp_buf) + 1;
}
else
{
- seek = playlist.indices[index];
-
- if(playlist.in_ram)
- {
- buf = playlist_buffer + seek;
- max = playlist_end_pos - seek;
- }
+ if (control_file)
+ fd = playlist.control_fd;
else
{
if(-1 == playlist.fd)
playlist.fd = open(playlist.filename, O_RDONLY);
+
+ fd = playlist.fd;
+ }
+
+ if(-1 != fd)
+ {
+ if (control_file)
+ mutex_lock(&playlist.control_mutex);
+
+ lseek(fd, seek, SEEK_SET);
+ max = read(fd, tmp_buf, buf_length);
+
+ if (control_file)
+ mutex_unlock(&playlist.control_mutex);
+ }
- if(-1 != playlist.fd)
- {
- buf = playlist_buffer;
- lseek(playlist.fd, seek, SEEK_SET);
- max = read(playlist.fd, buf, MAX_PATH);
- }
+ if (max < 0)
+ {
+ if (control_file)
+ splash(HZ*2, 0, true,
+ str(LANG_PLAYLIST_CONTROL_ACCESS_ERROR));
else
- return NULL;
+ splash(HZ*2, 0, true, str(LANG_PLAYLIST_ACCESS_ERROR));
+
+ return max;
}
}
+ strncpy(dir_buf, playlist.filename, playlist.dirlen-1);
+ dir_buf[playlist.dirlen-1] = 0;
+
+ return (format_track_path(buf, tmp_buf, buf_length, max, dir_buf));
+}
+
+/*
+ * Returns absolute path of track
+ */
+static int format_track_path(char *dest, char *src, int buf_length, int max,
+ char *dir)
+{
+ int i = 0;
+ int j;
+ char *temp_ptr;
+
/* Zero-terminate the file name */
- seek=0;
- while((buf[seek] != '\n') &&
- (buf[seek] != '\r') &&
- (seek < max))
- seek++;
+ while((src[i] != '\n') &&
+ (src[i] != '\r') &&
+ (i < max))
+ i++;
/* Now work back killing white space */
- while((buf[seek-1] == ' ') ||
- (buf[seek-1] == '\t'))
- seek--;
+ while((src[i-1] == ' ') ||
+ (src[i-1] == '\t'))
+ i--;
- buf[seek]=0;
+ src[i]=0;
/* replace backslashes with forward slashes */
- for ( i=0; i<seek; i++ )
- if ( buf[i] == '\\' )
- buf[i] = '/';
+ for ( j=0; j<i; j++ )
+ if ( src[j] == '\\' )
+ src[j] = '/';
- if('/' == buf[0]) {
- strcpy(now_playing, &buf[0]);
+ if('/' == src[0])
+ {
+ strncpy(dest, src, buf_length);
}
- else {
- strncpy(dir_buf, playlist.filename, playlist.dirlen-1);
- dir_buf[playlist.dirlen-1] = 0;
-
+ else
+ {
/* handle dos style drive letter */
- if ( ':' == buf[1] ) {
- strcpy(now_playing, &buf[2]);
- }
- else if ( '.' == buf[0] && '.' == buf[1] && '/' == buf[2] ) {
+ if (':' == src[1])
+ strcpy(src, &dest[2]);
+ else if ('.' == src[0] && '.' == src[1] && '/' == src[2])
+ {
/* handle relative paths */
- seek=3;
- while(buf[seek] == '.' &&
- buf[seek+1] == '.' &&
- buf[seek+2] == '/')
- seek += 3;
- for (i=0; i<seek/3; i++) {
- dir_end = strrchr(dir_buf, '/');
- if (dir_end)
- *dir_end = '\0';
+ i=3;
+ while(src[i] == '.' &&
+ src[i] == '.' &&
+ src[i] == '/')
+ i += 3;
+ for (j=0; j<i/3; j++) {
+ temp_ptr = strrchr(dir, '/');
+ if (temp_ptr)
+ *temp_ptr = '\0';
else
break;
}
- snprintf(now_playing, MAX_PATH+1, "%s/%s", dir_buf, &buf[seek]);
+ snprintf(dest, buf_length, "%s/%s", dir, &src[i]);
}
- else if ( '.' == buf[0] && '/' == buf[1] ) {
- snprintf(now_playing, MAX_PATH+1, "%s/%s", dir_buf, &buf[2]);
+ else if ( '.' == src[0] && '/' == src[1] ) {
+ snprintf(dest, buf_length, "%s/%s", dir, &src[2]);
}
else {
- snprintf(now_playing, MAX_PATH+1, "%s/%s", dir_buf, buf);
- }
- }
-
- buf = now_playing;
-
- /* remove bogus dirs from beginning of path
- (workaround for buggy playlist creation tools) */
- if(!playlist.in_ram)
- {
- while (buf)
- {
- fd = open(buf, O_RDONLY);
- if (fd >= 0)
- {
- close(fd);
- break;
- }
-
- buf = strchr(buf+1, '/');
+ snprintf(dest, buf_length, "%s/%s", dir, src);
}
}
-
- if (!buf)
- {
- /* 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 now_playing;
- }
- return buf;
+ return 0;
}
/*
- * This function is called to start playback of a given playlist. This
- * playlist may be stored in RAM (when using full-dir playback).
- *
- * Return: the new index (possibly different due to shuffle)
+ * Display splash message showing progress of playlist/directory insertion or
+ * save.
*/
-int play_list(char *dir, /* "current directory" */
- char *file, /* playlist */
- int start_index, /* index in the playlist */
- bool shuffled_index, /* if TRUE the specified index is for the
- playlist AFTER the shuffle */
- int start_offset, /* offset in the file */
- int random_seed, /* used for shuffling */
- int first_index, /* first index of playlist */
- int queue_resume, /* resume queue list? */
- int queue_resume_index ) /* queue list seek pos */
+static void display_playlist_count(int count, char *fmt)
{
- char *sep="";
- int dirlen;
- empty_playlist(queue_resume);
-
- playlist.index = start_index;
- playlist.first_index = first_index;
+ lcd_clear_display();
#ifdef HAVE_LCD_BITMAP
if(global_settings.statusbar)
@@ -569,228 +919,915 @@ int play_list(char *dir, /* "current directory" */
lcd_setmargins(0, 0);
#endif
- /* If file is NULL, the list is in RAM */
- if(file) {
- splash(0, 0, true, str(LANG_PLAYLIST_LOAD));
- playlist.in_ram = false;
- } else {
- /* Assign a dummy filename */
- file = "";
- playlist.in_ram = true;
+#ifdef HAVE_PLAYER_KEYPAD
+ splash(0, 0, true, fmt, count, str(LANG_STOP_ABORT));
+#else
+ splash(0, 0, true, fmt, count, str(LANG_OFF_ABORT));
+#endif
+}
+
+/*
+ * Display buffer full message
+ */
+static void display_buffer_full(void)
+{
+ lcd_clear_display();
+ lcd_puts(0,0,str(LANG_PLAYINDICES_PLAYLIST));
+ lcd_puts(0,1,str(LANG_PLAYINDICES_BUFFER));
+ lcd_update();
+ sleep(HZ*2);
+ lcd_clear_display();
+}
+
+/*
+ * Initialize playlist entries at startup
+ */
+void playlist_init(void)
+{
+ playlist.fd = -1;
+ playlist.control_fd = -1;
+ playlist.max_playlist_size = global_settings.max_files_in_playlist;
+ playlist.indices = buffer_alloc(playlist.max_playlist_size * sizeof(int));
+ playlist.buffer_size =
+ AVERAGE_FILENAME_LENGTH * global_settings.max_files_in_dir;
+ playlist.buffer = buffer_alloc(playlist.buffer_size);
+ mutex_init(&playlist.control_mutex);
+ empty_playlist(true);
+}
+
+/*
+ * Create new playlist
+ */
+int playlist_create(char *dir, char *file)
+{
+ empty_playlist(false);
+
+ playlist.control_fd = open(PLAYLIST_CONTROL_FILE, O_RDWR);
+ if (-1 == playlist.control_fd)
+ {
+ splash(HZ*2, 0, true, str(LANG_PLAYLIST_CONTROL_ACCESS_ERROR));
+ return -1;
}
- dirlen = strlen(dir);
+ if (!file)
+ {
+ file = "";
- /* If the dir does not end in trailing slash, we use a separator.
- Otherwise we don't. */
- if('/' != dir[dirlen-1]) {
- sep="/";
- dirlen++;
+ if (dir)
+ playlist.in_ram = true;
+ else
+ dir = ""; /* empty playlist */
}
- playlist.dirlen = dirlen;
+ update_playlist_filename(dir, file);
- snprintf(playlist.filename, sizeof(playlist.filename),
- "%s%s%s",
- dir, sep, file);
+ if (fprintf(playlist.control_fd, "P:%d:%s:%s\n",
+ PLAYLIST_CONTROL_FILE_VERSION, dir, file) > 0)
+ fsync(playlist.control_fd);
+ else
+ {
+ splash(HZ*2, 0, true, str(LANG_PLAYLIST_CONTROL_UPDATE_ERROR));
+ return -1;
+ }
- /* add track indices to playlist data structure */
- add_indices_to_playlist();
-
- if(global_settings.playlist_shuffle) {
- if(!playlist.in_ram) {
- splash(0, 0, true, str(LANG_PLAYLIST_SHUFFLE));
- randomise_playlist( random_seed );
- }
- else {
- int i;
+ /* load the playlist file */
+ if (file[0] != '\0')
+ add_indices_to_playlist();
+
+ 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)
+{
+ char *buffer;
+ int buflen;
+ int nread;
+ int total_read = 0;
+ bool first = true;
+
+ enum {
+ resume_playlist,
+ resume_add,
+ resume_queue,
+ resume_delete,
+ resume_shuffle,
+ resume_unshuffle,
+ resume_reset,
+ resume_comment
+ };
+
+ /* use mp3 buffer for maximum load speed */
+ buflen = (mp3end - mp3buf);
+ buffer = mp3buf;
+
+ empty_playlist(true);
+
+ playlist.control_fd = open(PLAYLIST_CONTROL_FILE, O_RDWR);
+ if (-1 == playlist.control_fd)
+ {
+ splash(HZ*2, 0, true, str(LANG_PLAYLIST_CONTROL_ACCESS_ERROR));
+ return -1;
+ }
- /* store the seek position before the shuffle */
- int seek_pos = playlist.indices[start_index];
+ /* 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, 0, true, str(LANG_PLAYLIST_CONTROL_ACCESS_ERROR));
+ return -1;
+ }
- /* now shuffle around the indices */
- randomise_playlist( random_seed );
+ while (1)
+ {
+ int result = 0;
+ int count;
+ int current_command = resume_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;
+
+ for(count=0; count<nread && !exit_loop; count++,p++)
+ {
+ /* Are we on a new line? */
+ if((*p == '\n') || (*p == '\r'))
+ {
+ *p = '\0';
- if(!shuffled_index && global_settings.play_selected) {
- /* The given index was for the unshuffled list, so we need
- to figure out the index AFTER the shuffle has been made.
- We scan for the seek position we remmber from before. */
+ /* save last_newline in case we need to load more data */
+ last_newline = count;
- for(i=0; i< playlist.amount; i++) {
- if(seek_pos == playlist.indices[i]) {
- /* here's the start song! yiepee! */
- playlist.index = i;
- playlist.first_index = i;
- break; /* now stop searching */
+ switch (current_command)
+ {
+ case resume_playlist:
+ {
+ /* str1=version str2=dir str3=file */
+ int version;
+
+ if (!str1)
+ {
+ result = -1;
+ exit_loop = true;
+ break;
+ }
+
+ if (!str2)
+ str2 = "";
+
+ if (!str3)
+ str3 = "";
+
+ version = atoi(str1);
+
+ if (version != PLAYLIST_CONTROL_FILE_VERSION)
+ {
+ result = -1;
+ exit_loop = true;
+ break;
+ }
+
+ update_playlist_filename(str2, str3);
+
+ if (str3[0] != '\0')
+ {
+ /* NOTE: add_indices_to_playlist() overwrites the
+ mp3buf so we need to reload control file
+ data */
+ add_indices_to_playlist();
+ }
+ 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 resume_add:
+ case resume_queue:
+ {
+ /* str1=position str2=last_position str3=file */
+ int position, last_position;
+ bool queue;
+
+ if (!str1 || !str2 || !str3)
+ {
+ result = -1;
+ exit_loop = true;
+ break;
+ }
+
+ position = atoi(str1);
+ last_position = atoi(str2);
+
+ queue = (current_command == resume_add)?false:true;
+
+ /* seek position is based on str3's position in
+ buffer */
+ if (add_track_to_playlist(str3, position, queue,
+ total_read+(str3-buffer)) < 0)
+ return -1;
+
+ playlist.last_insert_pos = last_position;
+
+ break;
+ }
+ case resume_delete:
+ {
+ /* str1=position */
+ int position;
+
+ if (!str1)
+ {
+ result = -1;
+ exit_loop = true;
+ break;
+ }
+
+ position = atoi(str1);
+
+ if (remove_track_from_playlist(position,
+ false) < 0)
+ return -1;
+
+ break;
+ }
+ case resume_shuffle:
+ {
+ /* str1=seed str2=first_index */
+ int seed;
+
+ if (!str1 || !str2)
+ {
+ result = -1;
+ exit_loop = true;
+ break;
+ }
+
+ seed = atoi(str1);
+ playlist.first_index = atoi(str2);
+
+ if (randomise_playlist(seed, false, false) < 0)
+ return -1;
+
+ break;
+ }
+ case resume_unshuffle:
+ {
+ /* str1=first_index */
+ if (!str1)
+ {
+ result = -1;
+ exit_loop = true;
+ break;
+ }
+
+ playlist.first_index = atoi(str1);
+
+ if (sort_playlist(false, false) < 0)
+ return -1;
+
+ break;
+ }
+ case resume_reset:
+ {
+ playlist.last_insert_pos = -1;
+ break;
+ }
+ case resume_comment:
+ default:
+ break;
+ }
+
+ newline = true;
+
+ /* to ignore any extra newlines */
+ current_command = resume_comment;
+ }
+ else if(newline)
+ {
+ newline = false;
+
+ /* first non-comment line must always specify playlist */
+ if (first && *p != 'P' && *p != '#')
+ {
+ result = -1;
+ exit_loop = true;
+ break;
+ }
+
+ switch (*p)
+ {
+ case 'P':
+ /* playlist can only be specified once */
+ if (!first)
+ {
+ result = -1;
+ exit_loop = true;
+ break;
+ }
+
+ current_command = resume_playlist;
+ break;
+ case 'A':
+ current_command = resume_add;
+ break;
+ case 'Q':
+ current_command = resume_queue;
+ break;
+ case 'D':
+ current_command = resume_delete;
+ break;
+ case 'S':
+ current_command = resume_shuffle;
+ break;
+ case 'U':
+ current_command = resume_unshuffle;
+ break;
+ case 'R':
+ current_command = resume_reset;
+ break;
+ case '#':
+ current_command = resume_comment;
+ break;
+ default:
+ result = -1;
+ exit_loop = true;
+ break;
+ }
+
+ str_count = -1;
+ str1 = NULL;
+ str2 = NULL;
+ str3 = NULL;
+ }
+ else if(current_command != resume_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 we for any reason wouldn't find the index again, it
- won't set the index again and we should start at index 0
- instead */
}
}
- }
- if (queue_resume)
- {
- /* update the queue indices */
- add_indices_to_queuelist(queue_resume_index);
+ if (result < 0)
+ {
+ splash(HZ*2, 0, true, str(LANG_PLAYLIST_CONTROL_INVALID));
+ return result;
+ }
- if (queue_resume == QUEUE_BEGIN_PLAYLIST)
+ if (!newline || (exit_loop && count<nread))
{
- /* begin with current playlist index */
- playlist.start_queue = 1;
- playlist.index++; /* so we begin at the correct track */
+ /* We didn't end on a newline or we exited loop prematurely.
+ Either way, re-read the remainder.
+ NOTE: because of this, control file must always end with a
+ newline */
+ 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);
+
+ /* Terminate on EOF */
+ if(nread <= 0)
+ break;
+ }
+
+ return 0;
+}
+
+/*
+ * Add track to in_ram playlist. Used when playing directories.
+ */
+int playlist_add(char *filename)
+{
+ 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;
}
- /* also make the first song get playing */
- mpeg_play(start_offset);
+ playlist.indices[playlist.amount++] = playlist.buffer_end_pos;
- return playlist.index;
+ strcpy(&playlist.buffer[playlist.buffer_end_pos], filename);
+ playlist.buffer_end_pos += len;
+ playlist.buffer[playlist.buffer_end_pos++] = '\0';
+
+ return 0;
}
/*
- * calculate track offsets within a playlist file
+ * Insert track into playlist at specified position (or one of the special
+ * positions). Returns position where track was inserted or -1 if error.
*/
-void add_indices_to_playlist(void)
+int playlist_insert_track(char *filename, int position, bool queue)
+{
+ int result = add_track_to_playlist(filename, position, queue, -1);
+
+ if (result != -1)
+ {
+ fsync(playlist.control_fd);
+ mpeg_flush_and_reload_tracks();
+ }
+
+ return result;
+}
+
+/*
+ * Insert all tracks from specified directory into playlist.
+ */
+int playlist_insert_directory(char *dirname, int position, bool queue)
{
- int nread;
- int i = 0;
int count = 0;
- unsigned char* buffer = playlist_buffer;
- int buflen = playlist.buffer_size;
- bool store_index;
- unsigned char *p;
+ int result;
+ char *count_str;
- if(!playlist.in_ram) {
- if(-1 == playlist.fd)
- playlist.fd = open(playlist.filename, O_RDONLY);
- if(-1 == playlist.fd)
- return; /* failure */
-
-#ifndef SIMULATOR
- /* use mp3 buffer for maximum load speed */
- buflen = (mp3end - mp3buf);
- buffer = mp3buf;
+ if (queue)
+ count_str = str(LANG_PLAYLIST_QUEUE_COUNT);
+ else
+ count_str = str(LANG_PLAYLIST_INSERT_COUNT);
+
+ display_playlist_count(count, count_str);
+
+ result = add_directory_to_playlist(dirname, &position, queue, &count);
+ fsync(playlist.control_fd);
+
+ display_playlist_count(count, count_str);
+ mpeg_flush_and_reload_tracks();
+
+ return result;
+}
+
+/*
+ * Insert all tracks from specified playlist into dynamic playlist
+ */
+int playlist_insert_playlist(char *filename, int position, bool queue)
+{
+ int fd;
+ int max;
+ char *temp_ptr;
+ char *dir;
+ char *count_str;
+ char temp_buf[MAX_PATH+1];
+ char trackname[MAX_PATH+1];
+ int count = 0;
+ int result = 0;
+
+ fd = open(filename, O_RDONLY);
+ if (fd < 0)
+ {
+ splash(HZ*2, 0, true, str(LANG_PLAYLIST_ACCESS_ERROR));
+ return -1;
+ }
+
+ /* we need the directory name for formatting purposes */
+ dir = filename;
+
+ temp_ptr = strrchr(filename+1,'/');
+ if (temp_ptr)
+ *temp_ptr = 0;
+ else
+ dir = "/";
+
+ if (queue)
+ count_str = str(LANG_PLAYLIST_QUEUE_COUNT);
+ else
+ count_str = str(LANG_PLAYLIST_INSERT_COUNT);
+
+ display_playlist_count(count, count_str);
+
+ while ((max = read_line(fd, temp_buf, sizeof(temp_buf))) > 0)
+ {
+ /* user abort */
+#ifdef HAVE_PLAYER_KEYPAD
+ if (button_get(false) == BUTTON_STOP)
+#else
+ if (button_get(false) == BUTTON_OFF)
#endif
+ break;
+
+ if (temp_buf[0] != '#' || temp_buf[0] != '\0')
+ {
+ int insert_pos;
+
+ /* we need to format so that relative paths are correctly
+ handled */
+ if (format_track_path(trackname, temp_buf, sizeof(trackname), max,
+ dir) < 0)
+ {
+ result = -1;
+ break;
+ }
+
+ insert_pos = add_track_to_playlist(trackname, position, queue,
+ -1);
+
+ if (insert_pos < 0)
+ {
+ result = -1;
+ break;
+ }
+
+ /* Make sure tracks are inserted in correct order if user
+ requests INSERT_FIRST */
+ if (position == PLAYLIST_INSERT_FIRST || position >= 0)
+ position = insert_pos + 1;
+
+ count++;
+
+ if ((count%PLAYLIST_DISPLAY_COUNT) == 0)
+ {
+ display_playlist_count(count, count_str);
+
+ if (count == PLAYLIST_DISPLAY_COUNT)
+ mpeg_flush_and_reload_tracks();
+ }
+ }
+
+ /* let the other threads work */
+ yield();
}
- store_index = true;
+ close(fd);
+ fsync(playlist.control_fd);
- mpeg_stop();
+ *temp_ptr = '/';
+
+ display_playlist_count(count, count_str);
+ mpeg_flush_and_reload_tracks();
+
+ return result;
+}
+
+/* delete track at specified index */
+int playlist_delete(int index)
+{
+ int result = remove_track_from_playlist(index, true);
+
+ if (result != -1)
+ mpeg_flush_and_reload_tracks();
+
+ return result;
+}
+
+/* shuffle newly created playlist using random seed. */
+int playlist_shuffle(int random_seed, int start_index)
+{
+ unsigned int seek_pos = 0;
+ bool start_current = false;
+
+ if (start_index >= 0 && global_settings.play_selected)
+ {
+ /* store the seek position before the shuffle */
+ seek_pos = playlist.indices[start_index];
+ playlist.index = playlist.first_index = start_index;
+ start_current = true;
+ }
+
+ splash(0, 0, true, str(LANG_PLAYLIST_SHUFFLE));
- while(1)
+ randomise_playlist(random_seed, start_current, true);
+
+ return playlist.index;
+}
+
+/* shuffle currently playing playlist */
+int playlist_randomise(unsigned int seed, bool start_current)
+{
+ int result = randomise_playlist(seed, start_current, true);
+
+ if (result != -1)
+ mpeg_flush_and_reload_tracks();
+
+ return result;
+}
+
+/* sort currently playing playlist */
+int playlist_sort(bool start_current)
+{
+ int result = sort_playlist(start_current, true);
+
+ if (result != -1)
+ mpeg_flush_and_reload_tracks();
+
+ return result;
+}
+
+/* start playing current playlist at specified index/offset */
+int playlist_start(int start_index, int offset)
+{
+ playlist.index = start_index;
+ mpeg_play(offset);
+
+ return 0;
+}
+
+/* Returns false if 'steps' is out of bounds, else true */
+bool playlist_check(int steps)
+{
+ int index = get_next_index(steps);
+ return (index >= 0);
+}
+
+/* get trackname of track that is "steps" away from current playing track.
+ NULL is used to identify end of playlist */
+char* playlist_peek(int steps)
+{
+ int seek;
+ int fd;
+ char *temp_ptr;
+ int index;
+ bool control_file;
+
+ index = get_next_index(steps);
+ if (index < 0)
+ return NULL;
+
+ control_file = playlist.indices[index] & PLAYLIST_INSERT_TYPE_MASK;
+ seek = playlist.indices[index] & PLAYLIST_SEEK_MASK;
+
+ if (get_filename(seek, control_file, now_playing, MAX_PATH+1) < 0)
+ return NULL;
+
+ temp_ptr = now_playing;
+
+ if (!playlist.in_ram || control_file)
{
- if(playlist.in_ram) {
- nread = playlist_end_pos;
- } else {
- nread = read(playlist.fd, buffer, buflen);
- /* Terminate on EOF */
- if(nread <= 0)
+ /* remove bogus dirs from beginning of path
+ (workaround for buggy playlist creation tools) */
+ while (temp_ptr)
+ {
+ fd = open(temp_ptr, O_RDONLY);
+ if (fd >= 0)
+ {
+ close(fd);
break;
+ }
+
+ temp_ptr = strchr(temp_ptr+1, '/');
}
- p = buffer;
+ 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 now_playing;
+ }
+ }
- for(count=0; count < nread; count++,p++) {
+ return temp_ptr;
+}
- /* Are we on a new line? */
- if((*p == '\n') || (*p == '\r'))
+/*
+ * Update indices as track has changed
+ */
+int playlist_next(int steps)
+{
+ int index;
+
+ if (steps > 0)
+ {
+ int i, j;
+
+ /* We need to delete all the queued songs */
+ for (i=0, j=steps; i<j; i++)
+ {
+ index = get_next_index(i);
+
+ if (playlist.indices[index] & PLAYLIST_QUEUE_MASK)
{
- store_index = true;
- }
- else if(store_index)
+ remove_track_from_playlist(index, true);
+ steps--; /* one less track */
+ }
+ }
+ }
+
+ index = get_next_index(steps);
+ playlist.index = index;
+
+ if (playlist.last_insert_pos >= 0)
+ {
+ /* check to see if we've gone beyond the last inserted track */
+ int rot_index = index;
+ int rot_last_pos = playlist.last_insert_pos;
+
+ rot_index -= playlist.first_index;
+ if (rot_index < 0)
+ rot_index += playlist.amount;
+
+ rot_last_pos -= playlist.first_index;
+ if (rot_last_pos < 0)
+ rot_last_pos += playlist.amount;
+
+ if (rot_index > rot_last_pos)
+ {
+ /* reset last inserted track */
+ playlist.last_insert_pos = -1;
+
+ if (playlist.control_fd >= 0)
{
- store_index = false;
+ int result = -1;
- if(playlist.in_ram || (*p != '#'))
+ mutex_lock(&playlist.control_mutex);
+
+ if (lseek(playlist.control_fd, 0, SEEK_END) >= 0)
{
- /* Store a new entry */
- playlist.indices[ playlist.amount ] = i+count;
- playlist.amount++;
- if ( playlist.amount >= playlist.max_playlist_size ) {
- lcd_clear_display();
- lcd_puts(0,0,str(LANG_PLAYINDICES_PLAYLIST));
- lcd_puts(0,1,str(LANG_PLAYINDICES_BUFFER));
- lcd_update();
- sleep(HZ*2);
- lcd_clear_display();
-
- return;
+ if (fprintf(playlist.control_fd, "R\n") > 0)
+ {
+ fsync(playlist.control_fd);
+ result = 0;
}
}
+
+ mutex_unlock(&playlist.control_mutex);
+
+ if (result < 0)
+ {
+ splash(HZ*2, 0, true,
+ str(LANG_PLAYLIST_CONTROL_UPDATE_ERROR));
+ return result;
+ }
}
}
+ }
- i+= count;
+ return index;
+}
- if(playlist.in_ram)
- break;
- }
+/* Get resume info for current playing song. If return value is -1 then
+ settings shouldn't be saved. */
+int playlist_get_resume_info(int *resume_index)
+{
+ *resume_index = playlist.index;
+
+ return 0;
}
-/*
- * randomly rearrange the array of indices for the playlist
- */
-void randomise_playlist( unsigned int 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)
{
- int count;
- int candidate;
- int store;
-
- /* seed with the given seed */
- srand( seed );
+ int index = playlist.index;
- /* randomise entire indices list */
- for(count = playlist.amount - 1; count >= 0; count--)
- {
- /* the rand is from 0 to RAND_MAX, so adjust to our value range */
- candidate = rand() % (count + 1);
+ /* first_index should always be index 0 for display purposes */
+ index -= playlist.first_index;
+ if (index < 0)
+ index += playlist.amount;
- /* now swap the values at the 'count' and 'candidate' positions */
- store = playlist.indices[candidate];
- playlist.indices[candidate] = playlist.indices[count];
- playlist.indices[count] = store;
- }
+ return (index+1);
+}
- mpeg_flush_and_reload_tracks();
+/* returns number of tracks in playlist (includes queued/inserted tracks) */
+int playlist_amount(void)
+{
+ return playlist.amount;
}
-static int compare(const void* p1, const void* p2)
+/* returns playlist name */
+char *playlist_name(char *buf, int buf_size)
{
- int* e1 = (int*) p1;
- int* e2 = (int*) p2;
+ char *sep;
+
+ snprintf(buf, buf_size, "%s", playlist.filename+playlist.dirlen);
+
+ if (0 == buf[0])
+ return NULL;
- return *e1 - *e2;
+ /* Remove extension */
+ sep = strrchr(buf, '.');
+ if (NULL != sep)
+ *sep = 0;
+
+ return buf;
}
-/*
- * 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.
- */
-void sort_playlist(bool start_current)
+/* save the current dynamic playlist to specified file */
+int playlist_save(char *filename)
{
- int i;
- int current = playlist.indices[playlist.index];
+ int fd;
+ int i, index;
+ int count = 0;
+ char tmp_buf[MAX_PATH+1];
+ int result = 0;
- if (playlist.amount > 0)
+ if (playlist.amount <= 0)
+ return -1;
+
+ /* use current working directory as base for pathname */
+ if (format_track_path(tmp_buf, filename, sizeof(tmp_buf),
+ strlen(filename)+1, getcwd(NULL, -1)) < 0)
+ return -1;
+
+ fd = open(tmp_buf, O_CREAT|O_WRONLY|O_TRUNC);
+ if (fd < 0)
{
- qsort(playlist.indices, playlist.amount, sizeof(playlist.indices[0]), compare);
+ splash(HZ*2, 0, true, str(LANG_PLAYLIST_ACCESS_ERROR));
+ return -1;
}
- if (start_current)
+ display_playlist_count(count, str(LANG_PLAYLIST_SAVE_COUNT));
+
+ index = playlist.first_index;
+ for (i=0; i<playlist.amount; i++)
{
- /* Set the index to the current song */
- for (i=0; i<playlist.amount; i++)
+ bool control_file;
+ bool queue;
+ int seek;
+
+ /* user abort */
+#ifdef HAVE_PLAYER_KEYPAD
+ if (button_get(false) == BUTTON_STOP)
+#else
+ if (button_get(false) == BUTTON_OFF)
+#endif
+ break;
+
+ control_file = playlist.indices[index] & PLAYLIST_INSERT_TYPE_MASK;
+ queue = playlist.indices[index] & PLAYLIST_QUEUE_MASK;
+ seek = playlist.indices[index] & PLAYLIST_SEEK_MASK;
+
+ /* Don't save queued files */
+ if (!queue)
{
- if (playlist.indices[i] == current)
+ if (get_filename(seek, control_file, tmp_buf, MAX_PATH+1) < 0)
{
- playlist.index = i;
+ result = -1;
break;
}
+
+ if (fprintf(fd, "%s\n", tmp_buf) < 0)
+ {
+ splash(HZ*2, 0, true,
+ str(LANG_PLAYLIST_CONTROL_UPDATE_ERROR));
+ result = -1;
+ break;
+ }
+
+ count++;
+
+ if ((count%PLAYLIST_DISPLAY_COUNT) == 0)
+ display_playlist_count(count, str(LANG_PLAYLIST_SAVE_COUNT));
+
+ yield();
}
+
+ index = (index+1)%playlist.amount;
}
- mpeg_flush_and_reload_tracks();
+ display_playlist_count(count, str(LANG_PLAYLIST_SAVE_COUNT));
+
+ close(fd);
+
+ return result;
}
diff --git a/apps/playlist.h b/apps/playlist.h
index 9ff5122f44..a5318cd202 100644
--- a/apps/playlist.h
+++ b/apps/playlist.h
@@ -22,57 +22,56 @@
#include <stdbool.h>
#include "file.h"
-#include "applimits.h"
+#include "kernel.h"
/* playlist data */
struct playlist_info
{
char filename[MAX_PATH]; /* path name of m3u playlist on disk */
- int fd; /* file descriptor of the open playlist */
+ int fd; /* descriptor of the open playlist file */
+ int control_fd; /* descriptor of the open control file */
int dirlen; /* Length of the path to the playlist file */
- int *indices; /* array of indices */
- int max_playlist_size; /* Max number of files in playlist. Mirror of
+ unsigned int *indices; /* array of indices */
+ int max_playlist_size; /* Max number of files in playlist. Mirror of
global_settings.max_files_in_playlist */
- int buffer_size; /* Playlist buffer size */
+ bool in_ram; /* playlist stored in ram (dirplay) */
+ char *buffer; /* buffer for in-ram playlists */
+ int buffer_size; /* size of buffer */
+ int buffer_end_pos; /* last position where buffer was written */
int index; /* index of current playing track */
int first_index; /* index of first song in playlist */
- int seed; /* random seed */
int amount; /* number of tracks in the index */
- bool in_ram; /* True if the playlist is RAM-based */
-
- /* Queue function */
- int queue_indices[MAX_QUEUED_FILES]; /* array of queue indices */
- int last_queue_index; /* index of last queued track */
- int queue_index; /* index of current playing queued track */
- int num_queued; /* number of songs queued */
- int start_queue; /* the first song was queued */
+ int last_insert_pos; /* last position we inserted a track */
+ struct mutex control_mutex; /* mutex for control file access */
};
-extern struct playlist_info playlist;
-extern bool playlist_shuffle;
-
void playlist_init(void);
-int play_list(char *dir, char *file, int start_index,
- bool shuffled_index, int start_offset,
- int random_seed, int first_index, int queue_resume,
- int queue_resume_index);
-char* playlist_peek(int steps);
-char* playlist_name(char *name, int name_size);
-int playlist_next(int steps);
-bool playlist_check(int steps);
-void randomise_playlist( unsigned int seed );
-void sort_playlist(bool start_current);
-void add_indices_to_playlist(void);
-void playlist_clear(void);
+int playlist_create(char *dir, char *file);
+int playlist_resume(void);
int playlist_add(char *filename);
-int queue_add(char *filename);
+int playlist_insert_track(char *filename, int position, bool queue);
+int playlist_insert_directory(char *dirname, int position, bool queue);
+int playlist_insert_playlist(char *filename, int position, bool queue);
+int playlist_delete(int index);
+int playlist_shuffle(int random_seed, int start_index);
+int playlist_randomise(unsigned int seed, bool start_current);
+int playlist_sort(bool start_current);
+int playlist_start(int start_index, int offset);
+bool playlist_check(int steps);
+char *playlist_peek(int steps);
+int playlist_next(int steps);
+int playlist_get_resume_info(int *resume_index);
+int playlist_get_display_index(void);
int playlist_amount(void);
-int playlist_first_index(void);
-int playlist_get_resume_info(int *resume_index, int *queue_resume,
- int *queue_resume_index);
+char *playlist_name(char *buf, int buf_size);
+int playlist_save(char *filename);
-enum { QUEUE_OFF, QUEUE_BEGIN_QUEUE, QUEUE_BEGIN_PLAYLIST, NUM_QUEUE_MODES };
+enum {
+ PLAYLIST_PREPEND = -1,
+ PLAYLIST_INSERT = -2,
+ PLAYLIST_INSERT_LAST = -3,
+ PLAYLIST_INSERT_FIRST = -4
+};
#endif /* __PLAYLIST_H__ */
-
diff --git a/apps/playlist_menu.c b/apps/playlist_menu.c
new file mode 100644
index 0000000000..3508240efe
--- /dev/null
+++ b/apps/playlist_menu.c
@@ -0,0 +1,71 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2002 Björn Stenberg
+ *
+ * All files in this archive are subject to the GNU General Public License.
+ * See the file COPYING in the source tree root for full license agreement.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+#include <string.h>
+
+#include "menu.h"
+#include "file.h"
+#include "keyboard.h"
+#include "playlist.h"
+#include "tree.h"
+#include "settings.h"
+
+#include "lang.h"
+
+#define DEFAULT_PLAYLIST_NAME "/dynamic.m3u"
+
+static bool save_playlist(void)
+{
+ char filename[MAX_PATH+1];
+
+ strncpy(filename, DEFAULT_PLAYLIST_NAME, sizeof(filename));
+
+ if (!kbd_input(filename, sizeof(filename)))
+ {
+ playlist_save(filename);
+
+ /* reload in case playlist was saved to cwd */
+ reload_directory();
+ }
+
+ return false;
+}
+
+static bool recurse_directory(void)
+{
+ return (set_bool( str(LANG_RECURSE_DIRECTORY),
+ &global_settings.recursive_dir_insert));
+}
+
+bool playlist_menu(void)
+{
+ int m;
+ bool result;
+
+ struct menu_items items[] = {
+ { str(LANG_CREATE_PLAYLIST), create_playlist },
+ { str(LANG_SAVE_DYNAMIC_PLAYLIST), save_playlist },
+ { str(LANG_RECURSE_DIRECTORY), recurse_directory },
+ };
+
+ m = menu_init( items, sizeof items / sizeof(struct menu_items) );
+ result = menu_run(m);
+ menu_exit(m);
+ return result;
+}
diff --git a/apps/playlist_menu.h b/apps/playlist_menu.h
new file mode 100644
index 0000000000..e10fc81299
--- /dev/null
+++ b/apps/playlist_menu.h
@@ -0,0 +1,24 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2002 Björn Stenberg
+ *
+ * All files in this archive are subject to the GNU General Public License.
+ * See the file COPYING in the source tree root for full license agreement.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+#ifndef _PLAYLIST_MENU_H
+#define _PLAYLIST_MENU_H
+
+bool playlist_menu(void);
+
+#endif
diff --git a/apps/screens.c b/apps/screens.c
index 6055be3a2d..eb31eff5cf 100644
--- a/apps/screens.c
+++ b/apps/screens.c
@@ -341,9 +341,9 @@ bool f2_screen(void)
if(mpeg_status() & MPEG_STATUS_PLAY)
{
if (global_settings.playlist_shuffle)
- randomise_playlist(current_tick);
+ playlist_randomise(current_tick, true);
else
- sort_playlist(true);
+ playlist_sort(true);
}
used = true;
break;
diff --git a/apps/settings.c b/apps/settings.c
index d6e555f03e..e5fbfb5e06 100644
--- a/apps/settings.c
+++ b/apps/settings.c
@@ -62,7 +62,7 @@
struct user_settings global_settings;
char rockboxdir[] = ROCKBOX_DIR; /* config/font/data file directory */
-#define CONFIG_BLOCK_VERSION 6
+#define CONFIG_BLOCK_VERSION 7
#define CONFIG_BLOCK_SIZE 512
#define RTC_BLOCK_SIZE 44
@@ -98,10 +98,10 @@ offset abs
0x12 0x26 <(int) Resume playlist index, or -1 if no playlist resume>
0x16 0x2a <(int) Byte offset into resume file>
0x1a 0x2e <time until disk spindown>
-0x1b 0x2f <browse current, play selected, queue_resume>
+0x1b 0x2f <browse current, play selected, recursive dir insert>
0x1c 0x30 <peak meter hold timeout (bit 0-4),
rec_editable (bit 7)>
-0x1d 0x31 <(int) queue resume index>
+0x1d 0x31 <unused>
0x21 0x35 <repeat mode (bit 0-1), rec. channels (bit 2),
mic gain (bit 4-7)>
0x22 0x36 <rec. quality (bit 0-2), source (bit 3-4), frequency (bit 5-7)>
@@ -144,9 +144,9 @@ Rest of config block, only saved to disk:
0xB8 (char[20]) WPS file
0xCC (char[20]) Lang file
0xE0 (char[20]) Font file
-0xF4 (int) Playlist first index
-0xF8 (int) Playlist shuffle seed
-0xFC-0x1FF (char[260]) Resume playlist (path/to/dir or path/to/playlist.m3u)
+0xF4 <unused>
+0xF8 <unused>
+0xFC <unused>
*************************************/
@@ -343,18 +343,19 @@ int settings_save( void )
memcpy(&config_block[0x12], &global_settings.resume_index, 4);
memcpy(&config_block[0x16], &global_settings.resume_offset, 4);
+ DEBUGF( "+Resume index %X offset %X\n",
+ global_settings.resume_index,
+ global_settings.resume_offset );
config_block[0x1a] = (unsigned char)global_settings.disk_spindown;
config_block[0x1b] = (unsigned char)
(((global_settings.browse_current & 1)) |
((global_settings.play_selected & 1) << 1) |
- ((global_settings.queue_resume & 3) << 2));
+ ((global_settings.recursive_dir_insert & 1) << 2));
config_block[0x1c] = (unsigned char)global_settings.peak_meter_hold |
(global_settings.rec_editable?0x80:0);
- memcpy(&config_block[0x1d], &global_settings.queue_resume_index, 4);
-
config_block[0x21] = (unsigned char)
((global_settings.repeat_mode & 3) |
((global_settings.rec_channels & 1) << 2) |
@@ -415,17 +416,6 @@ int settings_save( void )
strncpy(&config_block[0xb8], global_settings.wps_file, MAX_FILENAME);
strncpy(&config_block[0xcc], global_settings.lang_file, MAX_FILENAME);
strncpy(&config_block[0xe0], global_settings.font_file, MAX_FILENAME);
- memcpy(&config_block[0xF4], &global_settings.resume_first_index, 4);
- memcpy(&config_block[0xF8], &global_settings.resume_seed, 4);
-
- strncpy(&config_block[0xFC], global_settings.resume_file, MAX_PATH);
- DEBUGF( "+Resume file %s\n",global_settings.resume_file );
- DEBUGF( "+Resume index %X offset %X\n",
- global_settings.resume_index,
- global_settings.resume_offset );
- DEBUGF( "+Resume shuffle %s seed %X\n",
- global_settings.playlist_shuffle?"on":"off",
- global_settings.resume_seed );
if(save_config_buffer())
{
@@ -655,7 +645,8 @@ void settings_load(void)
if (config_block[0x1b] != 0xFF) {
global_settings.browse_current = (config_block[0x1b]) & 1;
global_settings.play_selected = (config_block[0x1b] >> 1) & 1;
- global_settings.queue_resume = (config_block[0x1b] >> 2) & 3;
+ global_settings.recursive_dir_insert =
+ (config_block[0x1b] >> 2) & 1;
}
if (config_block[0x1c] != 0xFF) {
@@ -664,10 +655,6 @@ void settings_load(void)
(config_block[0x1c] & 0x80)?true:false;
}
- if (config_block[0x1d] != 0xFF)
- memcpy(&global_settings.queue_resume_index, &config_block[0x1d],
- 4);
-
if (config_block[0x21] != 0xFF)
{
global_settings.repeat_mode = config_block[0x21] & 3;
@@ -745,14 +732,9 @@ void settings_load(void)
global_settings.max_files_in_playlist =
config_block[0xaa] | (config_block[0xab] << 8);
- memcpy(&global_settings.resume_first_index, &config_block[0xF4], 4);
- memcpy(&global_settings.resume_seed, &config_block[0xF8], 4);
-
strncpy(global_settings.wps_file, &config_block[0xb8], MAX_FILENAME);
strncpy(global_settings.lang_file, &config_block[0xcc], MAX_FILENAME);
strncpy(global_settings.font_file, &config_block[0xe0], MAX_FILENAME);
- strncpy(global_settings.resume_file, &config_block[0xFC], MAX_PATH);
- global_settings.resume_file[MAX_PATH]=0;
#ifdef HAVE_LCD_CHARCELLS
if (config_block[0xa8] != 0xff)
global_settings.jump_scroll = config_block[0xa8];
@@ -1097,6 +1079,8 @@ bool settings_load_config(char* file)
else if (!strcasecmp(name, "max files in playlist"))
set_cfg_int(&global_settings.max_files_in_playlist, value,
1000, 20000);
+ else if (!strcasecmp(name, "recursive directory insert"))
+ set_cfg_bool(&global_settings.recursive_dir_insert, value);
}
close(fd);
@@ -1385,6 +1369,9 @@ bool settings_save_config(void)
fprintf(fd, "max files in playlist: %d\r\n",
global_settings.max_files_in_playlist);
+ fprintf(fd, "recursive directory insert: %s\r\n",
+ boolopt[global_settings.recursive_dir_insert]);
+
close(fd);
lcd_clear_display();
@@ -1450,9 +1437,6 @@ void settings_reset(void) {
global_settings.ff_rewind_accel = DEFAULT_FF_REWIND_ACCEL_SETTING;
global_settings.resume_index = -1;
global_settings.resume_offset = -1;
- global_settings.save_queue_resume = true;
- global_settings.queue_resume = 0;
- global_settings.queue_resume_index = -1;
global_settings.disk_spindown = 5;
global_settings.disk_poweroff = false;
global_settings.buffer_margin = 0;
@@ -1475,6 +1459,7 @@ void settings_reset(void) {
global_settings.max_files_in_dir = 400;
global_settings.max_files_in_playlist = 10000;
global_settings.show_icons = true;
+ global_settings.recursive_dir_insert = false;
}
bool set_bool(char* string, bool* variable )
diff --git a/apps/settings.h b/apps/settings.h
index 624b06e06b..25efd65e9a 100644
--- a/apps/settings.h
+++ b/apps/settings.h
@@ -103,16 +103,7 @@ struct user_settings
int resume; /* resume option: 0=off, 1=ask, 2=on */
int resume_index; /* index in playlist (-1 for no active resume) */
int resume_offset; /* byte offset in mp3 file */
- int resume_seed; /* random seed for playlist shuffle */
- int resume_first_index; /* first index of playlist */
- bool save_queue_resume; /* save queued songs for resume */
- int queue_resume; /* resume queue file?: 0 = no
- 1 = resume at queue index
- 2 = resume at playlist index */
- int queue_resume_index; /* queue index (seek point in queue file) */
-
- unsigned char resume_file[MAX_PATH+1]; /* playlist name (or dir) */
unsigned char font_file[MAX_FILENAME+1]; /* last font */
unsigned char wps_file[MAX_FILENAME+1]; /* last wps */
unsigned char lang_file[MAX_FILENAME+1]; /* last language */
@@ -175,6 +166,7 @@ struct user_settings
int max_files_in_dir; /* Max entries in directory (file browser) */
int max_files_in_playlist; /* Max entries in playlist */
bool show_icons; /* 0=hide 1=show */
+ bool recursive_dir_insert;/* should directories be inserted recursively */
};
enum optiontype { INT, BOOL };
diff --git a/apps/settings_menu.c b/apps/settings_menu.c
index 57ba18132a..f64ee17ccd 100644
--- a/apps/settings_menu.c
+++ b/apps/settings_menu.c
@@ -724,11 +724,11 @@ static bool playback_settings_menu(void)
{
if (global_settings.playlist_shuffle)
{
- randomise_playlist(current_tick);
+ playlist_randomise(current_tick, true);
}
else
{
- sort_playlist(true);
+ playlist_sort(true);
}
}
return result;
diff --git a/apps/tree.c b/apps/tree.c
index f7a83de85f..29ca4dfe15 100644
--- a/apps/tree.c
+++ b/apps/tree.c
@@ -66,10 +66,6 @@ static int max_files_in_dir;
static char *name_buffer;
static int name_buffer_size; /* Size of allocated buffer */
static int name_buffer_length; /* Currently used amount */
-struct entry {
- short attr; /* FAT attributes + file type flags */
- char *name;
-};
static struct entry *dircache;
@@ -87,6 +83,8 @@ static int boot_size = 0;
static int boot_cluster;
static bool boot_changed = false;
+static bool dirbrowse(char *root);
+
void browse_root(void)
{
#ifndef SIMULATOR
@@ -158,14 +156,13 @@ static int build_playlist(int start_index)
int i;
int start=start_index;
- playlist_clear();
-
for(i = 0;i < filesindir;i++)
{
if(dircache[i].attr & TREE_ATTR_MPA)
{
DEBUGF("Adding %s\n", dircache[i].name);
- playlist_add(dircache[i].name);
+ if (playlist_add(dircache[i].name) < 0)
+ break;
}
else
{
@@ -237,6 +234,133 @@ static void showfileline(int line, int direntry, bool scroll)
}
}
+/* load sorted directory into dircache. returns NULL on failure. */
+struct entry* load_and_sort_directory(char *dirname, int dirfilter,
+ int *num_files, bool *buffer_full)
+{
+ int i;
+
+ DIR *dir = opendir(dirname);
+ if(!dir)
+ return NULL; /* not a directory */
+
+ name_buffer_length = 0;
+ *buffer_full = false;
+
+ for ( i=0; i < max_files_in_dir; i++ ) {
+ int len;
+ struct dirent *entry = readdir(dir);
+ struct entry* dptr = &dircache[i];
+ if (!entry)
+ break;
+
+ len = strlen(entry->d_name);
+
+ /* skip directories . and .. */
+ if ((entry->attribute & ATTR_DIRECTORY) &&
+ (((len == 1) &&
+ (!strncmp(entry->d_name, ".", 1))) ||
+ ((len == 2) &&
+ (!strncmp(entry->d_name, "..", 2))))) {
+ i--;
+ continue;
+ }
+
+ /* Skip FAT volume ID */
+ if (entry->attribute & ATTR_VOLUME_ID) {
+ i--;
+ continue;
+ }
+
+ /* filter out dotfiles and hidden files */
+ if (dirfilter != SHOW_ALL &&
+ ((entry->d_name[0]=='.') ||
+ (entry->attribute & ATTR_HIDDEN))) {
+ i--;
+ continue;
+ }
+
+ dptr->attr = entry->attribute;
+
+ /* mark mp? and m3u files as such */
+ if ( !(dptr->attr & ATTR_DIRECTORY) && (len > 4) ) {
+ if (!strcasecmp(&entry->d_name[len-4], ".mp3") ||
+ (!strcasecmp(&entry->d_name[len-4], ".mp2")) ||
+ (!strcasecmp(&entry->d_name[len-4], ".mpa")))
+ dptr->attr |= TREE_ATTR_MPA;
+ else if (!strcasecmp(&entry->d_name[len-4], ".m3u"))
+ dptr->attr |= TREE_ATTR_M3U;
+ else if (!strcasecmp(&entry->d_name[len-4], ".cfg"))
+ dptr->attr |= TREE_ATTR_CFG;
+ else if (!strcasecmp(&entry->d_name[len-4], ".wps"))
+ dptr->attr |= TREE_ATTR_WPS;
+ else if (!strcasecmp(&entry->d_name[len-4], ".txt"))
+ dptr->attr |= TREE_ATTR_TXT;
+ else if (!strcasecmp(&entry->d_name[len-4], ".lng"))
+ dptr->attr |= TREE_ATTR_LNG;
+#ifdef HAVE_RECORDER_KEYPAD
+ else if (!strcasecmp(&entry->d_name[len-4], ".fnt"))
+ dptr->attr |= TREE_ATTR_FONT;
+ else if (!strcasecmp(&entry->d_name[len-4], ".ajz"))
+#else
+ else if (!strcasecmp(&entry->d_name[len-4], ".mod"))
+#endif
+ dptr->attr |= TREE_ATTR_MOD;
+ else if (!strcasecmp(&entry->d_name[len-5], ".rock"))
+ dptr->attr |= TREE_ATTR_ROCK;
+ }
+
+ /* memorize/compare details about the boot file */
+ if ((currdir[1] == 0) && !strcmp(entry->d_name, BOOTFILE)) {
+ if (boot_size) {
+ if ((entry->size != boot_size) ||
+ (entry->startcluster != boot_cluster))
+ boot_changed = true;
+ }
+ boot_size = entry->size;
+ boot_cluster = entry->startcluster;
+ }
+
+ /* filter out all non-playlist files */
+ if ( dirfilter == SHOW_PLAYLIST &&
+ (!(dptr->attr &
+ (ATTR_DIRECTORY|TREE_ATTR_M3U))) ) {
+ i--;
+ continue;
+ }
+
+ /* filter out non-music files */
+ if ( dirfilter == SHOW_MUSIC &&
+ (!(dptr->attr &
+ (ATTR_DIRECTORY|TREE_ATTR_MPA|TREE_ATTR_M3U))) ) {
+ i--;
+ continue;
+ }
+
+ /* filter out non-supported files */
+ if ( dirfilter == SHOW_SUPPORTED &&
+ (!(dptr->attr & TREE_ATTR_MASK)) ) {
+ i--;
+ continue;
+ }
+
+ if (len > name_buffer_size - name_buffer_length - 1) {
+ /* Tell the world that we ran out of buffer space */
+ *buffer_full = true;
+ break;
+ }
+ dptr->name = &name_buffer[name_buffer_length];
+ strcpy(dptr->name,entry->d_name);
+ name_buffer_length += len + 1;
+ }
+ *num_files = i;
+ closedir(dir);
+ strncpy(lastdir,dirname,sizeof(lastdir));
+ lastdir[sizeof(lastdir)-1] = 0;
+ qsort(dircache,i,sizeof(struct entry),compare);
+
+ return dircache;
+}
static int showdir(char *path, int start)
{
@@ -258,124 +382,9 @@ static int showdir(char *path, int start)
/* new dir? cache it */
if (strncmp(path,lastdir,sizeof(lastdir)) || reload_dir) {
- DIR *dir = opendir(path);
- if(!dir)
- return -1; /* not a directory */
-
- name_buffer_length = 0;
- dir_buffer_full = false;
-
- for ( i=0; i < max_files_in_dir; i++ ) {
- int len;
- struct dirent *entry = readdir(dir);
- struct entry* dptr = &dircache[i];
- if (!entry)
- break;
-
- len = strlen(entry->d_name);
-
- /* skip directories . and .. */
- if ((entry->attribute & ATTR_DIRECTORY) &&
- (((len == 1) &&
- (!strncmp(entry->d_name, ".", 1))) ||
- ((len == 2) &&
- (!strncmp(entry->d_name, "..", 2))))) {
- i--;
- continue;
- }
-
- /* Skip FAT volume ID */
- if (entry->attribute & ATTR_VOLUME_ID) {
- i--;
- continue;
- }
-
- /* filter out dotfiles and hidden files */
- if (global_settings.dirfilter != SHOW_ALL &&
- ((entry->d_name[0]=='.') ||
- (entry->attribute & ATTR_HIDDEN))) {
- i--;
- continue;
- }
-
- dptr->attr = entry->attribute;
-
- /* mark mp? and m3u files as such */
- if ( !(dptr->attr & ATTR_DIRECTORY) && (len > 4) ) {
- if (!strcasecmp(&entry->d_name[len-4], ".mp3") ||
- (!strcasecmp(&entry->d_name[len-4], ".mp2")) ||
- (!strcasecmp(&entry->d_name[len-4], ".mpa")))
- dptr->attr |= TREE_ATTR_MPA;
- else if (!strcasecmp(&entry->d_name[len-4], ".m3u"))
- dptr->attr |= TREE_ATTR_M3U;
- else if (!strcasecmp(&entry->d_name[len-4], ".cfg"))
- dptr->attr |= TREE_ATTR_CFG;
- else if (!strcasecmp(&entry->d_name[len-4], ".wps"))
- dptr->attr |= TREE_ATTR_WPS;
- else if (!strcasecmp(&entry->d_name[len-4], ".txt"))
- dptr->attr |= TREE_ATTR_TXT;
- else if (!strcasecmp(&entry->d_name[len-4], ".lng"))
- dptr->attr |= TREE_ATTR_LNG;
-#ifdef HAVE_RECORDER_KEYPAD
- else if (!strcasecmp(&entry->d_name[len-4], ".fnt"))
- dptr->attr |= TREE_ATTR_FONT;
- else if (!strcasecmp(&entry->d_name[len-4], ".ajz"))
-#else
- else if (!strcasecmp(&entry->d_name[len-4], ".mod"))
-#endif
- dptr->attr |= TREE_ATTR_MOD;
- else if (!strcasecmp(&entry->d_name[len-5], ".rock"))
- dptr->attr |= TREE_ATTR_ROCK;
- }
-
- /* memorize/compare details about the boot file */
- if ((currdir[1] == 0) && !strcmp(entry->d_name, BOOTFILE)) {
- if (boot_size) {
- if ((entry->size != boot_size) ||
- (entry->startcluster != boot_cluster))
- boot_changed = true;
- }
- boot_size = entry->size;
- boot_cluster = entry->startcluster;
- }
-
- /* filter out all non-playlist files */
- if ( global_settings.dirfilter == SHOW_PLAYLIST &&
- (!(dptr->attr &
- (ATTR_DIRECTORY|TREE_ATTR_M3U))) ) {
- i--;
- continue;
- }
-
- /* filter out non-music files */
- if ( global_settings.dirfilter == SHOW_MUSIC &&
- (!(dptr->attr &
- (ATTR_DIRECTORY|TREE_ATTR_MPA|TREE_ATTR_M3U))) ) {
- i--;
- continue;
- }
-
- /* filter out non-supported files */
- if ( global_settings.dirfilter == SHOW_SUPPORTED &&
- (!(dptr->attr & TREE_ATTR_MASK)) ) {
- i--;
- continue;
- }
-
- if (len > name_buffer_size - name_buffer_length - 1) {
- /* Tell the world that we ran out of buffer space */
- dir_buffer_full = true;
- break;
- }
- dptr->name = &name_buffer[name_buffer_length];
- strcpy(dptr->name,entry->d_name);
- name_buffer_length += len + 1;
- }
- filesindir = i;
- closedir(dir);
- strncpy(lastdir,path,sizeof(lastdir));
- lastdir[sizeof(lastdir)-1] = 0;
- qsort(dircache,filesindir,sizeof(struct entry),compare);
+ if (!load_and_sort_directory(path, global_settings.dirfilter,
+ &filesindir, &dir_buffer_full))
+ return -1;
if ( dir_buffer_full || filesindir == max_files_in_dir ) {
#ifdef HAVE_LCD_CHARCELLS
@@ -531,7 +540,7 @@ static int showdir(char *path, int start)
return filesindir;
}
-bool ask_resume(void)
+static bool ask_resume(void)
{
#ifdef HAVE_LCD_CHARCELLS
lcd_double_height(false);
@@ -570,92 +579,62 @@ bool ask_resume(void)
return false;
}
-void start_resume(void)
+/* load tracks from specified directory to resume play */
+void resume_directory(char *dir)
+{
+ bool buffer_full;
+
+ if (!load_and_sort_directory(dir, global_settings.dirfilter, &filesindir,
+ &buffer_full))
+ return;
+ lastdir[0] = 0;
+
+ build_playlist(0);
+}
+
+/* Returns the current working directory and also writes cwd to buf if
+ non-NULL. In case of error, returns NULL. */
+char *getcwd(char *buf, int size)
+{
+ if (!buf)
+ return currdir;
+ else if (size > 0)
+ {
+ strncpy(buf, currdir, size);
+ return buf;
+ }
+ else
+ return NULL;
+}
+
+/* Force a reload of the directory next time directory browser is called */
+void reload_directory(void)
+{
+ reload_dir = true;
+}
+
+static void start_resume(void)
{
if ( global_settings.resume &&
global_settings.resume_index != -1 ) {
- int len = strlen(global_settings.resume_file);
-
- DEBUGF("Resume file %s\n",global_settings.resume_file);
DEBUGF("Resume index %X offset %X\n",
global_settings.resume_index,
global_settings.resume_offset);
- DEBUGF("Resume shuffle %s seed %X\n",
- global_settings.playlist_shuffle?"on":"off",
- global_settings.resume_seed);
-
- /* playlist? */
- if (!strcasecmp(&global_settings.resume_file[len-4], ".m3u")) {
- char* slash;
-
- /* check that the file exists */
- int fd = open(global_settings.resume_file, O_RDONLY);
- if(fd<0)
- return;
- close(fd);
-
- if (!ask_resume())
- return;
-
- slash = strrchr(global_settings.resume_file,'/');
- if (slash) {
- *slash=0;
- play_list(global_settings.resume_file,
- slash+1,
- global_settings.resume_index,
- true, /* the index is AFTER shuffle */
- global_settings.resume_offset,
- global_settings.resume_seed,
- global_settings.resume_first_index,
- global_settings.queue_resume,
- global_settings.queue_resume_index);
- *slash='/';
- }
- else {
- /* check that the dir exists */
- DIR* dir = opendir(global_settings.resume_file);
- if(!dir)
- return;
- closedir(dir);
-
- if (!ask_resume())
- return;
-
- play_list("/",
- global_settings.resume_file,
- global_settings.resume_index,
- true,
- global_settings.resume_offset,
- global_settings.resume_seed,
- global_settings.resume_first_index,
- global_settings.queue_resume,
- global_settings.queue_resume_index);
- }
- }
- else {
- if (!ask_resume())
- return;
-
- if (showdir(global_settings.resume_file, 0) < 0 )
- return;
-
- lastdir[0] = '\0';
-
- build_playlist(global_settings.resume_index);
- play_list(global_settings.resume_file,
- NULL,
- global_settings.resume_index,
- true,
- global_settings.resume_offset,
- global_settings.resume_seed,
- global_settings.resume_first_index,
- global_settings.queue_resume,
- global_settings.queue_resume_index);
- }
- status_set_playmode(STATUS_PLAY);
- status_draw(true);
- wps_show();
+ if (!ask_resume())
+ return;
+
+ if (playlist_resume() != -1)
+ {
+ playlist_start(global_settings.resume_index,
+ global_settings.resume_offset);
+
+ status_set_playmode(STATUS_PLAY);
+ status_draw(true);
+ wps_show();
+ }
+ else
+ return;
}
}
@@ -751,19 +730,33 @@ static bool handle_on(int* ds, int* dc, int numentries, int tree_max_on_screen)
case BUTTON_PLAY:
case BUTTON_RC_PLAY:
- case BUTTON_ON | BUTTON_PLAY:
+ case BUTTON_ON | BUTTON_PLAY: {
+ int onplay_result;
+
if (currdir[1])
snprintf(buf, sizeof buf, "%s/%s",
currdir, dircache[dircursor+dirstart].name);
else
snprintf(buf, sizeof buf, "/%s",
dircache[dircursor+dirstart].name);
- if (onplay(buf, dircache[dircursor+dirstart].attr))
- reload_dir = 1;
+ onplay_result = onplay(buf,
+ dircache[dircursor+dirstart].attr);
+ switch (onplay_result)
+ {
+ case ONPLAY_OK:
+ used = true;
+ break;
+ case ONPLAY_RELOAD_DIR:
+ reload_dir = 1;
+ used = true;
+ break;
+ case ONPLAY_START_PLAY:
+ used = false; /* this will enable the wps */
+ break;
+ }
exit = true;
- used = true;
break;
-
+ }
case BUTTON_ON | BUTTON_REL:
case BUTTON_ON | TREE_PREV | BUTTON_REL:
case BUTTON_ON | TREE_NEXT | BUTTON_REL:
@@ -793,7 +786,7 @@ static bool handle_on(int* ds, int* dc, int numentries, int tree_max_on_screen)
return used;
}
-bool dirbrowse(char *root)
+static bool dirbrowse(char *root)
{
int numentries=0;
char buf[MAX_PATH];
@@ -934,41 +927,36 @@ bool dirbrowse(char *root)
lcd_stop_scroll();
switch ( file->attr & TREE_ATTR_MASK ) {
case TREE_ATTR_M3U:
- if ( global_settings.resume ) {
- if (currdir[1])
- snprintf(global_settings.resume_file,
- MAX_PATH, "%s/%s",
- currdir, file->name);
- else
- snprintf(global_settings.resume_file,
- MAX_PATH, "/%s", file->name);
+ if (playlist_create(currdir, file->name) != -1)
+ {
+ if (global_settings.playlist_shuffle)
+ playlist_shuffle(seed, -1);
+ start_index = 0;
+ playlist_start(start_index,0);
+ play = true;
}
- play_list(currdir, file->name, 0, false, 0,
- seed, 0, 0, -1);
- start_index = 0;
- play = true;
break;
-
+
case TREE_ATTR_MPA:
- if ( global_settings.resume )
- strncpy(global_settings.resume_file,
- currdir, MAX_PATH);
-
- start_index =
- build_playlist(dircursor+dirstart);
-
- /* when shuffling dir.: play all files even if the
- file selected by user is not the first one */
- if (global_settings.playlist_shuffle
- && !global_settings.play_selected)
- start_index = 0;
-
- /* it is important that we get back the index
- in the (shuffled) list and store that */
- start_index = play_list(currdir, NULL,
- start_index, false,
- 0, seed, 0, 0, -1);
- play = true;
+ if (playlist_create(currdir, NULL) != -1)
+ {
+ start_index =
+ build_playlist(dircursor+dirstart);
+ if (global_settings.playlist_shuffle)
+ {
+ start_index =
+ playlist_shuffle(seed,start_index);
+
+ /* when shuffling dir.: play all files
+ even if the file selected by user is
+ not the first one */
+ if (!global_settings.play_selected)
+ start_index = 0;
+ }
+
+ playlist_start(start_index, 0);
+ play = true;
+ }
break;
/* wps config file */
@@ -1055,9 +1043,6 @@ bool dirbrowse(char *root)
shuffled list in case shuffle is enabled */
global_settings.resume_index = start_index;
global_settings.resume_offset = 0;
- global_settings.resume_first_index =
- playlist_first_index();
- global_settings.resume_seed = seed;
settings_save();
}
diff --git a/apps/tree.h b/apps/tree.h
index aa8f2127f3..66e83bc8f0 100644
--- a/apps/tree.h
+++ b/apps/tree.h
@@ -21,6 +21,11 @@
#include <stdbool.h>
+struct entry {
+ short attr; /* FAT attributes + file type flags */
+ char *name;
+};
+
/* using attribute not used by FAT */
#define TREE_ATTR_MPA 0x40 /* mpeg audio file */
#define TREE_ATTR_M3U 0x80 /* playlist */
@@ -36,7 +41,11 @@
void tree_init(void);
void browse_root(void);
void set_current_file(char *path);
-bool dirbrowse(char *root);
bool create_playlist(void);
+void resume_directory(char *dir);
+char *getcwd(char *buf, int size);
+void reload_directory(void);
+struct entry* load_and_sort_directory(char *dirname, int dirfilter,
+ int *num_files, bool *buffer_full);
#endif
diff --git a/apps/wps-display.c b/apps/wps-display.c
index 71ba4c5433..d3c2613a1e 100644
--- a/apps/wps-display.c
+++ b/apps/wps-display.c
@@ -418,12 +418,7 @@ static char* get_tag(struct mp3entry* id3,
#endif
case 'p': /* Playlist Position */
*flags |= WPS_REFRESH_STATIC;
- {
- int index = id3->index - playlist_first_index();
- if (index < 0)
- index += playlist_amount();
- snprintf(buf, buf_size, "%d", index + 1);
- }
+ snprintf(buf, buf_size, "%d", playlist_get_display_index());
return buf;
case 'n': /* Playlist Name (without path) */
diff --git a/apps/wps.c b/apps/wps.c
index c5a27d9e9d..ca1d80fe28 100644
--- a/apps/wps.c
+++ b/apps/wps.c
@@ -223,13 +223,8 @@ bool browse_id3(void)
case 7:
lcd_puts(0, 0, str(LANG_ID3_PLAYLIST));
- {
- int index = id3->index - playlist_first_index();
- if (index < 0)
- index += playlist_amount();
- snprintf(scroll_text,sizeof(scroll_text), "%d/%d",
- index + 1, playlist_amount());
- }
+ snprintf(scroll_text,sizeof(scroll_text), "%d/%d",
+ playlist_get_display_index(), playlist_amount());
lcd_puts_scroll(0, 1, scroll_text);
break;
@@ -455,9 +450,7 @@ static bool update(void)
DEBUGF("R%X,%X (%X)\n", global_settings.resume_offset,
id3->offset,id3);
- if (!playlist_get_resume_info(&global_settings.resume_index,
- &global_settings.queue_resume,
- &global_settings.queue_resume_index))
+ if (!playlist_get_resume_info(&global_settings.resume_index))
{
global_settings.resume_offset = id3->offset;
settings_save();