diff options
author | toma <toma@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2009-11-25 17:56:58 +0000 |
---|---|---|
committer | toma <toma@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2009-11-25 17:56:58 +0000 |
commit | e2de64d6f1beb9e492daf5b886e19933c1fa41dd (patch) | |
tree | 9047cf9e6b5c43878d5bf82660adae77ceee097a /kscd/libwm/cdrom.c | |
download | tdemultimedia-e2de64d6f1beb9e492daf5b886e19933c1fa41dd.tar.gz tdemultimedia-e2de64d6f1beb9e492daf5b886e19933c1fa41dd.zip |
Copy the KDE 3.5 branch to branches/trinity for new KDE 3.5 features.
BUG:215923
git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/kdemultimedia@1054174 283d02a7-25f6-0310-bc7c-ecb5cbfe19da
Diffstat (limited to 'kscd/libwm/cdrom.c')
-rw-r--r-- | kscd/libwm/cdrom.c | 899 |
1 files changed, 899 insertions, 0 deletions
diff --git a/kscd/libwm/cdrom.c b/kscd/libwm/cdrom.c new file mode 100644 index 00000000..980ce3ce --- /dev/null +++ b/kscd/libwm/cdrom.c @@ -0,0 +1,899 @@ +/* + * $Id$ + * + * This file is part of WorkMan, the civilized CD player library + * (c) 1991-1997 by Steven Grimm (original author) + * (c) by Dirk Fö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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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; + } + } +} |