Add the cdcontrol program by Serge Vakulenko. It's my understanding

that this is a superset of cdplay, and perhaps it's time to send cdplay
into the bit bucket if this works well.  According to the docs, it has
a friendlier command structure, command line interface etc.
Submitted by:	Serge Vakulenko <vak@cronyx.ru>
This commit is contained in:
Jordan K. Hubbard 1995-08-18 11:41:32 +00:00
parent 736267d692
commit eae0c0a88b
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=10099
4 changed files with 776 additions and 6 deletions

View File

@ -1,13 +1,13 @@
# From: @(#)Makefile 5.20 (Berkeley) 6/12/93
# $Id: Makefile,v 1.66 1995/07/12 18:43:38 bde Exp $
# $Id: Makefile,v 1.67 1995/08/13 15:28:18 peter Exp $
# XXX MISSING: mkproto
# XXX MISSING SOURCES: quot
SUBDIR= ac accton adduser amd arp bootparamd chown chroot cron crunch ctm \
dev_mkdb diskpart edquota inetd kernbb kgmon kvm_mkdb lpr lsdev \
manctl mrouted mtree named nslookup pcvt pkg_install pkg_manage \
portmap ppp pppd pppstats pstat pwd_mkdb quotaon rarpd repquota \
routed rmt rwhod sa sendmail sliplogin slstat sysctl syslogd \
SUBDIR= ac accton adduser amd arp bootparamd cdcontrol chown chroot cron \
crunch ctm dev_mkdb diskpart edquota inetd kernbb kgmon kvm_mkdb \
lpr lsdev manctl mrouted mtree named nslookup pcvt pkg_install \
pkg_manage portmap ppp pppd pppstats pstat pwd_mkdb quotaon rarpd \
repquota routed rmt rwhod sa sendmail sliplogin slstat sysctl syslogd \
tcpdump timed traceroute trpt tzsetup vipw vnconfig watch xntpd \
xten ypbind yppoll ypset zic

View File

@ -0,0 +1,4 @@
PROG= cdcontrol
CFLAGS+= -Wall
.include <bsd.prog.mk>

View File

