~ubuntu-branches/ubuntu/edgy/libcdio/edgy-updates

« back to all changes in this revision

Viewing changes to src/cdda-player.c

  • Committer: Bazaar Package Importer
  • Author(s): Martin Pitt
  • Date: 2005-11-15 16:53:23 UTC
  • mfrom: (3.1.1 etch)
  • Revision ID: james.westby@ubuntu.com-20051115165323-peroku75syl2j36u
Tags: 0.76-1ubuntu1
* Sync to new Debian version, manually apply Ubuntu patches:
  - debian/control: Remove dpkg-awk build dependency.
  - debian/rules: hardcode $LIBCDEV. This keeps the diff small (compared to
    the original patch of changing every ${libcdev} occurence).

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
    $Id: cdda-player.c,v 1.36 2005/09/15 06:36:01 rocky Exp $
 
3
 
 
4
    Copyright (C) 2005 Rocky Bernstein <rocky@panix.com>
 
5
 
 
6
    Adapted from Gerd Knorr's player.c program  <kraxel@bytesex.org>
 
7
    Copyright (C) 1997, 1998 
 
8
 
 
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.
 
13
 
 
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.
 
18
 
 
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
 
22
*/
 
23
 
 
24
#ifdef HAVE_CONFIG_H
 
25
#include "config.h"
 
26
#endif
 
27
 
 
28
#ifdef HAVE_STDIO_H
 
29
#include <stdio.h>
 
30
#endif
 
31
 
 
32
#ifdef HAVE_STDLIB_H
 
33
#include <stdlib.h>
 
34
#endif
 
35
 
 
36
#ifdef HAVE_UNISTD_H
 
37
#include <unistd.h>
 
38
#endif
 
39
 
 
40
#ifdef HAVE_STRING_H
 
41
#include <string.h>
 
42
#endif
 
43
 
 
44
#include <sys/time.h>
 
45
 
 
46
#ifdef HAVE_CDDB
 
47
#include <cddb/cddb.h>
 
48
#endif
 
49
 
 
50
#include <signal.h>
 
51
 
 
52
#ifdef HAVE_GETOPT_H
 
53
#include <getopt.h>
 
54
#endif
 
55
 
 
56
#ifdef HAVE_ERRNO_H
 
57
#include <errno.h>
 
58
#endif
 
59
 
 
60
#ifdef HAVE_CURSES_H
 
61
#include <curses.h>
 
62
#else 
 
63
#ifdef HAVE_NCURSES_H
 
64
#include <ncurses.h>
 
65
#else
 
66
#  error "You need <curses.h> or <ncurses.h to build cdda-player"
 
67
#endif
 
68
#endif
 
69
 
 
70
#include <cdio/cdio.h>
 
71
#include <cdio/mmc.h>
 
72
#include <cdio/util.h>
 
73
#include <cdio/cd_types.h>
 
74
#include <cdio/logging.h>
 
75
 
 
76
#include "cddb.h"
 
77
 
 
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);
 
85
 
 
86
CdIo_t             *p_cdio;               /* libcdio handle */
 
87
driver_id_t        driver_id = DRIVER_DEVICE;
 
88
int b_sig = false;                                /* set on some signals */
 
89
 
 
90
/* cdrom data */
 
91
track_t            i_first_track;
 
92
track_t            i_last_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;
 
96
track_t            i_tracks;
 
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 ? */
 
100
int                start_track = 0;
 
101
int                stop_track = 0;
 
102
int                one_track = 0;
 
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.
 
106
                                       */
 
107
 
 
108
bool               b_cd         = false;
 
109
bool               auto_mode    = false;
 
110
bool               b_verbose    = false;
 
111
bool               debug        = 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
 
117
                                        the inserted CD */
 
118
bool               b_all_tracks = false; /* True if we display all tracks*/
 
119
 
 
120
char *psz_device=NULL;
 
121
char *psz_program;
 
122
 
 
123
/* Info about songs and titles. The 0 entry will contain the disc info.
 
124
 */
 
125
typedef struct 
 
126
{
 
127
  char title[80];
 
128
  char artist[80];
 
129
  char length[8];
 
130
  char ext_data[80];
 
131
  bool b_cdtext;  /* true if from CD-Text, false if from CDDB */
 
132
} cd_track_info_rec_t;
 
133
 
 
134
cd_track_info_rec_t cd_info[CDIO_CD_MAX_TRACKS+2];
 
135
 
 
136
char title[80];
 
137
char artist[80];
 
138
char genre[40];
 
139
char category[40];
 
140
char year[5];
 
141
 
 
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 */
 
147
 
 
148
#ifdef HAVE_CDDB
 
149
cddb_conn_t *p_conn = NULL;
 
150
cddb_disc_t *p_cddb_disc = NULL;
 
151
int i_cddb_matches = 0;
 
152
#endif
 
153
 
 
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",
 
162
  " ",
 
163
  "    k, h, ?   show this key help",
 
164
  "    l,        toggle listing all tracks",
 
165
  "    e         eject",
 
166
  "    c         close tray",
 
167
  "    p, space  pause / resume",
 
168
  "    s         stop",
 
169
  "    q, ^C     quit",
 
170
  "    x         quit and continue playing",
 
171
  "    a         toggle auto-mode",
 
172
};
 
173
 
 
174
const unsigned int i_key_bindings = sizeof(key_bindings) / MAX_KEY_STR;
 
175
 
 
176
/* ---------------------------------------------------------------------- */
 
177
/* tty stuff                                                              */
 
178
 
 
179
typedef enum {
 
180
  LINE_STATUS       =  0,
 
181
  LINE_CDINFO       =  1,
 
182
 
 
183
  LINE_ARTIST       =  3,
 
184
  LINE_CDNAME       =  4,
 
185
  LINE_GENRE        =  5,
 
186
  LINE_YEAR         =  6,
 
187
 
 
188
  LINE_TRACK_PREV   =  8,
 
189
  LINE_TRACK_TITLE  =  9,
 
190
  LINE_TRACK_ARTIST = 10,
 
191
  LINE_TRACK_NEXT   = 11,
 
192
 
 
193
  LINE_ACTION       = 24,
 
194
  LINE_LAST         = 25
 
195
} track_line_t;
 
196
 
 
197
/*! Curses window initialization. */
 
198
static void
 
199
tty_raw()
 
