|
|
|
|
/*
|
|
|
|
|
* $Id$
|
|
|
|
|
*
|
|
|
|
|
* This file is part of WorkMan, the civilized CD player library
|
|
|
|
|
* (c) 1991-1997 by Steven Grimm (original author)
|
|
|
|
|
* (c) by Dirk F<EFBFBD>rsterling (current 'author' = maintainer)
|
|
|
|
|
* The maintainer can be contacted by his e-mail address:
|
|
|
|
|
* milliByte@DeathsDoor.com
|
|
|
|
|
*
|
|
|
|
|
* This library is free software; you can redistribute it and/or
|
|
|
|
|
* modify it under the terms of the GNU Library General Public
|
|
|
|
|
* License as published by the Free Software Foundation; either
|
|
|
|
|
* version 2 of the License, or (at your option) any later version.
|
|
|
|
|
*
|
|
|
|
|
* This library is distributed in the hope that it will be useful,
|
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
|
|
* Library General Public License for more details.
|
|
|
|
|
*
|
|
|
|
|
* You should have received a copy of the GNU Library General Public
|
|
|
|
|
* License along with this library; if not, write to the Free
|
|
|
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
|
|
|
*
|
|
|
|
|
*
|
|
|
|
|
* Get information about a CD.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#include <errno.h>
|
|
|
|
|
#include <stdio.h>
|
|
|
|
|
#include <stdlib.h>
|
|
|
|
|
#include <string.h>
|
|
|
|
|
#include <sys/types.h>
|
|
|
|
|
|
|
|
|
|
#include "include/wm_config.h"
|
|
|
|
|
|
|
|
|
|
#include "include/wm_struct.h"
|
|
|
|
|
#include "include/wm_cdrom.h"
|
|
|
|
|
#include "include/wm_cdinfo.h"
|
|
|
|
|
#include "include/wm_database.h"
|
|
|
|
|
#include "include/wm_helpers.h"
|
|
|
|
|
|
|
|
|
|
struct wm_play *playlist = NULL;
|
|
|
|
|
struct wm_cdinfo thiscd, *cd = &thiscd;
|
|
|
|
|
|
|
|
|
|
int cur_track = -1; /* Current track number, starting at 1 */
|
|
|
|
|
int cur_index = 0; /* Current index mark */
|
|
|
|
|
int cur_lasttrack = 999; /* Last track to play in current chunk */
|
|
|
|
|
int cur_firsttrack = 0; /* First track of current chunk */
|
|
|
|
|
int cur_pos_abs; /* Current absolute position in seconds */
|
|
|
|
|
int cur_frame; /* Current frame number */
|
|
|
|
|
int cur_pos_rel; /* Current track-relative position in seconds */
|
|
|
|
|
int cur_tracklen; /* Length in seconds of current track */
|
|
|
|
|
int cur_cdlen; /* Length in seconds of entire CD */
|
|
|
|
|
int cur_ntracks; /* Number of tracks on CD (= tracks + sections) */
|
|
|
|
|
int cur_nsections; /* Number of sections currently defined */
|
|
|
|
|
|
|
|
|
|
int cur_listno; /* Current index into the play list, if playing */
|
|
|
|
|
char * cur_artist; /* Name of current CD's artist */
|
|
|
|
|
char * cur_cdname; /* Album name */
|
|
|
|
|
char * cur_trackname; /* Take a guess */
|
|
|
|
|
char cur_contd; /* Continued flag */
|
|
|
|
|
char cur_avoid; /* Avoid flag */
|
|
|
|
|
|
|
|
|
|
int exit_on_eject = 0;
|
|
|
|
|
|
|
|
|
|
int cur_stopmode = -1;
|
|
|
|
|
int info_modified;
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* insert_trackinfo()
|
|
|
|
|
*
|
|
|
|
|
* Add a new track to the CD info structure. Pass the position of the new
|
|
|
|
|
* entry in the track list -- 0 will make this the first track, 1 the second,
|
|
|
|
|
* etc. The new entry will be zeroed out.
|
|
|
|
|
*/
|
|
|
|
|
static void
|
|
|
|
|
insert_trackinfo(int num)
|
|
|
|
|
{
|
|
|
|
|
struct wm_trackinfo *newtrk;
|
|
|
|
|
|
|
|
|
|
/* Easy case: the list is empty */
|
|
|
|
|
if (cd->trk == NULL) {
|
|
|
|
|
if ((cd->trk = (struct wm_trackinfo *) calloc(1,
|
|
|
|
|
sizeof(*newtrk))) == NULL)
|
|
|
|
|
{
|
|
|
|
|
perror("insert_trackinfo");
|
|
|
|
|
exit(1);
|
|
|
|
|
} else {
|
|
|
|
|
return;
|
|
|
|
|
} /* if() else */
|
|
|
|
|
} /* if() */
|
|
|
|
|
/* Stick the new entry in cd->trk[]. */
|
|
|
|
|
if ((newtrk = (struct wm_trackinfo *) malloc(sizeof(*newtrk) *
|
|
|
|
|
(cur_ntracks + 1))) == NULL)
|
|
|
|
|
{
|
|
|
|
|
perror("insert_trackinfo");
|
|
|
|
|
exit(1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (num)
|
|
|
|
|
memcpy(newtrk, cd->trk, sizeof(*newtrk) * num);
|
|
|
|
|
memset(&newtrk[num], 0, sizeof(*newtrk));
|
|
|
|
|
if (num < cur_ntracks)
|
|
|
|
|
memcpy(&newtrk[num + 1], &cd->trk[num], sizeof(*newtrk) *
|
|
|
|
|
(cur_ntracks - num));
|
|
|
|
|
|
|
|
|
|
free(cd->trk);
|
|
|
|
|
cd->trk = newtrk;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* split_trackinfo()
|
|
|
|
|
*
|
|
|
|
|
* Split a track in two at a particular position (absolute, in frames). All
|
|
|
|
|
* internal data structures and variables will be adjusted to the new
|
|
|
|
|
* numbering scheme. Pass in the track number (>=1) to split, which is also
|
|
|
|
|
* the index into cd->trk[] of the new entry.
|
|
|
|
|
*
|
|
|
|
|
* If pos is within 1 second of the start of another track, the split fails.
|
|
|
|
|
*
|
|
|
|
|
* Returns 1 on success, 0 if the track couldn't be inserted.
|
|
|
|
|
*
|
|
|
|
|
* Note: updating user interface elements is up to the caller.
|
|
|
|
|
*/
|
|
|
|
|
int
|
|
|
|
|
split_trackinfo( int pos )
|
|
|
|
|
{
|
|
|
|
|
int i, l, num;
|
|
|
|
|
|
|
|
|
|
if (pos < cd->trk[0].start)
|
|
|
|
|
return (0);
|
|
|
|
|
|
|
|
|
|
/* First find the appropriate track. */
|
|
|
|
|
for (num = 0; num < cur_ntracks; num++)
|
|
|
|
|
if (cd->trk[num].start - 75 < pos &&
|
|
|
|
|
cd->trk[num].start + 75 > pos)
|
|
|
|
|
return (0);
|
|
|
|
|
else if (cd->trk[num].start > pos)
|
|
|
|
|
break;
|
|
|
|
|
if (num == 0)
|
|
|
|
|
return (0);
|
|
|
|
|
|
|
|
|
|
/* Insert the new entry into the track array. */
|
|
|
|
|
insert_trackinfo(num);
|
|
|
|
|
|
|
|
|
|
/* Update the easy variables. */
|
|
|
|
|
if (cur_track > num)
|
|
|
|
|
cur_track++;
|
|
|
|
|
if (cur_firsttrack > num)
|
|
|
|
|
cur_firsttrack++;
|
|
|
|
|
if (cur_lasttrack > num)
|
|
|
|
|
cur_lasttrack++;
|
|
|
|
|
|
|
|
|
|
/* Update the user-defined playlists. */
|
|
|
|
|
if (cd->lists != NULL)
|
|
|
|
|
for (l = 0; cd->lists[l].name != NULL; l++)
|
|
|
|
|
if (cd->lists[l].list != NULL)
|
|
|
|
|
for (i = 0; cd->lists[l].list[i]; i++)
|
|
|
|
|
if (cd->lists[l].list[i] > num)
|
|
|
|
|
cd->lists[l].list[i]++;
|
|
|
|
|
|
|
|
|
|
/* Update the internal playlist. */
|
|
|
|
|
if (playlist != NULL)
|
|
|
|
|
for (i = 0; playlist[i].start; i++)
|
|
|
|
|
{
|
|
|
|
|
if (playlist[i].start > num)
|
|
|
|
|
playlist[i].start++;
|
|
|
|
|
if (playlist[i].end > num)
|
|
|
|
|
playlist[i].end++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Now adjust the information in cd->trk[]. */
|
|
|
|
|
cd->trk[num].start = pos;
|
|
|
|
|
if (num == cur_ntracks)
|
|
|
|
|
cd->trk[num].length = cur_cdlen - pos / 75;
|
|
|
|
|
else
|
|
|
|
|
cd->trk[num].length = (cd->trk[num + 1].start - pos) / 75;
|
|
|
|
|
cd->trk[num - 1].length -= cd->trk[num].length;
|
|
|
|
|
if (cur_track == num)
|
|
|
|
|
cur_tracklen -= cd->trk[num].length;
|
|
|
|
|
cd->trk[num].track = cd->trk[num - 1].track;
|
|
|
|
|
cd->trk[num].data = cd->trk[num - 1].data;
|
|
|
|
|
cd->trk[num].contd = 1;
|
|
|
|
|
cd->trk[num].volume = cd->trk[num - 1].volume;
|
|
|
|
|
|
|
|
|
|
if (cd->trk[num - 1].section == 0)
|
|
|
|
|
cd->trk[num - 1].section = 1;
|
|
|
|
|
cd->trk[num].section = cd->trk[num - 1].section + 1;
|
|
|
|
|
|
|
|
|
|
cur_ntracks++;
|
|
|
|
|
cur_nsections++;
|
|
|
|
|
|
|
|
|
|
for (i = num + 1; i < cur_ntracks; i++)
|
|
|
|
|
if (cd->trk[i].track == cd->trk[num].track)
|
|
|
|
|
cd->trk[i].section++;
|
|
|
|
|
|
|
|
|
|
return (1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* remove_trackinfo()
|
|
|
|
|
*
|
|
|
|
|
* Remove a track's internal data. This is similar to split_trackinfo()
|
|
|
|
|
* above, but simpler. A track's initial section can't be removed. Track
|
|
|
|
|
* numbers start at 0.
|
|
|
|
|
*
|
|
|
|
|
* Returns 1 on success, 0 on failure.
|
|
|
|
|
*/
|
|
|
|
|
int
|
|
|
|
|
remove_trackinfo( int num )
|
|
|
|
|
{
|
|
|
|
|
int i, l;
|
|
|
|
|
|
|
|
|
|
if (num < 1 || num >= cur_ntracks || cd->trk[num].section < 2)
|
|
|
|
|
return (0);
|
|
|
|
|
|
|
|
|
|
cd->trk[num - 1].length += cd->trk[num].length;
|
|
|
|
|
|
|
|
|
|
for (i = num; i < cur_ntracks - 1; i++)
|
|
|
|
|
memcpy(&cd->trk[i], &cd->trk[i + 1], sizeof(cd->trk[0]));
|
|
|
|
|
|
|
|
|
|
if (cur_track > num)
|
|
|
|
|
cur_track--;
|
|
|
|
|
if (cur_firsttrack > num)
|
|
|
|
|
cur_firsttrack--;
|
|
|
|
|
if (cur_lasttrack > num)
|
|
|
|
|
cur_lasttrack--;
|
|
|
|
|
|
|
|
|
|
/* Update the user-defined playlists. */
|
|
|
|
|
if (cd->lists != NULL)
|
|
|
|
|
for (l = 0; cd->lists[l].name != NULL; l++)
|
|
|
|
|
if (cd->lists[l].list != NULL)
|
|
|
|
|
for (i = 0; cd->lists[l].list[i]; i++)
|
|
|
|
|
if (cd->lists[l].list[i] > num)
|
|
|
|
|
cd->lists[l].list[i]--;
|
|
|
|
|
|
|
|
|
|
/* Update the internal playlist. */
|
|
|
|
|
if (playlist != NULL)
|
|
|
|
|
for (i = 0; playlist[i].start; i++)
|
|
|
|
|
{
|
|
|
|
|
if (playlist[i].start > num)
|
|
|
|
|
playlist[i].start--;
|
|
|
|
|
if (playlist[i].end > num)
|
|
|
|
|
playlist[i].end--;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
cur_ntracks--;
|
|
|
|
|
cur_nsections--;
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Update the section numbers for this track. If this is the only
|
|
|
|
|
* user-created section in a track, get rid of the section number
|
|
|
|
|
* in the track's entry.
|
|
|
|
|
*/
|
|
|
|
|
if (num == cur_ntracks || cd->trk[num - 1].track != cd->trk[num].track)
|
|
|
|
|
{
|
|
|
|
|
if (cd->trk[num - 1].section == 1)
|
|
|
|
|
cd->trk[num - 1].section = 0;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
for (i = num; i < cur_ntracks; i++)
|
|
|
|
|
if (cd->trk[i].track == cd->trk[num - 1].track)
|
|
|
|
|
cd->trk[i].section--;
|
|
|
|
|
|
|
|
|
|
return (1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* listentry()
|
|
|
|
|
*
|
|
|
|
|
* Return a scrolling list entry.
|
|
|
|
|
*/
|
|
|
|
|
char *
|
|
|
|
|
listentry( int num )
|
|
|
|
|
{
|
|
|
|
|
static char buf[600];
|
|
|
|
|
const char *name;
|
|
|
|
|
char tracknum[20];
|
|
|
|
|
int digits;
|
|
|
|
|
int sdigits;
|
|
|
|
|
|
|
|
|
|
if (num >= 0 && num < cur_ntracks)
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
if (big_spaces)
|
|
|
|
|
{
|
|
|
|
|
digits = 2;
|
|
|
|
|
sdigits = cur_nsections < 9 ? -1 : -2;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
digits = cd->trk[num].track < 10 ? 3 : 2;
|
|
|
|
|
sdigits = cur_nsections < 9 ? -1 : -3;
|
|
|
|
|
}
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
digits = 2;
|
|
|
|
|
sdigits = cur_nsections < 9 ? -1 : -2;
|
|
|
|
|
|
|
|
|
|
name = cd->trk[num].songname ? cd->trk[num].songname : "";
|
|
|
|
|
|
|
|
|
|
if (cur_nsections)
|
|
|
|
|
{
|
|
|
|
|
if (cd->trk[num].section > 9)
|
|
|
|
|
{
|
|
|
|
|
sprintf(tracknum, "%*d.%d", digits,
|
|
|
|
|
cd->trk[num].track,
|
|
|
|
|
cd->trk[num].section);
|
|
|
|
|
} else {
|
|
|
|
|
if (cd->trk[num].section)
|
|
|
|
|
{
|
|
|
|
|
sprintf(tracknum, "%*d.%*d", digits,
|
|
|
|
|
cd->trk[num].track, sdigits,
|
|
|
|
|
cd->trk[num].section);
|
|
|
|
|
} else {
|
|
|
|
|
sprintf(tracknum, "%*d%*s", digits,
|
|
|
|
|
cd->trk[num].track,
|
|
|
|
|
2 - sdigits, " ");
|
|
|
|
|
/* 2 - sdigits - big_spaces, " ");*/
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
sprintf(tracknum, "%*d", digits, cd->trk[num].track);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (cd->trk[num].data)
|
|
|
|
|
{
|
|
|
|
|
sprintf(buf, "%s) %3dMB %s", tracknum,
|
|
|
|
|
cd->trk[num].length / 1024, name);
|
|
|
|
|
} else {
|
|
|
|
|
sprintf(buf, "%s) %02d:%02d %s", tracknum,
|
|
|
|
|
cd->trk[num].length / 60,
|
|
|
|
|
cd->trk[num].length % 60, name);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return (buf);
|
|
|
|
|
} else {
|
|
|
|
|
return (NULL);
|
|
|
|
|
}
|
|
|
|
|
} /* listentry() */
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* trackname()
|
|
|
|
|
*
|
|
|
|
|
* Return a track's name.
|
|
|
|
|
*/
|
|
|
|
|
const char *
|
|
|
|
|
trackname( int num )
|
|
|
|
|
{
|
|
|
|
|
if (num >= 0 && num < cur_ntracks)
|
|
|
|
|
{
|
|
|
|
|
if (cd->trk[num].songname)
|
|
|
|
|
{
|
|
|
|
|
return (cd->trk[num].songname);
|
|
|
|
|
} else {
|
|
|
|
|
return ("");
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
return (NULL);
|
|
|
|
|
}
|
|
|
|
|
} /* trackname() */
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* tracklen()
|
|
|
|
|
*
|
|
|
|
|
* Return a track's length in seconds.
|
|
|
|
|
*/
|
|
|
|
|
int
|
|
|
|
|
tracklen( int num )
|
|
|
|
|
{
|
|
|
|
|
if (cd != NULL && num >= 0 && num < cur_ntracks)
|
|
|
|
|
return (cd->trk[num].length);
|
|
|
|
|
else
|
|
|
|
|
return (0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* get_default_volume()
|
|
|
|
|
*
|
|
|
|
|
* Return the default volume (0-32, 0=none) for the CD or a track.
|
|
|
|
|
*/
|
|
|
|
|
int
|
|
|
|
|
get_default_volume( int track )
|
|
|
|
|
{
|
|
|
|
|
if (! track)
|
|
|
|
|
return (cd->volume);
|
|
|
|
|
else if (track <= cur_ntracks)
|
|
|
|
|
return (cd->trk[track - 1].volume);
|
|
|
|
|
else
|
|
|
|
|
return (0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* get_contd()
|
|
|
|
|
*
|
|
|
|
|
* Return the contd value for a track.
|
|
|
|
|
*/
|
|
|
|
|
int
|
|
|
|
|
get_contd( int num )
|
|
|
|
|
{
|
|
|
|
|
if (num >= 0 && num < cur_ntracks)
|
|
|
|
|
return (cd->trk[num].contd);
|
|
|
|
|
else
|
|
|
|
|
return (0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* get_avoid()
|
|
|
|
|
*
|
|
|
|
|
* Return the avoid value for a track.
|
|
|
|
|
*/
|
|
|
|
|
int
|
|
|
|
|
get_avoid( int num )
|
|
|
|
|
{
|
|
|
|
|
if (num >= 0 && num < cur_ntracks)
|
|
|
|
|
return (cd->trk[num].avoid);
|
|
|
|
|
else
|
|
|
|
|
return (0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* get_autoplay()
|
|
|
|
|
*
|
|
|
|
|
* Is autoplay set on this disc?
|
|
|
|
|
*/
|
|
|
|
|
int
|
|
|
|
|
get_autoplay( void )
|
|
|
|
|
{
|
|
|
|
|
return ( cd->autoplay );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* get_playmode()
|
|
|
|
|
*
|
|
|
|
|
* Return the default playmode for the CD.
|
|
|
|
|
*/
|
|
|
|
|
int
|
|
|
|
|
get_playmode( void )
|
|
|
|
|
{
|
|
|
|
|
return ( cd->playmode );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* get_runtime()
|
|
|
|
|
*
|
|
|
|
|
* Return the total running time for the current playlist in seconds.
|
|
|
|
|
*/
|
|
|
|
|
int
|
|
|
|
|
get_runtime( void )
|
|
|
|
|
{
|
|
|
|
|
int i;
|
|
|
|
|
|
|
|
|
|
if (playlist == NULL || playlist[0].start == 0 || cur_firsttrack == -1)
|
|
|
|
|
return (cd == NULL ? 0 : cd->length);
|
|
|
|
|
|
|
|
|
|
for (i = 0; playlist[i].start; i++)
|
|
|
|
|
;
|
|
|
|
|
|
|
|
|
|
return (playlist[i].starttime);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* default_volume()
|
|
|
|
|
*
|
|
|
|
|
* Set the default volume for the CD or a track.
|
|
|
|
|
*/
|
|
|
|
|
void
|
|
|
|
|
default_volume( int track, int vol )
|
|
|
|
|
{
|
|
|
|
|
if (track == 0)
|
|
|
|
|
cd->volume = vol;
|
|
|
|
|
else if (track <= cur_ntracks)
|
|
|
|
|
cd->trk[track - 1].volume = vol;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Play the next thing on the playlist, if any.
|
|
|
|
|
*/
|
|
|
|
|
void
|
|
|
|
|
play_next_entry( int forward )
|
|
|
|
|
{
|
|
|
|
|
if (cd == NULL)
|
|
|
|
|
return;
|
|
|
|
|
if (playlist != NULL && playlist[cur_listno].start)
|
|
|
|
|
{
|
|
|
|
|
wm_cd_play(playlist[cur_listno].start, 0,
|
|
|
|
|
playlist[cur_listno].end);
|
|
|
|
|
cur_listno++;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
wm_cd_stop();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Play the next track, following playlists as necessary.
|
|
|
|
|
*/
|
|
|
|
|
void
|
|
|
|
|
play_next_track( int forward )
|
|
|
|
|
{
|
|
|
|
|
if (cd == NULL)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
/* Is the current playlist entry done? Move on, if so. */
|
|
|
|
|
if (playlist == NULL || cur_track + 1 == playlist[cur_listno - 1].end)
|
|
|
|
|
play_next_entry( forward );
|
|
|
|
|
else
|
|
|
|
|
wm_cd_play(cur_track + 1, 0, playlist[cur_listno - 1].end);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Play the previous track, hopping around the playlist as necessary.
|
|
|
|
|
*/
|
|
|
|
|
void
|
|
|
|
|
play_prev_track( int forward )
|
|
|
|
|
{
|
|
|
|
|
if (cd == NULL)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
if (playlist == NULL)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
/* If we're in the middle of this playlist entry, go back one track */
|
|
|
|
|
if (cur_track > playlist[cur_listno - 1].start)
|
|
|
|
|
wm_cd_play(cur_track - 1, 0, playlist[cur_listno - 1].end);
|
|
|
|
|
else
|
|
|
|
|
if (cur_listno > 1)
|
|
|
|
|
{
|
|
|
|
|
cur_listno--;
|
|
|
|
|
wm_cd_play(playlist[cur_listno - 1].end - 1, 0,
|
|
|
|
|
playlist[cur_listno - 1].end);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
wm_cd_play(playlist[0].start, 0, playlist[0].end);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* stash_cdinfo(artist, cdname)
|
|
|
|
|
*/
|
|
|
|
|
void
|
|
|
|
|
stash_cdinfo(char *artist, char *cdname, int autoplay, int playmode )
|
|
|
|
|
{
|
|
|
|
|
if (cd != NULL)
|
|
|
|
|
{
|
|
|
|
|
if (strcmp(cd->artist, artist))
|
|
|
|
|
info_modified = 1;
|
|
|
|
|
strncpy(cd->artist, artist,sizeof(cd->artist)-1);
|
|
|
|
|
cd->artist[sizeof(cd->artist)-1]='\0';
|
|
|
|
|
|
|
|
|
|
if (strcmp(cd->cdname, cdname))
|
|
|
|
|
info_modified = 1;
|
|
|
|
|
strncpy(cd->cdname, cdname,sizeof(cd->cdname)-1);
|
|
|
|
|
cd->cdname[sizeof(cd->cdname)-1]='\0';
|
|
|
|
|
|
|
|
|
|
if (!!cd->autoplay != !!autoplay)
|
|
|
|
|
info_modified = 1;
|
|
|
|
|
cd->autoplay = autoplay;
|
|
|
|
|
|
|
|
|
|
if (!!cd->playmode != !!playmode)
|
|
|
|
|
info_modified = 1;
|
|
|
|
|
cd->playmode = playmode;
|
|
|
|
|
}
|
|
|
|
|
} /* stash_cdinfo() */
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* wipe_cdinfo()
|
|
|
|
|
*
|
|
|
|
|
* Clear out all a CD's soft information (presumably in preparation for
|
|
|
|
|
* reloading from the database.)
|
|
|
|
|
*/
|
|
|
|
|
void
|
|
|
|
|
wipe_cdinfo( void )
|
|
|
|
|
{
|
|
|
|
|
struct wm_playlist *l;
|
|
|
|
|
int i;
|
|
|
|
|
|
|
|
|
|
if (cd != NULL)
|
|
|
|
|
{
|
|
|
|
|
cd->artist[0] = cd->cdname[0] = '\0';
|
|
|
|
|
cd->autoplay = cd->playmode = cd->volume = 0;
|
|
|
|
|
cd->whichdb = NULL;
|
|
|
|
|
freeup(&cd->otherrc);
|
|
|
|
|
freeup(&cd->otherdb);
|
|
|
|
|
|
|
|
|
|
if (thiscd.lists != NULL)
|
|
|
|
|
{
|
|
|
|
|
for (l = thiscd.lists; l->name != NULL; l++)
|
|
|
|
|
{
|
|
|
|
|
free(l->name);
|
|
|
|
|
free(l->list);
|
|
|
|
|
}
|
|
|
|
|
free(thiscd.lists);
|
|
|
|
|
thiscd.lists = NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < cur_ntracks; i++)
|
|
|
|
|
{
|
|
|
|
|
freeup(&cd->trk[i].songname);
|
|
|
|
|
freeup(&cd->trk[i].otherrc);
|
|
|
|
|
freeup(&cd->trk[i].otherdb);
|
|
|
|
|
cd->trk[i].avoid = cd->trk[i].contd = 0;
|
|
|
|
|
cd->trk[i].volume = 0;
|
|
|
|
|
if (cd->trk[i].section > 1)
|
|
|
|
|
remove_trackinfo(i--);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* stash_trkinfo(track, songname, contd, avoid)
|
|
|
|
|
*
|
|
|
|
|
* Update information about a track on the current CD.
|
|
|
|
|
*/
|
|
|
|
|
void
|
|
|
|
|
stash_trkinfo( int track, char *songname, int contd, int avoid )
|
|
|
|
|
{
|
|
|
|
|
if (cd != NULL)
|
|
|
|
|
{
|
|
|
|
|
track--;
|
|
|
|
|
if (!!cd->trk[track].contd != !!contd)
|
|
|
|
|
info_modified = 1;
|
|
|
|
|
cd->trk[track].contd = track ? contd : 0;
|
|
|
|
|
|
|
|
|
|
if (!!cd->trk[track].avoid != !!avoid)
|
|
|
|
|
info_modified = 1;
|
|
|
|
|
cd->trk[track].avoid = avoid;
|
|
|
|
|
|
|
|
|
|
if ((cd->trk[track].songname == NULL && songname[0]) ||
|
|
|
|
|
(cd->trk[track].songname != NULL &&
|
|
|
|
|
strcmp(cd->trk[track].songname, songname)))
|
|
|
|
|
{
|
|
|
|
|
info_modified = 1;
|
|
|
|
|
wm_strmcpy(&cd->trk[track].songname, songname);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* new_playlist()
|
|
|
|
|
*
|
|
|
|
|
* Add a playlist to a CD.
|
|
|
|
|
*/
|
|
|
|
|
struct wm_playlist *
|
|
|
|
|
new_playlist(struct wm_cdinfo* cdinfo, char* listname)
|
|
|
|
|
{
|
|
|
|
|
int nlists = 0;
|
|
|
|
|
struct wm_playlist *l;
|
|
|
|
|
|
|
|
|
|
if (cdinfo->lists != NULL)
|
|
|
|
|
{
|
|
|
|
|
for (nlists = 0; cdinfo->lists[nlists].name != NULL; nlists++)
|
|
|
|
|
;
|
|
|
|
|
l = (struct wm_playlist *)realloc(cdinfo->lists, (nlists + 2) *
|
|
|
|
|
sizeof (struct wm_playlist));
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
l = (struct wm_playlist *)malloc(2 * sizeof (struct wm_playlist));
|
|
|
|
|
|
|
|
|
|
if (l == NULL)
|
|
|
|
|
return (NULL);
|
|
|
|
|
|
|
|
|
|
l[nlists + 1].name = NULL;
|
|
|
|
|
l[nlists].name = NULL; /* so wm_strmcpy doesn't free() it */
|
|
|
|
|
wm_strmcpy(&l[nlists].name, listname);
|
|
|
|
|
l[nlists].list = NULL;
|
|
|
|
|
cdinfo->lists = l;
|
|
|
|
|
|
|
|
|
|
return (&l[nlists]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* make_playlist()
|
|
|
|
|
*
|
|
|
|
|
* Construct a playlist for the current CD. If we're in shuffle mode, play
|
|
|
|
|
* each non-avoided track once, keeping continued tracks in the right order.
|
|
|
|
|
*
|
|
|
|
|
* If playmode is 2, use playlist number (playmode-2). XXX should do
|
|
|
|
|
* bounds checking on this, probably.
|
|
|
|
|
*
|
|
|
|
|
* If consecutive tracks are being played, only make one playlist entry for
|
|
|
|
|
* them, so the CD player won't pause between tracks while we wake up.
|
|
|
|
|
*/
|
|
|
|
|
void
|
|
|
|
|
make_playlist( int playmode, int starttrack )
|
|
|
|
|
{
|
|
|
|
|
int i, avoiding = 1, entry = 0, count, track,
|
|
|
|
|
*thislist;
|
|
|
|
|
|
|
|
|
|
cur_listno = 0;
|
|
|
|
|
if (playlist != NULL)
|
|
|
|
|
free(playlist);
|
|
|
|
|
playlist = malloc(sizeof (*playlist) * (cur_ntracks + 1));
|
|
|
|
|
if (playlist == NULL)
|
|
|
|
|
{
|
|
|
|
|
perror("playlist");
|
|
|
|
|
exit(1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* If this is a data-only CD, we can't play it. */
|
|
|
|
|
if ((starttrack && cd->trk[starttrack - 1].data) ||
|
|
|
|
|
(cur_ntracks == 1 && cd->trk[0].data))
|
|
|
|
|
{
|
|
|
|
|
playlist[0].start = 0;
|
|
|
|
|
playlist[0].end = 0;
|
|
|
|
|
playlist[1].start = 0;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (playmode == 1)
|
|
|
|
|
{
|
|
|
|
|
char *done = malloc(cur_ntracks);
|
|
|
|
|
|
|
|
|
|
if (done == NULL)
|
|
|
|
|
{
|
|
|
|
|
perror("randomizer");
|
|
|
|
|
exit(1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
count = cur_ntracks;
|
|
|
|
|
if (starttrack && cd->trk[starttrack - 1].avoid)
|
|
|
|
|
count++;
|
|
|
|
|
for (i = 0; i < cur_ntracks; i++)
|
|
|
|
|
if (cd->trk[i].contd || cd->trk[i].avoid ||
|
|
|
|
|
cd->trk[i].data)
|
|
|
|
|
{
|
|
|
|
|
done[i] = 1;
|
|
|
|
|
count--;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
done[i] = 0;
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < count; i++)
|
|
|
|
|
{
|
|
|
|
|
int end; /* for readability */
|
|
|
|
|
if (starttrack)
|
|
|
|
|
{
|
|
|
|
|
track = starttrack - 1;
|
|
|
|
|
starttrack = 0;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
while (done[track = rand() % cur_ntracks])
|
|
|
|
|
;
|
|
|
|
|
|
|
|
|
|
playlist[i].start = track + 1;
|
|
|
|
|
|
|
|
|
|
/* play all subsequent continuation tracks too */
|
|
|
|
|
for (end = track + 1; end < cur_ntracks + 1; end++)
|
|
|
|
|
if (! cd->trk[end].contd ||
|
|
|
|
|
cd->trk[end].avoid ||
|
|
|
|
|
cd->trk[end].data)
|
|
|
|
|
break;
|
|
|
|
|
playlist[i].end = end + 1;
|
|
|
|
|
|
|
|
|
|
done[track]++;
|
|
|
|
|
}
|
|
|
|
|
playlist[i].start = 0;
|
|
|
|
|
|
|
|
|
|
free(done);
|
|
|
|
|
}
|
|
|
|
|
else if (playmode >= 2 && cd->lists && cd->lists[playmode - 2].name)
|
|
|
|
|
{
|
|
|
|
|
count = 2; /* one terminating entry, and one for start */
|
|
|
|
|
thislist = cd->lists[playmode - 2].list;
|
|
|
|
|
|
|
|
|
|
for (i = 0; thislist[i]; i++)
|
|
|
|
|
if (thislist[i + 1] != thislist[i] + 1)
|
|
|
|
|
count++;
|
|
|
|
|
|
|
|
|
|
if (playlist != NULL)
|
|
|
|
|
free(playlist);
|
|
|
|
|
playlist = malloc(sizeof (*playlist) * count);
|
|
|
|
|
if (playlist == NULL)
|
|
|
|
|
{
|
|
|
|
|
perror("playlist");
|
|
|
|
|
exit(1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
count = 0;
|
|
|
|
|
if (starttrack)
|
|
|
|
|
{
|
|
|
|
|
playlist[0].start = starttrack;
|
|
|
|
|
for (track = 0; thislist[track]; track++)
|
|
|
|
|
if (starttrack == thislist[track])
|
|
|
|
|
break;
|
|
|
|
|
if (! thislist[track])
|
|
|
|
|
{
|
|
|
|
|
playlist[0].end = starttrack + 1;
|
|
|
|
|
playlist[1].start = thislist[0];
|
|
|
|
|
count = 1;
|
|
|
|
|
track = 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
playlist[0].start = thislist[0];
|
|
|
|
|
track = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (i = track; thislist[i]; i++)
|
|
|
|
|
if (thislist[i + 1] != thislist[i] + 1)
|
|
|
|
|
{
|
|
|
|
|
playlist[count].end = thislist[i] + 1;
|
|
|
|
|
count++;
|
|
|
|
|
playlist[count].start = thislist[i + 1];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
for (i = starttrack ? starttrack - 1 : 0; i < cur_ntracks; i++)
|
|
|
|
|
if (avoiding && ! (cd->trk[i].avoid || cd->trk[i].data))
|
|
|
|
|
{
|
|
|
|
|
playlist[entry].start = i + 1;
|
|
|
|
|
avoiding = 0;
|
|
|
|
|
}
|
|
|
|
|
else if (! avoiding && (cd->trk[i].avoid ||
|
|
|
|
|
cd->trk[i].data))
|
|
|
|
|
{
|
|
|
|
|
playlist[entry].end = i + 1;
|
|
|
|
|
avoiding = 1;
|
|
|
|
|
entry++;
|
|
|
|
|
}
|
|
|
|
|
if (! avoiding)
|
|
|
|
|
playlist[entry].end = i + 1;
|
|
|
|
|
playlist[entry + !avoiding].start = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Now go through the list, whatever its source, and figure out
|
|
|
|
|
* cumulative starting times for each entry.
|
|
|
|
|
*/
|
|
|
|
|
entry = count = 0;
|
|
|
|
|
do {
|
|
|
|
|
playlist[entry].starttime = count;
|
|
|
|
|
|
|
|
|
|
if (playlist[entry].start)
|
|
|
|
|
for (i = playlist[entry].start; i <
|
|
|
|
|
playlist[entry].end; i++)
|
|
|
|
|
count += cd->trk[i - 1].length;
|
|
|
|
|
} while (playlist[entry++].start);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Find a particular track's location in the current playlist. Sets the
|
|
|
|
|
* appropriate variables (cur_listno, cur_firsttrack, cur_lasttrack).
|
|
|
|
|
*/
|
|
|
|
|
void
|
|
|
|
|
pl_find_track( int track )
|
|
|
|
|
{
|
|
|
|
|
int i;
|
|
|
|
|
|
|
|
|
|
if (playlist == NULL)
|
|
|
|
|
{
|
|
|
|
|
#ifndef NDEBUG
|
|
|
|
|
fprintf(stderr, "Null playlist! Huh?\n");
|
|
|
|
|
#endif
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (i = 0; playlist[i].start; i++)
|
|
|
|
|
if (track >= playlist[i].start && track < playlist[i].end)
|
|
|
|
|
{
|
|
|
|
|
cur_listno = i + 1;
|
|
|
|
|
cur_firsttrack = playlist[i].start;
|
|
|
|
|
cur_lasttrack = playlist[i].end - 1;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Couldn't find the track in question. Make a special entry with
|
|
|
|
|
* just that track.
|
|
|
|
|
*/
|
|
|
|
|
if (! playlist[i].start)
|
|
|
|
|
{
|
|
|
|
|
playlist = realloc(playlist, (i + 2) * sizeof(*playlist));
|
|
|
|
|
if (playlist == NULL)
|
|
|
|
|
{
|
|
|
|
|
perror("playlist realloc");
|
|
|
|
|
exit(1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
playlist[i + 1].start = playlist[i + 1].end = 0;
|
|
|
|
|
playlist[i + 1].starttime = playlist[i].starttime +
|
|
|
|
|
cd->trk[track - 1].length;
|
|
|
|
|
playlist[i].start = track;
|
|
|
|
|
playlist[i].end = track + 1;
|
|
|
|
|
cur_listno = i + 1;
|
|
|
|
|
cur_firsttrack = track;
|
|
|
|
|
cur_lasttrack = track;
|
|
|
|
|
}
|
|
|
|
|
}
|