2
$Id: cdda-player.c,v 1.36 2005/09/15 06:36:01 rocky Exp $
4
Copyright (C) 2005 Rocky Bernstein <rocky@panix.com>
6
Adapted from Gerd Knorr's player.c program <kraxel@bytesex.org>
7
Copyright (C) 1997, 1998
9
This program is free software; you can redistribute it and/or modify
10
it under the terms of the GNU General Public License as published by
11
the Free Software Foundation; either version 2 of the License, or
12
(at your option) any later version.
14
This program is distributed in the hope that it will be useful,
15
but WITHOUT ANY WARRANTY; without even the implied warranty of
16
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17
GNU General Public License for more details.
19
You should have received a copy of the GNU General Public License
20
along with this program; if not, write to the Free Software
21
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
47
#include <cddb/cddb.h>
66
# error "You need <curses.h> or <ncurses.h to build cdda-player"
70
#include <cdio/cdio.h>
72
#include <cdio/util.h>
73
#include <cdio/cd_types.h>
74
#include <cdio/logging.h>
78
static bool play_track(track_t t1, track_t t2);
79
static void display_cdinfo(CdIo_t *p_cdio, track_t i_tracks,
80
track_t i_first_track);
81
static void get_cddb_track_info(track_t i_track);
82
static void get_cdtext_track_info(track_t i_track);
83
static void get_track_info(track_t i_track);
84
static void display_tracks(void);
86
CdIo_t *p_cdio; /* libcdio handle */
87
driver_id_t driver_id = DRIVER_DEVICE;
88
int b_sig = false; /* set on some signals */
91
track_t i_first_track;
93
track_t i_first_audio_track;
94
track_t i_last_audio_track;
95
track_t i_last_display_track = CDIO_INVALID_TRACK;
97
msf_t toc[CDIO_CDROM_LEADOUT_TRACK+1];
98
cdio_subchannel_t sub; /* subchannel last time read */
99
int i_data; /* # of data tracks present ? */
103
int i_vol_port = -1; /* If -1 get retrieve volume port.
104
Otherwise the port number 0..3
105
of a working volume port.
109
bool auto_mode = false;
110
bool b_verbose = false;
112
bool interactive = true;
113
bool b_prefer_cdtext = true;
114
bool b_cddb = false; /* CDDB database present */
115
bool b_db = false; /* we have a database at all */
116
bool b_record = false; /* we have a record for
118
bool b_all_tracks = false; /* True if we display all tracks*/
120
char *psz_device=NULL;
123
/* Info about songs and titles. The 0 entry will contain the disc info.
131
bool b_cdtext; /* true if from CD-Text, false if from CDDB */
132
} cd_track_info_rec_t;
134
cd_track_info_rec_t cd_info[CDIO_CD_MAX_TRACKS+2];
142
bool b_cdtext_title; /* true if from CD-Text, false if from CDDB */
143
bool b_cdtext_artist; /* true if from CD-Text, false if from CDDB */
144
bool b_cdtext_genre; /* true if from CD-Text, false if from CDDB */
145
bool b_cdtext_category; /* true if from CD-Text, false if from CDDB */
146
bool b_cdtext_year; /* true if from CD-Text, false if from CDDB */
149
cddb_conn_t *p_conn = NULL;
150
cddb_disc_t *p_cddb_disc = NULL;
151
int i_cddb_matches = 0;
154
#define MAX_KEY_STR 50
155
const char key_bindings[][MAX_KEY_STR] = {
156
" right play / next track",
157
" left previous track",
158
" up/down 10 sec forward / back",
159
" 1-9 jump to track 1-9",
160
" 0 jump to track 10",
161
" F1-F20 jump to track 11-30",
163
" k, h, ? show this key help",
164
" l, toggle listing all tracks",
167
" p, space pause / resume",
170
" x quit and continue playing",
171
" a toggle auto-mode",
174
const unsigned int i_key_bindings = sizeof(key_bindings) / MAX_KEY_STR;
176
/* ---------------------------------------------------------------------- */
189
LINE_TRACK_TITLE = 9,
190
LINE_TRACK_ARTIST = 10,
191
LINE_TRACK_NEXT = 11,
197
/*! Curses window initialization. */
201
if (!interactive) return;
212
/*! Curses window finalization. */
216
if (!interactive) return;
220
/* Signal handler - Ctrl-C and others. */
227
/* Timed wait on an event. */
238
return select(1,&se,NULL,NULL,&tv);
241
/* ---------------------------------------------------------------------- */
244
action(const char *psz_action)
247
if (b_verbose && psz_action)
248
fprintf(stderr,"action: %s\n", psz_action);
252
if (psz_action && strlen(psz_action))
253
mvprintw(LINE_ACTION, 0, (char *) "action : %s\n", psz_action);
255
mvprintw(LINE_ACTION, 0, (char *) "");
261
xperror(const char *psz_msg)
267
fprintf(stderr, "error: ");
274
sprintf(line,"%s: %s", psz_msg, strerror(errno));
275
mvprintw(LINE_ACTION, 0, (char *) "error : %s", line);
284
finish(const char *psz_msg, int rc)
287
mvprintw(LINE_LAST, 0, (char *) "%s, exiting...\n", psz_msg);
293
if (p_conn) cddb_destroy(p_conn);
294
cddb_disc_destroy(p_cddb_disc);
297
cdio_destroy (p_cdio);
302
/* ---------------------------------------------------------------------- */
304
/*! Stop playing audio CD */
306
cd_stop(CdIo_t *p_cdio)
309
if (b_cd && p_cdio) {
311
i_last_audio_track = CDIO_INVALID_TRACK;
312
b_ok = DRIVER_OP_SUCCESS == cdio_audio_stop(p_cdio);
315
if (b_all_tracks) display_tracks();
328
b_ok = DRIVER_OP_SUCCESS == cdio_eject_media(&p_cdio);
332
cdio_destroy (p_cdio);
340
cd_close(const char *psz_device)
345
b_ok = DRIVER_OP_SUCCESS == cdio_close_tray(psz_device, &driver_id);
352
/*! Pause playing audio CD */
354
cd_pause(CdIo_t *p_cdio)
357
if (sub.audio_status == CDIO_MMC_READ_SUB_ST_PLAY) {
358
b_ok = DRIVER_OP_SUCCESS == cdio_audio_pause(p_cdio);
365
/*! Get status/track/position info of an audio CD */
367
read_subchannel(CdIo_t *p_cdio)
370
if (!p_cdio) return false;
372
b_ok = DRIVER_OP_SUCCESS == cdio_audio_read_subchannel(p_cdio, &sub);
374
xperror("read subchannel");
377
if (auto_mode && sub.audio_status == CDIO_MMC_READ_SUB_ST_COMPLETED)
383
get_cddb_disc_info(CdIo_t *p_cdio)
386
b_db = init_cddb(p_cdio, &p_conn, &p_cddb_disc, xperror, i_first_track,
387
i_tracks, &i_cddb_matches);
391
cddb_disc_set_artist(p_cddb_disc, artist);
392
cddb_disc_set_title(p_cddb_disc, title);
393
cddb_disc_set_genre(p_cddb_disc, genre);
394
cddb_disc_set_year(p_cddb_disc, i_year);
400
#define add_cdtext_disc_info(format_str, info_field, FIELD) \
401
if (p_cdtext->field[FIELD] && !strlen(info_field)) { \
402
snprintf(info_field, sizeof(info_field), format_str, \
403
p_cdtext->field[FIELD]); \
404
b_cdtext_ ## info_field = true; \
408
get_cdtext_disc_info(CdIo_t *p_cdio)
410
cdtext_t *p_cdtext = cdio_get_cdtext(p_cdio, 0);
413
add_cdtext_disc_info("%s", title, CDTEXT_TITLE);
414
add_cdtext_disc_info("%s", artist, CDTEXT_PERFORMER);
415
add_cdtext_disc_info("%s", genre, CDTEXT_GENRE);
416
cdtext_destroy(p_cdtext);
421
get_disc_info(CdIo_t *p_cdio)
424
if (b_prefer_cdtext) {
425
get_cdtext_disc_info(p_cdio);
426
get_cddb_disc_info(p_cdio);
428
get_cddb_disc_info(p_cdio);
429
get_cdtext_disc_info(p_cdio);
433
/*! Read CD TOC and set CD information. */
435
read_toc(CdIo_t *p_cdio)
439
action("read toc...");
441
memset(cd_info, 0, sizeof(cd_info));
442
title[0] = artist[0] = genre[0] = category[0] = year[0] = '\0';
444
i_first_track = cdio_get_first_track_num(p_cdio);
445
i_last_track = cdio_get_last_track_num(p_cdio);
446
i_tracks = cdio_get_num_tracks(p_cdio);
447
i_first_audio_track = i_first_track;
448
i_last_audio_track = i_last_track;
451
if ( DRIVER_OP_SUCCESS == cdio_audio_get_volume(p_cdio, NULL) ) {
452
for (i_vol_port=0; i_vol_port<4; i_vol_port++) {
453
if (i_vol_port > 0) break;
456
/* Didn't find a non-zero volume level, maybe we've got everthing muted.
457
Set port to 0 to distinguis from -1 (driver can't get volume).
462
if ( CDIO_INVALID_TRACK == i_first_track ||
463
CDIO_INVALID_TRACK == i_last_track ) {
464
xperror("read toc header");
467
i_last_display_track = CDIO_INVALID_TRACK;
471
get_disc_info(p_cdio);
472
for (i = i_first_track; i <= i_last_track+1; i++) {
474
if ( !cdio_get_track_msf(p_cdio, i, &(toc[i])) )
476
xperror("read toc entry");
480
if ( TRACK_FORMAT_AUDIO == cdio_get_track_format(p_cdio, i) ) {
482
if (i != i_first_track)
484
s = cdio_audio_get_msf_seconds(&toc[i])
485
- cdio_audio_get_msf_seconds(&toc[i-1]);
486
snprintf(cd_info[i-1].length, sizeof(cd_info[0].length),
488
s / CDIO_CD_SECS_PER_MIN, s % CDIO_CD_SECS_PER_MIN);
491
if ((i != i_last_track+1) ) {
493
if (i == i_first_track) {
494
if (i == i_last_track)
495
i_first_audio_track = CDIO_CDROM_LEADOUT_TRACK;
497
i_first_audio_track++;
504
read_subchannel(p_cdio);
505
if (auto_mode && sub.audio_status != CDIO_MMC_READ_SUB_ST_PLAY)
506
play_track(1, CDIO_CDROM_LEADOUT_TRACK);
510
display_cdinfo(p_cdio, i_tracks, i_first_track);
513
/*! Play an audio track. */
515
play_track(track_t i_start_track, track_t i_end_track)
524
read_subchannel(p_cdio);
525
if (!b_cd || i_first_track == CDIO_CDROM_LEADOUT_TRACK)
529
fprintf(stderr,"play tracks: %d-%d => ", i_start_track, i_end_track);
530
if (i_start_track < i_first_track) i_start_track = i_first_track;
531
if (i_start_track > i_last_audio_track) i_start_track = i_last_audio_track;
532
if (i_end_track < i_first_track) i_end_track = i_first_track;
533
if (i_end_track > i_last_audio_track) i_end_track = i_last_audio_track;
536
fprintf(stderr,"%d-%d\n",i_start_track, i_end_track);
539
sprintf(line,"play track %d...", i_start_track);
541
b_ok = (DRIVER_OP_SUCCESS == cdio_audio_play_msf(p_cdio,
542
&(toc[i_start_track]),
543
&(toc[i_end_track])) );
544
if (!b_ok) xperror("play");
554
read_subchannel(p_cdio);
555
if (!b_cd || i_first_track == CDIO_CDROM_LEADOUT_TRACK)
558
sec = cdio_audio_get_msf_seconds(&sub.abs_addr);
560
if (sec < 0) sec = 0;
562
start_msf.m = cdio_to_bcd8(sec / CDIO_CD_SECS_PER_MIN);
563
start_msf.s = cdio_to_bcd8(sec % CDIO_CD_SECS_PER_MIN);
567
if ( DRIVER_OP_SUCCESS != cdio_audio_play_msf(p_cdio, &start_msf,
568
&(toc[i_last_audio_track])) )
576
if (!b_cd) return false;
578
if (CDIO_MMC_READ_SUB_ST_PAUSED == sub.audio_status) {
579
b_ok = DRIVER_OP_SUCCESS != cdio_audio_resume(p_cdio);
583
b_ok = DRIVER_OP_SUCCESS != cdio_audio_pause(p_cdio);
590
/*! Update windows with status and possibly track info. This used in
591
interactive playing not batch mode.
594
display_status(bool b_status_only)
598
if (!interactive) return;
601
sprintf(line,"no CD in drive (%s)", psz_device);
603
} else if (i_first_track == CDIO_CDROM_LEADOUT_TRACK) {
604
sprintf(line,"CD has only data tracks");
606
} else if (sub.audio_status == CDIO_MMC_READ_SUB_ST_PAUSED ||
607
sub.audio_status == CDIO_MMC_READ_SUB_ST_PLAY) {
608
cdio_audio_volume_t audio_volume;
609
if (i_vol_port > 0 &&
610
DRIVER_OP_SUCCESS == cdio_audio_get_volume(p_cdio, &audio_volume) )
612
uint8_t i_level = audio_volume.level[i_vol_port];
614
"track %2d - %02x:%02x of %s (%02x:%02x abs) %s volume: %d",
615
sub.track, sub.rel_addr.m, sub.rel_addr.s,
616
cd_info[sub.track].length,
617
sub.abs_addr.m, sub.abs_addr.s,
618
mmc_audio_state2str(sub.audio_status),
619
(i_level*100+128) / 256 );
622
sprintf(line,"track %2d - %02x:%02x of %s (%02x:%02x abs) %s",
623
sub.track, sub.rel_addr.m, sub.rel_addr.s,
624
cd_info[sub.track].length, sub.abs_addr.m, sub.abs_addr.s,
625
mmc_audio_state2str(sub.audio_status));
627
sprintf(line,"%s", mmc_audio_state2str(sub.audio_status));
630
mvprintw(LINE_STATUS, 0, (char *) "status%s: %s",auto_mode ? "*" : " ", line);
633
if ( !b_status_only && b_db && i_last_display_track != sub.track &&
634
(sub.audio_status == CDIO_MMC_READ_SUB_ST_PAUSED ||
635
sub.audio_status == CDIO_MMC_READ_SUB_ST_PLAY) &&
638
if (b_all_tracks) display_tracks();
640
const cd_track_info_rec_t *p_cd_info = &cd_info[sub.track];
641
i_last_display_track = sub.track;
642
if (i_first_audio_track != sub.track &&
643
strlen(cd_info[sub.track-1].title)) {
644
const cd_track_info_rec_t *p_cd_info = &cd_info[sub.track-1];
645
mvprintw(LINE_TRACK_PREV, 0, (char *) " track %2d title : %s [%s]",
646
sub.track-1, p_cd_info->title,
647
p_cd_info->b_cdtext ? "CD-Text" : "CDDB");
650
mvprintw(LINE_TRACK_PREV, 0, (char *) "%s","");
653
if (strlen(p_cd_info->title)) {
654
mvprintw(LINE_TRACK_TITLE, 0, (char *) ">track %2d title : %s [%s]",
655
sub.track, p_cd_info->title,
656
(char *) (p_cd_info->b_cdtext ? "CD-Text" : "CDDB"));
659
if (strlen(p_cd_info->artist)) {
660
mvprintw(LINE_TRACK_ARTIST, 0, (char *) ">track %2d artist: %s [%s]",
661
sub.track, p_cd_info->artist,
662
p_cd_info->b_cdtext ? "CD-Text" : "CDDB");
665
if (i_last_audio_track != sub.track &&
666
strlen(cd_info[sub.track+1].title)) {
667
const cd_track_info_rec_t *p_cd_info = &cd_info[sub.track+1];
668
mvprintw(LINE_TRACK_NEXT, 0, (char *) " track %2d title : %s [%s]",
669
sub.track+1, p_cd_info->title,
670
p_cd_info->b_cdtext ? "CD-Text" : "CDDB");
673
mvprintw(LINE_TRACK_NEXT, 0, (char *) "%s","");
685
get_cddb_track_info(track_t i_track)
688
cddb_track_t *t = cddb_disc_get_track(p_cddb_disc,
689
i_track - i_first_track);
691
cddb_track_set_title(t, title);
692
cddb_track_set_artist(t, artist);
700
#define add_cdtext_track_info(format_str, info_field, FIELD) \
701
if (p_cdtext->field[FIELD]) { \
702
snprintf(cd_info[i_track].info_field, \
703
sizeof(cd_info[i_track].info_field), \
704
format_str, p_cdtext->field[FIELD]); \
705
cd_info[i_track].b_cdtext = true; \
710
get_cdtext_track_info(track_t i_track)
713
cdtext_t *p_cdtext = cdio_get_cdtext(p_cdio, i_track);
715
if (NULL != p_cdtext) {
716
add_cdtext_track_info("%s", title, CDTEXT_TITLE);
717
add_cdtext_track_info("%s", title, CDTEXT_PERFORMER);
718
cdtext_destroy(p_cdtext);
723
get_track_info(track_t i_track)
726
if (b_prefer_cdtext) {
727
get_cdtext_track_info(i_track);
728
get_cddb_track_info(i_track);
730
get_cddb_track_info(i_track);
731
get_cdtext_track_info(i_track);
735
#define display_line(LINE_NO, COL_NO, format_str, field) \
736
if (field && field[0]) { \
737
mvprintw(LINE_NO, COL_NO, (char *) format_str " [%s]", \
739
b_cdtext_ ## field ? "CD-Text": "CDDB"); \
744
display_cdinfo(CdIo_t *p_cdio, track_t i_tracks, track_t i_first_track)
749
if (!interactive) return;
751
if (!b_cd) sprintf(line, "-");
753
len = sprintf(line, "%2u tracks (%02x:%02x min)",
754
(unsigned int) i_last_track,
755
toc[i_last_track+1].m, toc[i_last_track+1].s);
756
if (i_data && i_first_track != CDIO_CDROM_LEADOUT_TRACK)
757
sprintf(line+len,", audio=%u-%u", (unsigned int) i_first_audio_track,
758
(unsigned int) i_last_audio_track);
760
display_line(LINE_ARTIST, 0, "CD Artist : %s", artist);
761
display_line(LINE_CDNAME, 0, "CD Title : %s", title);
762
display_line(LINE_GENRE, 0, "CD Genre : %s", genre);
763
display_line(LINE_YEAR, 0, "CD Year : %s", year);
766
mvprintw(LINE_CDINFO, 0, (char *) "CD info: %0s", line);
771
/* ---------------------------------------------------------------------- */
777
"%s is a simple curses CD player. It can pick up artist,\n"
778
"CD name and song title from CD-Text info on the CD or\n"
781
"usage: %s [options] [device]\n"
783
"default for to search for a CD-ROM device with a CD-DA loaded\n"
785
"These command line options available:\n"
786
" -h print this help\n"
787
" -k print key mapping\n"
788
" -a start up in auto-mode\n"
791
"for non-interactive use (only one) of these:\n"
793
" -c print cover (PostScript to stdout)\n"
794
" -C close CD-ROM tray. If you use this option,\n"
795
" a CD-ROM device name must be specified.\n"
796
" -p play the whole CD\n"
797
" -t n play track >n<\n"
798
" -t a-b play all tracks between a and b (inclusive)\n"
799
" -L set volume level\n"
801
" -S list audio subchannel information\n"
804
"That's all. Oh, maybe a few words more about the auto-mode. This\n"
805
"is the 'dont-touch-any-key' feature. You load a CD, player starts\n"
806
"to play it, and when it is done it ejects the CD. Start it that\n"
807
"way on a spare console and forget about it...\n"
809
"(c) 1997,98 Gerd Knorr <kraxel@goldbach.in-berlin.de>\n"
810
"(c) 2005 Rocky Bernstein <rocky@panix.com>\n"
818
for (i=0; i < i_key_bindings; i++)
819
fprintf(stderr, "%s\n", key_bindings[i]);
823
keypress_wait(CdIo_t *p_cdio)
826
action("press any key to continue");
827
while (1 != select_wait(b_cd ? 1 : 5)) {
828
read_subchannel(p_cdio);
829
display_status(true);
835
display_cdinfo(p_cdio, i_tracks, i_first_track);
836
i_last_display_track = CDIO_INVALID_TRACK;
843
for (i=0; i < i_key_bindings; i++) {
844
mvprintw(LINE_TRACK_PREV+i, 0, (char *) "%s", key_bindings[i]);
847
keypress_wait(p_cdio);
858
i_line=LINE_TRACK_PREV - 1;
859
for (i = i_first_track; i <= i_last_track; i++) {
861
s = cdio_audio_get_msf_seconds(&toc[i+1])
862
- cdio_audio_get_msf_seconds(&toc[i]);
863
read_subchannel(p_cdio);
864
sprintf(line, "%2d %02d:%02d %s ", i,
865
s / CDIO_CD_SECS_PER_MIN, s % CDIO_CD_SECS_PER_MIN,
866
( ( sub.audio_status == CDIO_MMC_READ_SUB_ST_PLAY ||
867
sub.audio_status == CDIO_MMC_READ_SUB_ST_PAUSED ) &&
868
sub.track == i ) ? "->" : " |");
870
if ( strlen(cd_info[i].title) )
871
strcat(line, cd_info[i].title);
872
if ( strlen(cd_info[i].artist) > 0 ) {
873
if (strlen(cd_info[i].title))
875
strcat(line, cd_info[i].artist);
878
mvprintw(i_line++, 0, line);
885
* PostScript 8bit latin1 handling
886
* stolen from mpage output -- please don't ask me how this works...
888
#define ENCODING_TRICKS \
889
"/reencsmalldict 12 dict def\n" \
890
"/ReEncodeSmall { reencsmalldict begin\n" \
891
"/newcodesandnames exch def /newfontname exch def\n" \
892
"/basefontname exch def\n" \
893
"/basefontdict basefontname findfont def\n" \
894
"/newfont basefontdict maxlength dict def\n" \
895
"basefontdict { exch dup /FID ne { dup /Encoding eq\n" \
896
"{ exch dup length array copy newfont 3 1 roll put }\n" \
897
"{ exch newfont 3 1 roll put }\n" \
900
"ifelse } forall\n" \
901
"newfont /FontName newfontname put\n" \
902
"newcodesandnames aload pop newcodesandnames length 2 idiv\n" \
903
"{ newfont /Encoding get 3 1 roll put } repeat\n" \
904
"newfontname newfont definefont pop end } def\n" \
915
"035 /numbersign\n" \
919
"039 /quoteright\n" \
921
"041 /parenright\n" \
971
"091 /bracketleft\n" \
973
"093 /bracketright\n" \
974
"094 /asciicircum\n" \
975
"095 /underscore\n" \
1003
"123 /braceleft\n" \
1005
"125 /braceright\n" \
1006
"126 /asciitilde\n" \
1010
"130 /quotesingle\n" \
1011
"131 /quotedblleft\n" \
1012
"132 /guilsinglleft\n" \
1013
"133 /guilsinglright\n" \
1018
"138 /daggerdbl\n" \
1020
"140 /quotesinglbase\n" \
1021
"141 /quotedblbase\n" \
1022
"142 /quotedblright\n" \
1027
"147 /circumflex\n" \
1031
"151 /dotaccent\n" \
1032
"152 /perthousand\n" \
1037
"157 /hungarumlaut\n" \
1041
"161 /exclamdown\n" \
1046
"166 /brokenbar\n" \
1049
"169 /copyright\n" \
1050
"170 /ordfeminine\n" \
1051
"171 /guillemotleft\n" \
1052
"172 /logicalnot\n" \
1054
"174 /registered\n" \
1057
"177 /plusminus\n" \
1058
"178 /twosuperior\n" \
1059
"179 /threesuperior\n" \
1062
"182 /paragraph\n" \
1063
"183 /periodcentered\n" \
1065
"185 /onesuperior\n" \
1066
"186 /ordmasculine\n" \
1067
"187 /guillemotright\n" \
1068
"188 /onequarter\n" \
1070
"190 /threequarters\n" \
1071
"191 /questiondown\n" \
1074
"194 /Acircumflex\n" \
1076
"196 /Adieresis\n" \
1082
"202 /Ecircumflex\n" \
1083
"203 /Edieresis\n" \
1086
"206 /Icircumflex\n" \
1087
"207 /Idieresis\n" \
1092
"212 /Ocircumflex\n" \
1094
"214 /Odieresis\n" \
1099
"219 /Ucircumflex\n" \
1100
"220 /Udieresis\n" \
1103
"223 /germandbls\n" \
1106
"226 /acircumflex\n" \
1108
"228 /adieresis\n" \
1114
"234 /ecircumflex\n" \
1115
"235 /edieresis\n" \
1118
"238 /icircumflex\n" \
1119
"239 /idieresis\n" \
1124
"244 /ocircumflex\n" \
1126
"246 /odieresis\n" \
1131
"251 /ucircumflex\n" \
1132
"252 /udieresis\n" \
1135
"255 /ydieresis\n" \
1140
ps_list_tracks(void)
1144
if (!b_record) return;
1146
printf("%%!PS-Adobe-2.0\n");
1148
/* encoding tricks */
1149
puts(ENCODING_TRICKS);
1150
printf("/Times /TimesLatin1 charvec ReEncodeSmall\n");
1151
printf("/Helvetica /HelveticaLatin1 charvec ReEncodeSmall\n");
1154
printf("0 setlinewidth\n");
1155
printf(" 100 100 moveto\n");
1156
printf(" 390 0 rlineto\n");
1157
printf(" 0 330 rlineto\n");
1158
printf("-390 0 rlineto\n");
1159
printf("closepath stroke\n");
1161
printf(" 100 100 moveto\n");
1162
printf("-16 0 rlineto\n");
1163
printf(" 0 330 rlineto\n");
1164
printf("422 0 rlineto\n");
1165
printf(" 0 -330 rlineto\n");
1166
printf("closepath stroke\n");
1169
printf("/TimesLatin1 findfont 24 scalefont setfont\n");
1170
printf("120 400 moveto (%s) show\n", title);
1171
printf("/TimesLatin1 findfont 18 scalefont setfont\n");
1172
printf("120 375 moveto (%s) show\n", artist);
1175
sy = 250 / i_last_track;
1176
if (sy > 14) sy = 14;
1177
printf("/labelfont /TimesLatin1 findfont %d scalefont def\n",sy-2);
1178
printf("/timefont /Courier findfont %d scalefont def\n",sy-2);
1179
for (i = i_first_track, y = 350; i <= i_last_track; i++, y -= sy) {
1180
s = cdio_audio_get_msf_seconds(&toc[i+1])
1181
- cdio_audio_get_msf_seconds(&toc[i]);
1183
printf("labelfont setfont\n");
1184
printf("120 %d moveto (%d) show\n", y, i);
1185
printf("150 %d moveto (%s) show\n", y, cd_info[i].title);
1187
printf("timefont setfont\n");
1188
printf("420 %d moveto (%2d:%02d) show\n", y,
1189
s / CDIO_CD_SECS_PER_MIN, s % CDIO_CD_SECS_PER_MIN);
1193
printf("/HelveticaLatin1 findfont 12 scalefont setfont\n");
1194
printf(" 97 105 moveto (%s: %s) 90 rotate show -90 rotate\n",
1196
printf("493 425 moveto (%s: %s) -90 rotate show 90 rotate\n",
1198
printf("showpage\n");
1206
if (!b_record) return;
1208
printf("Title : %s\n", title);
1209
printf("Artist: %s\n", artist);
1211
for (i = i_first_track; i <= i_last_track; i++) {
1212
s = cdio_audio_get_msf_seconds(&toc[i+1])
1213
- cdio_audio_get_msf_seconds(&toc[i]);
1214
printf("%2d: %s [%d seconds]\n", i, cd_info[i].title, s);
1235
main(int argc, char *argv[])
1240
int i_volume_level = 0;
1241
cd_operation_t cd_op = NO_OP; /* operation to do in non-interactive mode */
1244
psz_program = strrchr(argv[0],'/');
1245
psz_program = psz_program ? psz_program+1 : argv[0];
1247
memset(&cddb_opts, 0, sizeof(cddb_opts));
1249
cdio_loglevel_default = CDIO_LOG_WARN;
1252
if (-1 == (c = getopt(argc, argv, "acCdehkplL:sSt:vx")))
1257
if (cdio_loglevel_default > CDIO_LOG_INFO)
1258
cdio_loglevel_default = CDIO_LOG_INFO;
1262
if (cdio_loglevel_default > CDIO_LOG_DEBUG)
1263
cdio_loglevel_default = CDIO_LOG_DEBUG;
1269
if (NULL != (h = strchr(optarg,'-'))) {
1270
i_volume_level = atoi(optarg);
1274
if (NULL != (h = strchr(optarg,'-'))) {
1276
start_track = atoi(optarg);
1277
stop_track = atoi(h+1)+1;
1278
if (0 == start_track) start_track = 1;
1279
if (1 == stop_track) stop_track = CDIO_CDROM_LEADOUT_TRACK;
1281
start_track = atoi(optarg);
1282
stop_track = start_track+1;
1285
interactive = false;
1289
interactive = false;
1293
interactive = false;
1294
cd_op = LIST_TRACKS;
1297
interactive = false;
1301
interactive = false;
1302
cd_op = PS_LIST_TRACKS;
1305
interactive = false;
1306
cd_op = STOP_PLAYING;
1309
interactive = false;
1310
cd_op = LIST_SUBCHANNEL;
1313
interactive = false;
1328
if (argc > optind) {
1329
psz_device = strdup(argv[optind]);
1331
char **ppsz_cdda_drives=NULL;
1332
char **ppsz_all_cd_drives = cdio_get_devices_ret(&driver_id);
1334
if (!ppsz_all_cd_drives) {
1335
fprintf(stderr, "Can't find a CD-ROM drive\n");
1338
ppsz_cdda_drives = cdio_get_devices_with_cap(ppsz_all_cd_drives,
1339
CDIO_FS_AUDIO, false);
1340
if (!ppsz_cdda_drives || !ppsz_cdda_drives[0]) {
1341
fprintf(stderr, "Can't find a CD-ROM drive with a CD-DA in it\n");
1344
psz_device = strdup(ppsz_cdda_drives[0]);
1345
cdio_free_device_list(ppsz_all_cd_drives);
1346
cdio_free_device_list(ppsz_cdda_drives);
1355
signal(SIGINT,ctrlc);
1356
signal(SIGQUIT,ctrlc);
1357
signal(SIGTERM,ctrlc);
1358
signal(SIGHUP,ctrlc);
1360
if (CLOSE_CD != cd_op) {
1363
fprintf(stderr, "open %s... ", psz_device);
1364
p_cdio = cdio_open (psz_device, driver_id);
1365
if (!p_cdio && cd_op != EJECT_CD) {
1366
cd_close(psz_device);
1367
p_cdio = cdio_open (psz_device, driver_id);
1370
if (p_cdio && b_verbose)
1371
fprintf(stderr,"ok\n");
1377
if (EJECT_CD == cd_op) {
1378
i_rc = cd_eject() ? 0 : 1;
1381
case PS_LIST_TRACKS:
1391
i_rc = cd_stop(p_cdio) ? 0 : 1;
1394
/* Should have been handled above. */
1400
case PS_LIST_TRACKS:
1404
/* play just this one track */
1406
printf("%s / %s\n", artist, title);
1408
printf("%s\n", cd_info[start_track].title);
1410
i_rc = play_track(start_track, stop_track) ? 0 : 1;
1414
printf("%s / %s\n", artist, title);
1415
play_track(1,CDIO_CDROM_LEADOUT_TRACK);
1419
cdio_audio_volume_t volume;
1420
volume.level[0] = i_volume_level;
1421
i_rc = (DRIVER_OP_SUCCESS == cdio_audio_set_volume(p_cdio,
1426
case LIST_SUBCHANNEL:
1427
if (read_subchannel(p_cdio)) {
1428
if (sub.audio_status == CDIO_MMC_READ_SUB_ST_PAUSED ||
1429
sub.audio_status == CDIO_MMC_READ_SUB_ST_PLAY) {
1431
printf("track %2d - %02x:%02x (%02x:%02x abs) ",
1432
sub.track, sub.rel_addr.m, sub.rel_addr.s,
1433
sub.abs_addr.m, sub.abs_addr.s);
1436
printf("drive state: %s\n",
1437
mmc_audio_state2str(sub.audio_status));
1442
case CLOSE_CD: /* Handled below */
1449
else if (CLOSE_CD == cd_op) {
1450
i_rc = (DRIVER_OP_SUCCESS == cdio_close_tray(psz_device, NULL))
1453
fprintf(stderr,"no CD in drive (%s)\n", psz_device);
1460
if (!b_cd) read_toc(p_cdio);
1461
read_subchannel(p_cdio);
1462
display_status(false);
1464
if (1 == select_wait(b_cd ? 1 : 5)) {
1465
switch (key = getch()) {
1468
auto_mode = !auto_mode;
1487
cd_close(psz_device);
1491
b_all_tracks = !b_all_tracks;
1495
i_last_display_track = CDIO_INVALID_TRACK;
1496
display_cdinfo(p_cdio, i_tracks, i_first_track);
1514
(sub.audio_status == CDIO_MMC_READ_SUB_ST_PAUSED ||
1515
sub.audio_status == CDIO_MMC_READ_SUB_ST_PLAY))
1516
play_track(sub.track+1, CDIO_CDROM_LEADOUT_TRACK);
1518
play_track(1,CDIO_CDROM_LEADOUT_TRACK);
1522
(sub.audio_status == CDIO_MMC_READ_SUB_ST_PAUSED ||
1523
sub.audio_status == CDIO_MMC_READ_SUB_ST_PLAY))
1524
play_track(sub.track-1,CDIO_CDROM_LEADOUT_TRACK);
1527
if (b_cd && sub.audio_status == CDIO_MMC_READ_SUB_ST_PLAY)
1531
if (b_cd && sub.audio_status == CDIO_MMC_READ_SUB_ST_PLAY)
1543
play_track(key - '0', CDIO_CDROM_LEADOUT_TRACK);
1546
play_track(10, CDIO_CDROM_LEADOUT_TRACK);
1568
play_track(key - KEY_F(1) + 11, CDIO_CDROM_LEADOUT_TRACK);
1573
if (!nostop) cd_stop(p_cdio);
1575
finish("bye", i_rc);
1577
return 0; /* keep compiler happy */