? cdio ? cdio.cat1 Index: cdio.1 =================================================================== RCS file: /cvs/openbsd/src/usr.bin/cdio/cdio.1,v retrieving revision 1.46 diff -u -r1.46 cdio.1 --- cdio.1 22 Jul 2007 21:27:55 -0000 1.46 +++ cdio.1 2 Aug 2007 20:26:03 -0000 @@ -105,6 +105,22 @@ If range is specified in descending order tracks will be played in descending order. If the first value in the range is omitted, tracks from first track on disk to the specified one will be played. If the last value in the range is omitted, tracks from the specified track to the last track on disk will be played. +.Pp +.Ic cdplay +supports keyboard control: +.Bl -tag -width "[space]XXX" -compact -offset indent +.It Cm [space] +Pause. +.It Cm < +Rewind 10 seconds. +.It Cm > +Forward 10 seconds. +.It Cm n +Jump to the next track. +.It Cm q +Exit from +.Ic cdplay . +.El .It Ic cdrip Op Ar track1-trackN ... Rip specified tracks from disk. Audio tracks are saved as WAVE sound files. Index: rip.c =================================================================== RCS file: /cvs/openbsd/src/usr.bin/cdio/rip.c,v retrieving revision 1.7 diff -u -r1.7 rip.c --- rip.c 2 Aug 2007 07:31:16 -0000 1.7 +++ rip.c 2 Aug 2007 20:26:06 -0000 @@ -37,6 +37,7 @@ #include #include #include +#include #include extern int fd; @@ -93,22 +94,43 @@ u_int32_t end_lba; /* starting address of the next track */ }; -int read_track(int fd, struct track_info *ti); +struct rip_func { + int (*next_track)(struct track_info *); + int (*next_sector)(void); +}; -int rip_next_track(struct track_info *info); -int play_next_track(struct track_info *info); +static int read_track(int fd, struct track_info *ti, + struct rip_func *func); -static int rip_tracks_loop(struct track_pair *tp, u_int n_tracks, - int (*next_track)(struct track_info *)); +#define READTRK_OK 0 +#define READTRK_FAIL 1 +#define READTRK_EXIT 2 +#define READTRK_FORWARD 3 +#define READTRK_REWIND 4 +#define READTRK_NEXT 5 + +static int rip_next_track(struct track_info *info); +static int play_next_track(struct track_info *info); +static int rip_next_sector(void); +static int play_next_sector(void); -int rip_tracks(char *arg, int (*next_track)(struct track_info *), - int issorted); +static int rip_tracks_loop(struct track_pair *tp, u_int n_tracks, + struct rip_func *func); +static int rip_tracks(char *arg, struct rip_func *func, int issorted); /* Next-Track function exit codes */ #define NXTRACK_OK 0 #define NXTRACK_FAIL 1 #define NXTRACK_SKIP 2 +/* + * CDplay Control + */ +static int ttyfd; +static void enable_playctrl(void); + +#define SKIP_SEC_NUM 750 /* 10 seconds */ + static int _parse_val(char *start, char *nxt, int *val) { @@ -355,8 +377,15 @@ return (0); } -int -read_track(int fd, struct track_info *ti) +/* + * RETURN VALUES + * The function can return + * [READTRK_OK] Play next track + * [READTRK_FAIL] Error + * [READTRK_EXIT] Exit from CDplay + */ +static int +read_track(int fd, struct track_info *ti, struct rip_func *func) { struct timeval tv, otv, atv; u_int32_t i, blksize, n_sec; @@ -367,7 +396,7 @@ blksize = (ti->isaudio) ? 2352 : 2048; sec = (u_char *)malloc(blksize); if (sec == NULL) - return (-1); + return (READTRK_FAIL); timerclear(&otv); atv.tv_sec = 1; @@ -383,31 +412,51 @@ timeradd(&tv, &atv, &otv); } + error = func->next_sector(); + switch (error) { + case READTRK_NEXT: + error = READTRK_OK; + /* FALLTHOUGH */ + case READTRK_FAIL: + case READTRK_EXIT: + free(sec); + fprintf(stderr, "\n"); + return (error); + + case READTRK_FORWARD: + i = (n_sec - i < SKIP_SEC_NUM) ? n_sec - 1 : + i + SKIP_SEC_NUM; + break; + + case READTRK_REWIND: + i = (i < SKIP_SEC_NUM) ? 0 : i - SKIP_SEC_NUM; + break; + } + error = read_data_sector(i + ti->start_lba, sec, blksize); if (error == 0) { if (write_sector(ti->fd, sec, blksize) != 0) { free(sec); warnx("\nerror while writing to the %s file", ti->name); - return (-1); + return (READTRK_FAIL); } i++; } else if (error != EAGAIN) { free(sec); warnx("\nerror while reading from device"); - return (-1); + return (READTRK_FAIL); } } free(sec); - fprintf(stderr, "\rtrack %u '%c' %08u/%08u 100%%\n", - ti->track, + fprintf(stderr, "\rtrack %u '%c' %08u/%08u 100%%\n", ti->track, (ti->isaudio) ? 'a' : 'd', i, n_sec); - return (0); + return (READTRK_OK); } -int +static int rip_next_track(struct track_info *info) { int error; @@ -477,8 +526,51 @@ } static int -rip_tracks_loop(struct track_pair *tp, u_int n_tracks, - int (*next_track)(struct track_info *)) +rip_next_sector(void) +{ + return (READTRK_OK); +} + +static int +play_next_sector(void) +{ + struct timespec ts; + ssize_t sz; + char ch; + int forever; + + ts.tv_sec = 0; + ts.tv_nsec = 10; /* release CPU for 10 nanosec */ + forever = 0; + do { + sz = read(ttyfd, &ch, sizeof(ch)); + if (sz > 0) { + if (ch == ' ') + forever = !forever; + else if (ch == '>') + return (READTRK_FORWARD); + else if (ch == '<') + return (READTRK_REWIND); + else if (ch == 'n') + return (READTRK_NEXT); + else if (ch == 'q') + return (READTRK_EXIT); + } + + if (forever) { + /* + * Without sleep process consumes too much CPU time + * when paused. + */ + nanosleep(&ts, NULL); + } + } while (forever); + + return (READTRK_OK); +} + +static int +rip_tracks_loop(struct track_pair *tp, u_int n_tracks, struct rip_func *func) { struct track_info info; u_char trk; @@ -517,19 +609,20 @@ info.end_lba = toc_buffer[i + 1].addr.lba; } - error = next_track(&info); + error = func->next_track(&info); if (error == NXTRACK_FAIL) { error = -1; break; } else if (error != NXTRACK_SKIP) { - error = read_track(fd, &info); + error = read_track(fd, &info, func); close(info.fd); - if (error != 0) { + if (error == READTRK_FAIL) { warnx("can't rip %u track", toc_buffer[i].track); break; - } + } else if (error == READTRK_EXIT) + break; } } @@ -541,8 +634,33 @@ return (error); } -int -rip_tracks(char *arg, int (*next_track)(struct track_info *), int issorted) +static void +enable_playctrl(void) +{ + struct termios tp; + + ttyfd = open("/dev/tty", O_RDONLY); + if (ttyfd == -1) { + warnx("can't open /dev/tty. Play control is disabled."); + return; + } + + if (fcntl(ttyfd, F_SETFL, O_NONBLOCK) != -1) { + if (tcgetattr(ttyfd, &tp) != -1) { + tp.c_lflag &= ~(ICANON | ECHO); + tp.c_lflag |= ISIG; + if (tcsetattr(ttyfd, TCSANOW, &tp) != -1) + return; + } + } + + close(ttyfd); + ttyfd = -1; + warnx("can't configure tty. Play control is disabled."); +} + +static int +rip_tracks(char *arg, struct rip_func *func, int issorted) { struct track_pair_head list; struct track_pair *tp; @@ -580,7 +698,7 @@ } TAILQ_FOREACH(tp, &list, list) { - rc = rip_tracks_loop(tp, n, next_track); + rc = rip_tracks_loop(tp, n, func); if (rc < 0) break; } @@ -592,11 +710,27 @@ int cdrip(char *arg) { - return rip_tracks(arg, rip_next_track, 1); + struct rip_func func; + + func.next_track = rip_next_track; + func.next_sector = rip_next_sector; + return rip_tracks(arg, &func, 1); } int cdplay(char *arg) { - return rip_tracks(arg, play_next_track, 0); + struct rip_func func; + int rv; + + func.next_track = play_next_track; + func.next_sector = play_next_sector; + + enable_playctrl(); + if (ttyfd == -1) + func.next_sector = rip_next_sector; /* it does nothing */ + rv = rip_tracks(arg, &func, 0); + if (ttyfd != -1) + close(ttyfd); + return (rv); }