@ -0,0 +1,144 @@
.Dd July 3, 1995
.Dt CDCONTROL 1
.Os FreeBSD
.Sh NAME
.Nm cdcontrol
.Nd compact disc control utility
.Sh SYNOPSIS
.Nm cdcontrol
.Op Fl s
.Op Fl v
.Op Fl f Ar discname
.Op Ar command args...
.Sh DESCRIPTION
.Nm cdcontrol
is a program to control audio features of a CD drive. The device is a name such
as cd0 or mcd0.
.Pp
If the device not specified, the environment variable
.Ev DISC
will be used to find the cd device.
.Pp
If no command is given, then
.Nm cdcontrol
enters an interactive mode, reading commands from the standard input.
.Pp
The following options are available:
.Bl -tag -width flag
.It Fl s
Silent mode - do not print table headers and human readable comments.
.It Fl v
Verbose mode - print as much information as possible.
.It Fl f Ar discname
Specifies a device name, such as /dev/cd0c or mcd0.
Both absolute path and relative to /dev filename are possible.
Suffix `c' is added to the device name if needed.
.El
.Pp
The available commands are listed below. Only as many
characters as are required to uniquely identify a command
need be specified.
.Bl -tag -width Cm
.It Cm play Op Ar first_track Op Ar last_track
Play from track
.Nm first_track
to track
.Nm last_track.
The first track has number 1.
.It Cm play Ar start_m:start_s.start_f Op Ar end_m:end_s.end_f
Play from the absolute address
(MSF) defined by
.Nm start_m
in minutes,
.Nm start_s,
in seconds and
.Nm start_f
(frame number) to the abolute address defined by
.Nm end_m
in minutes,
.Nm end_s,
in seconds and
.Nm end_f
(frame number). Minutes are in the range 0-99. Seconds are in the range 0-59.
Frame numbers are in the range 0-74.
.It Cm play Ar #start_block Op length
Play starting from the logical block
.Nm start_block
using
.Nm length
logical blocks.
.It Cm pause
Stop playing. Do not stop the disc.
.It Cm resume
Resume playing. Used after the
.Nm pause
command.
.It Cm stop
Stop the disc.
.It Cm eject
Eject the disc.
.It Cm volume Ar left_channel Ar right_channel
Set the volume of left channel to
.Nm left_channel
and the volume of right channel to
.Nm right_channel.
Allowed values are in the range 0-255.
.It Cm volume Ar mute
Turn the sound off.
.It Cm volume Ar mono
Set the mono mode.
.It Cm volume Ar stereo
Set the stereo mode.
.It Cm volume Ar left
Play the left subtrack on both left and right channels.
.It Cm volume Ar right
Play the right subtrack on both left and right channels.
.It Cm info
Print the information about the disc:
the current playing status and position,
the current values of the volume for left and right channels,
and the table of contents.
.It Cm help
Print the list of available commands.
.It Cm debug Ar on
Enable the debugging mode of the CD device driver.
.It Cm debug Ar off
Disable the driver debugging mode.
.It Cm debug Ar reset
Perform the hardware reset of the device.
.It Cm quit
Quit the program.
.Sh FILES
.Bl -tag -width /dev/rmcd0c -compact
.It Pa /dev/rcd0c
.It Pa /dev/rmcd0c
.It Pa /dev/rwcd0c
.El
.Sh AUTHORS
Jean-Marc Zucconi,
Andrew A.\ Chernov,
Serge V.\ Vakulenko
.Sh HISTORY
The
.Nm cdcontrol
command appeared in FreeBSD 2.1

View File

@ -0,0 +1,622 @@
/*
* Compact Disc Control Utility by Serge V. Vakulenko, <vak@cronyx.ru>.
* Based on the non-X based CD player by Jean-Marc Zucconi and
* Andrew A. Chernov.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <sys/file.h>
#include <sys/cdio.h>
#include <sys/ioctl.h>
#define VERSION "1.0"
/*
* Audio Status Codes
*/
#define ASTS_INVALID 0x00 /* Audio status byte not valid */
#define ASTS_PLAYING 0x11 /* Audio play operation in progress */
#define ASTS_PAUSED 0x12 /* Audio play operation paused */
#define ASTS_COMPLETED 0x13 /* Audio play operation successfully completed */
#define ASTS_ERROR 0x14 /* Audio play operation stopped due to error */
#define ASTS_VOID 0x15 /* No current audio status to return */
struct cmdtab {
int command;
char *name;
char *args;
} cmdtab[] = {
#define CMD_DEBUG 1
{ CMD_DEBUG, "Debug", "[ on | off | reset ]", },
#define CMD_EJECT 2
{ CMD_EJECT, "Eject", "", },
#define CMD_HELP 3
{ CMD_HELP, "?", 0, },
{ CMD_HELP, "Help", "", },
#define CMD_INFO 4
{ CMD_INFO, "Info", "", },
#define CMD_PAUSE 5
{ CMD_PAUSE, "PAuse", "", },
#define CMD_PLAY 6
{ CMD_PLAY, "P", 0, },
{ CMD_PLAY, "Play", "min1:sec1.fr1 [ min2:sec2.fr2 ]", },
{ CMD_PLAY, "Play", "track1.index1 [ track2.index2 ]", },
{ CMD_PLAY, "Play", "#block [ len ]", },
#define CMD_QUIT 7
{ CMD_QUIT, "Quit", "", },
#define CMD_RESUME 8
{ CMD_RESUME, "Resume", "", },
#define CMD_STOP 9
{ CMD_STOP, "Stop", "", },
#define CMD_VOLUME 10
{ CMD_VOLUME, "Volume", "<l> <r> | left | right | mute | mono | stereo", },
{ 0, 0, },
};
struct cd_toc_entry toc_buffer[100];
char *cdname;
int fd = -1;
int verbose = 1;
extern char *optarg;
extern int optind;
int setvol (int, int);
int read_toc_entrys (int);
int play_msf (int, int, int, int, int, int);
int play_track (int, int, int, int);
int get_vol (int *, int *);
int status (int *, int *, int *, int *);
int open_cd (void);
int play (char *arg);
int info (char *arg);
char *input (int*);
void prtrack (struct cd_toc_entry *e, int lastflag);
void lba2msf (int lba, u_char *m, u_char *s, u_char *f);
int msf2lba (u_char m, u_char s, u_char f);
int play_blocks (int blk, int len);
int run (int cmd, char *arg);
char *parse (char *buf, int *cmd);
extern int errno;
void help ()
{
struct cmdtab *c;
for (c=cmdtab; c->name; ++c) {
if (! c->args)
continue;
printf ("\t%s", c->name);
if (*c->args)
printf (" %s", c->args);
printf ("\n");
}
}
void usage ()
{
printf ("Usage:\n\tcdcontrol [ -vs ] [ -f disc ] [ command args... ]\n");
printf ("Options:\n");
printf ("\t-v - verbose mode\n");
printf ("\t-s - silent mode\n");
printf ("\t-f disc - device name such as /dev/cd0c\n");
printf ("\tDISC - shell variable with device name\n");
printf ("Commands:\n");
help ();
exit (1);
}
int main (int argc, char **argv)
{
int cmd;
char *arg;
cdname = getenv ("DISC");
if (! cdname)
cdname = getenv ("CDPLAY");
for (;;) {
switch (getopt (argc, argv, "svhf:")) {
case EOF:
break;
case 's':
verbose = 0;
continue;
case 'v':
verbose = 2;
continue;
case 'f':
cdname = optarg;
continue;
case 'h':
default:
usage ();
}
break;
}
argc -= optind;
argv += optind;
if (argc > 0 && strcasecmp (*argv, "help") == 0)
usage ();
if (! cdname) {
fprintf (stderr, "No CD device name specified.\n");
usage ();
}
if (argc > 0) {
char buf[80], *p;
int len;
for (p=buf; argc-- > 0; ++argv) {
len = strlen (*argv);
if (p + len >= buf + sizeof (buf) - 1)
usage ();
if (p > buf)
*p++ = ' ';
strcpy (p, *argv);
p += len;
}
*p = 0;
arg = parse (buf, &cmd);
return run (cmd, arg);
}
if (verbose == 1)
verbose = isatty (0);
if (verbose) {
printf ("Compact Disc Control Utility, Version %s\n", VERSION);
printf ("Type `?' for command list\n\n");
}
for (;;) {
arg = input (&cmd);
if (run (cmd, arg) < 0) {
if (verbose)
perror ("cdplay");
close (fd);
fd = -1;
}
fflush (stdout);
}
}
int run (int cmd, char *arg)
{
int l, r, rc;
switch (cmd) {
case CMD_QUIT:
exit (0);
default:
case CMD_HELP:
help ();
return (0);
case CMD_INFO:
if (fd<0 && ! open_cd ()) return (0);
return info (arg);
case CMD_PAUSE:
if (fd<0 && ! open_cd ()) return (0);
return ioctl (fd, CDIOCPAUSE);
case CMD_RESUME:
if (fd<0 && ! open_cd ()) return (0);
return ioctl (fd, CDIOCRESUME);
case CMD_STOP:
if (fd<0 && ! open_cd ()) return (0);
return ioctl (fd, CDIOCSTOP);
case CMD_DEBUG:
if (fd<0 && ! open_cd ()) return (0);
if (strcasecmp (arg, "on") == 0)
return ioctl (fd, CDIOCSETDEBUG);
if (strcasecmp (arg, "off") == 0)
return ioctl (fd, CDIOCCLRDEBUG);
if (strcasecmp (arg, "reset") == 0)
return ioctl (fd, CDIOCRESET);
printf ("Invalid command arguments\n");
return (0);
case CMD_EJECT:
if (fd<0 && ! open_cd ()) return (0);
(void) ioctl (fd, CDIOCALLOW);
rc = ioctl (fd, CDIOCEJECT);
if (rc < 0)
return (rc);
close (fd);
fd = -1;
return (0);
case CMD_PLAY:
if (fd<0 && ! open_cd ()) return (0);
return play (arg);
case CMD_VOLUME:
if (fd<0 && ! open_cd ()) return (0);
if (strcasecmp (arg, "left") == 0)
return ioctl (fd, CDIOCSETLEFT);
else if (strcasecmp (arg, "right") == 0)
return ioctl (fd, CDIOCSETRIGHT);
else if (strcasecmp (arg, "mute") == 0)
return ioctl (fd, CDIOCSETMUTE);
else if (strcasecmp (arg, "mono") == 0)
return ioctl (fd, CDIOCSETMONO);
else if (strcasecmp (arg, "stereo") == 0)
return ioctl (fd, CDIOCSETSTERIO);
if (2 != sscanf (arg, "%d %d", &l, &r)) {
printf ("Invalid command arguments\n");
return (0);
}
return setvol (l, r);
}
}
int play (char *arg)
{
struct ioc_toc_header h;
int rc, n, start, end = 0, istart = 1, iend = 1;
rc = ioctl (fd, CDIOREADTOCHEADER, &h);
if (rc < 0)
return (rc);
n = h.ending_track - h.starting_track + 1;
rc = read_toc_entrys ((n + 1) * sizeof (struct cd_toc_entry));
if (rc < 0)
return (rc);
if (! *arg)
/*
* Play the whole disc
*/
return play_blocks (0, msf2lba (toc_buffer[n].addr.msf.minute,
toc_buffer[n].addr.msf.second,
toc_buffer[n].addr.msf.frame));
if (strchr (arg, '#')) {
/*
* Play block #blk [ len ]
*/
int blk, len = 0;
if (2 != sscanf (arg, "#%d%d", &blk, &len) &&
1 != sscanf (arg, "#%d", &blk)) {
err: printf ("Invalid command arguments\n");
return (0);
}
if (len == 0)
len = msf2lba (toc_buffer[n].addr.msf.minute,
toc_buffer[n].addr.msf.second,
toc_buffer[n].addr.msf.frame) - blk;
return play_blocks (blk, len);
}
if (strchr (arg, ':')) {
/*
* Play MSF m1:s1 [ .f1 ] [ m2:s2 [ .f2 ] ]
*/
int m1, m2 = 0, s1, s2 = 0, f1 = 0, f2 = 0;
if (6 != sscanf (arg, "%d:%d.%d%d:%d.%d", &m1, &s1, &f1, &m2, &s2, &f2) &&
5 != sscanf (arg, "%d:%d.%d%d:%d", &m1, &s1, &f1, &m2, &s2) &&
5 != sscanf (arg, "%d:%d%d:%d.%d", &m1, &s1, &m2, &s2, &f2) &&
3 != sscanf (arg, "%d:%d.%d", &m1, &s1, &f1) &&
4 != sscanf (arg, "%d:%d%d:%d", &m1, &s1, &m2, &s2) &&
2 != sscanf (arg, "%d:%d", &m1, &s1))
goto err;
if (m2 == 0) {
m2 = toc_buffer[n].addr.msf.minute;
s2 = toc_buffer[n].addr.msf.second;
f2 = toc_buffer[n].addr.msf.frame;
}
return play_msf (m1, s1, f1, m2, s2, f2);
}
/*
* Play track trk1 [ .idx1 ] [ trk2 [ .idx2 ] ]
*/
if (4 != sscanf (arg, "%d.%d%d.%d", &start, &istart, &end, &iend) &&
3 != sscanf (arg, "%d.%d%d", &start, &istart, &end) &&
3 != sscanf (arg, "%d%d.%d", &start, &end, &iend) &&
2 != sscanf (arg, "%d.%d", &start, &istart) &&
2 != sscanf (arg, "%d%d", &start, &end) &&
1 != sscanf (arg, "%d", &start))
goto err;
if (end == 0)
end = n;
return play_track (start, istart, end, iend);
}
char *strstatus (int sts)
{
switch (sts) {
case ASTS_INVALID: return ("invalid");
case ASTS_PLAYING: return ("playing");
case ASTS_PAUSED: return ("paused");
case ASTS_COMPLETED: return ("completed");
case ASTS_ERROR: return ("error");
case ASTS_VOID: return ("void");
default: return ("??");
}
}
int info (char *arg)
{
struct ioc_toc_header h;
struct ioc_vol v;
int rc, i, n, trk, m, s, f;
rc = status (&trk, &m, &s, &f);
if (rc >= 0)
if (verbose)
printf ("Audio status = %d<%s>, current track = %d, current position = %d:%02d.%02d\n",
rc, strstatus (rc), trk, m, s, f);
else
printf ("%d %d %d:%02d.%02d\n", rc, trk, m, s, f);
else
printf ("No current status info\n");
rc = ioctl (fd, CDIOCGETVOL, &v);
if (rc >= 0)
if (verbose)
printf ("Left volume = %d, right volume = %d\n",
v.vol[0], v.vol[1]);
else
printf ("%d %d\n", v.vol[0], v.vol[1]);
else
printf ("No volume info\n");
rc = ioctl (fd, CDIOREADTOCHEADER, &h);
if (rc >= 0)
if (verbose)
printf ("Starting track = %d, ending track = %d, TOC size = %d bytes\n",
h.starting_track, h.ending_track, h.len);
else
printf ("%d %d %d\n", h.starting_track,
h.ending_track, h.len);
else {
perror ("getting toc header");
return (rc);
}
n = h.ending_track - h.starting_track + 1;
rc = read_toc_entrys ((n + 1) * sizeof (struct cd_toc_entry));
if (rc < 0)
return (rc);
if (verbose) {
printf ("track start duration block length type\n");
printf ("-------------------------------------------------\n");
}
for (i = 0; i < n; i++) {
printf ("%5d ", toc_buffer[i].track);
prtrack (toc_buffer + i, 0);
}
printf (" end ");
prtrack (toc_buffer + n, 1);
return (0);
}
void lba2msf (int lba, u_char *m, u_char *s, u_char *f)
{
lba += 150; /* block start offset */
lba &= 0xffffff; /* negative lbas use only 24 bits */
*m = lba / (60 * 75);
lba %= (60 * 75);
*s = lba / 75;
*f = lba % 75;
}
int msf2lba (u_char m, u_char s, u_char f)
{
return (((m * 60) + s) * 75 + f) - 150;
}
void prtrack (struct cd_toc_entry *e, int lastflag)
{
int block, next, len;
u_char m, s, f;
/* Print track start */
printf ("%2d:%02d.%02d ", e->addr.msf.minute,
e->addr.msf.second, e->addr.msf.frame);
block = msf2lba (e->addr.msf.minute, e->addr.msf.second,
e->addr.msf.frame);
if (lastflag) {
/* Last track -- print block */
printf (" - %6d - -\n", block);
return;
}
next = msf2lba (e[1].addr.msf.minute, e[1].addr.msf.second,
e[1].addr.msf.frame);
len = next - block;
lba2msf (len, &m, &s, &f);
/* Print duration, block, length, type */
printf ("%2d:%02d.%02d %6d %6d %5s\n", m, s, f, block, len,
e->control & 4 ? "data" : "audio");
}
int play_track (int tstart, int istart, int tend, int iend)
{
struct ioc_play_track t;
t.start_track = tstart;
t.start_index = istart;
t.end_track = tend;
t.end_index = iend;
return ioctl (fd, CDIOCPLAYTRACKS, &t);
}
int play_blocks (int blk, int len)
{
struct ioc_play_blocks t;
t.blk = blk;
t.len = len;
return ioctl (fd, CDIOCPLAYBLOCKS, &t);
}
int setvol (int l, int r)
{
struct ioc_vol v;
v.vol[0] = l;
v.vol[1] = r;
v.vol[2] = 0;
v.vol[3] = 0;
return ioctl (fd, CDIOCSETVOL, &v);
}
int read_toc_entrys (int len)
{
struct ioc_read_toc_entry t;
t.address_format = CD_MSF_FORMAT;
t.starting_track = 1;
t.data_len = len;
t.data = toc_buffer;
return ioctl (fd, CDIOREADTOCENTRYS, (char *) &t);
}
int play_msf (int start_m, int start_s, int start_f,
int end_m, int end_s, int end_f)
{
struct ioc_play_msf a;
a.start_m = start_m;
a.start_s = start_s;
a.start_f = start_f;
a.end_m = end_m;
a.end_s = end_s;
a.end_f = end_f;
return ioctl (fd, CDIOCPLAYMSF, (char *) &a);
}
int status (int *trk, int *min, int *sec, int *frame)
{
struct ioc_read_subchannel s;
struct cd_sub_channel_info data;
bzero (&s, sizeof (s));
s.data = &data;
s.data_len = sizeof (data);
s.address_format = CD_MSF_FORMAT;
s.data_format = CD_CURRENT_POSITION;
if (ioctl (fd, CDIOCREADSUBCHANNEL, (char *) &s) < 0)
return -1;
*trk = s.data->what.position.track_number;
*min = s.data->what.position.reladdr.msf.minute;
*sec = s.data->what.position.reladdr.msf.second;
*frame = s.data->what.position.reladdr.msf.frame;
return s.data->header.audio_status;
}
char *input (int *cmd)
{
static char buf[80];
char *p;
do {
if (verbose)
fprintf (stderr, "cd> ");
if (! fgets (buf, sizeof (buf), stdin)) {
*cmd = CMD_QUIT;
return 0;
}
p = parse (buf, cmd);
} while (! p);
return (p);
}
char *parse (char *buf, int *cmd)
{
struct cmdtab *c;
char *p;
int len;
for (p=buf; *p; ++p)
if (*p == '\t')
*p = ' ';
else if (*p == '\n')
*p = 0;
for (p=buf; *p; ++p)
if (*p == ' ') {
*p++ = 0;
break;
}
while (*p == ' ')
++p;
len = strlen (buf);
if (! len)
return (0);
*cmd = -1;
for (c=cmdtab; c->name; ++c) {
/* Try short command form. */
if (! c->args && len == strlen (c->name) &&
strncasecmp (buf, c->name, len) == 0) {
*cmd = c->command;
break;
}
/* Try long form. */
if (strncasecmp (buf, c->name, len) != 0)
continue;
/* Check inambiguity. */
if (*cmd != -1) {
fprintf (stderr, "Ambiguous command\n");
return (0);
}
*cmd = c->command;
}
if (*cmd == -1) {
fprintf (stderr, "Invalid command, enter ``help'' for command list\n");
return (0);
}
return p;
}
int open_cd ()
{
char devbuf[80];
if (fd > -1)
return (1);
if (*cdname == '/')
strcpy (devbuf, cdname);
else if (*cdname == 'r')
sprintf (devbuf, "/dev/%s", cdname);
else
sprintf (devbuf, "/dev/r%s", cdname);
fd = open (devbuf, O_RDONLY);
if (fd < 0 && errno == ENOENT) {
strcat (devbuf, "c");
fd = open (devbuf, O_RDONLY);
}
if (fd < 0) {
if (errno != ENXIO) {
perror (devbuf);
exit (1);
}
/* open says 'Device not configured' if no cd in */
fprintf (stderr, "open: No CD in\n");
return (0);
}
return (1);
}