200
{
 
201
  if (!interactive) return;
 
202
  
 
203
  initscr();
 
204
  cbreak();
 
205
  noecho();
 
206
#ifdef HAVE_KEYPAD
 
207
  keypad(stdscr,1);
 
208
#endif
 
209
  refresh();
 
210
}
 
211
 
 
212
/*! Curses window finalization. */
 
213
static void
 
214
tty_restore()
 
215
{
 
216
  if (!interactive) return;
 
217
  endwin();
 
218
}
 
219
 
 
220
/* Signal handler - Ctrl-C and others. */
 
221
static void
 
222
ctrlc(int signal)
 
223
{
 
224
  b_sig = true;
 
225
}
 
226
 
 
227
/* Timed wait on an event. */
 
228
static int
 
229
select_wait(int sec)
 
230
{
 
231
  struct timeval  tv;
 
232
  fd_set          se;
 
233
  
 
234
  FD_ZERO(&se);
 
235
  FD_SET(0,&se);
 
236
  tv.tv_sec = sec;
 
237
  tv.tv_usec = 0;
 
238
  return select(1,&se,NULL,NULL,&tv);
 
239
}
 
240
 
 
241
/* ---------------------------------------------------------------------- */
 
242
 
 
243
static void 
 
244
action(const char *psz_action)
 
245
{
 
246
  if (!interactive) {
 
247
    if (b_verbose && psz_action)
 
248
      fprintf(stderr,"action: %s\n", psz_action);
 
249
    return;
 
250
  }
 
251
  
 
252
  if (psz_action && strlen(psz_action))
 
253
    mvprintw(LINE_ACTION, 0, (char *) "action : %s\n", psz_action);
 
254
  else
 
255
    mvprintw(LINE_ACTION, 0, (char *) "");
 
256
  clrtoeol();
 
257
  refresh();
 
258
}
 
259
 
 
260
static void inline
 
261
xperror(const char *psz_msg)
 
262
{
 
263
  char line[80];
 
264
  
 
265
  if (!interactive) {
 
266
    if (b_verbose) {
 
267
      fprintf(stderr, "error: ");
 
268
      perror(psz_msg);
 
269
    }
 
270
    return;
 
271
  }
 
272
  
 
273
  if (b_verbose) {
 
274
    sprintf(line,"%s: %s", psz_msg, strerror(errno));
 
275
    mvprintw(LINE_ACTION, 0, (char *) "error  : %s", line);
 
276
    clrtoeol();
 
277
    refresh();
 
278
    select_wait(3);
 
279
    action(NULL);
 
280
  }
 
281
}
 
282
 
 
283
static void 
 
284
finish(const char *psz_msg, int rc)
 
285
{
 
286
  if (interactive) {
 
287
    mvprintw(LINE_LAST, 0, (char *) "%s, exiting...\n", psz_msg);
 
288
    clrtoeol();
 
289
    refresh();
 
290
  }
 
291
  tty_restore();
 
292
#ifdef HAVE_CDDB
 
293
  if (p_conn) cddb_destroy(p_conn);
 
294
  cddb_disc_destroy(p_cddb_disc);
 
295
  libcddb_shutdown();
 
296
#endif /*HAVE_CDDB*/
 
297
  cdio_destroy (p_cdio);
 
298
  free (psz_device);
 
299
  exit (rc);
 
300
}
 
301
 
 
302
/* ---------------------------------------------------------------------- */
 
303
 
 
304
/*! Stop playing audio CD */
 
305
static bool
 
306
cd_stop(CdIo_t *p_cdio)
 
307
{
 
308
  bool b_ok = true;
 
309
  if (b_cd && p_cdio) {
 
310
    action("stop...");
 
311
    i_last_audio_track = CDIO_INVALID_TRACK;
 
312
    b_ok = DRIVER_OP_SUCCESS == cdio_audio_stop(p_cdio);
 
313
    if ( !b_ok )
 
314
      xperror("stop");
 
315
    if (b_all_tracks) display_tracks();
 
316
  }
 
317
  return b_ok;
 
318
}
 
319
 
 
320
/*! Eject CD */
 
321
static bool
 
322
cd_eject(void)
 
323
{
 
324
  bool b_ok = true;
 
325
  if (p_cdio) {
 
326
    cd_stop(p_cdio);
 
327
    action("eject...");
 
328
    b_ok = DRIVER_OP_SUCCESS == cdio_eject_media(&p_cdio);
 
329
    if (!b_ok)
 
330
      xperror("eject");
 
331
    b_cd = false;
 
332
    cdio_destroy (p_cdio);
 
333
    p_cdio = NULL;
 
334
  }
 
335
  return b_ok;
 
336
}
 
337
 
 
338
/*! Close CD tray */
 
339
static bool
 
340
cd_close(const char *psz_device)
 
341
{
 
342
  bool b_ok = true;
 
343
  if (!b_cd) {
 
344
    action("close...");
 
345
    b_ok = DRIVER_OP_SUCCESS == cdio_close_tray(psz_device, &driver_id);
 
346
    if (!b_ok)
 
347
      xperror("close");
 
348
  }
 
349
  return b_ok;
 
350
}
 
351
 
 
352
/*! Pause playing audio CD */
 
353
static bool
 
354
cd_pause(CdIo_t *p_cdio)
 
355
{
 
356
  bool b_ok = true;
 
357
  if (sub.audio_status == CDIO_MMC_READ_SUB_ST_PLAY) {
 
358
    b_ok = DRIVER_OP_SUCCESS == cdio_audio_pause(p_cdio);
 
359
    if (!b_ok)
 
360
      xperror("pause");
 
361
  }
 
362
  return b_ok;
 
363
}
 
364
 
 
365
/*! Get status/track/position info of an audio CD */
 
366
static bool
 
367
read_subchannel(CdIo_t *p_cdio)
 
368
{
 
369
  bool b_ok = true;
 
370
  if (!p_cdio) return false;
 
371
 
 
372
  b_ok = DRIVER_OP_SUCCESS == cdio_audio_read_subchannel(p_cdio, &sub);
 
373
  if (!b_ok) {
 
374
    xperror("read subchannel");
 
375
    b_cd = 0;
 
376
  }
 
377
  if (auto_mode && sub.audio_status == CDIO_MMC_READ_SUB_ST_COMPLETED)
 
378
    cd_eject();
 
379
  return b_ok;
 
380
}
 
381
 
 
382
static void 
 
383
get_cddb_disc_info(CdIo_t *p_cdio) 
 
