rockbox/apps/playlist.c

421 lines
11 KiB
C
Raw Normal View History

/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id$
*
* Copyright (C) 2002 by wavey@wavey.org
*
* 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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "playlist.h"
#include <file.h>
#include "sprintf.h"
#include "debug.h"
#include "mpeg.h"
#include "lcd.h"
#include "kernel.h"
#include "settings.h"
#include "status.h"
#include "applimits.h"
#ifdef HAVE_LCD_BITMAP
#include "icons.h"
#include "widgets.h"
#endif
playlist_info_t playlist;
#define PLAYLIST_BUFFER_SIZE (AVERAGE_FILENAME_LENGTH*MAX_FILES_IN_DIR)
unsigned char playlist_buffer[PLAYLIST_BUFFER_SIZE];
static int playlist_end_pos = 0;
char now_playing[MAX_PATH+1];
void playlist_clear(void)
{
playlist_end_pos = 0;
playlist_buffer[0] = 0;
}
int playlist_add(char *filename)
{
int len = strlen(filename);
if(len+2 > PLAYLIST_BUFFER_SIZE - playlist_end_pos)
return -1;
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;
}
char* playlist_next(int steps, int* index)
{
int seek;
int max;
int fd;
int i;
char *buf;
char dir_buf[MAX_PATH+1];
char *dir_end;
playlist.index = (playlist.index+steps) % playlist.amount;
while ( playlist.index < 0 ) {
if ( global_settings.loop_playlist )
playlist.index += playlist.amount;
else
playlist.index = 0;
}
seek = playlist.indices[playlist.index];
if(playlist.in_ram)
{
buf = playlist_buffer + seek;
max = playlist_end_pos - seek;
}
else
{
fd = open(playlist.filename, O_RDONLY);
if(-1 != fd)
{
buf = playlist_buffer;
lseek(fd, seek, SEEK_SET);
max = read(fd, buf, MAX_PATH);
close(fd);
}
else
return NULL;
}
if (index)
*index = playlist.index;
/* Zero-terminate the file name */
seek=0;
while((buf[seek] != '\n') &&
(buf[seek] != '\r') &&
(seek < max))
seek++;
/* Now work back killing white space */
while((buf[seek-1] == ' ') ||
(buf[seek-1] == '\t'))
seek--;
buf[seek]=0;
/* replace backslashes with forward slashes */
for ( i=0; i<seek; i++ )
if ( buf[i] == '\\' )
buf[i] = '/';
if('/' == buf[0]) {
strcpy(now_playing, &buf[0]);
return now_playing;
}
else {
strncpy(dir_buf, playlist.filename, playlist.dirlen-1);
dir_buf[playlist.dirlen-1] = 0;
/* handle dos style drive letter */
if ( ':' == buf[1] ) {
strcpy(now_playing, &buf[2]);
return now_playing;
}
else if ( '.' == buf[0] && '.' == buf[1] && '/' == buf[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';
else
break;
}
snprintf(now_playing, MAX_PATH+1, "%s/%s", dir_buf, &buf[seek]);
return now_playing;
}
else if ( '.' == buf[0] && '/' == buf[1] ) {
snprintf(now_playing, MAX_PATH+1, "%s/%s", dir_buf, &buf[2]);
return now_playing;
}
else {
snprintf(now_playing, MAX_PATH+1, "%s/%s", dir_buf, buf);
return now_playing;
}
}
}
/*
* This function is called to start playback of a given playlist. This
* playlist may be stored in RAM (when using full-dir playback).
*/
void play_list(char *dir, /* "current directory" */
char *file, /* playlist */
int start_index, /* index in the playlist */
int start_offset, /* offset in the file */
int random_seed ) /* used for shuffling */
{
char *sep="";
int dirlen;
empty_playlist();
playlist.index = start_index;
#ifdef HAVE_LCD_BITMAP
if(global_settings.statusbar)
lcd_setmargins(0, STATUSBAR_HEIGHT);
else
lcd_setmargins(0, 0);
#endif
/* If file is NULL, the list is in RAM */
if(file) {
lcd_clear_display();
lcd_puts(0,0,"Loading...");
status_draw();
lcd_update();
playlist.in_ram = false;
} else {
/* Assign a dummy filename */
file = "";
playlist.in_ram = true;
}
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);
/* add track indices to playlist data structure */
add_indices_to_playlist();
if(global_settings.playlist_shuffle) {
if(!playlist.in_ram) {
lcd_puts(0,0,"Shuffling...");
status_draw();
lcd_update();
randomise_playlist( random_seed );
}
else {
int i;
/* store the seek position before the shuffle */
int seek_pos = playlist.indices[start_index];
/* now shuffle around the indices */
randomise_playlist( random_seed );
/* Because the playlists in RAM is always dir-based playlists,
we want the selected file played first so we scan the list
for that file to be able to play that one first. */
for(i=0; i< playlist.amount; i++) {
if(seek_pos == playlist.indices[i]) {
/* here's the start song! yiepee! */
playlist.index = i;
break; /* now stop searching */
}
}
/* 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(!playlist.in_ram) {
lcd_puts(0,0,"Playing... ");
status_draw();
lcd_update();
}
/* also make the first song get playing */
mpeg_play(start_offset);
}
/*
* remove any filename and indices associated with the playlist
*/
void empty_playlist(void)
{
playlist.filename[0] = '\0';
playlist.index = 0;
playlist.amount = 0;
}
/*
* calculate track offsets within a playlist file
*/
void add_indices_to_playlist(void)
{
int nread;
int fd = -1;
int i = 0;
int store_index = 0;
int count = 0;
int next_tick = current_tick + HZ;
unsigned char *p = playlist_buffer;
char line[16];
if(!playlist.in_ram) {
fd = open(playlist.filename, O_RDONLY);
if(-1 == fd)
return; /* failure */
}
store_index = 1;
while(1)
{
if(playlist.in_ram) {
nread = playlist_end_pos;
} else {
nread = read(fd, playlist_buffer, PLAYLIST_BUFFER_SIZE);
/* Terminate on EOF */
if(nread <= 0)
break;
}
p = playlist_buffer;
for(count=0; count < nread; count++,p++) {
/* Are we on a new line? */
if((*p == '\n') || (*p == '\r'))
{
store_index = 1;
}
else if((*p == '#') && store_index)
{
/* If the first character on a new line is a hash
sign, we treat it as a comment. So called winamp
style playlist. */
store_index = 0;
}
else if(store_index)
{
/* Store a new entry */
playlist.indices[ playlist.amount ] = i+count;
playlist.amount++;
if ( playlist.amount >= MAX_PLAYLIST_SIZE ) {
if(!playlist.in_ram)
close(fd);
lcd_clear_display();
lcd_puts(0,0,"Playlist");
lcd_puts(0,1,"buffer full");
lcd_update();
sleep(HZ*2);
lcd_clear_display();
return;
}
store_index = 0;
/* Update the screen if it takes very long */
if(!playlist.in_ram) {
if ( current_tick >= next_tick ) {
next_tick = current_tick + HZ;
snprintf(line, sizeof line, "%d files",
playlist.amount);
lcd_puts(0,1,line);
status_draw();
lcd_update();
}
}
}
}
i+= count;
if(playlist.in_ram)
break;
}
if(!playlist.in_ram) {
snprintf(line, sizeof line, "%d files", playlist.amount);
lcd_puts(0,1,line);
status_draw();
lcd_update();
close(fd);
}
}
/*
* randomly rearrange the array of indices for the playlist
*/
void randomise_playlist( unsigned int seed )
{
int count;
int candidate;
int store;
/* seed with the given seed */
srand( seed );
/* 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);
/* now swap the values at the 'count' and 'candidate' positions */
store = playlist.indices[candidate];
playlist.indices[candidate] = playlist.indices[count];
playlist.indices[count] = store;
}
}
static int compare(const void* p1, const void* p2)
{
int* e1 = (int*) p1;
int* e2 = (int*) p2;
return *e1 - *e2;
}
/*
* sort the array of indices for the playlist
*/
void sort_playlist(void)
{
if (playlist.amount > 0)
{
qsort(&playlist.indices, playlist.amount, sizeof(playlist.indices[0]), compare);
}
}
/* -----------------------------------------------------------------
* local variables:
* eval: (load-file "../firmware/rockbox-mode.el")
* end:
*/