|
|
|
|
/*
|
|
|
|
|
* $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
|
|
|
|
|
*
|
|
|
|
|
*
|
|
|
|
|
* Interface between most of WorkMan and the low-level CD-ROM library
|
|
|
|
|
* routines defined in plat_*.c and drv_*.c. The goal is to have no
|
|
|
|
|
* platform- or drive-dependent code here.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#include <errno.h>
|
|
|
|
|
#include <stdio.h>
|
|
|
|
|
#include <unistd.h>
|
|
|
|
|
#include <stdlib.h>
|
|
|
|
|
#include <string.h>
|
|
|
|
|
#include <sys/types.h>
|
|
|
|
|
/* #include <sys/time.h> */
|
|
|
|
|
|
|
|
|
|
#include "config.h"
|
|
|
|
|
|
|
|
|
|
#include "include/wm_config.h"
|
|
|
|
|
#include "include/wm_struct.h"
|
|
|
|
|
#include "include/wm_cddb.h"
|
|
|
|
|
#include "include/wm_cdrom.h"
|
|
|
|
|
#include "include/wm_database.h"
|
|
|
|
|
#include "include/wm_platform.h"
|
|
|
|
|
#include "include/wm_helpers.h"
|
|
|
|
|
#include "include/wm_cdinfo.h"
|
|
|
|
|
#include "include/wm_cdtext.h"
|
|
|
|
|
|
|
|
|
|
#ifdef CAN_CLOSE
|
|
|
|
|
#include <fcntl.h>
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
/* local prototypes */
|
|
|
|
|
int read_toc(void);
|
|
|
|
|
|
|
|
|
|
#define WM_MSG_CLASS WM_MSG_CLASS_CDROM
|
|
|
|
|
|
|
|
|
|
/* extern struct wm_drive generic_proto, toshiba_proto, sony_proto; */
|
|
|
|
|
/* toshiba33_proto; <=== Somehow, this got lost */
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* The supported drive types are listed here. NULL means match anything.
|
|
|
|
|
* The first match in the list is used, and substring matches are done (so
|
|
|
|
|
* put long names before their shorter prefixes.)
|
|
|
|
|
*/
|
|
|
|
|
struct drivelist {
|
|
|
|
|
const char *ven;
|
|
|
|
|
const char *mod;
|
|
|
|
|
const char *rev;
|
|
|
|
|
struct wm_drive_proto *proto;
|
|
|
|
|
} drives[] = {
|
|
|
|
|
{ "TOSHIBA", "XM-3501", NULL, &toshiba_proto },
|
|
|
|
|
{ "TOSHIBA", "XM-3401", NULL, &toshiba_proto },
|
|
|
|
|
{ "TOSHIBA", "XM-3301", NULL, &toshiba_proto },
|
|
|
|
|
{ "SONY", "CDU-8012", NULL, &sony_proto },
|
|
|
|
|
{ "SONY", "CDU 561", NULL, &sony_proto },
|
|
|
|
|
{ "SONY", "CDU-76S", NULL, &sony_proto },
|
|
|
|
|
{ WM_STR_GENVENDOR, WM_STR_GENMODEL, WM_STR_GENREV, &generic_proto },
|
|
|
|
|
{ NULL, NULL, NULL, &generic_proto }
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Solaris 2.2 will remove the device out from under us. Getting an ENOENT
|
|
|
|
|
* is therefore sometimes not a problem.
|
|
|
|
|
*/
|
|
|
|
|
int intermittent_dev = 0;
|
|
|
|
|
|
|
|
|
|
static int wm_cd_cur_balance = 10;
|
|
|
|
|
static int wm_cur_cdmode = WM_CDM_UNKNOWN;
|
|
|
|
|
static char *wm_cd_device = NULL; /* do not use this extern */
|
|
|
|
|
|
|
|
|
|
static struct wm_drive drive = {
|
|
|
|
|
0,
|
|
|
|
|
NULL,
|
|
|
|
|
NULL,
|
|
|
|
|
NULL,
|
|
|
|
|
NULL,
|
|
|
|
|
-1,
|
|
|
|
|
-1,
|
|
|
|
|
NULL,
|
|
|
|
|
NULL,
|
|
|
|
|
NULL,
|
|
|
|
|
NULL,
|
|
|
|
|
NULL,
|
|
|
|
|
|
|
|
|
|
NULL
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
extern struct wm_cdinfo thiscd;
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Macro magic
|
|
|
|
|
*
|
|
|
|
|
*/
|
|
|
|
|
#define FREE(x); if(x) free(x); x = NULL;
|
|
|
|
|
|
|
|
|
|
#define STRDUP(old, neu); \
|
|
|
|
|
if(old) free(old); old = NULL;\
|
|
|
|
|
if(neu) old = strdup(neu);
|
|
|
|
|
|
|
|
|
|
#define CARRAY(id) ((id)-1)
|
|
|
|
|
|
|
|
|
|
#define DATATRACK 1
|
|
|
|
|
/*
|
|
|
|
|
* init the workmanlib
|
|
|
|
|
*/
|
|
|
|
|
int wm_cd_init( int cdin, const char *cd_device, const char *soundsystem,
|
|
|
|
|
const char *sounddevice, const char *ctldevice )
|
|
|
|
|
{
|
|
|
|
|
drive.cdda = (WM_CDDA == cdin);
|
|
|
|
|
#if !defined(BUILD_CDDA)
|
|
|
|
|
if(drive.cdda) {
|
|
|
|
|
wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "Libworkman library was compiled without cdda support\n");
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
wm_cd_destroy();
|
|
|
|
|
|
|
|
|
|
STRDUP(wm_cd_device, cd_device);
|
|
|
|
|
drive.cd_device = wm_cd_device;
|
|
|
|
|
STRDUP(drive.soundsystem, soundsystem);
|
|
|
|
|
STRDUP(drive.sounddevice, sounddevice);
|
|
|
|
|
STRDUP(drive.ctldevice, ctldevice);
|
|
|
|
|
|
|
|
|
|
return wm_cd_status();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int wm_cd_destroy( void )
|
|
|
|
|
{
|
|
|
|
|
free_cdtext();
|
|
|
|
|
|
|
|
|
|
if(drive.fd != -1) {
|
|
|
|
|
/* first free one old */
|
|
|
|
|
if(drive.proto && drive.proto->gen_close)
|
|
|
|
|
drive.proto->gen_close(&drive);
|
|
|
|
|
else
|
|
|
|
|
close(drive.fd);
|
|
|
|
|
}
|
|
|
|
|
drive.fd = -1;
|
|
|
|
|
FREE(wm_cd_device);
|
|
|
|
|
FREE(drive.soundsystem);
|
|
|
|
|
FREE(drive.sounddevice);
|
|
|
|
|
FREE(drive.ctldevice);
|
|
|
|
|
FREE(drive.vendor);
|
|
|
|
|
FREE(drive.model);
|
|
|
|
|
FREE(drive.revision);
|
|
|
|
|
drive.proto = NULL;
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
/*
|
|
|
|
|
* Give information about the drive we found during wmcd_open()
|
|
|
|
|
*/
|
|
|
|
|
const char *wm_drive_vendor( void )
|
|
|
|
|
{
|
|
|
|
|
return drive.vendor?drive.vendor:"";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const char *wm_drive_model( void )
|
|
|
|
|
{
|
|
|
|
|
return drive.model?drive.model:"";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const char *wm_drive_revision( void )
|
|
|
|
|
{
|
|
|
|
|
return drive.revision?drive.revision:"";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const char *wm_drive_device( void )
|
|
|
|
|
{
|
|
|
|
|
return drive.cd_device ? drive.cd_device : "";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Figure out which prototype drive structure we should be using based
|
|
|
|
|
* on the vendor, model, and revision of the current drive.
|
|
|
|
|
*/
|
|
|
|
|
int
|
|
|
|
|
find_drive_struct(const char *vendor, const char *model, const char *rev)
|
|
|
|
|
{
|
|
|
|
|
struct drivelist *d;
|
|
|
|
|
|
|
|
|
|
for (d = drives; d; d++) {
|
|
|
|
|
if(((d->ven != NULL) && strncmp(d->ven, vendor, strlen(d->ven))) ||
|
|
|
|
|
((d->mod != NULL) && strncmp(d->mod, model, strlen(d->mod))) ||
|
|
|
|
|
((d->rev != NULL) && strncmp(d->rev, rev, strlen(d->rev))))
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
if(!(d->proto))
|
|
|
|
|
goto fail;
|
|
|
|
|
|
|
|
|
|
STRDUP(drive.vendor, vendor);
|
|
|
|
|
STRDUP(drive.model, model);
|
|
|
|
|
STRDUP(drive.revision, rev);
|
|
|
|
|
|
|
|
|
|
drive.proto = d->proto;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fail:
|
|
|
|
|
return -1;
|
|
|
|
|
} /* find_drive_struct() */
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* read_toc()
|
|
|
|
|
*
|
|
|
|
|
* Read the table of contents from the CD. Return a pointer to a wm_cdinfo
|
|
|
|
|
* struct containing the relevant information (minus artist/cdname/etc.)
|
|
|
|
|
* This is a static struct. Returns NULL if there was an error.
|
|
|
|
|
*
|
|
|
|
|
* XXX allocates one trackinfo too many.
|
|
|
|
|
*/
|
|
|
|
|
int
|
|
|
|
|
read_toc( void )
|
|
|
|
|
{
|
|
|
|
|
struct wm_playlist *l;
|
|
|
|
|
int i;
|
|
|
|
|
int pos;
|
|
|
|
|
|
|
|
|
|
if(!drive.proto)
|
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
|
|
if(drive.proto && drive.proto->gen_get_trackcount &&
|
|
|
|
|
(drive.proto->gen_get_trackcount)(&drive, &thiscd.ntracks) < 0) {
|
|
|
|
|
return -1 ;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
thiscd.artist[0] = thiscd.cdname[0] = '\0';
|
|
|
|
|
thiscd.whichdb = thiscd.otherrc = thiscd.otherdb = thiscd.user = NULL;
|
|
|
|
|
thiscd.length = 0;
|
|
|
|
|
thiscd.autoplay = thiscd.playmode = thiscd.volume = 0;
|
|
|
|
|
|
|
|
|
|
/* Free up any left-over playlists. */
|
|
|
|
|
if (thiscd.lists != NULL) {
|
|
|
|
|
for (l = thiscd.lists; l->name != NULL; l++) {
|
|
|
|
|
free(l->name);
|
|
|
|
|
free(l->list);
|
|
|
|
|
}
|
|
|
|
|
free(thiscd.lists);
|
|
|
|
|
thiscd.lists = NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (thiscd.trk != NULL)
|
|
|
|
|
free(thiscd.trk);
|
|
|
|
|
|
|
|
|
|
thiscd.trk = malloc((thiscd.ntracks + 1) * sizeof(struct wm_trackinfo));
|
|
|
|
|
if (thiscd.trk == NULL) {
|
|
|
|
|
perror("malloc");
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < thiscd.ntracks; i++) {
|
|
|
|
|
if(drive.proto && drive.proto->gen_get_trackinfo &&
|
|
|
|
|
(drive.proto->gen_get_trackinfo)(&drive, i + 1, &thiscd.trk[i].data,
|
|
|
|
|
&thiscd.trk[i].start) < 0) {
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
thiscd.trk[i].avoid = thiscd.trk[i].data;
|
|
|
|
|
thiscd.trk[i].length = thiscd.trk[i].start / 75;
|
|
|
|
|
|
|
|
|
|
thiscd.trk[i].songname = thiscd.trk[i].otherrc =
|
|
|
|
|
thiscd.trk[i].otherdb = NULL;
|
|
|
|
|
thiscd.trk[i].contd = 0;
|
|
|
|
|
thiscd.trk[i].volume = 0;
|
|
|
|
|
thiscd.trk[i].track = i + 1;
|
|
|
|
|
thiscd.trk[i].section = 0;
|
|
|
|
|
wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "track %i, start frame %i\n",
|
|
|
|
|
thiscd.trk[i].track, thiscd.trk[i].start);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(drive.proto && drive.proto->gen_get_cdlen &&
|
|
|
|
|
(drive.proto->gen_get_cdlen)(&drive, &thiscd.trk[i].start) < 0) {
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
thiscd.trk[i].length = thiscd.trk[i].start / 75;
|
|
|
|
|
|
|
|
|
|
/* Now compute actual track lengths. */
|
|
|
|
|
pos = thiscd.trk[0].length;
|
|
|
|
|
for (i = 0; i < thiscd.ntracks; i++) {
|
|
|
|
|
thiscd.trk[i].length = thiscd.trk[i+1].length - pos;
|
|
|
|
|
pos = thiscd.trk[i+1].length;
|
|
|
|
|
if (thiscd.trk[i].data)
|
|
|
|
|
thiscd.trk[i].length = (thiscd.trk[i + 1].start - thiscd.trk[i].start) * 2;
|
|
|
|
|
if (thiscd.trk[i].avoid)
|
|
|
|
|
wm_strmcpy(&thiscd.trk[i].songname, "DATA TRACK");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
thiscd.length = thiscd.trk[thiscd.ntracks].length;
|
|
|
|
|
thiscd.cddbid = cddb_discid();
|
|
|
|
|
|
|
|
|
|
wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "read_toc() successful\n");
|
|
|
|
|
return 0;
|
|
|
|
|
} /* read_toc() */
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* wm_cd_status()
|
|
|
|
|
*
|
|
|
|
|
* Return values:
|
|
|
|
|
* see wm_cdrom.h
|
|
|
|
|
*
|
|
|
|
|
* Updates variables.
|
|
|
|
|
*/
|
|
|
|
|
int
|
|
|
|
|
wm_cd_status( void )
|
|
|
|
|
{
|
|
|
|
|
static int oldmode = WM_CDM_UNKNOWN;
|
|
|
|
|
int mode, err, tmp;
|
|
|
|
|
|
|
|
|
|
if(!drive.proto) {
|
|
|
|
|
oldmode = WM_CDM_UNKNOWN;
|
|
|
|
|
err = wmcd_open( &drive );
|
|
|
|
|
if (err < 0) {
|
|
|
|
|
wm_cur_cdmode = WM_CDM_UNKNOWN;
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(drive.proto && drive.proto->gen_get_drive_status &&
|
|
|
|
|
(drive.proto->gen_get_drive_status)(&drive, oldmode, &mode, &cur_frame,
|
|
|
|
|
&(thiscd.curtrack), &cur_index) < 0) {
|
|
|
|
|
perror("WM gen_get_drive_status");
|
|
|
|
|
return -1;
|
|
|
|
|
} else {
|
|
|
|
|
wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS,
|
|
|
|
|
"gen_get_drive_status returns status %s, track %i, frame %i\n",
|
|
|
|
|
gen_status(mode), thiscd.curtrack, cur_frame);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(WM_CDS_NO_DISC(oldmode) && WM_CDS_DISC_READY(mode)) {
|
|
|
|
|
/* device changed */
|
|
|
|
|
thiscd.ntracks = 0;
|
|
|
|
|
if(read_toc() || 0 == thiscd.ntracks)
|
|
|
|
|
{
|
|
|
|
|
close(drive.fd);
|
|
|
|
|
drive.fd = -1;
|
|
|
|
|
mode = WM_CDM_NO_DISC;
|
|
|
|
|
}
|
|
|
|
|
else /* refresh cdtext info */
|
|
|
|
|
get_glob_cdtext(&drive, 1);
|
|
|
|
|
|
|
|
|
|
wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "device status changed() from %s to %s\n",
|
|
|
|
|
gen_status(oldmode), gen_status(mode));
|
|
|
|
|
}
|
|
|
|
|
oldmode = mode;
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* it seems all driver have'nt state for stop
|
|
|
|
|
*/
|
|
|
|
|
if(WM_CDM_PAUSED == mode && 0 == cur_frame) {
|
|
|
|
|
mode = WM_CDM_STOPPED;
|
|
|
|
|
thiscd.curtrack = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
switch (mode) {
|
|
|
|
|
case WM_CDM_PLAYING:
|
|
|
|
|
case WM_CDM_PAUSED:
|
|
|
|
|
cur_pos_abs = cur_frame / 75;
|
|
|
|
|
/* search for right track */
|
|
|
|
|
for(tmp = thiscd.ntracks;
|
|
|
|
|
tmp > 1 && cur_frame < thiscd.trk[CARRAY(tmp)].start;
|
|
|
|
|
tmp--)
|
|
|
|
|
;
|
|
|
|
|
thiscd.curtrack = tmp;
|
|
|
|
|
/* Fall through */
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
case WM_CDM_UNKNOWN:
|
|
|
|
|
if (mode == WM_CDM_UNKNOWN)
|
|
|
|
|
{
|
|
|
|
|
mode = WM_CDM_NO_DISC;
|
|
|
|
|
cur_lasttrack = cur_firsttrack = -1;
|
|
|
|
|
}
|
|
|
|
|
/* Fall through */
|
|
|
|
|
|
|
|
|
|
case WM_CDM_STOPPED:
|
|
|
|
|
if(thiscd.curtrack >= 1 && thiscd.curtrack <= thiscd.ntracks && thiscd.trk != NULL) {
|
|
|
|
|
cur_trackname = thiscd.trk[CARRAY(thiscd.curtrack)].songname;
|
|
|
|
|
cur_avoid = thiscd.trk[CARRAY(thiscd.curtrack)].avoid;
|
|
|
|
|
cur_contd = thiscd.trk[CARRAY(thiscd.curtrack)].contd;
|
|
|
|
|
cur_pos_rel = (cur_frame - thiscd.trk[CARRAY(thiscd.curtrack)].start) / 75;
|
|
|
|
|
if (cur_pos_rel < 0)
|
|
|
|
|
cur_pos_rel = -cur_pos_rel;
|
|
|
|
|
}
|
|
|
|
|
if((playlist != NULL) && playlist[0].start & (cur_listno > 0)) {
|
|
|
|
|
cur_pos_abs -= thiscd.trk[playlist[CARRAY(cur_listno)].start - 1].start / 75;
|
|
|
|
|
cur_pos_abs += playlist[CARRAY(cur_listno)].starttime;
|
|
|
|
|
}
|
|
|
|
|
if (cur_pos_abs < 0)
|
|
|
|
|
cur_pos_abs = cur_frame = 0;
|
|
|
|
|
|
|
|
|
|
if (thiscd.curtrack < 1)
|
|
|
|
|
thiscd.curtracklen = thiscd.length;
|
|
|
|
|
else
|
|
|
|
|
thiscd.curtracklen = thiscd.trk[CARRAY(thiscd.curtrack)].length;
|
|
|
|
|
/* Fall through */
|
|
|
|
|
|
|
|
|
|
case WM_CDM_TRACK_DONE:
|
|
|
|
|
wm_cur_cdmode = mode;
|
|
|
|
|
break;
|
|
|
|
|
case WM_CDM_FORWARD:
|
|
|
|
|
case WM_CDM_EJECTED:
|
|
|
|
|
wm_cur_cdmode = mode;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS,
|
|
|
|
|
"wm_cd_status returns %s\n", gen_status(wm_cur_cdmode));
|
|
|
|
|
return wm_cur_cdmode;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
wm_cd_getcurtrack( void )
|
|
|
|
|
{
|
|
|
|
|
if(WM_CDS_NO_DISC(wm_cur_cdmode))
|
|
|
|
|
return 0;
|
|
|
|
|
return thiscd.curtrack;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
wm_cd_getcurtracklen( void )
|
|
|
|
|
{
|
|
|
|
|
if(WM_CDS_NO_DISC(wm_cur_cdmode))
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
return thiscd.curtracklen;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
wm_cd_getcountoftracks( void )
|
|
|
|
|
{
|
|
|
|
|
if(WM_CDS_NO_DISC(wm_cur_cdmode))
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
return thiscd.ntracks;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int wm_cd_gettracklen( int track )
|
|
|
|
|
{
|
|
|
|
|
if (track < 1 ||
|
|
|
|
|
track > thiscd.ntracks ||
|
|
|
|
|
thiscd.trk == NULL)
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
return thiscd.trk[CARRAY(track)].length;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* wm_cd_play(starttrack, pos, endtrack)
|
|
|
|
|
*
|
|
|
|
|
* Start playing the CD or jump to a new position. "pos" is in seconds,
|
|
|
|
|
* relative to start of track.
|
|
|
|
|
*/
|
|
|
|
|
int
|
|
|
|
|
wm_cd_play( int start, int pos, int end )
|
|
|
|
|
{
|
|
|
|
|
int real_start, real_end, status;
|
|
|
|
|
|
|
|
|
|
status = wm_cd_status();
|
|
|
|
|
if(WM_CDS_NO_DISC(status) || thiscd.ntracks < 1)
|
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* check ranges
|
|
|
|
|
*/
|
|
|
|
|
for(real_end = thiscd.ntracks; (thiscd.trk[CARRAY(real_end)].data == DATATRACK); real_end--)
|
|
|
|
|
;
|
|
|
|
|
for(real_start = 1; (thiscd.trk[CARRAY(real_start)].data == DATATRACK); real_start++)
|
|
|
|
|
;
|
|
|
|
|
|
|
|
|
|
if(end == WM_ENDTRACK) end = real_end;
|
|
|
|
|
if(end > real_end) end = real_end;
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* handle as overrun
|
|
|
|
|
*/
|
|
|
|
|
if(start < real_start) start = real_start;
|
|
|
|
|
if(start > real_end) start = real_end;
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Try to avoid mixed mode and CD-EXTRA data tracks
|
|
|
|
|
*/
|
|
|
|
|
if(start > end || thiscd.trk[CARRAY(start)].data == DATATRACK) {
|
|
|
|
|
wm_cd_stop();
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
cur_firsttrack = start;
|
|
|
|
|
cur_lasttrack = end;
|
|
|
|
|
|
|
|
|
|
wm_cd_play_chunk(thiscd.trk[CARRAY(start)].start + pos * 75, end == thiscd.ntracks ?
|
|
|
|
|
thiscd.length * 75 : thiscd.trk[CARRAY(end)].start - 1, thiscd.trk[CARRAY(start)].start);
|
|
|
|
|
/* So we don't update the display with the old frame number */
|
|
|
|
|
wm_cd_status();
|
|
|
|
|
|
|
|
|
|
return thiscd.curtrack;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* wm_cd_play_chunk(start, end)
|
|
|
|
|
*
|
|
|
|
|
* Play the CD from one position to another (both in frames.)
|
|
|
|
|
*/
|
|
|
|
|
int
|
|
|
|
|
wm_cd_play_chunk( int start, int end, int realstart )
|
|
|
|
|
{
|
|
|
|
|
int status;
|
|
|
|
|
|
|
|
|
|
status = wm_cd_status();
|
|
|
|
|
if(WM_CDS_NO_DISC(status))
|
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
|
|
end--;
|
|
|
|
|
if (start >= end)
|
|
|
|
|
start = end-1;
|
|
|
|
|
|
|
|
|
|
if(!(drive.proto) || !(drive.proto->gen_play)) {
|
|
|
|
|
perror("WM gen_play: function pointer NULL");
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return (drive.proto->gen_play)(&drive, start, end, realstart);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Set the offset into the current track and play. -1 means end of track
|
|
|
|
|
* (i.e., go to next track.)
|
|
|
|
|
*/
|
|
|
|
|
int
|
|
|
|
|
wm_cd_play_from_pos( int pos )
|
|
|
|
|
{
|
|
|
|
|
int status;
|
|
|
|
|
|
|
|
|
|
status = wm_cd_status();
|
|
|
|
|
if(WM_CDS_NO_DISC(status))
|
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
|
|
if (pos == -1)
|
|
|
|
|
pos = thiscd.trk[thiscd.curtrack - 1].length - 1;
|
|
|
|
|
|
|
|
|
|
if (wm_cur_cdmode == WM_CDM_PLAYING)
|
|
|
|
|
return wm_cd_play(thiscd.curtrack, pos, playlist[cur_listno-1].end);
|
|
|
|
|
else
|
|
|
|
|
return -1;
|
|
|
|
|
} /* wm_cd_play_from_pos() */
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* wm_cd_pause()
|
|
|
|
|
*
|
|
|
|
|
* Pause the CD, if it's in play mode. If it's already paused, go back to
|
|
|
|
|
* play mode.
|
|
|
|
|
*/
|
|
|
|
|
int
|
|
|
|
|
wm_cd_pause( void )
|
|
|
|
|
{
|
|
|
|
|
static int paused_pos;
|
|
|
|
|
int status;
|
|
|
|
|
|
|
|
|
|
status = wm_cd_status();
|
|
|
|
|
if(WM_CDS_NO_DISC(status))
|
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
|
|
if(WM_CDM_PLAYING == wm_cur_cdmode) {
|
|
|
|
|
if(drive.proto && drive.proto->gen_pause)
|
|
|
|
|
(drive.proto->gen_pause)(&drive);
|
|
|
|
|
|
|
|
|
|
paused_pos = cur_pos_rel;
|
|
|
|
|
} else if(WM_CDM_PAUSED == status) {
|
|
|
|
|
if(!(drive.proto->gen_resume) || (drive.proto->gen_resume(&drive) > 0)) {
|
|
|
|
|
wm_cd_play(thiscd.curtrack, paused_pos, playlist[cur_listno-1].end);
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
wm_cd_status();
|
|
|
|
|
return 0;
|
|
|
|
|
} /* wm_cd_pause() */
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* wm_cd_stop()
|
|
|
|
|
*
|
|
|
|
|
* Stop the CD if it's not already stopped.
|
|
|
|
|
*/
|
|
|
|
|
int
|
|
|
|
|
wm_cd_stop( void )
|
|
|
|
|
{
|
|
|
|
|
int status;
|
|
|
|
|
|
|
|
|
|
status = wm_cd_status();
|
|
|
|
|
if(WM_CDS_NO_DISC(status))
|
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
|
|
if (status != WM_CDM_STOPPED) {
|
|
|
|
|
|
|
|
|
|
if(drive.proto && drive.proto->gen_stop)
|
|
|
|
|
(drive.proto->gen_stop)(&drive);
|
|
|
|
|
|
|
|
|
|
status = wm_cd_status();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return (status != WM_CDM_STOPPED);
|
|
|
|
|
} /* wm_cd_stop() */
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Eject the current CD, if there is one, and set the mode to 5.
|
|
|
|
|
*
|
|
|
|
|
* Returns 0 on success, 1 if the CD couldn't be ejected, or 2 if the
|
|
|
|
|
* CD contains a mounted filesystem.
|
|
|
|
|
*/
|
|
|
|
|
int
|
|
|
|
|
wm_cd_eject( void )
|
|
|
|
|
{
|
|
|
|
|
int err = -1;
|
|
|
|
|
|
|
|
|
|
wm_cd_stop();
|
|
|
|
|
|
|
|
|
|
if(drive.proto && drive.proto->gen_eject)
|
|
|
|
|
err = (drive.proto->gen_eject)(&drive);
|
|
|
|
|
|
|
|
|
|
if (err < 0) {
|
|
|
|
|
if (err == -3) {
|
|
|
|
|
return 2;
|
|
|
|
|
} else {
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
wm_cd_status();
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int wm_cd_closetray(void)
|
|
|
|
|
{
|
|
|
|
|
int status, err = -1;
|
|
|
|
|
|
|
|
|
|
status = wm_cd_status();
|
|
|
|
|
if (status == WM_CDM_UNKNOWN || status == WM_CDM_NO_DISC)
|
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
|
|
if(drive.proto->gen_closetray)
|
|
|
|
|
err = (drive.proto->gen_closetray)(&drive);
|
|
|
|
|
|
|
|
|
|
return(err ? 0 : wm_cd_status()==2 ? 1 : 0);
|
|
|
|
|
} /* wm_cd_closetray() */
|
|
|
|
|
|
|
|
|
|
struct cdtext_info*
|
|
|
|
|
wm_cd_get_cdtext( void )
|
|
|
|
|
{
|
|
|
|
|
int status;
|
|
|
|
|
|
|
|
|
|
status = wm_cd_status();
|
|
|
|
|
|
|
|
|
|
if(WM_CDS_NO_DISC(status))
|
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
|
|
return get_glob_cdtext(&drive, 0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* find_trkind(track, index, start)
|
|
|
|
|
*
|
|
|
|
|
* Start playing at a particular track and index, optionally using a particular
|
|
|
|
|
* frame as a starting position. Returns a frame number near the start of the
|
|
|
|
|
* index mark if successful, 0 if the track/index didn't exist.
|
|
|
|
|
*
|
|
|
|
|
* This is made significantly more tedious (though probably easier to port)
|
|
|
|
|
* by the fact that CDROMPLAYTRKIND doesn't work as advertised. The routine
|
|
|
|
|
* does a binary search of the track, terminating when the interval gets to
|
|
|
|
|
* around 10 frames or when the next track is encountered, at which point
|
|
|
|
|
* it's a fair bet the index in question doesn't exist.
|
|
|
|
|
*/
|
|
|
|
|
int
|
|
|
|
|
wm_find_trkind( int track, int ind, int start )
|
|
|
|
|
{
|
|
|
|
|
int top = 0, bottom, current, interval, ret = 0, i, status;
|
|
|
|
|
|
|
|
|
|
status = wm_cd_status();
|
|
|
|
|
if(WM_CDS_NO_DISC(status))
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < thiscd.ntracks; i++) {
|
|
|
|
|
if (thiscd.trk[i].track == track)
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bottom = thiscd.trk[i].start;
|
|
|
|
|
|
|
|
|
|
for (; i < thiscd.ntracks; i++) {
|
|
|
|
|
if (thiscd.trk[i].track > track)
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
top = i == thiscd.ntracks ? (thiscd.length - 1) * 75 : thiscd.trk[i].start;
|
|
|
|
|
if (start > bottom && start < top)
|
|
|
|
|
bottom = start;
|
|
|
|
|
|
|
|
|
|
current = (top + bottom) / 2;
|
|
|
|
|
interval = (top - bottom) / 4;
|
|
|
|
|
|
|
|
|
|
do {
|
|
|
|
|
wm_cd_play_chunk(current, current + 75, current);
|
|
|
|
|
|
|
|
|
|
if (wm_cd_status() != 1)
|
|
|
|
|
return 0;
|
|
|
|
|
while (cur_frame < current) {
|
|
|
|
|
if(wm_cd_status() != 1 || wm_cur_cdmode != WM_CDM_PLAYING)
|
|
|
|
|
return 0;
|
|
|
|
|
else
|
|
|
|
|
wm_susleep(1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (thiscd.trk[thiscd.curtrack - 1].track > track)
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
if(cur_index >= ind) {
|
|
|
|
|
ret = current;
|
|
|
|
|
current -= interval;
|
|
|
|
|
} else {
|
|
|
|
|
current += interval;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
interval /= 2;
|
|
|
|
|
|
|
|
|
|
} while (interval > 2);
|
|
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
|
} /* find_trkind() */
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
wm_cd_set_verbosity( int level )
|
|
|
|
|
{
|
|
|
|
|
wm_lib_set_verbosity(level);
|
|
|
|
|
return wm_lib_get_verbosity();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* volume is valid WM_VOLUME_MUTE <= vol <= WM_VOLUME_MAXIMAL,
|
|
|
|
|
* balance is valid WM_BALANCE_ALL_LEFTS <= balance <= WM_BALANCE_ALL_RIGHTS
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
wm_cd_volume( int vol, int bal )
|
|
|
|
|
{
|
|
|
|
|
int left, right;
|
|
|
|
|
const int bal1 = (vol - WM_VOLUME_MUTE)/(WM_BALANCE_ALL_RIGHTS - WM_BALANCE_SYMMETRED);
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Set "left" and "right" to volume-slider values accounting for the
|
|
|
|
|
* balance setting.
|
|
|
|
|
*
|
|
|
|
|
*/
|
|
|
|
|
if(vol < WM_VOLUME_MUTE) vol = WM_VOLUME_MUTE;
|
|
|
|
|
if(vol > WM_VOLUME_MAXIMAL) vol = WM_VOLUME_MAXIMAL;
|
|
|
|
|
if(bal < WM_BALANCE_ALL_LEFTS) bal = WM_BALANCE_ALL_LEFTS;
|
|
|
|
|
if(bal > WM_BALANCE_ALL_RIGHTS) bal = WM_BALANCE_ALL_RIGHTS;
|
|
|
|
|
|
|
|
|
|
left = vol - (bal * bal1);
|
|
|
|
|
right = vol + (bal * bal1);
|
|
|
|
|
|
|
|
|
|
wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "calculate volume left %i, right %i\n", left, right);
|
|
|
|
|
|
|
|
|
|
if (left > WM_VOLUME_MAXIMAL)
|
|
|
|
|
left = WM_VOLUME_MAXIMAL;
|
|
|
|
|
if (right > WM_VOLUME_MAXIMAL)
|
|
|
|
|
right = WM_VOLUME_MAXIMAL;
|
|
|
|
|
|
|
|
|
|
if(!(drive.proto) || !(drive.proto->gen_set_volume))
|
|
|
|
|
return -1;
|
|
|
|
|
else
|
|
|
|
|
return (drive.proto->gen_set_volume)(&drive, left, right);
|
|
|
|
|
} /* cd_volume() */
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
wm_cd_getvolume( void )
|
|
|
|
|
{
|
|
|
|
|
int left, right;
|
|
|
|
|
|
|
|
|
|
if(!(drive.proto) || !(drive.proto->gen_get_volume) ||
|
|
|
|
|
(drive.proto->gen_get_volume)(&drive, &left, &right) < 0 || left == -1)
|
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
|
|
if (left < right) {
|
|
|
|
|
wm_cd_cur_balance = (right - left) / 2;
|
|
|
|
|
if (wm_cd_cur_balance > WM_BALANCE_ALL_RIGHTS)
|
|
|
|
|
wm_cd_cur_balance = WM_BALANCE_ALL_RIGHTS;
|
|
|
|
|
return right;
|
|
|
|
|
} else if (left == right) {
|
|
|
|
|
wm_cd_cur_balance = WM_BALANCE_SYMMETRED;
|
|
|
|
|
return left;
|
|
|
|
|
} else {
|
|
|
|
|
wm_cd_cur_balance = (right - left) / 2;
|
|
|
|
|
if (wm_cd_cur_balance < WM_BALANCE_ALL_LEFTS)
|
|
|
|
|
wm_cd_cur_balance = WM_BALANCE_ALL_LEFTS;
|
|
|
|
|
return left;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
wm_cd_getbalance( void )
|
|
|
|
|
{
|
|
|
|
|
int left, right;
|
|
|
|
|
|
|
|
|
|
if(!(drive.proto) || !(drive.proto->gen_get_volume) ||
|
|
|
|
|
(drive.proto->gen_get_volume)(&drive, &left, &right) < 0 || left == -1)
|
|
|
|
|
return WM_BALANCE_SYMMETRED;
|
|
|
|
|
|
|
|
|
|
if (left < right) {
|
|
|
|
|
wm_cd_cur_balance = (right - left) / 2;
|
|
|
|
|
if (wm_cd_cur_balance > WM_BALANCE_ALL_RIGHTS)
|
|
|
|
|
wm_cd_cur_balance = WM_BALANCE_ALL_RIGHTS;
|
|
|
|
|
} else if (left == right) {
|
|
|
|
|
wm_cd_cur_balance = WM_BALANCE_SYMMETRED;
|
|
|
|
|
} else {
|
|
|
|
|
wm_cd_cur_balance = (right - left) / 2;
|
|
|
|
|
if (wm_cd_cur_balance < WM_BALANCE_ALL_LEFTS)
|
|
|
|
|
wm_cd_cur_balance = WM_BALANCE_ALL_LEFTS;
|
|
|
|
|
}
|
|
|
|
|
return wm_cd_cur_balance;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Prototype wm_drive structure, with generic functions. The generic functions
|
|
|
|
|
* will be replaced with drive-specific functions as appropriate once the drive
|
|
|
|
|
* type has been sensed.
|
|
|
|
|
*/
|
|
|
|
|
struct wm_drive_proto generic_proto = {
|
|
|
|
|
gen_init, /* functions... */
|
|
|
|
|
gen_close,
|
|
|
|
|
gen_get_trackcount,
|
|
|
|
|
gen_get_cdlen,
|
|
|
|
|
gen_get_trackinfo,
|
|
|
|
|
gen_get_drive_status,
|
|
|
|
|
gen_get_volume,
|
|
|
|
|
gen_set_volume,
|
|
|
|
|
gen_pause,
|
|
|
|
|
gen_resume,
|
|
|
|
|
gen_stop,
|
|
|
|
|
gen_play,
|
|
|
|
|
gen_eject,
|
|
|
|
|
gen_closetray,
|
|
|
|
|
gen_get_cdtext
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const char*
|
|
|
|
|
gen_status(int status)
|
|
|
|
|
{
|
|
|
|
|
static char tmp[250];
|
|
|
|
|
switch(status) {
|
|
|
|
|
case WM_CDM_TRACK_DONE:
|
|
|
|
|
return "WM_CDM_TRACK_DONE";
|
|
|
|
|
case WM_CDM_PLAYING:
|
|
|
|
|
return "WM_CDM_PLAYING";
|
|
|
|
|
case WM_CDM_FORWARD:
|
|
|
|
|
return "WM_CDM_FORWARD";
|
|
|
|
|
case WM_CDM_PAUSED:
|
|
|
|
|
return "WM_CDM_PAUSED";
|
|
|
|
|
case WM_CDM_STOPPED:
|
|
|
|
|
return "WM_CDM_STOPPED";
|
|
|
|
|
case WM_CDM_EJECTED:
|
|
|
|
|
return "WM_CDM_EJECTED";
|
|
|
|
|
case WM_CDM_DEVICECHANGED:
|
|
|
|
|
return "WM_CDM_DEVICECHANGED";
|
|
|
|
|
case WM_CDM_NO_DISC:
|
|
|
|
|
return "WM_CDM_NO_DISC";
|
|
|
|
|
case WM_CDM_UNKNOWN:
|
|
|
|
|
return "WM_CDM_UNKNOWN";
|
|
|
|
|
case WM_CDM_CDDAERROR:
|
|
|
|
|
return "WM_CDM_CDDAERROR";
|
|
|
|
|
case WM_CDM_CDDAACK:
|
|
|
|
|
return "WM_CDM_CDDAACK";
|
|
|
|
|
default:
|
|
|
|
|
{
|
|
|
|
|
sprintf(tmp, "unexpected status %i", status);
|
|
|
|
|
return tmp;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|