384
{
 
385
#ifdef HAVE_CDDB
 
386
  b_db = init_cddb(p_cdio, &p_conn, &p_cddb_disc, xperror, i_first_track, 
 
387
                   i_tracks, &i_cddb_matches);
 
388
  if (b_db) {
 
389
    int i_year;
 
390
    i_year = atoi(year);
 
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);
 
395
  }
 
396
#endif
 
397
  return;
 
398
}
 
399
 
 
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;                         \
 
405
  }
 
406
 
 
407
static void 
 
408
get_cdtext_disc_info(CdIo_t *p_cdio) 
 
409
{
 
410
  cdtext_t *p_cdtext = cdio_get_cdtext(p_cdio, 0);
 
411
 
 
412
  if (p_cdtext) {
 
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);
 
417
  }
 
418
}
 
419
 
 
420
static void 
 
421
get_disc_info(CdIo_t *p_cdio) 
 
422
{
 
423
  b_db = false;
 
424
  if (b_prefer_cdtext) {
 
425
    get_cdtext_disc_info(p_cdio);
 
426
    get_cddb_disc_info(p_cdio);
 
427
  } else {
 
428
    get_cddb_disc_info(p_cdio);
 
429
    get_cdtext_disc_info(p_cdio);
 
430
  }
 
431
}
 
432
 
 
433
/*! Read CD TOC  and set CD information. */
 
434
static void
 
435
read_toc(CdIo_t *p_cdio)
 
436
{
 
437
  track_t i;
 
438
 
 
439
  action("read toc...");
 
440
 
 
441
  memset(cd_info, 0, sizeof(cd_info));
 
442
  title[0] = artist[0] = genre[0] = category[0] = year[0] = '\0';
 
443
 
 
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;
 
449
 
 
450
 
 
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;
 
454
    }
 
455
    if (4 == i_vol_port) 
 
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).
 
458
      */
 
459
      i_vol_port = 0;
 
460
  }
 
461
  
 
462
  if ( CDIO_INVALID_TRACK == i_first_track ||
 
463
       CDIO_INVALID_TRACK == i_last_track ) {
 
464
    xperror("read toc header");
 
465
    b_cd = false;
 
466
    b_record = false;
 
467
    i_last_display_track = CDIO_INVALID_TRACK;
 
468
  } else {
 
469
    b_cd = true;
 
470
    i_data = 0;
 
471
    get_disc_info(p_cdio);
 
472
    for (i = i_first_track; i <= i_last_track+1; i++) {
 
473
      int s;
 
474
      if ( !cdio_get_track_msf(p_cdio, i, &(toc[i])) )
 
475
      {
 
476
        xperror("read toc entry");
 
477
        b_cd = false;
 
478
        return;
 
479
      }
 
480
      if ( TRACK_FORMAT_AUDIO == cdio_get_track_format(p_cdio, i) ) {
 
481
        
 
482
        if (i != i_first_track) 
 
483
          {
 
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), 
 
487
                     "%02d:%02d",
 
488
                     s / CDIO_CD_SECS_PER_MIN,  s % CDIO_CD_SECS_PER_MIN);
 
489
          }
 
490
      } else {
 
491
        if ((i != i_last_track+1) ) {
 
492
          i_data++;
 
493
          if (i == i_first_track) {
 
494
            if (i == i_last_track)
 
495
              i_first_audio_track = CDIO_CDROM_LEADOUT_TRACK;
 
496
            else
 
497
              i_first_audio_track++;
 
498
          }
 
499
        }
 
500
      }
 
501
      get_track_info(i);
 
502
    }
 
503
    b_record = true;
 
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);
 
507
  }
 
508
  action(NULL);
 
509
  if (!b_all_tracks)
 
510
    display_cdinfo(p_cdio, i_tracks, i_first_track);
 
511
}
 
512
 
 
513
/*! Play an audio track. */
 
514
static bool
 
515
play_track(track_t i_start_track, track_t i_end_track)
 
516
{
 
517
  bool b_ok = true;
 
518
  char line[80];
 
519
  
 
520
  if (!b_cd) {
 
521
    read_toc(p_cdio);
 
522
  }
 
523
  
 
524
  read_subchannel(p_cdio);
 
525
  if (!b_cd || i_first_track == CDIO_CDROM_LEADOUT_TRACK)
 
526
    return false;
 
527
  
 
528
  if (debug)
 
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;
 
534
  i_end_track++;
 
535
  if (debug)
 
536
    fprintf(stderr,"%d-%d\n",i_start_track, i_end_track);
 
537
  
 
538
  cd_pause(p_cdio);
 
539
  sprintf(line,"play track %d...", i_start_track);
 
540
  action(line);
 
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");
 
545
  return b_ok;
 
546
}
 
547
 
 
548
static void
 
549
skip(int diff)
 
550
{
 
551
  msf_t start_msf;
 
552
  int   sec;
 
553
  
 
554
  read_subchannel(p_cdio);
 
555
  if (!b_cd ||  i_first_track == CDIO_CDROM_LEADOUT_TRACK)
 
556
    return;
 
557
  
 
558
  sec  = cdio_audio_get_msf_seconds(&sub.abs_addr);
 
559
  sec += diff;
 
560
  if (sec < 0) sec = 0;
 
561
  
 
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);
 
564
  start_msf.f = 0;
 
565
  
 
566
  cd_pause(p_cdio);
 
567
  if ( DRIVER_OP_SUCCESS != cdio_audio_play_msf(p_cdio, &start_msf, 
 
568
                                                &(toc[i_last_audio_track])) )
 
569
    xperror("play");
 
570
}
 
571
 
 
572
static bool
 
573
toggle_pause()
 
574
{
 
575
  bool b_ok = true;
 
576
  if (!b_cd) return false;
 
577
  
 
578
  if (CDIO_MMC_READ_SUB_ST_PAUSED == sub.audio_status) {
 
579
    b_ok = DRIVER_OP_SUCCESS != cdio_audio_resume(p_cdio);
 
580
    if (!b_ok)
 
581
      xperror("resume");
 
582
  } else {
 
583
    b_ok = DRIVER_OP_SUCCESS != cdio_audio_pause(p_cdio);
 
584
    if (!b_ok)
 
585
      xperror("pause");
 
586
  }
 
587
  return b_ok;
 
588
}
 
589
 
 
590
/*! Update windows with status and possibly track info. This used in 
 
591
  interactive playing not batch mode.
 
592
 */
 
593
static void
 
594
display_status(bool b_status_only)
 
