1 /* $NetBSD: cdplay.c,v 1.41 2009/02/22 08:32:25 dholland Exp $ */
4 * Copyright (c) 1999, 2000, 2001 Andrew Doran.
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * Compact Disc Control Utility, originally by Serge V. Vakulenko
32 * <vak@cronyx.ru>. First appeared in FreeBSD under the guise of
33 * cdcontrol(1). Based on the non-X based CD player by Jean-Marc
34 * Zucconi and Andrey A. Chernov. Fixed and further modified on
35 * by Jukka Ukkonen <jau@funet.fi>. Lots of fixes and improvements
36 * made subsequently by The NetBSD Project.
38 * from FreeBSD: cdcontrol.c,v 1.17.2.1 1999/01/31 15:36:01 billf Exp
41 #include <sys/cdefs.h>
43 __RCSID("$NetBSD: cdplay.c,v 1.41 2009/02/22 08:32:25 dholland Exp $");
46 #include <sys/types.h>
48 #include <sys/endian.h>
49 #include <sys/ioctl.h>
53 #include <sys/audioio.h>
54 #include <sys/scsiio.h>
99 { CMD_HELP
, "?", 1, 0 },
100 { CMD_CLOSE
, "close", 1, NULL
},
101 { CMD_DIGITAL
, "digital", 1, "fpw" },
102 { CMD_EJECT
, "eject", 1, NULL
},
103 { CMD_HELP
, "help", 1, NULL
},
104 { CMD_INFO
, "info", 1, NULL
},
105 { CMD_NEXT
, "next", 1, NULL
},
106 { CMD_PAUSE
, "pause", 2, NULL
},
107 { CMD_PLAY
, "play", 1, "min1:sec1[.fram1] [min2:sec2[.fram2]]" },
108 { CMD_PLAY
, "play", 1, "track1[.index1] [track2[.index2]]" },
109 { CMD_PLAY
, "play", 1, "tr1 m1:s1[.f1] [[tr2] [m2:s2[.f2]]]" },
110 { CMD_PLAY
, "play", 1, "[#block [len]]" },
111 { CMD_PREV
, "prev", 2, NULL
},
112 { CMD_QUIT
, "quit", 1, NULL
},
113 { CMD_RESET
, "reset", 4, NULL
},
114 { CMD_RESUME
, "resume", 4, NULL
},
115 { CMD_SET
, "set", 2, "msf | lba" },
116 { CMD_SHUFFLE
, "shuffle", 2, NULL
},
117 { CMD_SINGLE
, "single", 2, "[<track>]" },
118 { CMD_SKIP
, "skip", 2, NULL
},
119 { CMD_STATUS
, "status", 3, NULL
},
120 { CMD_STOP
, "stop", 3, NULL
},
121 { CMD_VOLUME
, "volume", 1, "<l> <r>|left|right|mute|mono|stereo" },
124 #define IOCTL_SIMPLE(fd, ctl) \
126 if (ioctl((fd), (ctl)) >= 0) { \
130 warn("ioctl(" #ctl ")");\
131 } while (/* CONSTCOND */ 0)
133 #define CDDA_SIZE 2352
135 #define CD_MAX_TRACK 99 /* largest 2 digit BCD number */
137 struct cd_toc_entry toc_buffer
[CD_MAX_TRACK
+ 1];
146 struct itimerval itv_timer
;
149 u_char
*audata
, *aubuf
;
152 int lba_start
, lba_end
, lba_current
;
153 int lowat
, hiwat
, readseek
, playseek
;
154 int playing
, changed
;
162 int get_vol(int *, int *);
163 int get_status(int *, int *, int *, int *, int *);
165 int info(const char *);
166 void lba2msf(u_long
, u_int
*, u_int
*, u_int
*);
167 int main(int, char **);
168 u_int
msf2lba(u_int
, u_int
, u_int
);
171 const char *parse(char *, int *);
172 int play(const char *, int);
173 int play_blocks(int, int);
174 int play_digital(int, int);
175 int play_msf(int, int, int, int, int, int);
176 int play_track(int, int, int, int);
177 int print_status(const char *);
178 void print_track(struct cd_toc_entry
*);
179 const char *prompt(void);
180 int readaudio(int, int, int, u_char
*);
181 int read_toc_entrys(int);
182 int run(int, const char *);
183 int setvol(int, int);
186 const char *strstatus(int);
189 void toc2msf(u_int
, u_int
*, u_int
*, u_int
*);
191 void addmsf(u_int
*, u_int
*, u_int
*, u_int
, u_int
, u_int
);
194 main(int argc
, char **argv
)
198 static char defdev
[16];
203 struct sigaction sa_timer
;
205 cdname
= getenv("MUSIC_CD");
207 cdname
= getenv("CD_DRIVE");
209 cdname
= getenv("DISC");
211 cdname
= getenv("CDPLAY");
213 da
.auname
= getenv("AUDIODEV");
215 da
.auname
= getenv("SPEAKER");
217 da
.auname
= "/dev/sound";
219 while ((c
= getopt(argc
, argv
, "a:f:h")) != -1)
235 if (argc
> 0 && strcasecmp(*argv
, "help") == 0)
238 if (cdname
== NULL
) {
239 snprintf(defdev
, sizeof(defdev
), "cd0%c",
240 'a' + getrawpartition());
245 srandom((u_long
)time(NULL
));
250 for (p
= buf
; argc
-- > 0; argv
++) {
253 if (p
+ len
>= buf
+ sizeof(buf
) - 1)
258 strlcpy(p
, *argv
, sizeof(buf
) - (p
- buf
));
262 arg
= parse(buf
, &cmd
);
263 return (run(cmd
, arg
));
266 setbuf(stdout
, NULL
);
267 printf("Type `?' for command list\n\n");
269 hist
= history_init();
270 history(hist
, &he
, H_SETSIZE
, 100); /* 100 elt history buffer */
271 elptr
= el_init(getprogname(), stdin
, stdout
, stderr
);
272 el_set(elptr
, EL_EDITOR
, "emacs");
273 el_set(elptr
, EL_PROMPT
, prompt
);
274 el_set(elptr
, EL_HIST
, history
, hist
);
275 el_set(elptr
, EL_SIGNAL
, 1);
276 el_source(elptr
, NULL
);
278 sigemptyset(&sa_timer
.sa_mask
);
279 sa_timer
.sa_handler
= sig_timer
;
280 sa_timer
.sa_flags
= SA_RESTART
;
281 if ((rv
= sigaction(SIGALRM
, &sa_timer
, NULL
)) < 0)
282 err(EXIT_FAILURE
, "sigaction()");
288 if (((elline
= el_gets(elptr
, &scratch
)) != NULL
)
290 history(hist
, &he
, H_ENTER
, elline
);
291 line
= strdup(elline
);
293 arg
= parse(line
, &cmd
);
302 } while (arg
== NULL
);
304 if (run(cmd
, arg
) < 0) {
321 fprintf(stderr
, "usage: cdplay [-a audio_device] [-f cd_device] [command ...]\n");
329 const struct cmdtab
*c
, *mc
;
333 mc
= cmdtab
+ sizeof(cmdtab
) / sizeof(cmdtab
[0]);
334 for (c
= cmdtab
; c
< mc
; c
++) {
335 for (i
= c
->min
, s
= c
->name
; *s
!= '\0'; s
++, i
--) {
336 n
= (i
> 0 ? toupper((unsigned char)*s
) : *s
);
340 printf(" %s", c
->args
);
344 "\nThe word \"play\" is not required for the play commands.\n"
345 "The plain target address is taken as a synonym for play.\n");
349 run(int cmd
, const char *arg
)
354 if (cmd
== CMD_QUIT
) {
360 if (fd
< 0 && !opencd())
369 rv
= print_status(arg
);
376 } else if ((rv
= ioctl(fd
, CDIOCPAUSE
)) < 0)
377 warn("ioctl(CDIOCPAUSE)");
384 } else if ((rv
= ioctl(fd
, CDIOCRESUME
)) < 0)
385 warn("ioctl(CDIOCRESUME)");
393 if ((rv
= ioctl(fd
, CDIOCSTOP
)) < 0)
394 warn("ioctl(CDIOCSTOP)");
395 if (ioctl(fd
, CDIOCALLOW
) < 0)
396 warn("ioctl(CDIOCALLOW)");
402 IOCTL_SIMPLE(fd
, CDIOCRESET
);
410 run(CMD_SHUFFLE
, NULL
);
411 if (ioctl(fd
, CDIOCALLOW
) < 0)
412 warn("ioctl(CDIOCALLOW)");
413 IOCTL_SIMPLE(fd
, CDIOCEJECT
);
417 ioctl(fd
, CDIOCALLOW
);
418 IOCTL_SIMPLE(fd
, CDIOCCLOSE
);
419 if (interactive
&& fd
== -1)
424 while (isspace((unsigned char)*arg
))
438 if (interactive
== 0)
440 "'single' valid only in interactive mode");
443 if (interactive
== 0)
445 "`shuffle' valid only in interactive mode");
448 itv_timer
.it_interval
.tv_sec
= 1;
449 itv_timer
.it_interval
.tv_usec
= 0;
450 itv_timer
.it_value
.tv_sec
= 1;
451 itv_timer
.it_value
.tv_usec
= 0;
452 if (setitimer(ITIMER_REAL
, &itv_timer
, NULL
) == 0) {
453 if (cmd
== CMD_SHUFFLE
) {
456 while (isspace((unsigned char)*arg
))
458 shuffle
= -atoi(arg
);
462 shuffle
= cmd
== CMD_SINGLE
? -atoi(arg
) : 1;
469 itv_timer
.it_interval
.tv_sec
= 0;
470 itv_timer
.it_interval
.tv_usec
= 0;
471 itv_timer
.it_value
.tv_sec
= 0;
472 itv_timer
.it_value
.tv_usec
= 0;
473 setitimer(ITIMER_REAL
, &itv_timer
, NULL
);
478 printf("single track:\t%d\n", -shuffle
);
480 printf("shuffle play:\t%s\n", (shuffle
!= 0) ? "on" : "off");
486 int fpw
, intv_usecs
, hz_usecs
;
495 /* real rate: 75 frames per second */
496 intv_usecs
= 13333 * da
.fpw
;
498 * interrupt earlier for safety, by a value which
499 * doesn't hurt interactice response if we block
500 * in the signal handler
503 hz_usecs
= 1000000 / sysconf(_SC_CLK_TCK
);
504 if (intv_usecs
< hz_usecs
) {
505 /* can't have a shorter interval, increase
506 buffer size to compensate */
507 da
.fpw
+= (hz_usecs
- intv_usecs
) / 13333;
508 intv_usecs
= hz_usecs
;
511 da
.aubuf
= malloc(da
.fpw
* CDDA_SIZE
);
512 if (da
.aubuf
== NULL
) {
513 warn("Not enough memory for audio buffers");
516 if (da
.afd
== -1 && !openaudio()) {
517 warn("Cannot open audio device");
520 itv_timer
.it_interval
.tv_sec
= itv_timer
.it_value
.tv_sec
=
521 intv_usecs
/ 1000000;
522 itv_timer
.it_interval
.tv_usec
= itv_timer
.it_value
.tv_usec
=
523 intv_usecs
% 1000000;
524 rv
= setitimer(ITIMER_REAL
, &itv_timer
, NULL
);
528 warnx("setitimer in CMD_DIGITAL");
533 itv_timer
.it_interval
.tv_sec
= itv_timer
.it_value
.tv_sec
= 1;
535 itv_timer
.it_interval
.tv_sec
= itv_timer
.it_value
.tv_sec
= 0;
536 itv_timer
.it_interval
.tv_usec
= itv_timer
.it_value
.tv_usec
= 0;
538 rv
= setitimer(ITIMER_REAL
, &itv_timer
, NULL
);
548 "`skip' valid only in interactive mode");
550 warnx("`skip' valid only in shuffle mode");
557 if (strcasecmp(arg
, "msf") == 0)
559 else if (strcasecmp(arg
, "lba") == 0)
562 warnx("invalid command arguments");
568 warnx("`volume' is ignored while in digital xfer mode");
569 } else if (strncasecmp(arg
, "left", strlen(arg
)) == 0)
570 rv
= ioctl(fd
, CDIOCSETLEFT
);
571 else if (strncasecmp(arg
, "right", strlen(arg
)) == 0)
572 rv
= ioctl(fd
, CDIOCSETRIGHT
);
573 else if (strncasecmp(arg
, "mono", strlen(arg
)) == 0)
574 rv
= ioctl(fd
, CDIOCSETMONO
);
575 else if (strncasecmp(arg
, "stereo", strlen(arg
)) == 0)
576 rv
= ioctl(fd
, CDIOCSETSTEREO
);
577 else if (strncasecmp(arg
, "mute", strlen(arg
)) == 0)
578 rv
= ioctl(fd
, CDIOCSETMUTE
);
581 if (sscanf(arg
, "%d %d", &l
, &r
) != 2) {
582 if (sscanf(arg
, "%d", &l
) == 1)
585 warnx("invalid command arguments");
604 play(const char *arg
, int fromuser
)
606 int rv
, start
, end
, istart
, iend
, blk
, len
, relend
;
607 u_int n
, tr1
, tr2
, m1
, m2
, s1
, s2
, f1
, f2
, tm
, ts
, tf
;
608 struct ioc_toc_header h
;
610 if (shuffle
&& fromuser
) {
611 warnx("`play' not valid in shuffle mode");
615 if ((rv
= ioctl(fd
, CDIOREADTOCHEADER
, &h
)) < 0) {
616 warn("ioctl(CDIOREADTOCHEADER)");
622 n
= h
.ending_track
- h
.starting_track
+ 1;
623 rv
= read_toc_entrys((n
+ 1) * sizeof(struct cd_toc_entry
));
627 if (arg
== NULL
|| *arg
== '\0') {
628 /* Play the whole disc */
629 return (play_track(h
.starting_track
, 1, h
.ending_track
, 99));
632 if (strchr(arg
, '#') != NULL
) {
633 /* Play block #blk [ len ] */
636 if (2 != sscanf(arg
, "#%d%d", &blk
, &len
) &&
637 1 != sscanf(arg
, "#%d", &blk
))
643 return (play_blocks(blk
, len
));
646 if (strchr(arg
, ':') != NULL
) {
648 * Play MSF m1:s1 [ .f1 ] [ m2:s2 [ .f2 ] ]
650 * Will now also undestand timed addresses relative
651 * to the beginning of a track in the form...
653 * tr1 m1:s1[.f1] [[tr2] [m2:s2[.f2]]]
656 tr2
= m2
= s2
= f2
= f1
= 0;
657 if (8 == sscanf(arg
, "%d %d:%d.%d %d %d:%d.%d", &tr1
, &m1
,
658 &s1
, &f1
, &tr2
, &m2
, &s2
, &f2
))
659 goto Play_Relative_Addresses
;
661 tr2
= m2
= s2
= f2
= f1
= 0;
662 if (7 == sscanf(arg
, "%d %d:%d %d %d:%d.%d", &tr1
, &m1
, &s1
,
663 &tr2
, &m2
, &s2
, &f2
))
664 goto Play_Relative_Addresses
;
666 tr2
= m2
= s2
= f2
= f1
= 0;
667 if (7 == sscanf(arg
, "%d %d:%d.%d %d %d:%d", &tr1
, &m1
, &s1
,
668 &f1
, &tr2
, &m2
, &s2
))
669 goto Play_Relative_Addresses
;
671 tr2
= m2
= s2
= f2
= f1
= 0;
672 if (7 == sscanf(arg
, "%d %d:%d.%d %d:%d.%d", &tr1
, &m1
, &s1
,
674 goto Play_Relative_Addresses
;
676 tr2
= m2
= s2
= f2
= f1
= 0;
677 if (6 == sscanf(arg
, "%d %d:%d.%d %d:%d", &tr1
, &m1
, &s1
, &f1
,
679 goto Play_Relative_Addresses
;
681 tr2
= m2
= s2
= f2
= f1
= 0;
682 if (6 == sscanf(arg
, "%d %d:%d %d:%d.%d", &tr1
, &m1
, &s1
, &m2
,
684 goto Play_Relative_Addresses
;
686 tr2
= m2
= s2
= f2
= f1
= 0;
687 if (6 == sscanf(arg
, "%d %d:%d.%d %d %d", &tr1
, &m1
, &s1
, &f1
,
689 goto Play_Relative_Addresses
;
691 tr2
= m2
= s2
= f2
= f1
= 0;
692 if (6 == sscanf(arg
, "%d %d:%d %d %d:%d", &tr1
, &m1
, &s1
, &tr2
,
694 goto Play_Relative_Addresses
;
696 tr2
= m2
= s2
= f2
= f1
= 0;
697 if (5 == sscanf(arg
, "%d %d:%d %d:%d", &tr1
, &m1
, &s1
, &m2
,
699 goto Play_Relative_Addresses
;
701 tr2
= m2
= s2
= f2
= f1
= 0;
702 if (5 == sscanf(arg
, "%d %d:%d %d %d", &tr1
, &m1
, &s1
, &tr2
,
704 goto Play_Relative_Addresses
;
707 tr2
= m2
= s2
= f2
= f1
= 0;
708 if (5 == sscanf(arg
, "%d %d:%d.%d %d", &tr1
, &m1
, &s1
, &f1
,
710 goto Play_Relative_Addresses
;
712 tr2
= m2
= s2
= f2
= f1
= 0;
713 if (4 == sscanf(arg
, "%d %d:%d %d", &tr1
, &m1
, &s1
, &tr2
))
714 goto Play_Relative_Addresses
;
716 tr2
= m2
= s2
= f2
= f1
= 0;
717 if (4 == sscanf(arg
, "%d %d:%d.%d", &tr1
, &m1
, &s1
, &f1
))
718 goto Play_Relative_Addresses
;
720 tr2
= m2
= s2
= f2
= f1
= 0;
721 if (3 == sscanf(arg
, "%d %d:%d", &tr1
, &m1
, &s1
))
722 goto Play_Relative_Addresses
;
724 tr2
= m2
= s2
= f2
= f1
= 0;
725 goto Try_Absolute_Timed_Addresses
;
727 Play_Relative_Addresses
:
733 toc2msf(tr1
-1, &tm
, &ts
, &tf
);
734 addmsf(&m1
, &s1
, &f1
, tm
, ts
, tf
);
736 toc2msf(tr1
, &tm
, &ts
, &tf
);
738 if ((m1
> tm
) || ((m1
== tm
) && ((s1
> ts
) || ((s1
== ts
) &&
740 warnx("Track %d is not that long.", tr1
);
743 tr1
--; /* XXXXX ???? */
750 addmsf(&m2
, &s2
, &f2
, m1
, s1
, f1
);
754 toc2msf(n
, &m2
, &s2
, &f2
);
764 toc2msf(tr2
, &tm
, &ts
, &tf
);
765 addmsf(&m2
, &s2
, &f2
, tm
, ts
, tf
);
769 toc2msf(n
, &tm
, &ts
, &tf
);
771 if ((tr2
< n
) && ((m2
> tm
) || ((m2
== tm
) && ((s2
> ts
) ||
772 ((s2
== ts
) && (f2
> tf
)))))) {
773 warnx("The playing time of the disc is not that long.");
777 return (play_msf(m1
, s1
, f1
, m2
, s2
, f2
));
779 Try_Absolute_Timed_Addresses
:
782 if (6 != sscanf(arg
, "%d:%d.%d%d:%d.%d",
783 &m1
, &s1
, &f1
, &m2
, &s2
, &f2
) &&
784 5 != sscanf(arg
, "%d:%d.%d%d:%d", &m1
, &s1
, &f1
, &m2
, &s2
) &&
785 5 != sscanf(arg
, "%d:%d%d:%d.%d", &m1
, &s1
, &m2
, &s2
, &f2
) &&
786 3 != sscanf(arg
, "%d:%d.%d", &m1
, &s1
, &f1
) &&
787 4 != sscanf(arg
, "%d:%d%d:%d", &m1
, &s1
, &m2
, &s2
) &&
788 2 != sscanf(arg
, "%d:%d", &m1
, &s1
))
791 if (m2
== UINT_MAX
) {
793 m2
= toc_buffer
[n
].addr
.msf
.minute
;
794 s2
= toc_buffer
[n
].addr
.msf
.second
;
795 f2
= toc_buffer
[n
].addr
.msf
.frame
;
797 lba2msf(toc_buffer
[n
].addr
.lba
, &tm
, &ts
, &tf
);
803 return (play_msf(m1
, s1
, f1
, m2
, s2
, f2
));
807 * Play track trk1 [ .idx1 ] [ trk2 [ .idx2 ] ]
809 if (4 != sscanf(arg
, "%d.%d%d.%d", &start
, &istart
, &end
, &iend
) &&
810 3 != sscanf(arg
, "%d.%d%d", &start
, &istart
, &end
) &&
811 3 != sscanf(arg
, "%d%d.%d", &start
, &end
, &iend
) &&
812 2 != sscanf(arg
, "%d.%d", &start
, &istart
) &&
813 2 != sscanf(arg
, "%d%d", &start
, &end
) &&
814 1 != sscanf(arg
, "%d", &start
))
819 return (play_track(start
, istart
, end
, iend
));
822 warnx("invalid command arguments");
829 int aulen
, auwr
, fpw
;
832 sigpending(&anymore
);
833 if (sigismember(&anymore
, SIGALRM
))
839 da
.lba_current
= da
.lba_start
;
842 /* read frames into circular buffer */
843 fpw
= da
.lba_end
- da
.lba_current
+ 1;
847 aulen
= readaudio(fd
, da
.lba_current
, fpw
, da
.aubuf
);
849 auwr
= write(da
.afd
, da
.aubuf
, aulen
);
850 da
.lba_current
+= fpw
;
853 if (da
.lba_current
> da
.lba_end
)
861 setitimer(ITIMER_REAL
, &itv_timer
, NULL
);
865 skip(int dir
, int fromuser
)
868 int rv
, trk
, idx
, m
, s
, f
;
869 struct ioc_toc_header h
;
871 if ((rv
= ioctl(fd
, CDIOREADTOCHEADER
, &h
)) < 0) {
872 warn("ioctl(CDIOREADTOCHEADER)");
875 if ((rv
= get_status(&trk
, &idx
, &m
, &s
, &f
)) < 0)
878 if (dir
== 0 || shuffle
!= 0) {
879 if (fromuser
|| (rv
!= CD_AS_PLAY_IN_PROGRESS
&&
880 rv
!= CD_AS_PLAY_PAUSED
))
881 trk
= shuffle
< 0 ? (-shuffle
) :
882 (int)((h
.starting_track
+
883 arc4random() % (h
.ending_track
- h
.starting_track
+ 1)));
888 if (trk
> h
.ending_track
)
889 trk
= h
.starting_track
;
890 else if(trk
< h
.starting_track
)
891 trk
= h
.ending_track
;
895 snprintf(str
, sizeof(str
), "%d %d", trk
, trk
);
897 snprintf(str
, sizeof(str
), "%d", trk
);
899 return (play(str
, 0));
908 case CD_AS_AUDIO_INVALID
:
911 case CD_AS_PLAY_IN_PROGRESS
:
914 case CD_AS_PLAY_PAUSED
:
917 case CD_AS_PLAY_COMPLETED
:
920 case CD_AS_PLAY_ERROR
:
923 case CD_AS_NO_STATUS
:
935 print_status(const char *arg
)
937 struct cd_sub_channel_info data
;
938 struct ioc_read_subchannel ss
;
939 int rv
, trk
, idx
, m
, s
, f
;
942 if ((rv
= get_status(&trk
, &idx
, &m
, &s
, &f
)) >= 0) {
943 printf("audio status:\t%s\n", strstatus(rv
));
944 printf("current track:\t%d\n", trk
);
946 printf("current index:\t%d\n", idx
);
947 printf("position:\t%d:%02d.%02d\n", m
, s
, f
);
949 printf("audio status:\tno info available\n");
952 printf("single track:\t%d\n", -shuffle
);
954 printf("shuffle play:\t%s\n", (shuffle
!= 0) ? "on" : "off");
956 printf("digital xfer:\tto %s "
957 "(%d frames per wakeup, %lld.%06lds period)\n",
959 (long long)itv_timer
.it_interval
.tv_sec
,
961 (long)itv_timer
.it_interval
.tv_usec
);
963 printf("digital xfer:\toff\n");
965 bzero(&ss
, sizeof(ss
));
967 ss
.data_len
= sizeof(data
);
968 ss
.address_format
= msf
? CD_MSF_FORMAT
: CD_LBA_FORMAT
;
969 ss
.data_format
= CD_MEDIA_CATALOG
;
971 if (!digital
&& ioctl(fd
, CDIOCREADSUBCHANNEL
, (char *) &ss
) >= 0) {
972 printf("media catalog:\t%sactive",
973 ss
.data
->what
.media_catalog
.mc_valid
? "" : "in");
974 if (ss
.data
->what
.media_catalog
.mc_valid
&&
975 ss
.data
->what
.media_catalog
.mc_number
[0])
977 ss
.data
->what
.media_catalog
.mc_number
);
980 printf("media catalog:\tnone\n");
984 if (ioctl(fd
, CDIOCGETVOL
, &v
) >= 0) {
985 printf("left volume:\t%d\n", v
.vol
[0]);
986 printf("right volume:\t%d\n", v
.vol
[1]);
988 printf("left volume:\tnot available\n");
989 printf("right volume:\tnot available\n");
996 info(const char *arg
)
998 struct ioc_toc_header h
;
1001 if ((rc
= ioctl(fd
, CDIOREADTOCHEADER
, &h
)) < 0) {
1002 warn("ioctl(CDIOREADTOCHEADER)");
1006 n
= h
.ending_track
- h
.starting_track
+ 1;
1007 rc
= read_toc_entrys((n
+ 1) * sizeof(struct cd_toc_entry
));
1011 printf("track start duration block length type\n");
1012 printf("--------------------------------------------------\n");
1014 for (i
= 0; i
< n
; i
++) {
1015 printf("%5d ", toc_buffer
[i
].track
);
1016 print_track(toc_buffer
+ i
);
1018 printf(" - "); /* Lead-out area */
1019 print_track(toc_buffer
+ n
);
1024 lba2msf(u_long lba
, u_int
*m
, u_int
*s
, u_int
*f
)
1027 lba
+= 150; /* block start offset */
1028 lba
&= 0xffffff; /* negative lbas use only 24 bits */
1029 *m
= lba
/ (60 * 75);
1036 msf2lba(u_int m
, u_int s
, u_int f
)
1039 return (((m
* 60) + s
) * 75 + f
) - 150;
1043 print_track(struct cd_toc_entry
*e
)
1045 int block
, next
, len
;
1049 /* Print track start */
1050 printf("%2d:%02d.%02d ", e
->addr
.msf
.minute
,
1051 e
->addr
.msf
.second
, e
->addr
.msf
.frame
);
1053 block
= msf2lba(e
->addr
.msf
.minute
, e
->addr
.msf
.second
,
1056 block
= e
->addr
.lba
;
1057 lba2msf(block
, &m
, &s
, &f
);
1058 /* Print track start */
1059 printf("%2d:%02d.%02d ", m
, s
, f
);
1061 if (e
->track
> CD_MAX_TRACK
) {
1062 /* lead-out area -- print block */
1063 printf(" - %6d - lead-out\n", block
);
1067 next
= msf2lba(e
[1].addr
.msf
.minute
, e
[1].addr
.msf
.second
,
1068 e
[1].addr
.msf
.frame
);
1070 next
= e
[1].addr
.lba
;
1072 /* XXX: take into account the 150 frame start offset time */
1073 /* XXX: this is a mis-use of lba2msf() because 'len' is a */
1074 /* XXX: length in frames and not a LBA! */
1075 lba2msf(len
- 150, &m
, &s
, &f
);
1077 /* Print duration, block, length, type */
1078 printf("%2d:%02d.%02d %6d %6d %8s\n", m
, s
, f
, block
, len
,
1079 (e
->control
& 4) ? "data" : "audio");
1083 play_track(int tstart
, int istart
, int tend
, int iend
)
1085 struct ioc_play_track t
;
1091 return (play_msf(toc_buffer
[tstart
].addr
.msf
.minute
,
1092 toc_buffer
[tstart
].addr
.msf
.second
, toc_buffer
[tstart
].addr
.msf
.frame
,
1093 toc_buffer
[tend
].addr
.msf
.minute
, toc_buffer
[tend
].addr
.msf
.second
,
1094 toc_buffer
[tend
].addr
.msf
.frame
));
1096 return (play_digital(toc_buffer
[tstart
].addr
.lba
,
1097 toc_buffer
[tend
].addr
.lba
));
1099 t
.start_track
= tstart
;
1100 t
.start_index
= istart
;
1104 if ((rv
= ioctl(fd
, CDIOCPLAYTRACKS
, &t
)) < 0)
1105 warn("ioctl(CDIOCPLAYTRACKS)");
1110 play_blocks(int blk
, int len
)
1112 struct ioc_play_blocks t
;
1118 if ((rv
= ioctl(fd
, CDIOCPLAYBLOCKS
, &t
)) < 0)
1119 warn("ioctl(CDIOCPLAYBLOCKS");
1124 play_digital(start
, end
)
1127 da
.lba_start
= start
;
1129 da
.changed
= da
.playing
= 1;
1134 setvol(int left
, int right
)
1144 if ((rv
= ioctl(fd
, CDIOCSETVOL
, &v
)) < 0)
1145 warn("ioctl(CDIOCSETVOL)");
1150 read_toc_entrys(int len
)
1152 struct ioc_read_toc_entry t
;
1155 t
.address_format
= msf
? CD_MSF_FORMAT
: CD_LBA_FORMAT
;
1156 t
.starting_track
= 0;
1158 t
.data
= toc_buffer
;
1160 if ((rv
= ioctl(fd
, CDIOREADTOCENTRYS
, &t
)) < 0)
1161 warn("ioctl(CDIOREADTOCENTRYS)");
1167 play_msf(int start_m
, int start_s
, int start_f
, int end_m
, int end_s
,
1170 struct ioc_play_msf a
;
1174 return (play_digital(msf2lba(start_m
, start_s
, start_f
),
1175 msf2lba(end_m
, end_s
, end_f
)));
1176 a
.start_m
= start_m
;
1177 a
.start_s
= start_s
;
1178 a
.start_f
= start_f
;
1183 if ((rv
= ioctl(fd
, CDIOCPLAYMSF
, &a
)) < 0)
1184 warn("ioctl(CDIOCPLAYMSF)");
1189 get_status(int *trk
, int *idx
, int *min
, int *sec
, int *frame
)
1191 struct ioc_read_subchannel s
;
1192 struct cd_sub_channel_info data
;
1193 struct ioc_toc_header h
;
1200 if ((rc
= ioctl(fd
, CDIOREADTOCHEADER
, &h
)) < 0) {
1201 warn("ioctl(CDIOREADTOCHEADER)");
1205 n
= h
.ending_track
- h
.starting_track
+ 1;
1206 rc
= read_toc_entrys((n
+ 1) * sizeof(struct cd_toc_entry
));
1211 #define SWAPLBA(x) (msf?be32toh(x):(x))
1212 if (digital
&& da
.playing
) {
1213 lba
= da
.lba_current
+ 150;
1214 for (i
= 1; i
< 99; i
++) {
1215 if (lba
< SWAPLBA(toc_buffer
[i
].addr
.lba
)) {
1216 lba
-= SWAPLBA(toc_buffer
[i
- 1].addr
.lba
);
1221 lba2msf(lba
- 150, &mm
, &ss
, &ff
);
1226 return CD_AS_PLAY_IN_PROGRESS
;
1228 bzero(&s
, sizeof(s
));
1230 s
.data_len
= sizeof(data
);
1231 s
.address_format
= msf
? CD_MSF_FORMAT
: CD_LBA_FORMAT
;
1232 s
.data_format
= CD_CURRENT_POSITION
;
1234 if ((rv
= ioctl(fd
, CDIOCREADSUBCHANNEL
, &s
)) < 0) {
1235 warn("ioctl(CDIOCREADSUBCHANNEL)");
1239 *trk
= s
.data
->what
.position
.track_number
;
1240 *idx
= s
.data
->what
.position
.index_number
;
1242 *min
= s
.data
->what
.position
.reladdr
.msf
.minute
;
1243 *sec
= s
.data
->what
.position
.reladdr
.msf
.second
;
1244 *frame
= s
.data
->what
.position
.reladdr
.msf
.frame
;
1246 lba2msf(s
.data
->what
.position
.reladdr
.lba
, &mm
,
1253 return (s
.data
->header
.audio_status
);
1260 return ("cdplay> ");
1264 parse(char *buf
, int *cmd
)
1266 const struct cmdtab
*c
, *mc
;
1270 for (p
= buf
; isspace((unsigned char)*p
); p
++)
1273 if (isdigit((unsigned char)*p
) || (p
[0] == '#' && isdigit((unsigned char)p
[1]))) {
1278 for (buf
= p
; *p
!= '\0' && !isspace((unsigned char)*p
); p
++)
1281 if ((len
= p
- buf
) == 0)
1284 if (*p
!= '\0') { /* It must be a spacing character! */
1286 for (q
= p
; *q
!= '\0' && *q
!= '\n' && *q
!= '\r'; q
++)
1293 mc
= cmdtab
+ sizeof(cmdtab
) / sizeof(cmdtab
[0]);
1294 for (c
= cmdtab
; c
< mc
; c
++) {
1295 /* Is it an exact match? */
1296 if (strcasecmp(buf
, c
->name
) == 0) {
1300 /* Try short hand forms then... */
1301 if (len
>= c
->min
&& strncasecmp(buf
, c
->name
, len
) == 0) {
1302 if (*cmd
!= -1 && *cmd
!= (int)c
->command
) {
1303 warnx("ambiguous command");
1311 warnx("invalid command, enter ``help'' for commands");
1315 while (isspace((unsigned char)*p
))
1328 fd
= opendisk(cdname
, O_RDONLY
, devbuf
, sizeof(devbuf
), 0);
1330 if (errno
== ENXIO
) {
1332 * ENXIO has an overloaded meaning here. The
1333 * original "Device not configured" should be
1334 * interpreted as "No disc in drive %s".
1336 warnx("no disc in drive %s", devbuf
);
1339 err(EXIT_FAILURE
, "%s", devbuf
);
1348 audio_encoding_t ae
;
1353 da
.afd
= open(da
.auname
, O_WRONLY
);
1358 AUDIO_INITINFO(&ai
);
1361 rc
= ioctl(da
.afd
, AUDIO_GETENC
, &ae
);
1363 if (ae
.encoding
== AUDIO_ENCODING_SLINEAR_LE
&& ae
.precision
== 16)
1366 rc
= ioctl(da
.afd
, AUDIO_GETENC
, &ae
);
1369 warn("No suitable audio encoding found!");
1374 ai
.mode
= AUMODE_PLAY_ALL
;
1375 ai
.play
.sample_rate
= 44100;
1376 ai
.play
.channels
= 2;
1377 ai
.play
.precision
= 16;
1378 ai
.play
.encoding
= AUDIO_ENCODING_SLINEAR_LE
;
1380 rc
= ioctl(da
.afd
, AUDIO_SETINFO
, &ai
);
1382 warn("AUDIO_SETINFO");
1391 readaudio(afd
, lba
, blocks
, data
)
1392 int afd
, lba
, blocks
;
1398 memset(&sc
, 0, sizeof(sc
));
1401 sc
.cmd
[2] = (lba
>> 24) & 0xff;
1402 sc
.cmd
[3] = (lba
>> 16) & 0xff;
1403 sc
.cmd
[4] = (lba
>> 8) & 0xff;
1404 sc
.cmd
[5] = lba
& 0xff;
1405 sc
.cmd
[6] = (blocks
>> 16) & 0xff;
1406 sc
.cmd
[7] = (blocks
>> 8) & 0xff;
1407 sc
.cmd
[8] = blocks
& 0xff;
1411 sc
.databuf
= (caddr_t
) data
;
1412 sc
.datalen
= CDDA_SIZE
* blocks
;
1413 sc
.senselen
= sizeof(sc
.sense
);
1414 sc
.flags
= SCCMD_READ
;
1415 sc
.timeout
= 10000; /* 10s */
1416 rc
= ioctl(afd
, SCIOCCOMMAND
, &sc
);
1417 if (rc
< 0 || sc
.retsts
!= SCCMD_OK
) {
1418 if (da
.read_errors
< 10) {
1419 warnx("scsi cmd failed: retsts %d status %d\n",
1420 sc
.retsts
, sc
.status
);
1425 return CDDA_SIZE
* blocks
;
1429 toc2msf(u_int i
, u_int
*m
, u_int
*s
, u_int
*f
)
1431 struct cd_toc_entry
*ctep
;
1433 assert(i
<= CD_MAX_TRACK
);
1435 ctep
= &toc_buffer
[i
];
1438 *m
= ctep
->addr
.msf
.minute
;
1439 *s
= ctep
->addr
.msf
.second
;
1440 *f
= ctep
->addr
.msf
.frame
;
1442 lba2msf(ctep
->addr
.lba
, m
, s
, f
);
1449 struct cd_toc_entry
*ctep
;
1452 assert(i
<= CD_MAX_TRACK
);
1454 ctep
= &toc_buffer
[i
-1];
1458 ctep
->addr
.msf
.minute
,
1459 ctep
->addr
.msf
.second
,
1460 ctep
->addr
.msf
.frame
);
1462 return (ctep
->addr
.lba
);
1467 addmsf(u_int
*m
, u_int
*s
, u_int
*f
, u_int m2
, u_int s2
, u_int f2
)