From 107ebc5ba950acee1108a5cb52f25be371b2ec8f Mon Sep 17 00:00:00 2001 From: Hardeep Sidhu Date: Mon, 26 Jan 2004 17:05:21 +0000 Subject: Modified playlist handling to allow for multiple playlists to be edited at the same time. Added support in playlist viewer for viewing/editing playlists on disk (accessed via ON+PLAY->Playlist->View on a playlist). Added menu in playlist viewer for changing a few settings and saving playlist. Added File Options menu in playlist viewer ON+PLAY menu. git-svn-id: svn://svn.rockbox.org/rockbox/trunk@4276 a1c6a512-1295-4272-9138-f99709370657 --- apps/bookmark.c | 11 +- apps/lang/english.lang | 35 ++ apps/menu.c | 2 +- apps/onplay.c | 51 +- apps/playlist.c | 1349 +++++++++++++++++++++++++++++------------------- apps/playlist.h | 48 +- apps/playlist_menu.c | 2 +- apps/playlist_viewer.c | 400 +++++++++++--- apps/playlist_viewer.h | 1 + apps/screens.c | 4 +- apps/settings.c | 40 +- apps/settings.h | 5 + apps/settings_menu.c | 4 +- apps/wps-display.c | 2 +- 14 files changed, 1308 insertions(+), 646 deletions(-) diff --git a/apps/bookmark.c b/apps/bookmark.c index 893c128a15..9e093c0d1f 100644 --- a/apps/bookmark.c +++ b/apps/bookmark.c @@ -140,7 +140,7 @@ static bool bookmark_load_menu(void) return false; else { - char* name = playlist_get_name(global_temp_buffer, + char* name = playlist_get_name(NULL, global_temp_buffer, sizeof(global_temp_buffer)); if (generate_bookmark_file_name(name, global_bookmark_file_name, @@ -306,7 +306,7 @@ static bool write_bookmark(bool create_bookmark_file) /* writing the bookmark */ if (create_bookmark_file) { - char* name = playlist_get_name(global_temp_buffer, + char* name = playlist_get_name(NULL, global_temp_buffer, sizeof(global_temp_buffer)); if (generate_bookmark_file_name(name, global_bookmark_file_name, @@ -428,12 +428,13 @@ static char* create_bookmark() "%d;%d;%d;%d;%d;%d;%d;%s;%s", resume_index, id3->offset, - playlist_get_seed(), + playlist_get_seed(NULL), 0, id3->elapsed, global_settings.repeat_mode, global_settings.playlist_shuffle, - playlist_get_name(global_temp_buffer,sizeof(global_temp_buffer)), + playlist_get_name(NULL, global_temp_buffer, + sizeof(global_temp_buffer)), file+1); /* checking to see if the bookmark is valid */ @@ -1090,7 +1091,7 @@ static bool system_check(void) /* something bad happened while getting the queue information */ return false; } - else if (playlist_modified()) + else if (playlist_modified(NULL)) { /* can't bookmark while in the queue */ return false; diff --git a/apps/lang/english.lang b/apps/lang/english.lang index 9995ac277b..f88e211192 100644 --- a/apps/lang/english.lang +++ b/apps/lang/english.lang @@ -1666,3 +1666,38 @@ id: LANG_CREATE_DIR desc: in main menu eng: "Create directory" new: + +id: LANG_VIEW +desc: in on+play menu +eng: "View" +new: + +id: LANG_SHOW_INDICES +desc: in playlist viewer menu +eng: "Show Indices" +new: + +id: LANG_TRACK_DISPLAY +desc: in playlist viewer on+play menu +eng: "Track Display" +new: + +id: LANG_DISPLAY_TRACK_NAME_ONLY +desc: track display options +eng: "Track name only" +new: + +id: LANG_DISPLAY_FULL_PATH +desc: track display options +eng: "Full path" +new: + +id: LANG_REMOVE +desc: in playlist viewer on+play menu +eng: "Remove" +new: + +id: LANG_FILE_OPTIONS +desc: in playlist viewer on+play menu +eng: "File Options" +new: diff --git a/apps/menu.c b/apps/menu.c index f317403f0d..0bd21b9108 100644 --- a/apps/menu.c +++ b/apps/menu.c @@ -44,7 +44,7 @@ struct menu { int itemcount; }; -#define MAX_MENUS 4 +#define MAX_MENUS 5 #ifdef HAVE_LCD_BITMAP diff --git a/apps/onplay.c b/apps/onplay.c index 3e085cea59..2f0ee3b861 100644 --- a/apps/onplay.c +++ b/apps/onplay.c @@ -40,6 +40,7 @@ #include "buffer.h" #include "settings.h" #include "status.h" +#include "playlist_viewer.h" #include "onplay.h" static char* selected_file = NULL; @@ -60,7 +61,7 @@ static bool add_to_playlist(int position, bool queue) playlist_create(NULL, NULL); if ((selected_file_attr & TREE_ATTR_MASK) == TREE_ATTR_MPA) - playlist_insert_track(selected_file, position, queue); + playlist_insert_track(NULL, selected_file, position, queue); else if (selected_file_attr & ATTR_DIRECTORY) { bool recurse = false; @@ -99,10 +100,11 @@ static bool add_to_playlist(int position, bool queue) } } - playlist_insert_directory(selected_file, position, queue, recurse); + playlist_insert_directory(NULL, selected_file, position, queue, + recurse); } else if ((selected_file_attr & TREE_ATTR_MASK) == TREE_ATTR_M3U) - playlist_insert_playlist(selected_file, position, queue); + playlist_insert_playlist(NULL, selected_file, position, queue); if (new_playlist && (playlist_amount() > 0)) { @@ -119,12 +121,36 @@ static bool add_to_playlist(int position, bool queue) return false; } +static bool view_playlist(void) +{ + bool was_playing = mpeg_status() & MPEG_STATUS_PLAY; + bool result; + + result = playlist_viewer_ex(selected_file); + + if (!was_playing && (mpeg_status() & MPEG_STATUS_PLAY) && + onplay_result == ONPLAY_OK) + /* playlist was started from viewer */ + onplay_result = ONPLAY_START_PLAY; + + return result; +} + /* 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; + struct menu_items menu[7]; + struct playlist_args args[7]; /* increase these 2 if you add entries! */ + int m, i=0, pstart=0, result; + bool ret = false; + + if ((selected_file_attr & TREE_ATTR_MASK) == TREE_ATTR_M3U) + { + menu[i].desc = str(LANG_VIEW); + menu[i].function = view_playlist; + i++; + pstart++; + } if (mpeg_status() & MPEG_STATUS_PLAY) { @@ -169,11 +195,13 @@ static bool playlist_options(void) m = menu_init( menu, i ); result = menu_show(m); - if (result >= 0) - add_to_playlist(args[result].position, args[result].queue); + if (result >= 0 && result < pstart) + ret = menu[result].function(); + else if (result >= pstart) + ret = add_to_playlist(args[result].position, args[result].queue); menu_exit(m); - return false; + return ret; } static bool delete_file(void) @@ -489,8 +517,9 @@ int onplay(char* file, int attr) selected_file = file; selected_file_attr = attr; - if (((attr & TREE_ATTR_MASK) == TREE_ATTR_MPA) || (attr & ATTR_DIRECTORY) || - (((attr & TREE_ATTR_MASK) == TREE_ATTR_M3U) && (mpeg_status() & MPEG_STATUS_PLAY))) + if (((attr & TREE_ATTR_MASK) == TREE_ATTR_MPA) || + (attr & ATTR_DIRECTORY) || + ((attr & TREE_ATTR_MASK) == TREE_ATTR_M3U)) { menu[i].desc = str(LANG_PLAYINDICES_PLAYLIST); menu[i].function = playlist_options; diff --git a/apps/playlist.c b/apps/playlist.c index 13919b2f02..8dede37d38 100644 --- a/apps/playlist.c +++ b/apps/playlist.c @@ -60,7 +60,9 @@ 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. + exactly the same as before shutdown. To avoid unnecessary disk + accesses, the shuffle mode settings are also saved in settings and only + flushed to disk when required. */ #include @@ -90,8 +92,6 @@ #include "lang.h" -static struct playlist_info playlist; - #define PLAYLIST_CONTROL_FILE ROCKBOX_DIR "/.playlist_control" #define PLAYLIST_CONTROL_FILE_VERSION 2 @@ -123,70 +123,83 @@ static struct playlist_info playlist; #define PLAYLIST_DISPLAY_COUNT 10 +static struct playlist_info current_playlist; static char now_playing[MAX_PATH+1]; -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, +static void empty_playlist(struct playlist_info* playlist, bool resume); +static void new_playlist(struct playlist_info* playlist, char *dir, + char *file); +static void create_control(struct playlist_info* playlist); +static int check_control(struct playlist_info* playlist); +static void update_playlist_filename(struct playlist_info* playlist, + char *dir, char *file); +static int add_indices_to_playlist(struct playlist_info* playlist, + char* buffer, int buflen); +static int add_track_to_playlist(struct playlist_info* playlist, + char *filename, int position, bool queue, int seek_pos); -static int add_directory_to_playlist(char *dirname, int *position, bool queue, +static int add_directory_to_playlist(struct playlist_info* playlist, + char *dirname, int *position, bool queue, int *count, bool recurse); -static int remove_track_from_playlist(int position, bool write); -static int randomise_playlist(unsigned int seed, bool start_current, +static int remove_track_from_playlist(struct playlist_info* playlist, + int position, bool write); +static int randomise_playlist(struct playlist_info* playlist, + unsigned int seed, bool start_current, bool write); -static int sort_playlist(bool start_current, bool write); -static int get_next_index(int steps); -static void find_and_set_playlist_index(unsigned int seek); +static int sort_playlist(struct playlist_info* playlist, bool start_current, + bool write); +static int get_next_index(struct playlist_info* playlist, int steps); +static void find_and_set_playlist_index(struct playlist_info* playlist, + unsigned int seek); static int compare(const void* p1, const void* p2); -static int get_filename(int seek, bool control_file, char *buf, - int buf_length); +static int get_filename(struct playlist_info* playlist, 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); -static int flush_pending_control(void); -static int rotate_index(int index); +static int flush_pending_control(struct playlist_info* playlist); +static int rotate_index(struct playlist_info* playlist, int index); /* * remove any files and indices associated with the playlist */ -static void empty_playlist(bool resume) +static void empty_playlist(struct playlist_info* playlist, bool resume) { - playlist.filename[0] = '\0'; + playlist->filename[0] = '\0'; - if(playlist.fd >= 0) + if(playlist->fd >= 0) /* If there is an already open playlist, close it. */ - close(playlist.fd); - playlist.fd = -1; - - if(playlist.control_fd >= 0) - 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.first_index = 0; - playlist.amount = 0; - playlist.last_insert_pos = -1; - playlist.seed = 0; - playlist.shuffle_modified = false; - playlist.deleted = false; - playlist.num_inserted_tracks = 0; - playlist.shuffle_flush = false; - - if (!resume) - { - int fd; + close(playlist->fd); + playlist->fd = -1; + + if(playlist->control_fd >= 0) + close(playlist->control_fd); + playlist->control_fd = -1; + playlist->control_created = false; + + playlist->in_ram = false; + if (playlist->buffer) + playlist->buffer[0] = 0; + + playlist->buffer_end_pos = 0; + + playlist->index = 0; + playlist->first_index = 0; + playlist->amount = 0; + playlist->last_insert_pos = -1; + playlist->seed = 0; + playlist->shuffle_modified = false; + playlist->deleted = false; + playlist->num_inserted_tracks = 0; + playlist->shuffle_flush = false; + + if (!resume && playlist->current) + { /* start with fresh playlist control file when starting new playlist */ - fd = creat(PLAYLIST_CONTROL_FILE, 0000200); - if (fd >= 0) - close(fd); + create_control(playlist); /* Reset resume settings */ global_settings.resume_first_index = 0; @@ -194,10 +207,87 @@ static void empty_playlist(bool resume) } } +/* + * Initialize a new playlist for viewing/editing/playing. dir is the + * directory where the playlist is located and file is the filename. + */ +static void new_playlist(struct playlist_info* playlist, char *dir, + char *file) +{ + empty_playlist(playlist, false); + + if (!file) + { + file = ""; + + if (dir && playlist->current) /* !current cannot be in_ram */ + playlist->in_ram = true; + else + dir = ""; /* empty playlist */ + } + + update_playlist_filename(playlist, dir, file); + + if (playlist->control_fd >= 0) + { + 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, true, str(LANG_PLAYLIST_CONTROL_UPDATE_ERROR)); + } +} + +/* + * create control file for playlist + */ +static void create_control(struct playlist_info* playlist) +{ + playlist->control_fd = creat(playlist->control_filename, 0000200); + if (playlist->control_fd < 0) + splash(HZ*2, true, str(LANG_PLAYLIST_CONTROL_ACCESS_ERROR)); + playlist->control_created = true; +} + +/* + * validate the control file. This may include creating/initializing it if + * necessary; + */ +static int check_control(struct playlist_info* playlist) +{ + if (!playlist->control_created) + { + create_control(playlist); + + if (playlist->control_fd >= 0) + { + char* dir = playlist->filename; + char* file = playlist->filename+playlist->dirlen; + char c = playlist->filename[playlist->dirlen-1]; + + playlist->filename[playlist->dirlen-1] = '\0'; + + 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, true, str(LANG_PLAYLIST_CONTROL_UPDATE_ERROR)); + + playlist->filename[playlist->dirlen-1] = c; + } + } + + if (playlist->control_fd < 0) + return -1; + + return 0; +} + /* * store directory and name of playlist file */ -static void update_playlist_filename(char *dir, char *file) +static void update_playlist_filename(struct playlist_info* playlist, + char *dir, char *file) { char *sep=""; int dirlen = strlen(dir); @@ -210,9 +300,9 @@ static void update_playlist_filename(char *dir, char *file) dirlen++; } - playlist.dirlen = dirlen; + playlist->dirlen = dirlen; - snprintf(playlist.filename, sizeof(playlist.filename), + snprintf(playlist->filename, sizeof(playlist->filename), "%s%s%s", dir, sep, file); } @@ -220,19 +310,18 @@ static void update_playlist_filename(char *dir, char *file) /* * calculate track offsets within a playlist file */ -static int add_indices_to_playlist(void) +static int add_indices_to_playlist(struct playlist_info* playlist, + char* buffer, int buflen) { unsigned int nread; unsigned int i = 0; unsigned int count = 0; - int buflen; bool store_index; - char *buffer; unsigned char *p; - if(-1 == playlist.fd) - playlist.fd = open(playlist.filename, O_RDONLY); - if(playlist.fd < 0) + if(-1 == playlist->fd) + playlist->fd = open(playlist->filename, O_RDONLY); + if(playlist->fd < 0) return -1; /* failure */ #ifdef HAVE_LCD_BITMAP @@ -244,17 +333,20 @@ static int add_indices_to_playlist(void) splash(0, true, str(LANG_PLAYLIST_LOAD)); - /* use mp3 buffer for maximum load speed */ - buflen = (mp3end - mp3buf); - buffer = mp3buf; + if (!buffer) + { + /* use mp3 buffer for maximum load speed */ + mpeg_stop(); + buffer = mp3buf; + buflen = (mp3end - mp3buf); + } + store_index = true; - mpeg_stop(); - while(1) { - nread = read(playlist.fd, buffer, buflen); + nread = read(playlist->fd, buffer, buflen); /* Terminate on EOF */ if(nread <= 0) break; @@ -275,9 +367,9 @@ static int add_indices_to_playlist(void) if(*p != '#') { /* Store a new entry */ - playlist.indices[ playlist.amount ] = i+count; - playlist.amount++; - if ( playlist.amount >= playlist.max_playlist_size ) { + playlist->indices[ playlist->amount ] = i+count; + playlist->amount++; + if ( playlist->amount >= playlist->max_playlist_size ) { display_buffer_full(); return -1; } @@ -302,14 +394,15 @@ static int add_indices_to_playlist(void) * 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, +static int add_track_to_playlist(struct playlist_info* playlist, + char *filename, int position, bool queue, int seek_pos) { int insert_position = position; unsigned int flags = PLAYLIST_INSERT_TYPE_INSERT; int i; - if (playlist.amount >= playlist.max_playlist_size) + if (playlist->amount >= playlist->max_playlist_size) { display_buffer_full(); return -1; @@ -318,38 +411,38 @@ static int add_track_to_playlist(char *filename, int position, bool queue, switch (position) { case PLAYLIST_PREPEND: - insert_position = playlist.first_index; + 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]& + 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; + 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; + playlist->last_insert_pos = position; break; case PLAYLIST_INSERT_FIRST: - if (playlist.amount > 0) - position = insert_position = playlist.index + 1; + 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; + 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; + if (playlist->first_index > 0) + insert_position = playlist->first_index; else - insert_position = playlist.amount; + insert_position = playlist->amount; flags = PLAYLIST_INSERT_TYPE_APPEND; break; @@ -359,52 +452,52 @@ static int add_track_to_playlist(char *filename, int position, bool queue, flags |= PLAYLIST_QUEUED; /* shift indices so that track can be added */ - for (i=playlist.amount; i>insert_position; i--) - playlist.indices[i] = playlist.indices[i-1]; + 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->index) + playlist->index++; - if (playlist.amount > 0 && insert_position <= playlist.first_index && + if (playlist->amount > 0 && insert_position <= playlist->first_index && position != PLAYLIST_PREPEND) { - playlist.first_index++; + playlist->first_index++; - if (seek_pos < 0) + if (seek_pos < 0 && playlist->current) { - global_settings.resume_first_index = playlist.first_index; + global_settings.resume_first_index = playlist->first_index; settings_save(); } } - if (insert_position < playlist.last_insert_pos || - (insert_position == playlist.last_insert_pos && position < 0)) - playlist.last_insert_pos++; + 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) + if (seek_pos < 0 && playlist->control_fd >= 0) { int result = -1; - if (flush_pending_control() < 0) + if (flush_pending_control(playlist) < 0) return -1; - mutex_lock(&playlist.control_mutex); + mutex_lock(&playlist->control_mutex); - if (lseek(playlist.control_fd, 0, SEEK_END) >= 0) + if (lseek(playlist->control_fd, 0, SEEK_END) >= 0) { - if (fprintf(playlist.control_fd, "%c:%d:%d:", (queue?'Q':'A'), - position, playlist.last_insert_pos) > 0) + if (fprintf(playlist->control_fd, "%c:%d:%d:", (queue?'Q':'A'), + position, playlist->last_insert_pos) > 0) { /* save the position in file where track name is written */ - seek_pos = lseek(playlist.control_fd, 0, SEEK_CUR); + seek_pos = lseek(playlist->control_fd, 0, SEEK_CUR); - if (fprintf(playlist.control_fd, "%s\n", filename) > 0) + if (fprintf(playlist->control_fd, "%s\n", filename) > 0) result = 0; } } - mutex_unlock(&playlist.control_mutex); + mutex_unlock(&playlist->control_mutex); if (result < 0) { @@ -413,10 +506,10 @@ static int add_track_to_playlist(char *filename, int position, bool queue, } } - playlist.indices[insert_position] = flags | seek_pos; + playlist->indices[insert_position] = flags | seek_pos; - playlist.amount++; - playlist.num_inserted_tracks++; + playlist->amount++; + playlist->num_inserted_tracks++; return insert_position; } @@ -424,7 +517,8 @@ static int add_track_to_playlist(char *filename, int position, bool queue, /* * Insert directory into playlist. May be called recursively. */ -static int add_directory_to_playlist(char *dirname, int *position, bool queue, +static int add_directory_to_playlist(struct playlist_info* playlist, + char *dirname, int *position, bool queue, int *count, bool recurse) { char buf[MAX_PATH+1]; @@ -474,8 +568,8 @@ static int add_directory_to_playlist(char *dirname, int *position, bool queue, { /* recursively add directories */ snprintf(buf, sizeof(buf), "%s/%s", dirname, files[i].name); - result = add_directory_to_playlist(buf, position, queue, - count, recurse); + result = add_directory_to_playlist(playlist, buf, position, + queue, count, recurse); if (result < 0) break; @@ -497,7 +591,8 @@ static int add_directory_to_playlist(char *dirname, int *position, bool queue, snprintf(buf, sizeof(buf), "%s/%s", dirname, files[i].name); - insert_pos = add_track_to_playlist(buf, *position, queue, -1); + insert_pos = add_track_to_playlist(playlist, buf, *position, + queue, -1); if (insert_pos < 0) { result = -1; @@ -530,64 +625,65 @@ static int add_directory_to_playlist(char *dirname, int *position, bool queue, /* * remove track at specified position */ -static int remove_track_from_playlist(int position, bool write) +static int remove_track_from_playlist(struct playlist_info* playlist, + int position, bool write) { int i; bool inserted; - if (playlist.amount <= 0) + if (playlist->amount <= 0) return -1; - inserted = playlist.indices[position] & PLAYLIST_INSERT_TYPE_MASK; + inserted = playlist->indices[position] & PLAYLIST_INSERT_TYPE_MASK; /* shift indices now that track has been removed */ - for (i=position; iamount; i++) + playlist->indices[i] = playlist->indices[i+1]; - playlist.amount--; + playlist->amount--; if (inserted) - playlist.num_inserted_tracks--; + playlist->num_inserted_tracks--; else - playlist.deleted = true; + playlist->deleted = true; /* update stored indices if needed */ - if (position < playlist.index) - playlist.index--; + if (position < playlist->index) + playlist->index--; - if (position < playlist.first_index) + if (position < playlist->first_index) { - playlist.first_index--; + playlist->first_index--; if (write) { - global_settings.resume_first_index = playlist.first_index; + global_settings.resume_first_index = playlist->first_index; settings_save(); } } - if (position <= playlist.last_insert_pos) - playlist.last_insert_pos--; + if (position <= playlist->last_insert_pos) + playlist->last_insert_pos--; - if (write && playlist.control_fd >= 0) + if (write && playlist->control_fd >= 0) { int result = -1; - if (flush_pending_control() < 0) + if (flush_pending_control(playlist) < 0) return -1; - mutex_lock(&playlist.control_mutex); + mutex_lock(&playlist->control_mutex); - if (lseek(playlist.control_fd, 0, SEEK_END) >= 0) + if (lseek(playlist->control_fd, 0, SEEK_END) >= 0) { - if (fprintf(playlist.control_fd, "D:%d\n", position) > 0) + if (fprintf(playlist->control_fd, "D:%d\n", position) > 0) { - fsync(playlist.control_fd); + fsync(playlist->control_fd); result = 0; } } - mutex_unlock(&playlist.control_mutex); + mutex_unlock(&playlist->control_mutex); if (result < 0) { @@ -603,12 +699,14 @@ static int remove_track_from_playlist(int position, bool write) * 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) +static int randomise_playlist(struct playlist_info* playlist, + unsigned int seed, bool start_current, + bool write) { int count; int candidate; int store; - unsigned int current = playlist.indices[playlist.index]; + unsigned int current = playlist->indices[playlist->index]; /* seed 0 is used to identify sorted playlist for resume purposes */ if (seed == 0) @@ -618,32 +716,32 @@ static int randomise_playlist(unsigned int seed, bool start_current, bool write) srand(seed); /* randomise entire indices list */ - for(count = playlist.amount - 1; count >= 0; count--) + 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); /* now swap the values at the 'count' and 'candidate' positions */ - store = playlist.indices[candidate]; - playlist.indices[candidate] = playlist.indices[count]; - playlist.indices[count] = store; + store = playlist->indices[candidate]; + playlist->indices[candidate] = playlist->indices[count]; + playlist->indices[count] = store; } if (start_current) - find_and_set_playlist_index(current); + find_and_set_playlist_index(playlist, current); /* indices have been moved so last insert position is no longer valid */ - playlist.last_insert_pos = -1; + playlist->last_insert_pos = -1; - playlist.seed = seed; - if (playlist.num_inserted_tracks > 0 || playlist.deleted) - playlist.shuffle_modified = true; + playlist->seed = seed; + if (playlist->num_inserted_tracks > 0 || playlist->deleted) + playlist->shuffle_modified = true; if (write) { /* Don't write to disk immediately. Instead, save in settings and only flush if playlist is modified (insertion/deletion) */ - playlist.shuffle_flush = true; + playlist->shuffle_flush = true; global_settings.resume_seed = seed; settings_save(); } @@ -655,27 +753,28 @@ static int randomise_playlist(unsigned int seed, bool start_current, bool write) * 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) +static int sort_playlist(struct playlist_info* playlist, bool start_current, + bool write) { - unsigned int current = playlist.indices[playlist.index]; + unsigned int current = playlist->indices[playlist->index]; - if (playlist.amount > 0) - qsort(playlist.indices, playlist.amount, sizeof(playlist.indices[0]), + if (playlist->amount > 0) + qsort(playlist->indices, playlist->amount, sizeof(playlist->indices[0]), compare); if (start_current) - find_and_set_playlist_index(current); + find_and_set_playlist_index(playlist, current); /* indices have been moved so last insert position is no longer valid */ - playlist.last_insert_pos = -1; + playlist->last_insert_pos = -1; - if (!playlist.num_inserted_tracks && !playlist.deleted) - playlist.shuffle_modified = false; - if (write && playlist.control_fd >= 0) + if (!playlist->num_inserted_tracks && !playlist->deleted) + playlist->shuffle_modified = false; + if (write && playlist->control_fd >= 0) { /* Don't write to disk immediately. Instead, save in settings and only flush if playlist is modified (insertion/deletion) */ - playlist.shuffle_flush = true; + playlist->shuffle_flush = true; global_settings.resume_seed = 0; settings_save(); } @@ -687,26 +786,26 @@ static int sort_playlist(bool start_current, bool write) * returns the index of the track that is "steps" away from current playing * track. */ -static int get_next_index(int steps) +static int get_next_index(struct playlist_info* playlist, int steps) { - int current_index = playlist.index; + int current_index = playlist->index; int next_index = -1; - if (playlist.amount <= 0) + if (playlist->amount <= 0) return -1; switch (global_settings.repeat_mode) { case REPEAT_OFF: { - current_index = rotate_index(current_index); + current_index = rotate_index(playlist, current_index); next_index = current_index+steps; - if ((next_index < 0) || (next_index >= playlist.amount)) + if ((next_index < 0) || (next_index >= playlist->amount)) next_index = -1; else - next_index = (next_index+playlist.first_index) % - playlist.amount; + next_index = (next_index+playlist->first_index) % + playlist->amount; break; } @@ -718,11 +817,11 @@ static int get_next_index(int steps) case REPEAT_ALL: default: { - next_index = (current_index+steps) % playlist.amount; + next_index = (current_index+steps) % playlist->amount; while (next_index < 0) - next_index += playlist.amount; + next_index += playlist->amount; - if (steps >= playlist.amount) + if (steps >= playlist->amount) { int i, index; @@ -730,10 +829,10 @@ static int get_next_index(int steps) next_index = -1; /* second time around so skip the queued files */ - for (i=0; iamount; i++) { - if (playlist.indices[index] & PLAYLIST_QUEUE_MASK) - index = (index+1) % playlist.amount; + if (playlist->indices[index] & PLAYLIST_QUEUE_MASK) + index = (index+1) % playlist->amount; else { next_index = index; @@ -752,18 +851,24 @@ static int get_next_index(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) +static void find_and_set_playlist_index(struct playlist_info* playlist, + unsigned int seek) { int i; /* Set the index to the current song */ - for (i=0; iamount; i++) { - if (playlist.indices[i] == seek) + if (playlist->indices[i] == seek) { - playlist.index = global_settings.resume_first_index = - playlist.first_index = i; - settings_save(); + playlist->index = playlist->first_index = i; + + if (playlist->current) + { + global_settings.resume_first_index = i; + settings_save(); + } + break; } } @@ -799,8 +904,8 @@ static int compare(const void* p1, const void* p2) /* * gets pathname for track at seek index */ -static int get_filename(int seek, bool control_file, char *buf, - int buf_length) +static int get_filename(struct playlist_info* playlist, int seek, + bool control_file, char *buf, int buf_length) { int fd; int max = -1; @@ -810,34 +915,34 @@ static int get_filename(int seek, bool control_file, char *buf, if (buf_length > MAX_PATH+1) buf_length = MAX_PATH+1; - if (playlist.in_ram && !control_file) + if (playlist->in_ram && !control_file) { - strncpy(tmp_buf, &playlist.buffer[seek], sizeof(tmp_buf)); + strncpy(tmp_buf, &playlist->buffer[seek], sizeof(tmp_buf)); tmp_buf[MAX_PATH] = '\0'; max = strlen(tmp_buf) + 1; } else { if (control_file) - fd = playlist.control_fd; + fd = playlist->control_fd; else { - if(-1 == playlist.fd) - playlist.fd = open(playlist.filename, O_RDONLY); + if(-1 == playlist->fd) + playlist->fd = open(playlist->filename, O_RDONLY); - fd = playlist.fd; + fd = playlist->fd; } if(-1 != fd) { if (control_file) - mutex_lock(&playlist.control_mutex); + 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); + mutex_unlock(&playlist->control_mutex); } if (max < 0) @@ -852,8 +957,8 @@ static int get_filename(int seek, bool control_file, char *buf, } } - strncpy(dir_buf, playlist.filename, playlist.dirlen-1); - dir_buf[playlist.dirlen-1] = 0; + 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)); } @@ -964,29 +1069,29 @@ static void display_buffer_full(void) * Flush any pending control commands to disk. Called when playlist is being * modified. Returns 0 on success and -1 on failure. */ -static int flush_pending_control(void) +static int flush_pending_control(struct playlist_info* playlist) { int result = 0; - if (playlist.shuffle_flush && global_settings.resume_seed >= 0) + if (playlist->shuffle_flush && global_settings.resume_seed >= 0) { /* pending shuffle */ - mutex_lock(&playlist.control_mutex); + mutex_lock(&playlist->control_mutex); - if (lseek(playlist.control_fd, 0, SEEK_END) >= 0) + if (lseek(playlist->control_fd, 0, SEEK_END) >= 0) { if (global_settings.resume_seed == 0) - result = fprintf(playlist.control_fd, "U:%d\n", - playlist.first_index); + result = fprintf(playlist->control_fd, "U:%d\n", + playlist->first_index); else - result = fprintf(playlist.control_fd, "S:%d:%d\n", - global_settings.resume_seed, playlist.first_index); + result = fprintf(playlist->control_fd, "S:%d:%d\n", + global_settings.resume_seed, playlist->first_index); if (result > 0) { - fsync(playlist.control_fd); + fsync(playlist->control_fd); - playlist.shuffle_flush = false; + playlist->shuffle_flush = false; global_settings.resume_seed = -1; settings_save(); @@ -998,7 +1103,7 @@ static int flush_pending_control(void) else result = -1; - mutex_unlock(&playlist.control_mutex); + mutex_unlock(&playlist->control_mutex); if (result < 0) { @@ -1013,11 +1118,11 @@ static int flush_pending_control(void) /* * Rotate indices such that first_index is index 0 */ -static int rotate_index(int index) +static int rotate_index(struct playlist_info* playlist, int index) { - index -= playlist.first_index; + index -= playlist->first_index; if (index < 0) - index += playlist.amount; + index += playlist->amount; return index; } @@ -1027,15 +1132,20 @@ static int rotate_index(int index) */ 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 = + struct playlist_info* playlist = ¤t_playlist; + + playlist->current = true; + snprintf(playlist->control_filename, sizeof(playlist->control_filename), + "%s", PLAYLIST_CONTROL_FILE); + 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); + playlist->buffer = buffer_alloc(playlist->buffer_size); + mutex_init(&playlist->control_mutex); + empty_playlist(playlist, true); } /* @@ -1043,36 +1153,13 @@ void playlist_init(void) */ int playlist_create(char *dir, char *file) { - empty_playlist(false); - - playlist.control_fd = open(PLAYLIST_CONTROL_FILE, O_RDWR); - if (playlist.control_fd < 0) - splash(HZ*2, true, str(LANG_PLAYLIST_CONTROL_ACCESS_ERROR)); - - if (!file) - { - file = ""; + struct playlist_info* playlist = ¤t_playlist; - if (dir) - playlist.in_ram = true; - else - dir = ""; /* empty playlist */ - } - - update_playlist_filename(dir, file); + new_playlist(playlist, dir, file); - if (playlist.control_fd >= 0) - { - 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, true, str(LANG_PLAYLIST_CONTROL_UPDATE_ERROR)); - } - - /* load the playlist file */ - if (file[0] != '\0') - add_indices_to_playlist(); + if (file) + /* load the playlist file */ + add_indices_to_playlist(playlist, NULL, 0); return 0; } @@ -1085,6 +1172,7 @@ int playlist_create(char *dir, char *file) */ int playlist_resume(void) { + struct playlist_info* playlist = ¤t_playlist; char *buffer; int buflen; int nread; @@ -1107,17 +1195,18 @@ int playlist_resume(void) buflen = (mp3end - mp3buf); buffer = mp3buf; - empty_playlist(true); + empty_playlist(playlist, true); - playlist.control_fd = open(PLAYLIST_CONTROL_FILE, O_RDWR); - if (playlist.control_fd < 0) + playlist->control_fd = open(playlist->control_filename, O_RDWR); + if (playlist->control_fd < 0) { splash(HZ*2, true, str(LANG_PLAYLIST_CONTROL_ACCESS_ERROR)); return -1; } + playlist->control_created = true; /* read a small amount first to get the header */ - nread = read(playlist.control_fd, buffer, + nread = read(playlist->control_fd, buffer, PLAYLIST_COMMAND_SIZEin_ram = true; resume_directory(str2); } @@ -1216,11 +1305,11 @@ int playlist_resume(void) /* seek position is based on str3's position in buffer */ - if (add_track_to_playlist(str3, position, queue, - total_read+(str3-buffer)) < 0) + if (add_track_to_playlist(playlist, str3, position, + queue, total_read+(str3-buffer)) < 0) return -1; - playlist.last_insert_pos = last_position; + playlist->last_insert_pos = last_position; break; } @@ -1238,8 +1327,8 @@ int playlist_resume(void) position = atoi(str1); - if (remove_track_from_playlist(position, - false) < 0) + if (remove_track_from_playlist(playlist, position, + false) < 0) return -1; break; @@ -1259,13 +1348,14 @@ int playlist_resume(void) if (!sorted) { /* Always sort list before shuffling */ - sort_playlist(false, false); + sort_playlist(playlist, false, false); } seed = atoi(str1); - playlist.first_index = atoi(str2); + playlist->first_index = atoi(str2); - if (randomise_playlist(seed, false, false) < 0) + if (randomise_playlist(playlist, seed, false, + false) < 0) return -1; sorted = false; @@ -1281,9 +1371,9 @@ int playlist_resume(void) break; } - playlist.first_index = atoi(str1); + playlist->first_index = atoi(str1); - if (sort_playlist(false, false) < 0) + if (sort_playlist(playlist, false, false) < 0) return -1; sorted = true; @@ -1291,7 +1381,7 @@ int playlist_resume(void) } case resume_reset: { - playlist.last_insert_pos = -1; + playlist->last_insert_pos = -1; break; } case resume_comment: @@ -1407,17 +1497,17 @@ int playlist_resume(void) NOTE: because of this, control file must always end with a newline */ count = last_newline; - lseek(playlist.control_fd, total_read+count, SEEK_SET); + lseek(playlist->control_fd, total_read+count, SEEK_SET); } total_read += count; if (first) /* still looking for header */ - nread = read(playlist.control_fd, buffer, + nread = read(playlist->control_fd, buffer, PLAYLIST_COMMAND_SIZEcontrol_fd, buffer, buflen); /* Terminate on EOF */ if(nread <= 0) @@ -1426,17 +1516,17 @@ int playlist_resume(void) { /* Apply shuffle command saved in settings */ if (global_settings.resume_seed == 0) - sort_playlist(false, true); + sort_playlist(playlist, false, true); else { if (!sorted) - sort_playlist(false, false); + sort_playlist(playlist, false, false); - randomise_playlist(global_settings.resume_seed, false, - true); + randomise_playlist(playlist, global_settings.resume_seed, + false, true); } - playlist.first_index = global_settings.resume_first_index; + playlist->first_index = global_settings.resume_first_index; } break; @@ -1451,43 +1541,364 @@ int playlist_resume(void) */ int playlist_add(char *filename) { + struct playlist_info* playlist = ¤t_playlist; int len = strlen(filename); - if((len+1 > playlist.buffer_size - playlist.buffer_end_pos) || - (playlist.amount >= playlist.max_playlist_size)) + if((len+1 > playlist->buffer_size - playlist->buffer_end_pos) || + (playlist->amount >= playlist->max_playlist_size)) { display_buffer_full(); return -1; } - playlist.indices[playlist.amount++] = playlist.buffer_end_pos; + playlist->indices[playlist->amount++] = playlist->buffer_end_pos; + + strcpy(&playlist->buffer[playlist->buffer_end_pos], filename); + playlist->buffer_end_pos += len; + playlist->buffer[playlist->buffer_end_pos++] = '\0'; + + return 0; +} + +/* shuffle newly created playlist using random seed. */ +int playlist_shuffle(int random_seed, int start_index) +{ + struct playlist_info* playlist = ¤t_playlist; + + 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 = global_settings.resume_first_index = + playlist->first_index = start_index; + start_current = true; + } + + splash(0, true, str(LANG_PLAYLIST_SHUFFLE)); + + randomise_playlist(playlist, random_seed, start_current, true); + + /* Flush shuffle command to disk */ + flush_pending_control(playlist); + + return playlist->index; +} + +/* start playing current playlist at specified index/offset */ +int playlist_start(int start_index, int offset) +{ + struct playlist_info* playlist = ¤t_playlist; + + playlist->index = start_index; + mpeg_play(offset); + + return 0; +} + +/* Returns false if 'steps' is out of bounds, else true */ +bool playlist_check(int steps) +{ + struct playlist_info* playlist = ¤t_playlist; + int index = get_next_index(playlist, 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) +{ + struct playlist_info* playlist = ¤t_playlist; + int seek; + int fd; + char *temp_ptr; + int index; + bool control_file; + + index = get_next_index(playlist, 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(playlist, seek, control_file, now_playing, + MAX_PATH+1) < 0) + return NULL; + + temp_ptr = now_playing; + + if (!playlist->in_ram || control_file) + { + /* 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, '/'); + } + + 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; + } + } + + return temp_ptr; +} + +/* + * Update indices as track has changed + */ +int playlist_next(int steps) +{ + struct playlist_info* playlist = ¤t_playlist; + int index; + + if (steps > 0 && global_settings.repeat_mode != REPEAT_ONE) + { + int i, j; + + /* We need to delete all the queued songs */ + for (i=0, j=steps; iindices[index] & PLAYLIST_QUEUE_MASK) + { + remove_track_from_playlist(playlist, index, true); + steps--; /* one less track */ + } + } + } + + index = get_next_index(playlist, steps); + playlist->index = index; + + if (playlist->last_insert_pos >= 0 && steps > 0) + { + /* check to see if we've gone beyond the last inserted track */ + int cur = rotate_index(playlist, index); + int last_pos = rotate_index(playlist, playlist->last_insert_pos); + + if (cur > last_pos) + { + /* reset last inserted track */ + playlist->last_insert_pos = -1; + + if (playlist->control_fd >= 0) + { + int result = -1; + + mutex_lock(&playlist->control_mutex); + + if (lseek(playlist->control_fd, 0, SEEK_END) >= 0) + { + 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, true, + str(LANG_PLAYLIST_CONTROL_UPDATE_ERROR)); + return result; + } + } + } + } + + return index; +} + +/* 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) +{ + struct playlist_info* playlist = ¤t_playlist; + + *resume_index = playlist->index; + + return 0; +} + +/* Returns index of current playing track for display purposes. This value + should not be used for resume purposes as it doesn't represent the actual + index into the playlist */ +int playlist_get_display_index(void) +{ + struct playlist_info* playlist = ¤t_playlist; + + /* first_index should always be index 0 for display purposes */ + int index = rotate_index(playlist, playlist->index); + + return (index+1); +} + +/* returns number of tracks in current playlist */ +int playlist_amount(void) +{ + return playlist_amount_ex(NULL); +} + +/* + * Create a new playlist If playlist is not NULL then we're loading a + * playlist off disk for viewing/editing. The index_buffer is used to store + * playlist indices (required for and only used if !current playlist). The + * temp_buffer (if not NULL) is used as a scratchpad when loading indices. + */ +int playlist_create_ex(struct playlist_info* playlist, char* dir, char* file, + void* index_buffer, int index_buffer_size, + void* temp_buffer, int temp_buffer_size) +{ + if (!playlist) + playlist = ¤t_playlist; + else + { + /* Initialize playlist structure */ + int r = rand() % 10; + playlist->current = false; + + /* Use random name for control file */ + snprintf(playlist->control_filename, sizeof(playlist->control_filename), + "%s.%d", PLAYLIST_CONTROL_FILE, r); + playlist->fd = -1; + playlist->control_fd = -1; + + if (index_buffer) + { + int num_indices = index_buffer_size / sizeof(int); + + if (num_indices > global_settings.max_files_in_playlist) + num_indices = global_settings.max_files_in_playlist; + + playlist->max_playlist_size = num_indices; + playlist->indices = index_buffer; + } + else + { + playlist->max_playlist_size = current_playlist.max_playlist_size; + playlist->indices = current_playlist.indices; + } + + playlist->buffer_size = 0; + playlist->buffer = NULL; + mutex_init(&playlist->control_mutex); + } + + new_playlist(playlist, dir, file); - strcpy(&playlist.buffer[playlist.buffer_end_pos], filename); - playlist.buffer_end_pos += len; - playlist.buffer[playlist.buffer_end_pos++] = '\0'; + if (file) + /* load the playlist file */ + add_indices_to_playlist(playlist, temp_buffer, temp_buffer_size); return 0; } +/* + * Set the specified playlist as the current. + * NOTE: You will get undefined behaviour if something is already playing so + * remember to stop before calling this. Also, this call will + * effectively close your playlist, making it unusable. + */ +int playlist_set_current(struct playlist_info* playlist) +{ + if (!playlist || (check_control(playlist) < 0)) + return -1; + + empty_playlist(¤t_playlist, false); + + strncpy(current_playlist.filename, playlist->filename, + sizeof(current_playlist.filename)); + + current_playlist.fd = playlist->fd; + + close(playlist->control_fd); + remove(current_playlist.control_filename); + if (rename(playlist->control_filename, + current_playlist.control_filename) < 0) + return -1; + current_playlist.control_fd = open(current_playlist.control_filename, + O_RDWR); + if (current_playlist.control_fd < 0) + return -1; + current_playlist.control_created = true; + + current_playlist.dirlen = playlist->dirlen; + + if (playlist->indices && playlist->indices != current_playlist.indices) + memcpy(current_playlist.indices, playlist->indices, + playlist->max_playlist_size*sizeof(int)); + + current_playlist.first_index = playlist->first_index; + current_playlist.amount = playlist->amount; + current_playlist.last_insert_pos = playlist->last_insert_pos; + current_playlist.seed = playlist->seed; + current_playlist.shuffle_modified = playlist->shuffle_modified; + current_playlist.deleted = playlist->deleted; + current_playlist.num_inserted_tracks = playlist->num_inserted_tracks; + current_playlist.shuffle_flush = playlist->shuffle_flush; + + return 0; +} + +/* + * Close files and delete control file for non-current playlist. + */ +void playlist_close(struct playlist_info* playlist) +{ + if (!playlist) + return; + + if (playlist->fd >= 0) + close(playlist->fd); + + if (playlist->control_fd >= 0) + close(playlist->control_fd); + + if (playlist->control_created) + remove(playlist->control_filename); +} + /* * Insert track into playlist at specified position (or one of the special * positions). Returns position where track was inserted or -1 if error. */ -int playlist_insert_track(char *filename, int position, bool queue) +int playlist_insert_track(struct playlist_info* playlist, char *filename, + int position, bool queue) { int result; - if (playlist.control_fd < 0) + if (!playlist) + playlist = ¤t_playlist; + + if (check_control(playlist) < 0) { splash(HZ*2, true, str(LANG_PLAYLIST_CONTROL_ACCESS_ERROR)); return -1; } - result = add_track_to_playlist(filename, position, queue, -1); + result = add_track_to_playlist(playlist, filename, position, queue, -1); if (result != -1) { - fsync(playlist.control_fd); + fsync(playlist->control_fd); mpeg_flush_and_reload_tracks(); } @@ -1497,14 +1908,17 @@ int playlist_insert_track(char *filename, int position, bool queue) /* * Insert all tracks from specified directory into playlist. */ -int playlist_insert_directory(char *dirname, int position, bool queue, - bool recurse) +int playlist_insert_directory(struct playlist_info* playlist, char *dirname, + int position, bool queue, bool recurse) { int count = 0; int result; char *count_str; - if (playlist.control_fd < 0) + if (!playlist) + playlist = ¤t_playlist; + + if (check_control(playlist) < 0) { splash(HZ*2, true, str(LANG_PLAYLIST_CONTROL_ACCESS_ERROR)); return -1; @@ -1517,9 +1931,9 @@ int playlist_insert_directory(char *dirname, int position, bool queue, display_playlist_count(count, count_str); - result = add_directory_to_playlist(dirname, &position, queue, &count, - recurse); - fsync(playlist.control_fd); + result = add_directory_to_playlist(playlist, dirname, &position, queue, + &count, recurse); + fsync(playlist->control_fd); display_playlist_count(count, count_str); mpeg_flush_and_reload_tracks(); @@ -1528,9 +1942,10 @@ int playlist_insert_directory(char *dirname, int position, bool queue, } /* - * Insert all tracks from specified playlist into dynamic playlist + * Insert all tracks from specified playlist into dynamic playlist. */ -int playlist_insert_playlist(char *filename, int position, bool queue) +int playlist_insert_playlist(struct playlist_info* playlist, char *filename, + int position, bool queue) { int fd; int max; @@ -1542,7 +1957,10 @@ int playlist_insert_playlist(char *filename, int position, bool queue) int count = 0; int result = 0; - if (playlist.control_fd < 0) + if (!playlist) + playlist = ¤t_playlist; + + if (check_control(playlist) < 0) { splash(HZ*2, true, str(LANG_PLAYLIST_CONTROL_ACCESS_ERROR)); return -1; @@ -1594,8 +2012,8 @@ int playlist_insert_playlist(char *filename, int position, bool queue) break; } - insert_pos = add_track_to_playlist(trackname, position, queue, - -1); + insert_pos = add_track_to_playlist(playlist, trackname, position, + queue, -1); if (insert_pos < 0) { @@ -1624,9 +2042,10 @@ int playlist_insert_playlist(char *filename, int position, bool queue) } close(fd); - fsync(playlist.control_fd); + fsync(playlist->control_fd); - *temp_ptr = '/'; + if (temp_ptr) + *temp_ptr = '/'; display_playlist_count(count, count_str); mpeg_flush_and_reload_tracks(); @@ -1638,20 +2057,23 @@ int playlist_insert_playlist(char *filename, int position, bool queue) * Delete track at specified index. If index is PLAYLIST_DELETE_CURRENT then * we want to delete the current playing track. */ -int playlist_delete(int index) +int playlist_delete(struct playlist_info* playlist, int index) { int result = 0; - if (playlist.control_fd < 0) + if (!playlist) + playlist = ¤t_playlist; + + if (check_control(playlist) < 0) { splash(HZ*2, true, str(LANG_PLAYLIST_CONTROL_ACCESS_ERROR)); return -1; } if (index == PLAYLIST_DELETE_CURRENT) - index = playlist.index; + index = playlist->index; - result = remove_track_from_playlist(index, true); + result = remove_track_from_playlist(playlist, index, true); if (result != -1) mpeg_flush_and_reload_tracks(); @@ -1663,49 +2085,62 @@ int playlist_delete(int index) * Move track at index to new_index. Tracks between the two are shifted * appropriately. Returns 0 on success and -1 on failure. */ -int playlist_move(int index, int new_index) +int playlist_move(struct playlist_info* playlist, int index, int new_index) { int result; int seek; bool control_file; bool queue; bool current = false; - int r = rotate_index(new_index); + int r; char filename[MAX_PATH]; + if (!playlist) + playlist = ¤t_playlist; + + if (check_control(playlist) < 0) + { + splash(HZ*2, true, str(LANG_PLAYLIST_CONTROL_ACCESS_ERROR)); + return -1; + } + if (index == new_index) return -1; - if (index == playlist.index) + if (index == playlist->index) /* Moving the current track */ current = true; - control_file = playlist.indices[index] & PLAYLIST_INSERT_TYPE_MASK; - queue = playlist.indices[index] & PLAYLIST_QUEUE_MASK; - seek = playlist.indices[index] & PLAYLIST_SEEK_MASK; + control_file = playlist->indices[index] & PLAYLIST_INSERT_TYPE_MASK; + queue = playlist->indices[index] & PLAYLIST_QUEUE_MASK; + seek = playlist->indices[index] & PLAYLIST_SEEK_MASK; - if (get_filename(seek, control_file, filename, sizeof(filename)) < 0) + if (get_filename(playlist, seek, control_file, filename, + sizeof(filename)) < 0) return -1; /* Delete track from original position */ - result = remove_track_from_playlist(index, true); + result = remove_track_from_playlist(playlist, index, true); if (result != -1) { /* We want to insert the track at the position that was specified by new_index. This may be different then new_index because of the shifting that occurred after the delete */ + r = rotate_index(playlist, new_index); + if (r == 0) /* First index */ new_index = PLAYLIST_PREPEND; - else if (r == playlist.amount) + else if (r == playlist->amount) /* Append */ new_index = PLAYLIST_INSERT_LAST; else /* Calculate index of desired position */ - new_index = (r+playlist.first_index)%playlist.amount; + new_index = (r+playlist->first_index)%playlist->amount; - result = add_track_to_playlist(filename, new_index, queue, -1); + result = add_track_to_playlist(playlist, filename, new_index, queue, + -1); if (result != -1) { @@ -1715,20 +2150,20 @@ int playlist_move(int index, int new_index) switch (new_index) { case PLAYLIST_PREPEND: - playlist.index = playlist.first_index; + playlist->index = playlist->first_index; break; case PLAYLIST_INSERT_LAST: - playlist.index = playlist.first_index - 1; - if (playlist.index < 0) - playlist.index += playlist.amount; + playlist->index = playlist->first_index - 1; + if (playlist->index < 0) + playlist->index += playlist->amount; break; default: - playlist.index = new_index; + playlist->index = new_index; break; } } - fsync(playlist.control_fd); + fsync(playlist->control_fd); mpeg_flush_and_reload_tracks(); } } @@ -1736,46 +2171,18 @@ int playlist_move(int index, int new_index) 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 = global_settings.resume_first_index = - playlist.first_index = start_index; - start_current = true; - } - - splash(0, true, str(LANG_PLAYLIST_SHUFFLE)); - - randomise_playlist(random_seed, start_current, true); - - /* Flush shuffle command to disk */ - flush_pending_control(); - - return playlist.index; -} - /* shuffle currently playing playlist */ -int playlist_randomise(unsigned int seed, bool start_current) +int playlist_randomise(struct playlist_info* playlist, unsigned int seed, + bool start_current) { - int result = randomise_playlist(seed, start_current, true); + int result; - if (result != -1) - mpeg_flush_and_reload_tracks(); + if (!playlist) + playlist = ¤t_playlist; - return result; -} + check_control(playlist); -/* sort currently playing playlist */ -int playlist_sort(bool start_current) -{ - int result = sort_playlist(start_current, true); + result = randomise_playlist(playlist, seed, start_current, true); if (result != -1) mpeg_flush_and_reload_tracks(); @@ -1783,231 +2190,119 @@ int playlist_sort(bool start_current) 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) +/* sort currently playing playlist */ +int playlist_sort(struct playlist_info* playlist, bool start_current) { - int seek; - int fd; - char *temp_ptr; - int index; - bool control_file; + int result; - index = get_next_index(steps); - if (index < 0) - return NULL; + if (!playlist) + playlist = ¤t_playlist; - control_file = playlist.indices[index] & PLAYLIST_INSERT_TYPE_MASK; - seek = playlist.indices[index] & PLAYLIST_SEEK_MASK; + check_control(playlist); - if (get_filename(seek, control_file, now_playing, MAX_PATH+1) < 0) - return NULL; + result = sort_playlist(playlist, start_current, true); - temp_ptr = now_playing; - - if (!playlist.in_ram || control_file) - { - /* 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, '/'); - } - - 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; - } - } + if (result != -1) + mpeg_flush_and_reload_tracks(); - return temp_ptr; + return result; } -/* - * Update indices as track has changed - */ -int playlist_next(int steps) +/* returns true if playlist has been modified */ +bool playlist_modified(struct playlist_info* playlist) { - int index; - - if (steps > 0 && global_settings.repeat_mode != REPEAT_ONE) - { - int i, j; - - /* We need to delete all the queued songs */ - for (i=0, j=steps; ishuffle_modified || + playlist->deleted || + playlist->num_inserted_tracks > 0) + return true; - if (playlist.last_insert_pos >= 0 && steps > 0) - { - /* check to see if we've gone beyond the last inserted track */ - int cur = rotate_index(index); - int last_pos = rotate_index(playlist.last_insert_pos); - - if (cur > last_pos) - { - /* reset last inserted track */ - playlist.last_insert_pos = -1; - - if (playlist.control_fd >= 0) - { - int result = -1; - - mutex_lock(&playlist.control_mutex); - - if (lseek(playlist.control_fd, 0, SEEK_END) >= 0) - { - 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, true, - str(LANG_PLAYLIST_CONTROL_UPDATE_ERROR)); - return result; - } - } - } - } - - return index; -} - -bool playlist_modified(void) -{ - if ((mpeg_status() & MPEG_STATUS_PLAY)) - { - if (playlist.shuffle_modified || - playlist.deleted || - playlist.num_inserted_tracks > 0) - return true; - } return false; } -int playlist_get_seed(void) +/* returns index of first track in playlist */ +int playlist_get_first_index(struct playlist_info* playlist) { - return playlist.seed; + if (!playlist) + playlist = ¤t_playlist; + + return playlist->first_index; } -/* 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) +/* returns shuffle seed of playlist */ +int playlist_get_seed(struct playlist_info* playlist) { - *resume_index = playlist.index; + if (!playlist) + playlist = ¤t_playlist; - return 0; + return playlist->seed; } -/* Returns index of current playing track for display purposes. This value - should not be used for resume purposes as it doesn't represent the actual - index into the playlist */ -int playlist_get_display_index(void) +/* returns number of tracks in playlist (includes queued/inserted tracks) */ +int playlist_amount_ex(struct playlist_info* playlist) { - /* first_index should always be index 0 for display purposes */ - int index = rotate_index(playlist.index); + if (!playlist) + playlist = ¤t_playlist; - return (index+1); + return playlist->amount; } -/* returns index of first track in playlist */ -int playlist_get_first_index(void) +/* returns full path of playlist (minus extension) */ +char *playlist_name(struct playlist_info* playlist, char *buf, int buf_size) { - return playlist.first_index; -} + char *sep; -char *playlist_get_name(char *buf, int buf_size) -{ - snprintf(buf, buf_size, "%s", playlist.filename); + if (!playlist) + playlist = ¤t_playlist; + snprintf(buf, buf_size, "%s", playlist->filename+playlist->dirlen); + if (!buf[0]) return NULL; - return buf; -} + /* Remove extension */ + sep = strrchr(buf, '.'); + if (sep) + *sep = 0; -/* returns number of tracks in playlist (includes queued/inserted tracks) */ -int playlist_amount(void) -{ - return playlist.amount; + return buf; } -/* returns playlist name */ -char *playlist_name(char *buf, int buf_size) +/* returns the playlist filename */ +char *playlist_get_name(struct playlist_info* playlist, char *buf, + int buf_size) { - char *sep; + if (!playlist) + playlist = ¤t_playlist; - snprintf(buf, buf_size, "%s", playlist.filename+playlist.dirlen); + snprintf(buf, buf_size, "%s", playlist->filename); if (!buf[0]) return NULL; - /* Remove extension */ - sep = strrchr(buf, '.'); - if (sep) - *sep = 0; - return buf; } /* Fills info structure with information about track at specified index. Returns 0 on success and -1 on failure */ -int playlist_get_track_info(int index, struct playlist_track_info* info) +int playlist_get_track_info(struct playlist_info* playlist, int index, + struct playlist_track_info* info) { int seek; bool control_file; - if (index < 0 || index >= playlist.amount) + if (!playlist) + playlist = ¤t_playlist; + + if (index < 0 || index >= playlist->amount) return -1; - control_file = playlist.indices[index] & PLAYLIST_INSERT_TYPE_MASK; - seek = playlist.indices[index] & PLAYLIST_SEEK_MASK; + control_file = playlist->indices[index] & PLAYLIST_INSERT_TYPE_MASK; + seek = playlist->indices[index] & PLAYLIST_SEEK_MASK; - if (get_filename(seek, control_file, info->filename, + if (get_filename(playlist, seek, control_file, info->filename, sizeof(info->filename)) < 0) return -1; @@ -2015,20 +2310,20 @@ int playlist_get_track_info(int index, struct playlist_track_info* info) if (control_file) { - if (playlist.indices[index] & PLAYLIST_QUEUE_MASK) + if (playlist->indices[index] & PLAYLIST_QUEUE_MASK) info->attr |= PLAYLIST_ATTR_QUEUED; else info->attr |= PLAYLIST_ATTR_INSERTED; } info->index = index; - info->display_index = rotate_index(index) + 1; + info->display_index = rotate_index(playlist, index) + 1; return 0; } /* save the current dynamic playlist to specified file */ -int playlist_save(char *filename) +int playlist_save(struct playlist_info* playlist, char *filename) { int fd; int i, index; @@ -2036,13 +2331,10 @@ int playlist_save(char *filename) char tmp_buf[MAX_PATH+1]; int result = 0; - if (playlist.control_fd < 0) - { - splash(HZ*2, true, str(LANG_PLAYLIST_CONTROL_ACCESS_ERROR)); - return -1; - } + if (!playlist) + playlist = ¤t_playlist; - if (playlist.amount <= 0) + if (playlist->amount <= 0) return -1; /* use current working directory as base for pathname */ @@ -2059,8 +2351,8 @@ int playlist_save(char *filename) display_playlist_count(count, str(LANG_PLAYLIST_SAVE_COUNT)); - index = playlist.first_index; - for (i=0; ifirst_index; + for (i=0; iamount; i++) { bool control_file; bool queue; @@ -2074,14 +2366,15 @@ int playlist_save(char *filename) #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; + 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 (get_filename(seek, control_file, tmp_buf, MAX_PATH+1) < 0) + if (get_filename(playlist, seek, control_file, tmp_buf, + MAX_PATH+1) < 0) { result = -1; break; @@ -2102,7 +2395,7 @@ int playlist_save(char *filename) yield(); } - index = (index+1)%playlist.amount; + index = (index+1)%playlist->amount; } display_playlist_count(count, str(LANG_PLAYLIST_SAVE_COUNT)); diff --git a/apps/playlist.h b/apps/playlist.h index 020d3332cb..482cecd128 100644 --- a/apps/playlist.h +++ b/apps/playlist.h @@ -28,9 +28,12 @@ struct playlist_info { + bool current; /* current playing playlist */ char filename[MAX_PATH]; /* path name of m3u playlist on disk */ + char control_filename[MAX_PATH]; /* full path of control file */ int fd; /* descriptor of the open playlist file */ int control_fd; /* descriptor of the open control file */ + bool control_created; /* has control file been created? */ int dirlen; /* Length of the path to the playlist file */ unsigned int *indices; /* array of indices */ int max_playlist_size; /* Max number of files in playlist. Mirror of @@ -63,33 +66,48 @@ struct playlist_track_info int display_index; /* index of track for display */ }; +/* Exported functions only for current playlist. */ void playlist_init(void); int playlist_create(char *dir, char *file); int playlist_resume(void); int playlist_add(char *filename); -int playlist_insert_track(char *filename, int position, bool queue); -int playlist_insert_directory(char *dirname, int position, bool queue, - bool recurse); -int playlist_insert_playlist(char *filename, int position, bool queue); -int playlist_delete(int index); -int playlist_move(int index, int new_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_get_first_index(void); int playlist_amount(void); -char *playlist_name(char *buf, int buf_size); -int playlist_get_track_info(int index, struct playlist_track_info* info); -int playlist_save(char *filename); -int playlist_get_seed(void); -char *playlist_get_name(char *buf, int buf_size); -bool playlist_modified(void); + +/* Exported functions for all playlists. Pass NULL for playlist_info + structure to work with current playlist. */ +int playlist_create_ex(struct playlist_info* playlist, char* dir, char* file, + void* index_buffer, int index_buffer_size, + void* temp_buffer, int temp_buffer_size); +int playlist_set_current(struct playlist_info* playlist); +void playlist_close(struct playlist_info* playlist); +int playlist_insert_track(struct playlist_info* playlist, char *filename, + int position, bool queue); +int playlist_insert_directory(struct playlist_info* playlist, char *dirname, + int position, bool queue, bool recurse); +int playlist_insert_playlist(struct playlist_info* playlist, char *filename, + int position, bool queue); +int playlist_delete(struct playlist_info* playlist, int index); +int playlist_move(struct playlist_info* playlist, int index, int new_index); +int playlist_randomise(struct playlist_info* playlist, unsigned int seed, + bool start_current); +int playlist_sort(struct playlist_info* playlist, bool start_current); +bool playlist_modified(struct playlist_info* playlist); +int playlist_get_first_index(struct playlist_info* playlist); +int playlist_get_seed(struct playlist_info* playlist); +int playlist_amount_ex(struct playlist_info* playlist); +char *playlist_name(struct playlist_info* playlist, char *buf, int buf_size); +char *playlist_get_name(struct playlist_info* playlist, char *buf, + int buf_size); +int playlist_get_track_info(struct playlist_info* playlist, int index, + struct playlist_track_info* info); +int playlist_save(struct playlist_info* playlist, char *filename); enum { PLAYLIST_PREPEND = -1, diff --git a/apps/playlist_menu.c b/apps/playlist_menu.c index f4e4867a92..b2742781a1 100644 --- a/apps/playlist_menu.c +++ b/apps/playlist_menu.c @@ -39,7 +39,7 @@ static bool save_playlist(void) if (!kbd_input(filename, sizeof(filename))) { - playlist_save(filename); + playlist_save(NULL, filename); /* reload in case playlist was saved to cwd */ reload_directory(); diff --git a/apps/playlist_viewer.c b/apps/playlist_viewer.c index 00da973732..20ac6c05a2 100644 --- a/apps/playlist_viewer.c +++ b/apps/playlist_viewer.c @@ -28,6 +28,9 @@ #include "icons.h" #include "menu.h" #include "plugin.h" +#include "keyboard.h" +#include "tree.h" +#include "onplay.h" #ifdef HAVE_LCD_BITMAP #include "widgets.h" @@ -35,6 +38,8 @@ #include "lang.h" +#include "playlist_viewer.h" + /* Defines for LCD display purposes. Taken from tree.c */ #ifdef HAVE_LCD_BITMAP #define CURSOR_X (global_settings.scrollbar && \ @@ -46,7 +51,9 @@ #define MARGIN_X ((global_settings.scrollbar && \ viewer.num_tracks > viewer.num_display_lines ? \ - SCROLLBAR_WIDTH : 0) + CURSOR_WIDTH + ICON_WIDTH) + SCROLLBAR_WIDTH : 0) + CURSOR_WIDTH + \ + (global_settings.playlist_viewer_icons ? \ + ICON_WIDTH : 0)) #define MARGIN_Y (global_settings.statusbar ? STATUSBAR_HEIGHT : 0) #define LINE_X 0 @@ -67,11 +74,15 @@ /* Maximum number of tracks we can have loaded at one time */ #define MAX_PLAYLIST_ENTRIES 200 +/* Default playlist name for saving */ +#define DEFAULT_PLAYLIST_NAME "/viewer.m3u" + /* Index of track on display line _pos */ #define INDEX(_pos) (viewer.first_display_index - viewer.first_index + (_pos)) /* Global playlist viewer settings */ struct playlist_viewer_info { + struct playlist_info* playlist; /* playlist being viewed */ char *name_buffer; /* Buffer used to store track names */ int buffer_size; /* Size of name buffer */ @@ -103,15 +114,19 @@ struct playlist_entry { static struct playlist_viewer_info viewer; static struct playlist_entry tracks[MAX_PLAYLIST_ENTRIES]; +/* Used when viewing playlists on disk */ +static struct playlist_info temp_playlist; + #ifdef HAVE_LCD_BITMAP extern unsigned char bitmap_icons_6x8[LastIcon][6]; #endif -static bool initialize(void); +static bool initialize(char* filename, bool reload); static void load_playlist_entries(int start_index); static void load_playlist_entries_r(int end_index); static int load_entry(int index, int pos, char* p, int size); static void format_name(char* dest, char* src); +static void format_line(struct playlist_entry* track, char* str, int len); static void display_playlist(void); static void update_display_line(int line, bool scroll); static void scroll_display(int lines); @@ -120,18 +135,74 @@ static bool update_playlist(bool force); #ifdef BUTTON_ON static int onplay_menu(int index); #endif - -/* Initialize the playlist viewer */ -static bool initialize(void) +static bool viewer_menu(void); +static bool show_icons(void); +static bool show_indices(void); +static bool track_display(void); +static bool save_playlist(void); + +/* Initialize the playlist viewer. */ +static bool initialize(char* filename, bool reload) { - if (!(mpeg_status() & MPEG_STATUS_PLAY)) + char* buffer; + int buffer_size; + bool is_playing = mpeg_status() & MPEG_STATUS_PLAY; + + if (!filename && !is_playing) /* Nothing is playing, exit */ return false; - viewer.name_buffer = plugin_get_buffer(&viewer.buffer_size); - if (!viewer.name_buffer) + buffer = plugin_get_buffer(&buffer_size); + if (!buffer) return false; + if (!filename) + viewer.playlist = NULL; + else + { + /* Viewing playlist on disk */ + char *dir, *file, *temp_ptr; + char *index_buffer = NULL; + int index_buffer_size = 0; + + viewer.playlist = &temp_playlist; + + /* Separate directory from filename */ + temp_ptr = strrchr(filename+1,'/'); + if (temp_ptr) + { + *temp_ptr = 0; + dir = filename; + file = temp_ptr + 1; + } + else + { + dir = "/"; + file = filename+1; + } + + if (is_playing) + { + /* Something is playing, use half the plugin buffer for playlist + indices */ + index_buffer_size = buffer_size / 2; + index_buffer = buffer; + } + + playlist_create_ex(viewer.playlist, dir, file, index_buffer, + index_buffer_size, buffer+index_buffer_size, + buffer_size-index_buffer_size); + + if (temp_ptr) + *temp_ptr = '/'; + + buffer += index_buffer_size; + buffer_size -= index_buffer_size; + } + + viewer.name_buffer = buffer; + viewer.buffer_size = buffer_size; + #ifdef HAVE_LCD_BITMAP { char icon_chars[] = "MQ"; /* characters used as icons */ @@ -166,12 +237,20 @@ static bool initialize(void) viewer.line_height = 1; #endif - viewer.cursor_pos = 0; viewer.move_track = -1; - /* Start displaying at current playing track */ - viewer.first_display_index = playlist_get_display_index() - 1; - update_first_index(); + if (!reload) + { + viewer.cursor_pos = 0; + + if (!viewer.playlist) + /* Start displaying at current playing track */ + viewer.first_display_index = playlist_get_display_index() - 1; + else + viewer.first_display_index = 0; + + update_first_index(); + } if (!update_playlist(true)) return false; @@ -287,21 +366,19 @@ static int load_entry(int index, int pos, char* p, int size) struct playlist_track_info info; int len; int result = 0; - char name[MAX_PATH]; /* Playlist viewer orders songs based on display index. We need to convert to real playlist index to access track */ - index = (index + playlist_get_first_index()) % viewer.num_tracks; - if (playlist_get_track_info(index, &info) < 0) + index = (index + playlist_get_first_index(viewer.playlist)) % + viewer.num_tracks; + if (playlist_get_track_info(viewer.playlist, index, &info) < 0) return -1; - format_name(name, info.filename); - - len = strlen(name) + 1; + len = strlen(info.filename) + 1; if (len <= size) { - strcpy(p, name); + strcpy(p, info.filename); tracks[pos].name = p; tracks[pos].index = info.index; @@ -319,18 +396,46 @@ static int load_entry(int index, int pos, char* p, int size) /* Format trackname for display purposes */ static void format_name(char* dest, char* src) { - char* p = strrchr(src, '/'); - int len; + switch (global_settings.playlist_viewer_track_display) + { + case 0: + default: + { + /* Only display the mp3 filename */ + char* p = strrchr(src, '/'); + int len; + + strcpy(dest, p+1); + len = strlen(dest); + + /* Remove the extension */ + if (!strcasecmp(&dest[len-4], ".mp3") || + !strcasecmp(&dest[len-4], ".mp2") || + !strcasecmp(&dest[len-4], ".mpa")) + dest[len-4] = '\0'; + + break; + } + case 1: + /* Full path */ + strcpy(dest, src); + break; + } +} + +/* Format display line */ +static void format_line(struct playlist_entry* track, char* str, int len) +{ + char name[MAX_PATH]; - /* Only display the mp3 filename */ - strcpy(dest, p+1); - len = strlen(dest); + format_name(name, track->name); + + if (global_settings.playlist_viewer_indices) + /* Display playlist index */ + snprintf(str, len, "%d. %s", track->display_index, name); + else + snprintf(str, len, "%s", name); - /* Remove the extension */ - if (!strcasecmp(&dest[len-4], ".mp3") || - !strcasecmp(&dest[len-4], ".mp2") || - !strcasecmp(&dest[len-4], ".mpa")) - dest[len-4] = '\0'; } /* Display tracks on screen */ @@ -349,41 +454,44 @@ static void display_playlist(void) for (i=0; i<=num_display_tracks; i++) { - /* Icons */ - if (tracks[INDEX(i)].index == viewer.current_playing_track) + if (global_settings.playlist_viewer_icons) { - /* Current playing track */ + /* Icons */ + if (tracks[INDEX(i)].index == viewer.current_playing_track) + { + /* Current playing track */ #ifdef HAVE_LCD_BITMAP - int offset=0; - if ( viewer.line_height > 8 ) - offset = (viewer.line_height - 8) / 2; - lcd_bitmap(bitmap_icons_6x8[File], - CURSOR_X * 6 + CURSOR_WIDTH, - MARGIN_Y+(i*viewer.line_height) + offset, - 6, 8, true); + int offset=0; + if ( viewer.line_height > 8 ) + offset = (viewer.line_height - 8) / 2; + lcd_bitmap(bitmap_icons_6x8[File], + CURSOR_X * 6 + CURSOR_WIDTH, + MARGIN_Y+(i*viewer.line_height) + offset, + 6, 8, true); #else - lcd_putc(LINE_X-1, i, File); + lcd_putc(LINE_X-1, i, File); #endif - } - else if (tracks[INDEX(i)].index == viewer.move_track) - { - /* Track we are moving */ + } + else if (tracks[INDEX(i)].index == viewer.move_track) + { + /* Track we are moving */ #ifdef HAVE_LCD_BITMAP - lcd_putsxy(CURSOR_X * 6 + CURSOR_WIDTH, - MARGIN_Y+(i*viewer.line_height), "M"); + lcd_putsxy(CURSOR_X * 6 + CURSOR_WIDTH, + MARGIN_Y+(i*viewer.line_height), "M"); #else - lcd_putc(LINE_X-1, i, 'M'); + lcd_putc(LINE_X-1, i, 'M'); #endif - } - else if (tracks[INDEX(i)].queued) - { - /* Queued track */ + } + else if (tracks[INDEX(i)].queued) + { + /* Queued track */ #ifdef HAVE_LCD_BITMAP - lcd_putsxy(CURSOR_X * 6 + CURSOR_WIDTH, - MARGIN_Y+(i*viewer.line_height), "Q"); + lcd_putsxy(CURSOR_X * 6 + CURSOR_WIDTH, + MARGIN_Y+(i*viewer.line_height), "Q"); #else - lcd_putc(LINE_X-1, i, 'Q'); + lcd_putc(LINE_X-1, i, 'Q'); #endif + } } update_display_line(i, false); @@ -494,10 +602,8 @@ static void update_display_line(int line, bool scroll) { char str[MAX_PATH + 16]; - snprintf(str, sizeof(str), "%d. %s", - tracks[INDEX(line)].display_index, - tracks[INDEX(line)].name); - + format_line(&tracks[INDEX(line)], str, sizeof(str)); + if (scroll) { #ifdef HAVE_LCD_BITMAP @@ -516,7 +622,7 @@ static void update_display_line(int line, bool scroll) static void update_first_index(void) { /* viewer.num_tracks may be invalid at this point */ - int num_tracks = playlist_amount(); + int num_tracks = playlist_amount_ex(viewer.playlist); if ((num_tracks - viewer.first_display_index) < viewer.num_display_lines) { @@ -535,14 +641,17 @@ static void update_first_index(void) /* Update playlist in case something has changed or forced */ static bool update_playlist(bool force) { - playlist_get_resume_info(&viewer.current_playing_track); + if (!viewer.playlist) + playlist_get_resume_info(&viewer.current_playing_track); + else + viewer.current_playing_track = -1; - if (force || playlist_amount() != viewer.num_tracks) + if (force || playlist_amount_ex(viewer.playlist) != viewer.num_tracks) { int index; /* Reload tracks */ - viewer.num_tracks = playlist_amount(); + viewer.num_tracks = playlist_amount_ex(viewer.playlist); if (viewer.num_tracks < 0) return false; @@ -571,16 +680,19 @@ static bool update_playlist(bool force) changed. */ static int onplay_menu(int index) { - struct menu_items menu[2]; /* increase this if you add entries! */ + struct menu_items menu[3]; /* increase this if you add entries! */ int m, i=0, result, ret = 0; bool current = (tracks[index].index == viewer.current_playing_track); - menu[i].desc = str(LANG_DELETE); + menu[i].desc = str(LANG_REMOVE); i++; menu[i].desc = str(LANG_MOVE); i++; + menu[i].desc = str(LANG_FILE_OPTIONS); + i++; + m = menu_init(menu, i); result = menu_show(m); if (result == MENU_ATTACHED_USB) @@ -597,7 +709,7 @@ static int onplay_menu(int index) if (current) mpeg_stop(); - playlist_delete(tracks[index].index); + playlist_delete(viewer.playlist, tracks[index].index); if (current) { @@ -618,6 +730,17 @@ static int onplay_menu(int index) viewer.move_track = tracks[index].index; ret = 0; break; + case 2: + { + onplay(tracks[index].name, TREE_ATTR_MPA); + + if (!viewer.playlist) + ret = 1; + else + ret = 0; + + break; + } } } @@ -627,17 +750,91 @@ static int onplay_menu(int index) } #endif -/* Main viewer function */ +/* Menu of viewer options. Invoked via F1(r) or Menu(p). */ +static bool viewer_menu(void) +{ + int m; + bool result; + + struct menu_items items[] = { + { str(LANG_SHOW_ICONS), show_icons }, + { str(LANG_SHOW_INDICES), show_indices }, + { str(LANG_TRACK_DISPLAY), track_display }, + { str(LANG_SAVE_DYNAMIC_PLAYLIST), save_playlist }, + }; + + m=menu_init( items, sizeof(items) / sizeof(*items) ); + result = menu_run(m); + menu_exit(m); + + settings_save(); + + return result; +} + +/* Show icons in viewer? */ +static bool show_icons(void) +{ + return set_bool(str(LANG_SHOW_ICONS), + &global_settings.playlist_viewer_icons); +} + +/* Show indices of tracks? */ +static bool show_indices(void) +{ + return set_bool(str(LANG_SHOW_INDICES), + &global_settings.playlist_viewer_indices); +} + +/* How to display a track */ +static bool track_display(void) +{ + char* names[] = { + str(LANG_DISPLAY_TRACK_NAME_ONLY), + str(LANG_DISPLAY_FULL_PATH) + }; + + return set_option(str(LANG_TRACK_DISPLAY), + &global_settings.playlist_viewer_track_display, INT, names, 2, NULL); +} + +/* Save playlist to disk */ +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(viewer.playlist, filename); + + /* reload in case playlist was saved to cwd */ + reload_directory(); + } + + return false; +} + +/* View current playlist */ bool playlist_viewer(void) { + return playlist_viewer_ex(NULL); +} + +/* Main viewer function. Filename identifies playlist to be viewed. If NULL, + view current playlist. */ +bool playlist_viewer_ex(char* filename) +{ + bool ret = false; /* return value */ bool exit=false; /* exit viewer */ bool update=true; /* update display */ bool cursor_on=true; /* used for flashing cursor */ int old_cursor_pos; /* last cursor position */ int button; - if (!initialize()) - return false; + if (!initialize(filename, false)) + goto exit; old_cursor_pos = viewer.cursor_pos; @@ -648,7 +845,7 @@ bool playlist_viewer(void) /* Timeout so we can determine if play status has changed */ button = button_get_w_tmo(HZ/2); - if (!(mpeg_status() & MPEG_STATUS_PLAY)) + if (!viewer.playlist && !(mpeg_status() & MPEG_STATUS_PLAY)) { /* Play has stopped */ #ifdef HAVE_LCD_CHARCELLS @@ -657,7 +854,7 @@ bool playlist_viewer(void) splash(HZ, true, str(LANG_END_PLAYLIST_RECORDER)); #endif status_set_playmode(STATUS_STOP); - return false;; + goto exit; } if (viewer.move_track != -1 || !cursor_on) @@ -685,10 +882,13 @@ bool playlist_viewer(void) #endif } - playlist_get_resume_info(&track); + if (!viewer.playlist) + playlist_get_resume_info(&track); + else + track = -1; if (track != viewer.current_playing_track || - playlist_amount() != viewer.num_tracks) + playlist_amount_ex(viewer.playlist) != viewer.num_tracks) { /* Playlist has changed (new track started?) */ update_first_index(); @@ -769,7 +969,7 @@ bool playlist_viewer(void) /* Move track */ int ret; - ret = playlist_move(viewer.move_track, + ret = playlist_move(viewer.playlist, viewer.move_track, tracks[INDEX(viewer.cursor_pos)].index); if (ret < 0) splash(HZ, true, str(LANG_MOVE_FAILED)); @@ -777,7 +977,7 @@ bool playlist_viewer(void) update_playlist(true); viewer.move_track = -1; } - else + else if (!viewer.playlist) { /* Stop current track and play new track */ mpeg_stop(); @@ -785,6 +985,22 @@ bool playlist_viewer(void) status_set_playmode(STATUS_PLAY); update_playlist(false); } + else + { + /* Play track from playlist on disk */ + mpeg_stop(); + + /* New playlist */ + if (playlist_set_current(viewer.playlist) < 0) + goto exit; + + playlist_start(tracks[INDEX(viewer.cursor_pos)].index, 0); + status_set_playmode(STATUS_PLAY); + + /* Our playlist is now the current list */ + if (!initialize(NULL, true)) + goto exit; + } display_playlist(); update = true; @@ -799,13 +1015,15 @@ bool playlist_viewer(void) ret = onplay_menu(INDEX(viewer.cursor_pos)); if (ret < 0) - /* USB attached */ - return true; + { + ret = true; + goto exit; + } else if (ret > 0) { /* Playlist changed */ update_first_index(); - update_playlist(true); + update_playlist(false); if (viewer.num_tracks <= 0) exit = true; } @@ -816,9 +1034,30 @@ bool playlist_viewer(void) break; } #endif /* BUTTON_ON */ +#ifdef HAVE_RECORDER_KEYPAD + case BUTTON_F1: +#else + case BUTTON_MENU: +#endif + if (viewer_menu()) + { + ret = true; + goto exit; + } + + display_playlist(); + update = true; + break; + case SYS_USB_CONNECTED: usb_screen(); - return true; + ret = true; + goto exit; + break; + + case BUTTON_NONE: + status_draw(false); + break; } if (update && !exit) @@ -852,5 +1091,8 @@ bool playlist_viewer(void) } } - return false; +exit: + if (viewer.playlist) + playlist_close(viewer.playlist); + return ret; } diff --git a/apps/playlist_viewer.h b/apps/playlist_viewer.h index ecc5197566..69f9d03559 100644 --- a/apps/playlist_viewer.h +++ b/apps/playlist_viewer.h @@ -22,5 +22,6 @@ #define _PLAYLIST_VIEWER_H_ bool playlist_viewer(void); +bool playlist_viewer_ex(char* filename); #endif diff --git a/apps/screens.c b/apps/screens.c index c2ffb44f3c..6987fc92e3 100644 --- a/apps/screens.c +++ b/apps/screens.c @@ -512,9 +512,9 @@ bool f2_screen(void) if(mpeg_status() & MPEG_STATUS_PLAY) { if (global_settings.playlist_shuffle) - playlist_randomise(current_tick, true); + playlist_randomise(NULL, current_tick, true); else - playlist_sort(true); + playlist_sort(NULL, true); } used = true; break; diff --git a/apps/settings.c b/apps/settings.c index bfc943bef0..7b9c1feb60 100644 --- a/apps/settings.c +++ b/apps/settings.c @@ -146,6 +146,9 @@ Rest of config block, only saved to disk: caption backlight (bit 1) car adapter mode (bit 2) line_in (Player only) (bit 3) + playlist viewer icons (bit 4) + playlist viewer indices (bit 5) + playlist viewer track display (bit 6) 0xAF 0xB0 peak meter clip hold timeout (bit 0-4), peak meter performance (bit 7) 0xB1 peak meter release step size, peak_meter_dbfs (bit 7) @@ -421,7 +424,10 @@ int settings_save( void ) ((global_settings.fade_on_stop & 1) | ((global_settings.caption_backlight & 1) << 1) | ((global_settings.car_adapter_mode & 1) << 2) | - ((global_settings.line_in & 1) << 3)); + ((global_settings.line_in & 1) << 3) | + ((global_settings.playlist_viewer_icons & 1) << 4) | + ((global_settings.playlist_viewer_indices & 1) << 5) | + ((global_settings.playlist_viewer_track_display & 1) << 6)); config_block[0xaf] = ((global_settings.usemrb << 5) | (global_settings.autocreatebookmark << 2) | (global_settings.autoloadbookmark)); @@ -726,6 +732,12 @@ void settings_load(void) global_settings.caption_backlight = (config_block[0xae] >> 1) & 1; global_settings.car_adapter_mode = (config_block[0xae] >> 2) & 1; global_settings.line_in = (config_block[0xae] >> 3) & 1; + global_settings.playlist_viewer_icons = + (config_block[0xae] >> 4) & 1; + global_settings.playlist_viewer_indices = + (config_block[0xae] >> 5) & 1; + global_settings.playlist_viewer_track_display = + (config_block[0xae] >> 6) & 1; } if(config_block[0xb0] != 0xff) { @@ -1161,6 +1173,16 @@ bool settings_load_config(char* file) static char* options[] = {"off", "on", "unique only"}; set_cfg_option(&global_settings.usemrb, value, options, 3); } + else if (!strcasecmp(name, "playlist viewer icons")) + set_cfg_bool(&global_settings.playlist_viewer_icons, value); + else if (!strcasecmp(name, "playlist viewer indices")) + set_cfg_bool(&global_settings.playlist_viewer_indices, value); + else if (!strcasecmp(name, "playlist viewer track display")) + { + static char* options[] = {"track name", "full path"}; + set_cfg_option(&global_settings.playlist_viewer_track_display, + value, options, 2); + } } close(fd); @@ -1494,6 +1516,19 @@ bool settings_save_config(void) triopt[global_settings.recursive_dir_insert]); } + fprintf(fd, "#\r\n# Playlist viewer\r\n#\r\n"); + { + fprintf(fd, "playlist viewer icons: %s\r\n", + boolopt[global_settings.playlist_viewer_icons]); + fprintf(fd, "playlist viewer indices: %s\r\n", + boolopt[global_settings.playlist_viewer_indices]); + { + static char* options[] = {"track name", "full path"}; + fprintf(fd, "playlist viewer track display: %s\r\n", + options[global_settings.playlist_viewer_track_display]); + } + } + close(fd); lcd_clear_display(); @@ -1596,6 +1631,9 @@ void settings_reset(void) { global_settings.show_icons = true; global_settings.recursive_dir_insert = RECURSE_OFF; global_settings.line_in = false; + global_settings.playlist_viewer_icons = true; + global_settings.playlist_viewer_indices = true; + global_settings.playlist_viewer_track_display = 0; } bool set_bool(char* string, bool* variable ) diff --git a/apps/settings.h b/apps/settings.h index 436b5204d5..3634087cab 100644 --- a/apps/settings.h +++ b/apps/settings.h @@ -194,6 +194,11 @@ struct user_settings int recursive_dir_insert; /* should directories be inserted recursively */ bool line_in; /* false=off, true=active */ + + /* playlist viewer settings */ + bool playlist_viewer_icons; /* display icons on viewer */ + bool playlist_viewer_indices; /* display playlist indices on viewer */ + int playlist_viewer_track_display; /* how to display tracks in viewer */ }; enum optiontype { INT, BOOL }; diff --git a/apps/settings_menu.c b/apps/settings_menu.c index cf7e55ae5b..24a67adc47 100644 --- a/apps/settings_menu.c +++ b/apps/settings_menu.c @@ -827,11 +827,11 @@ static bool playback_settings_menu(void) { if (global_settings.playlist_shuffle) { - playlist_randomise(current_tick, true); + playlist_randomise(NULL, current_tick, true); } else { - playlist_sort(true); + playlist_sort(NULL, true); } } return result; diff --git a/apps/wps-display.c b/apps/wps-display.c index 6553e9b542..a24cae9717 100644 --- a/apps/wps-display.c +++ b/apps/wps-display.c @@ -499,7 +499,7 @@ static char* get_tag(struct mp3entry* id3, case 'n': /* Playlist Name (without path) */ *flags |= WPS_REFRESH_STATIC; - return playlist_name(buf, buf_size); + return playlist_name(NULL, buf, buf_size); case 'e': /* Playlist Total Entries */ *flags |= WPS_REFRESH_STATIC; -- cgit v1.2.3