595
{
 
596
  char line[80];
 
597
 
 
598
  if (!interactive) return;
 
599
 
 
600
  if (!b_cd) {
 
601
    sprintf(line,"no CD in drive (%s)", psz_device);
 
602
 
 
603
  } else if (i_first_track == CDIO_CDROM_LEADOUT_TRACK) {
 
604
    sprintf(line,"CD has only data tracks");
 
605
    
 
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) ) 
 
611
      {
 
612
        uint8_t i_level = audio_volume.level[i_vol_port];
 
613
        sprintf(line,
 
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 );
 
620
      
 
621
      } else 
 
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));
 
626
  } else {
 
627
    sprintf(line,"%s", mmc_audio_state2str(sub.audio_status));
 
628
    
 
629
  }
 
630
  mvprintw(LINE_STATUS, 0, (char *) "status%s: %s",auto_mode ? "*" : " ", line);
 
631
  clrtoeol();
 
632
  
 
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)  &&
 
636
        b_cd) {
 
637
 
 
638
    if (b_all_tracks) display_tracks();
 
639
    else {
 
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");
 
648
        clrtoeol();
 
649
      } else {
 
650
        mvprintw(LINE_TRACK_PREV, 0, (char *) "%s","");
 
651
        clrtoeol();
 
652
      }
 
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"));
 
657
        clrtoeol();
 
658
      }
 
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");
 
663
        clrtoeol();
 
664
      }
 
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");
 
671
        clrtoeol();
 
672
      } else {
 
673
        mvprintw(LINE_TRACK_NEXT, 0, (char *) "%s","");
 
674
        clrtoeol();
 
675
      }
 
676
      clrtobot();
 
677
    }
 
678
  }
 
679
 
 
680
  refresh();
 
681
 
 
682
}
 
683
 
 
684
static void
 
685
get_cddb_track_info(track_t i_track)
 
686
{
 
687
#ifdef HAVE_CDDB
 
688
  cddb_track_t *t = cddb_disc_get_track(p_cddb_disc, 
 
689
                                        i_track - i_first_track);
 
690
  if (t) {
 
691
    cddb_track_set_title(t, title);
 
692
    cddb_track_set_artist(t, artist);
 
693
  }
 
694
  
 
695
#else
 
696
    ;
 
697
#endif      
 
698
}
 
699
 
 
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; \
 
706
  }
 
707
 
 
708
 
 
709
static void
 
710
get_cdtext_track_info(track_t i_track)
 
711
{
 
712
 
 
713
  cdtext_t *p_cdtext = cdio_get_cdtext(p_cdio, i_track);
 
714
 
 
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);
 
719
  }
 
720
}
 
721
 
 
722
static void
 
723
get_track_info(track_t i_track)
 
724
{
 
725
 
 
726
  if (b_prefer_cdtext) {
 
727
    get_cdtext_track_info(i_track);
 
728
    get_cddb_track_info(i_track);
 
729
  } else {
 
730
    get_cddb_track_info(i_track);
 
731
    get_cdtext_track_info(i_track);
 
732
  }
 
733
}
 
734
 
 
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]", \
 
738
             field,                                        \
 
739
             b_cdtext_ ## field ? "CD-Text": "CDDB");      \
 
740
    clrtoeol();                                            \
 
741
  }
 
742
    
 
743
static void
 
744
display_cdinfo(CdIo_t *p_cdio, track_t i_tracks, track_t i_first_track)
 
745
{
 
746
  int len;
 
747
  char line[80];
 
748
  
 
749
  if (!interactive) return;
 
750
 
 
751
  if (!b_cd) sprintf(line, "-");
 
752
  else {
 
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);
 
759
    
 
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);
 
764
  }
 
765
  
 
766
  mvprintw(LINE_CDINFO, 0, (char *) "CD info: %0s", line);
 
767
  clrtoeol();
 
768
  refresh();
 
769
}
 
770
    
 
771
/* ---------------------------------------------------------------------- */
 
772
 
 
773
static void
 
774
usage(char *prog)
 
775
{
 
776
    fprintf(stderr,
 
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"
 
779
            "via CDDB.\n"
 
780
            "\n"
 
781
            "usage: %s [options] [device]\n"
 
782
            "\n"
 
783
            "default for to search for a CD-ROM device with a CD-DA loaded\n"
 
784
            "\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"
 
789
            "  -v      verbose\n"
 
790
            "\n"
 
791
            "for non-interactive use (only one) of these:\n"
 
792
            "  -l      list tracks\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"
 
800
            "  -s      stop playing\n"
 
801
            "  -S      list audio subchannel information\n"
 
802
            "  -e      eject cdrom\n"
 
803
            "\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"
 
808
            "\n"
 
809
            "(c) 1997,98 Gerd Knorr <kraxel@goldbach.in-berlin.de>\n"
 
810
            "(c) 2005 Rocky Bernstein <rocky@panix.com>\n"
 
811
            , prog, prog);
 
812
}
 
813
 
 
814
static void
 
815
print_keys()
 
816
{
 
817
  unsigned int i;
 
818
  for (i=0; i < i_key_bindings; i++)
 
819
    fprintf(stderr, "%s\n", key_bindings[i]);
 
820
}
 
821
 
 
822
static void 
 
823
keypress_wait(CdIo_t *p_cdio)
 
824
  {
 
825
    int key;
 
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);
 
830
    }
 
831
    key = getch();
 
832
    clrtobot();
 
833
    action(NULL);
 
834
    if (!b_all_tracks)
 
835
      display_cdinfo(p_cdio, i_tracks, i_first_track);
 
836
    i_last_display_track = CDIO_INVALID_TRACK;
 
837
  }
 
838
 
 
839
static void
 
840
list_keys()
 
841
{
 
842
  unsigned int i;
 
843
  for (i=0; i < i_key_bindings; i++) {
 
844
    mvprintw(LINE_TRACK_PREV+i, 0, (char *) "%s", key_bindings[i]);
 
845
    clrtoeol();
 
846
  }
 
847
  keypress_wait(p_cdio);
 
848
}
 
849
 
 
850
static void
 
851
display_tracks(void)
 
852
{
 
853
  track_t i;
 
854
  int i_line=0;
 
855
  int s;
 
856
 
 
857
  if (b_record) {
 
858
    i_line=LINE_TRACK_PREV - 1;
 
859
    for (i = i_first_track; i <= i_last_track; i++) {
 
860
      char line[80];
 
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 ) ? "->" : " |");
 
869
      if (b_record) {
 
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))
 
874
            strcat(line, " / ");
 
875
          strcat(line, cd_info[i].artist);
 
876
        }
 
877
      }
 
878
      mvprintw(i_line++, 0, line);
 
879
      clrtoeol();
 
880
    }
 
881
  }
 
882
}
 
883
 
 
884
/*
 
885
 * PostScript 8bit latin1 handling
 
886
 * stolen from mpage output -- please don't ask me how this works...
 
887
 */
 
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"                       \
 
898
        "ifelse }\n"                                            \
 
899
        "{ pop pop }\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"        \
 
905
        "/charvec [\n"          \
 
906
        "026 /Scaron\n"         \
 
907
        "027 /Ydieresis\n"      \
 
908
        "028 /Zcaron\n"         \
 
909
        "029 /scaron\n"         \
 
910
        "030 /trademark\n"      \
 
911
        "031 /zcaron\n"         \
 
912
        "032 /space\n"          \
 
913
        "033 /exclam\n"         \
 
914
        "034 /quotedbl\n"       \
 
915
        "035 /numbersign\n"     \
 
916
        "036 /dollar\n"         \
 
917
        "037 /percent\n"        \
 
918
        "038 /ampersand\n"      \
 
919
        "039 /quoteright\n"     \
 
920
        "040 /parenleft\n"      \
 
921
        "041 /parenright\n"     \
 
922
        "042 /asterisk\n"       \
 
923
        "043 /plus\n"           \
 
924
        "044 /comma\n"          \
 
925
        "045 /minus\n"          \
 
926
        "046 /period\n"         \
 
927
        "047 /slash\n"          \
 
928
        "048 /zero\n"           \
 
929
        "049 /one\n"            \
 
930
        "050 /two\n"            \
 
931
        "051 /three\n"          \
 
932
        "052 /four\n"           \
 
933
        "053 /five\n"           \
 
934
        "054 /six\n"            \
 
935
        "055 /seven\n"          \
 
936
        "056 /eight\n"          \
 
937
        "057 /nine\n"           \
 
938
        "058 /colon\n"          \
 
939
        "059 /semicolon\n"      \
 
940
        "060 /less\n"           \
 
941
        "061 /equal\n"          \
 
942
        "062 /greater\n"        \
 
943
        "063 /question\n"       \
 
944
        "064 /at\n"             \
 
945
        "065 /A\n"              \
 
946
        "066 /B\n"              \
 
947
        "067 /C\n"              \
 
948
        "068 /D\n"              \
 
949
        "069 /E\n"              \
 
950
        "070 /F\n"              \
 
951
        "071 /G\n"              \
 
952
        "072 /H\n"              \
 
953
        "073 /I\n"              \
 
954
        "074 /J\n"              \
 
955
        "075 /K\n"              \
 
956
        "076 /L\n"              \
 
957
        "077 /M\n"              \
 
958
        "078 /N\n"              \
 
959
        "079 /O\n"              \
 
960
        "080 /P\n"              \
 
961
        "081 /Q\n"              \
 
962
        "082 /R\n"              \
 
963
        "083 /S\n"              \
 
964
        "084 /T\n"              \
 
965
        "085 /U\n"              \
 
966
        "086 /V\n"              \
 
967
        "087 /W\n"              \
 
968
        "088 /X\n"              \
 
969
        "089 /Y\n"              \
 
970
        "090 /Z\n"              \
 
971
        "091 /bracketleft\n"    \
 
972
        "092 /backslash\n"      \
 
973
        "093 /bracketright\n"   \
 
974
        "094 /asciicircum\n"    \
 
975
        "095 /underscore\n"     \
 
976
        "096 /quoteleft\n"      \
 
977
        "097 /a\n"              \
 
978
        "098 /b\n"              \
 
979
        "099 /c\n"              \
 
980
        "100 /d\n"              \
 
981
        "101 /e\n"              \
 
982
        "102 /f\n"              \
 
983
        "103 /g\n"              \
 
984
        "104 /h\n"              \
 
985
        "105 /i\n"              \
 
986
        "106 /j\n"              \
 
987
        "107 /k\n"              \
 
988
        "108 /l\n"              \
 
989
        "109 /m\n"              \
 
990
        "110 /n\n"              \
 
991
        "111 /o\n"              \
 
992
        "112 /p\n"              \
 
993
        "113 /q\n"              \
 
994
        "114 /r\n"              \
 
995
        "115 /s\n"              \
 
996
        "116 /t\n"              \
 
997
        "117 /u\n"              \
 
998
        "118 /v\n"              \
 
999
        "119 /w\n"              \
 
1000
        "120 /x\n"              \
 
1001
        "121 /y\n"              \
 
1002
        "122 /z\n"              \
 
1003
        "123 /braceleft\n"      \
 
1004
        "124 /bar\n"            \
 
1005
        "125 /braceright\n"     \
 
1006
        "126 /asciitilde\n"     \
 
1007
        "127 /.notdef\n"        \
 
1008
        "128 /fraction\n"       \
 
1009
        "129 /florin\n"         \
 
1010
        "130 /quotesingle\n"    \
 
1011
        "131 /quotedblleft\n"   \
 
1012
        "132 /guilsinglleft\n"  \
 
1013
        "133 /guilsinglright\n" \
 
1014
        "134 /fi\n"             \
 
1015
        "135 /fl\n"             \
 
1016
        "136 /endash\n"         \
 
1017
        "137 /dagger\n"         \
 
1018
        "138 /daggerdbl\n"      \
 
1019
        "139 /bullet\n"         \
 
1020
        "140 /quotesinglbase\n" \
 
1021
        "141 /quotedblbase\n"   \
 
1022
        "142 /quotedblright\n"  \
 
1023
        "143 /ellipsis\n"       \
 
1024
        "144 /dotlessi\n"       \
 
1025
        "145 /grave\n"          \
 
1026
        "146 /acute\n"          \
 
1027
        "147 /circumflex\n"     \
 
1028
        "148 /tilde\n"          \
 
1029
        "149 /oe\n"             \
 
1030
        "150 /breve\n"          \
 
1031
        "151 /dotaccent\n"      \
 
1032
        "152 /perthousand\n"    \
 
1033
        "153 /emdash\n"         \
 
1034
        "154 /ring\n"           \
 
1035
        "155 /Lslash\n"         \
 
1036
        "156 /OE\n"             \
 
1037
        "157 /hungarumlaut\n"   \
 
1038
        "158 /ogonek\n"         \
 
1039
        "159 /caron\n"          \
 
1040
        "160 /lslash\n"         \
 
1041
        "161 /exclamdown\n"     \
 
1042
        "162 /cent\n"           \
 
1043
        "163 /sterling\n"       \
 
1044
        "164 /currency\n"       \
 
1045
        "165 /yen\n"            \
 
1046
        "166 /brokenbar\n"      \
 
1047
        "167 /section\n"        \
 
1048
        "168 /dieresis\n"       \
 
1049
        "169 /copyright\n"      \
 
1050
        "170 /ordfeminine\n"    \
 
1051
        "171 /guillemotleft\n"  \
 
1052
        "172 /logicalnot\n"     \
 
1053
        "173 /hyphen\n"         \
 
1054
        "174 /registered\n"     \
 
1055
        "175 /macron\n"         \
 
1056
        "176 /degree\n"         \
 
1057
        "177 /plusminus\n"      \
 
1058
        "178 /twosuperior\n"    \
 
1059
        "179 /threesuperior\n"  \
 
1060
        "180 /acute\n"          \
 
1061
        "181 /mu\n"             \
 
1062
        "182 /paragraph\n"      \
 
1063
        "183 /periodcentered\n" \
 
1064
        "184 /cedilla\n"        \
 
1065
        "185 /onesuperior\n"    \
 
1066
        "186 /ordmasculine\n"   \
 
1067
        "187 /guillemotright\n" \
 
1068
        "188 /onequarter\n"     \
 
1069
        "189 /onehalf\n"        \
 
1070
        "190 /threequarters\n"  \
 
1071
        "191 /questiondown\n"   \
 
1072
        "192 /Agrave\n"         \
 
1073
        "193 /Aacute\n"         \
 
1074
        "194 /Acircumflex\n"    \
 
1075
        "195 /Atilde\n"         \
 
1076
        "196 /Adieresis\n"      \
 
1077
        "197 /Aring\n"          \
 
1078
        "198 /AE\n"             \
 
1079
        "199 /Ccedilla\n"       \
 
1080
        "200 /Egrave\n"         \
 
1081
        "201 /Eacute\n"         \
 
1082
        "202 /Ecircumflex\n"    \
 
1083
        "203 /Edieresis\n"      \
 
1084
        "204 /Igrave\n"         \
 
1085
        "205 /Iacute\n"         \
 
1086
        "206 /Icircumflex\n"    \
 
1087
        "207 /Idieresis\n"      \
 
1088
        "208 /Eth\n"            \
 
1089
        "209 /Ntilde\n"         \
 
1090
        "210 /Ograve\n"         \
 
1091
        "211 /Oacute\n"         \
 
1092
        "212 /Ocircumflex\n"    \
 
1093
        "213 /Otilde\n"         \
 
1094
        "214 /Odieresis\n"      \
 
1095
        "215 /multiply\n"       \
 
1096
        "216 /Oslash\n"         \
 
1097
        "217 /Ugrave\n"         \
 
1098
        "218 /Uacute\n"         \
 
1099
        "219 /Ucircumflex\n"    \
 
1100
        "220 /Udieresis\n"      \
 
1101
        "221 /Yacute\n"         \
 
1102
        "222 /Thorn\n"          \
 
1103
        "223 /germandbls\n"     \
 
1104
        "224 /agrave\n"         \
 
1105
        "225 /aacute\n"         \
 
1106
        "226 /acircumflex\n"    \
 
1107
        "227 /atilde\n"         \
 
1108
        "228 /adieresis\n"      \
 
1109
        "229 /aring\n"          \
 
1110
        "230 /ae\n"             \
 
1111
        "231 /ccedilla\n"       \
 
1112
        "232 /egrave\n"         \
 
1113
        "233 /eacute\n"         \
 
1114
        "234 /ecircumflex\n"    \
 
1115
        "235 /edieresis\n"      \
 
1116
        "236 /igrave\n"         \
 
1117
        "237 /iacute\n"         \
 
1118
        "238 /icircumflex\n"    \
 
1119
        "239 /idieresis\n"      \
 
1120
        "240 /eth\n"            \
 
1121
        "241 /ntilde\n"         \
 
1122
        "242 /ograve\n"         \
 
1123
        "243 /oacute\n"         \
 
1124
        "244 /ocircumflex\n"    \
 
1125
        "245 /otilde\n"         \
 
1126
        "246 /odieresis\n"      \
 
1127
        "247 /divide\n"         \
 
1128
        "248 /oslash\n"         \
 
1129
        "249 /ugrave\n"         \
 
1130
        "250 /uacute\n"         \
 
1131
        "251 /ucircumflex\n"    \
 
1132
        "252 /udieresis\n"      \
 
1133
        "253 /yacute\n"         \
 
1134
        "254 /thorn\n"          \
 
1135
        "255 /ydieresis\n"      \
 
1136
        "] def"
 
1137
 
 
1138
 
 
1139
static void
 
1140
ps_list_tracks(void)
 
1141
{
 
1142
  int i,s,y,sy;
 
1143
  
 
1144
  if (!b_record) return;
 
1145
 
 
1146
  printf("%%!PS-Adobe-2.0\n");
 
1147
  
 
1148
  /* encoding tricks */
 
1149
  puts(ENCODING_TRICKS);
 
1150
  printf("/Times /TimesLatin1 charvec ReEncodeSmall\n");
 
1151
  printf("/Helvetica /HelveticaLatin1 charvec ReEncodeSmall\n");
 
1152
  
 
1153
  /* Spaces */
 
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");
 
1160
  
 
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");
 
1167
  
 
1168
  /* Title */
 
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);
 
1173
  
 
1174
  /* List */
 
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]);
 
1182
    
 
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);
 
1186
    
 
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);
 
1190
  }
 
1191
  
 
1192
  /* Seitenbanner */
 
1193
  printf("/HelveticaLatin1 findfont 12 scalefont setfont\n");
 
1194
  printf(" 97 105 moveto (%s: %s) 90 rotate show -90 rotate\n",
 
1195
         artist, title);
 
1196
  printf("493 425 moveto (%s: %s) -90 rotate show 90 rotate\n",
 
1197
         artist, title);
 
1198
  printf("showpage\n");
 
1199
}
 
1200
 
 
1201
static void
 
1202
list_tracks(void)
 
1203
{
 
1204
  int i,s;
 
1205
 
 
1206
  if (!b_record) return;
 
1207
 
 
1208
  printf("Title : %s\n", title);
 
1209
  printf("Artist: %s\n", artist);
 
1210
  
 
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);
 
1215
  }
 
1216
}
 
1217
 
 
1218
typedef enum {
 
1219
  NO_OP=0,
 
1220
  PLAY_CD=1,
 
1221
  PLAY_TRACK=2,
 
1222
  STOP_PLAYING=3,
 
1223
  EJECT_CD=4,
 
1224
  CLOSE_CD=5,
 
1225
  SET_VOLUME=6,
 
1226
  LIST_SUBCHANNEL=7,
 
1227
  LIST_KEYS=8,
 
1228
  LIST_TRACKS=9,
 
1229
  PS_LIST_TRACKS=10,
 
1230
  TOGGLE_PAUSE=11,
 
1231
  EXIT_PROGRAM=12
 
1232
} cd_operation_t;
 
1233
 
 
1234
int
 
1235
main(int argc, char *argv[])
 
1236
{    
 
1237
  int  c, nostop=0;
 
1238
  char *h;
 
1239
  int  i_rc = 0;
 
1240
  int  i_volume_level = 0;
 
1241
  cd_operation_t cd_op = NO_OP; /* operation to do in non-interactive mode */
 
1242
  
 
1243
  
 
1244
  psz_program = strrchr(argv[0],'/');
 
1245
  psz_program = psz_program ? psz_program+1 : argv[0];
 
1246
 
 
1247
  memset(&cddb_opts, 0, sizeof(cddb_opts));
 
1248
  
 
1249
  cdio_loglevel_default = CDIO_LOG_WARN;
 
1250
  /* parse options */
 
1251
  while ( 1 ) {
 
1252
    if (-1 == (c = getopt(argc, argv, "acCdehkplL:sSt:vx")))
 
1253
      break;
 
1254
    switch (c) {
 
1255
    case 'v':
 
1256
      b_verbose = true;
 
1257
      if (cdio_loglevel_default > CDIO_LOG_INFO) 
 
1258
        cdio_loglevel_default = CDIO_LOG_INFO;
 
1259
      break;
 
1260
    case 'd':
 
1261
      debug = 1;
 
1262
      if (cdio_loglevel_default > CDIO_LOG_DEBUG) 
 
1263
      cdio_loglevel_default = CDIO_LOG_DEBUG;
 
1264
      break;
 
1265
    case 'a':
 
1266
      auto_mode = 1;
 
1267
      break;
 
1268
    case 'L':
 
1269
      if (NULL != (h = strchr(optarg,'-'))) {
 
1270
        i_volume_level = atoi(optarg);
 
1271
        cd_op = SET_VOLUME;
 
1272
      }
 
1273
    case 't':
 
1274
      if (NULL != (h = strchr(optarg,'-'))) {
 
1275
        *h = 0;
 
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;
 
1280
      } else {
 
1281
        start_track = atoi(optarg);
 
1282
        stop_track = start_track+1;
 
1283
        one_track = 1;
 
1284
      }
 
1285
      interactive = false;
 
1286
      cd_op = PLAY_TRACK;
 
1287
      break;
 
1288
    case 'p':
 
1289
      interactive = false;
 
1290
      cd_op = PLAY_CD;
 
1291
      break;
 
1292
    case 'l':
 
1293
      interactive = false;
 
1294
      cd_op = LIST_TRACKS;
 
1295
      break;
 
1296
    case 'C':
 
1297
      interactive = false;
 
1298
      cd_op = CLOSE_CD;
 
1299
      break;
 
1300
    case 'c':
 
1301
      interactive = false;
 
1302
      cd_op = PS_LIST_TRACKS;
 
1303
      break;
 
1304
    case 's':
 
1305
      interactive = false;
 
1306
      cd_op = STOP_PLAYING;
 
1307
      break;
 
1308
    case 'S':
 
1309
      interactive = false;
 
1310
      cd_op = LIST_SUBCHANNEL;
 
1311
      break;
 
1312
    case 'e':
 
1313
      interactive = false;
 
1314
      cd_op = EJECT_CD;
 
1315
      break;
 
1316
    case 'k':
 
1317
      print_keys();
 
1318
      exit(1);
 
1319
    case 'h':
 
1320
      usage(psz_program);
 
1321
      exit(1);
 
1322
    default:
 
1323
      usage(psz_program);
 
1324
      exit(1);
 
1325
    }
 
1326
  }
 
1327
  
 
1328
  if (argc > optind) {
 
1329
    psz_device = strdup(argv[optind]);
 
1330
  } else {
 
1331
    char **ppsz_cdda_drives=NULL;
 
1332
    char **ppsz_all_cd_drives = cdio_get_devices_ret(&driver_id);
 
1333
 
 
1334
    if (!ppsz_all_cd_drives) {
 
1335
      fprintf(stderr, "Can't find a CD-ROM drive\n");
 
1336
      exit(2);
 
1337
    }
 
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");
 
1342
      exit(3);
 
1343
    }
 
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);
 
1347
  }
 
1348
  
 
1349
  if (!interactive) {
 
1350
    b_sig = true;
 
1351
    nostop=1;
 
1352
  }
 
1353
 
 
1354
  tty_raw();
 
1355
  signal(SIGINT,ctrlc);
 
1356
  signal(SIGQUIT,ctrlc);
 
1357
  signal(SIGTERM,ctrlc);
 
1358
  signal(SIGHUP,ctrlc);
 
1359
 
 
1360
  if (CLOSE_CD != cd_op) {
 
1361
    /* open device */
 
1362
    if (b_verbose)
 
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);
 
1368
    }
 
1369
    
 
1370
    if (p_cdio && b_verbose)
 
1371
      fprintf(stderr,"ok\n");
 
1372
  }
 
1373
  
 
1374
  if (!interactive) {
 
1375
    b_sig = true;
 
1376
    nostop=1;
 
1377
    if (EJECT_CD == cd_op) {
 
1378
      i_rc = cd_eject() ? 0 : 1;
 
1379
    } else {
 
1380
      switch (cd_op) {
 
1381
      case PS_LIST_TRACKS:
 
1382
      case LIST_TRACKS:
 
1383
      case PLAY_TRACK:
 
1384
        read_toc(p_cdio);
 
1385
      default:
 
1386
        break;
 
1387
      }
 
1388
      if (p_cdio)
 
1389
        switch (cd_op) {
 
1390
        case STOP_PLAYING:
 
1391
          i_rc = cd_stop(p_cdio) ? 0 : 1;
 
1392
          break;
 
1393
        case EJECT_CD:
 
1394
          /* Should have been handled above. */
 
1395
          cd_eject();
 
1396
          break;
 
1397
        case LIST_TRACKS:
 
1398
          list_tracks();
 
1399
          break;
 
1400
        case PS_LIST_TRACKS:
 
1401
          ps_list_tracks();
 
1402
          break;
 
1403
        case PLAY_TRACK:
 
1404
          /* play just this one track */
 
1405
          if (b_record) {
 
1406
            printf("%s / %s\n", artist, title);
 
1407
            if (one_track)
 
1408
              printf("%s\n", cd_info[start_track].title);
 
1409
          }
 
1410
          i_rc = play_track(start_track, stop_track) ? 0 : 1;
 
1411
          break;
 
1412
        case PLAY_CD:
 
1413
          if (b_record)
 
1414
            printf("%s / %s\n", artist, title);
 
1415
          play_track(1,CDIO_CDROM_LEADOUT_TRACK);
 
1416
          break;
 
1417
        case SET_VOLUME:
 
1418
          {
 
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, 
 
1422
                                                               &volume))
 
1423
              ? 0 : 1;
 
1424
            break;
 
1425
          }
 
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) {
 
1430
              {
 
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);
 
1434
              }
 
1435
            }
 
1436
            printf("drive state: %s\n", 
 
1437
                   mmc_audio_state2str(sub.audio_status));
 
1438
          } else {
 
1439
            i_rc = 1;
 
1440
          }
 
1441
          break;
 
1442
        case CLOSE_CD: /* Handled below */
 
1443
        case LIST_KEYS:
 
1444
        case TOGGLE_PAUSE:
 
1445
        case EXIT_PROGRAM:
 
1446
        case NO_OP:
 
1447
          break;
 
1448
        }
 
1449
      else if (CLOSE_CD == cd_op) {
 
1450
        i_rc = (DRIVER_OP_SUCCESS == cdio_close_tray(psz_device, NULL))
 
1451
                ? 0 : 1;
 
1452
      } else {
 
1453
        fprintf(stderr,"no CD in drive (%s)\n", psz_device);
 
1454
      }
 
1455
    }
 
1456
  }
 
1457
  
 
1458
  while ( !b_sig ) {
 
1459
    int key;
 
1460
    if (!b_cd) read_toc(p_cdio);
 
1461
    read_subchannel(p_cdio);
 
1462
    display_status(false);
 
1463
    
 
1464
    if (1 == select_wait(b_cd ? 1 : 5)) {
 
1465
      switch (key = getch()) {
 
1466
      case 'A':
 
1467
      case 'a':
 
1468
        auto_mode = !auto_mode;
 
1469
        break;
 
1470
      case 'X':
 
1471
      case 'x':
 
1472
        nostop=1;
 
1473
        /* fall through */
 
1474
      case 'Q':
 
1475
      case 'q':
 
1476
        b_sig = true;
 
1477
        break;
 
1478
      case 'E':
 
1479
      case 'e':
 
1480
        cd_eject();
 
1481
        break;
 
1482
      case 's':
 
1483
        cd_stop(p_cdio);
 
1484
        break;
 
1485
      case 'C':
 
1486
      case 'c':
 
1487
        cd_close(psz_device);
 
1488
        break;
 
1489
      case 'L':
 
1490
      case 'l':
 
1491
        b_all_tracks = !b_all_tracks;
 
1492
        if (b_all_tracks)
 
1493
          display_tracks();
 
1494
        else {
 
1495
          i_last_display_track = CDIO_INVALID_TRACK;
 
1496
          display_cdinfo(p_cdio, i_tracks, i_first_track);
 
1497
        }
 
1498
        
 
1499
        break;
 
1500
      case 'K':
 
1501
      case 'k':
 
1502
      case 'h':
 
1503
      case 'H':
 
1504
      case '?':
 
1505
        list_keys();
 
1506
        break;
 
1507
      case ' ':
 
1508
      case 'P':
 
1509
      case 'p':
 
1510
        toggle_pause();
 
1511
        break;
 
1512
      case KEY_RIGHT:
 
1513
        if (b_cd &&
 
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);
 
1517
        else
 
1518
          play_track(1,CDIO_CDROM_LEADOUT_TRACK);
 
1519
        break;
 
1520
      case KEY_LEFT:
 
1521
        if (b_cd &&
 
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);
 
1525
        break;
 
1526
      case KEY_UP:
 
1527
        if (b_cd && sub.audio_status == CDIO_MMC_READ_SUB_ST_PLAY)
 
1528
          skip(10);
 
1529
        break;
 
1530
      case KEY_DOWN:
 
1531
        if (b_cd && sub.audio_status == CDIO_MMC_READ_SUB_ST_PLAY)
 
1532
          skip(-10);
 
1533
        break;
 
1534
      case '1':
 
1535
      case '2':
 
1536
      case '3':
 
1537
      case '4':
 
1538
      case '5':
 
1539
      case '6':
 
1540
      case '7':
 
1541
      case '8':
 
1542
      case '9':
 
1543
        play_track(key - '0', CDIO_CDROM_LEADOUT_TRACK);
 
1544
        break;
 
1545
      case '0':
 
1546
        play_track(10, CDIO_CDROM_LEADOUT_TRACK);
 
1547
        break;
 
1548
      case KEY_F(1):
 
1549
      case KEY_F(2):
 
1550
      case KEY_F(3):
 
1551
      case KEY_F(4):
 
1552
      case KEY_F(5):
 
1553
      case KEY_F(6):
 
1554
      case KEY_F(7):
 
1555
      case KEY_F(8):
 
1556
      case KEY_F(9):
 
1557
      case KEY_F(10):
 
1558
      case KEY_F(11):
 
1559
      case KEY_F(12):
 
1560
      case KEY_F(13):
 
1561
      case KEY_F(14):
 
1562
      case KEY_F(15):
 
1563
      case KEY_F(16):
 
1564
      case KEY_F(17):
 
1565
      case KEY_F(18):
 
1566
      case KEY_F(19):
 
1567
      case KEY_F(20):
 
1568
        play_track(key - KEY_F(1) + 11, CDIO_CDROM_LEADOUT_TRACK);
 
1569
        break;
 
1570
      }
 
1571
    }
 
1572
  }
 
1573
  if (!nostop) cd_stop(p_cdio);
 
1574
  tty_restore();
 
1575
  finish("bye", i_rc);
 
1576
  
 
1577
  return 0; /* keep compiler happy */
 
1578
}