Blame example/audio.c

Packit dd8086
/*
Packit dd8086
  Copyright (C) 2005, 2008-2009, 2012, 2014, 2017 Rocky Bernstein
Packit dd8086
  <rocky@gnu.org>
Packit dd8086
Packit dd8086
  Adapted from Gerd Knorr's player.c program  <kraxel@bytesex.org>
Packit dd8086
  Copyright (C) 1997, 1998
Packit dd8086
Packit dd8086
  This program is free software: you can redistribute it and/or modify
Packit dd8086
  it under the terms of the GNU General Public License as published by
Packit dd8086
  the Free Software Foundation, either version 3 of the License, or
Packit dd8086
  (at your option) any later version.
Packit dd8086
Packit dd8086
  This program is distributed in the hope that it will be useful,
Packit dd8086
  but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit dd8086
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit dd8086
  GNU General Public License for more details.
Packit dd8086
Packit dd8086
  You should have received a copy of the GNU General Public License
Packit dd8086
  along with this program.  If not, see <http://www.gnu.org/licenses/>.
Packit dd8086
*/
Packit dd8086
Packit dd8086
/* A program to show use of audio controls. For a more expanded
Packit dd8086
   CDDA player program using curses display see cdda-player in this
Packit dd8086
   distribution.
Packit dd8086
*/
Packit dd8086
#ifdef HAVE_CONFIG_H
Packit dd8086
#include "config.h"
Packit dd8086
#endif
Packit dd8086
Packit dd8086
#ifdef HAVE_STDIO_H
Packit dd8086
#include <stdio.h>
Packit dd8086
#endif
Packit dd8086
#ifdef HAVE_STDLIB_H
Packit dd8086
#include <stdlib.h>
Packit dd8086
#endif
Packit dd8086
#ifdef HAVE_UNISTD_H
Packit dd8086
#include <unistd.h>
Packit dd8086
#endif
Packit dd8086
#ifdef HAVE_STRING_H
Packit dd8086
#include <string.h>
Packit dd8086
#endif
Packit dd8086
#ifdef HAVE_SYS_TIME_H
Packit dd8086
#include <sys/time.h>
Packit dd8086
#endif
Packit dd8086
#ifdef HAVE_GETOPT_H
Packit dd8086
#include <getopt.h>
Packit dd8086
#endif
Packit dd8086
#ifdef HAVE_ERRNO_H
Packit dd8086
#include <errno.h>
Packit dd8086
#endif
Packit dd8086
#include <signal.h>
Packit dd8086
Packit dd8086
#include <cdio/cdio.h>
Packit dd8086
#include <cdio/mmc.h>
Packit dd8086
#include <cdio/util.h>
Packit dd8086
#include <cdio/cd_types.h>
Packit dd8086
Packit dd8086
static bool play_track(track_t t1, track_t t2);
Packit dd8086
Packit dd8086
static CdIo_t             *p_cdio_global = NULL;         /* libcdio handle */
Packit dd8086
static driver_id_t        driver_id = DRIVER_DEVICE;
Packit dd8086
Packit dd8086
/* cdrom data */
Packit dd8086
static track_t            i_first_track;
Packit dd8086
static track_t            i_last_track;
Packit dd8086
static track_t            i_first_audio_track;
Packit dd8086
static track_t            i_last_audio_track;
Packit dd8086
static track_t            i_tracks;
Packit dd8086
static msf_t              toc[CDIO_CDROM_LEADOUT_TRACK+1];
Packit dd8086
static cdio_subchannel_t  sub;      /* subchannel last time read */
Packit dd8086
static int                i_data;     /* # of data tracks present ? */
Packit dd8086
static int                start_track = 0;
Packit dd8086
static int                stop_track = 0;
Packit dd8086
static int                one_track = 0;
Packit dd8086
Packit dd8086
static bool               b_cd         = false;
Packit dd8086
static bool               auto_mode    = false;
Packit dd8086
static bool               b_verbose    = false;
Packit dd8086
static bool               debug        = false;
Packit dd8086
static bool               b_record = false; /* we have a record for
Packit dd8086
					the inserted CD */
Packit dd8086
Packit dd8086
static char *psz_device_global=NULL;
Packit dd8086
static char *psz_program;
Packit dd8086
Packit dd8086
inline static void
Packit dd8086
xperror(const char *psz_msg)
Packit dd8086
{
Packit dd8086
  if (b_verbose) {
Packit dd8086
    fprintf(stderr, "error: ");
Packit dd8086
    perror(psz_msg);
Packit dd8086
  }
Packit dd8086
  return;
Packit dd8086
}
Packit dd8086
Packit dd8086
Packit dd8086
static void
Packit dd8086
oops(const char *psz_msg, int rc)
Packit dd8086
{
Packit dd8086
  cdio_destroy (p_cdio_global);
Packit dd8086
  free (psz_device_global);
Packit dd8086
  exit (rc);
Packit dd8086
}
Packit dd8086
Packit dd8086
/* ---------------------------------------------------------------------- */
Packit dd8086
Packit dd8086
/*! Stop playing audio CD */
Packit dd8086
static bool
Packit dd8086
cd_stop(CdIo_t *p_cdio)
Packit dd8086
{
Packit dd8086
  bool b_ok = true;
Packit dd8086
  if (b_cd && p_cdio) {
Packit dd8086
    i_last_audio_track = CDIO_INVALID_TRACK;
Packit dd8086
    b_ok = DRIVER_OP_SUCCESS == cdio_audio_stop(p_cdio);
Packit dd8086
    if ( !b_ok )
Packit dd8086
      xperror("stop");
Packit dd8086
  }
Packit dd8086
  return b_ok;
Packit dd8086
}
Packit dd8086
Packit dd8086
/*! Eject CD */
Packit dd8086
static bool
Packit dd8086
cd_eject(void)
Packit dd8086
{
Packit dd8086
  bool b_ok = true;
Packit dd8086
  if (p_cdio_global) {
Packit dd8086
    cd_stop(p_cdio_global);
Packit dd8086
    b_ok = DRIVER_OP_SUCCESS == cdio_eject_media(&p_cdio_global);
Packit dd8086
    if (!b_ok)
Packit dd8086
      xperror("eject");
Packit dd8086
    b_cd = false;
Packit dd8086
    p_cdio_global = NULL;
Packit dd8086
  }
Packit dd8086
  return b_ok;
Packit dd8086
}
Packit dd8086
Packit dd8086
/*! Close CD tray */
Packit dd8086
static bool
Packit dd8086
cd_close(const char *psz_device)
Packit dd8086
{
Packit dd8086
  bool b_ok = true;
Packit dd8086
  if (!b_cd) {
Packit dd8086
    b_ok = DRIVER_OP_SUCCESS == cdio_close_tray(psz_device, &driver_id);
Packit dd8086
    if (!b_ok)
Packit dd8086
      xperror("close");
Packit dd8086
  }
Packit dd8086
  return b_ok;
Packit dd8086
}
Packit dd8086
Packit dd8086
/*! Pause playing audio CD */
Packit dd8086
static bool
Packit dd8086
cd_pause(CdIo_t *p_cdio)
Packit dd8086
{
Packit dd8086
  bool b_ok = true;
Packit dd8086
  if (sub.audio_status == CDIO_MMC_READ_SUB_ST_PLAY) {
Packit dd8086
    b_ok = DRIVER_OP_SUCCESS == cdio_audio_pause(p_cdio);
Packit dd8086
    if (!b_ok)
Packit dd8086
      xperror("pause");
Packit dd8086
  }
Packit dd8086
  return b_ok;
Packit dd8086
}
Packit dd8086
Packit dd8086
/*! Get status/track/position info of an audio CD */
Packit dd8086
static bool
Packit dd8086
read_subchannel(CdIo_t *p_cdio)
Packit dd8086
{
Packit dd8086
  bool b_ok = true;
Packit dd8086
  if (!b_cd) return false;
Packit dd8086
Packit dd8086
  b_ok = DRIVER_OP_SUCCESS == cdio_audio_read_subchannel(p_cdio, &sub);
Packit dd8086
  if (!b_ok) {
Packit dd8086
    xperror("read subchannel");
Packit dd8086
    b_cd = 0;
Packit dd8086
  }
Packit dd8086
  if (auto_mode && sub.audio_status == CDIO_MMC_READ_SUB_ST_COMPLETED)
Packit dd8086
    cd_eject();
Packit dd8086
  return b_ok;
Packit dd8086
}
Packit dd8086
Packit dd8086
/*! Read CD TOC  and set CD information. */
Packit dd8086
static void
Packit dd8086
read_toc(CdIo_t *p_cdio)
Packit dd8086
{
Packit dd8086
  track_t i;
Packit dd8086
Packit dd8086
  i_first_track       = cdio_get_first_track_num(p_cdio);
Packit dd8086
  i_last_track        = cdio_get_last_track_num(p_cdio);
Packit dd8086
  i_tracks            = cdio_get_num_tracks(p_cdio);
Packit dd8086
  i_first_audio_track = i_first_track;
Packit dd8086
  i_last_audio_track  = i_last_track;
Packit dd8086
Packit dd8086
Packit dd8086
  if ( CDIO_INVALID_TRACK == i_first_track ||
Packit dd8086
       CDIO_INVALID_TRACK == i_last_track ) {
Packit dd8086
    xperror("read toc header");
Packit dd8086
    b_cd = false;
Packit dd8086
    b_record = false;
Packit dd8086
  } else {
Packit dd8086
    b_cd = true;
Packit dd8086
    i_data = 0;
Packit dd8086
    for (i = i_first_track; i <= i_last_track+1; i++) {
Packit dd8086
      if ( !cdio_get_track_msf(p_cdio, i, &(toc[i])) )
Packit dd8086
      {
Packit dd8086
	xperror("read toc entry");
Packit dd8086
	b_cd = false;
Packit dd8086
	return;
Packit dd8086
      }
Packit dd8086
      if ( TRACK_FORMAT_AUDIO != cdio_get_track_format(p_cdio, i) ) {
Packit dd8086
	if ((i != i_last_track+1) ) {
Packit dd8086
	  i_data++;
Packit dd8086
	  if (i == i_first_track) {
Packit dd8086
	    if (i == i_last_track)
Packit dd8086
	      i_first_audio_track = CDIO_CDROM_LEADOUT_TRACK;
Packit dd8086
	    else
Packit dd8086
	      i_first_audio_track++;
Packit dd8086
	  }
Packit dd8086
	}
Packit dd8086
      }
Packit dd8086
    }
Packit dd8086
    b_record = true;
Packit dd8086
    read_subchannel(p_cdio);
Packit dd8086
    if (auto_mode && sub.audio_status != CDIO_MMC_READ_SUB_ST_PLAY)
Packit dd8086
      play_track(1, CDIO_CDROM_LEADOUT_TRACK);
Packit dd8086
  }
Packit dd8086
}
Packit dd8086
Packit dd8086
/*! Play an audio track. */
Packit dd8086
static bool
Packit dd8086
play_track(track_t i_start_track, track_t i_end_track)
Packit dd8086
{
Packit dd8086
  bool b_ok = true;
Packit dd8086
Packit dd8086
  if (!b_cd) {
Packit dd8086
    cd_close(psz_device_global);
Packit dd8086
    read_toc(p_cdio_global);
Packit dd8086
  }
Packit dd8086
Packit dd8086
  read_subchannel(p_cdio_global);
Packit dd8086
  if (!b_cd || i_first_track == CDIO_CDROM_LEADOUT_TRACK)
Packit dd8086
    return false;
Packit dd8086
Packit dd8086
  if (debug)
Packit dd8086
    fprintf(stderr,"play tracks: %d-%d => ", i_start_track, i_end_track);
Packit dd8086
  if (i_start_track < i_first_track)       i_start_track = i_first_track;
Packit dd8086
  if (i_start_track > i_last_audio_track)  i_start_track = i_last_audio_track;
Packit dd8086
  if (i_end_track < i_first_track)         i_end_track   = i_first_track;
Packit dd8086
  if (i_end_track > i_last_audio_track)    i_end_track   = i_last_audio_track;
Packit dd8086
  if (debug)
Packit dd8086
    fprintf(stderr,"%d-%d\n",i_start_track, i_end_track);
Packit dd8086
Packit dd8086
  cd_pause(p_cdio_global);
Packit dd8086
  b_ok = (DRIVER_OP_SUCCESS == cdio_audio_play_msf(p_cdio_global,
Packit dd8086
						   &(toc[i_start_track]),
Packit dd8086
						   &(toc[i_end_track])) );
Packit dd8086
  if (!b_ok) xperror("play");
Packit dd8086
  return b_ok;
Packit dd8086
}
Packit dd8086
Packit dd8086
static void
Packit dd8086
usage(char *prog)
Packit dd8086
{
Packit dd8086
    fprintf(stderr,
Packit dd8086
	    "%s is a simple interface to issuing CD audio comamnds\n"
Packit dd8086
	    "\n"
Packit dd8086
	    "usage: %s [options] [device]\n"
Packit dd8086
            "\n"
Packit dd8086
	    "default for to search for a CD-ROM device with a CD-DA loaded\n"
Packit dd8086
	    "\n"
Packit dd8086
	    "These command line options available:\n"
Packit dd8086
	    "  -h      print this help\n"
Packit dd8086
	    "  -a      start up in auto-mode\n"
Packit dd8086
	    "  -v      verbose\n"
Packit dd8086
	    "\n"
Packit dd8086
	    " Use only one of these:\n"
Packit dd8086
	    "  -C      close CD-ROM tray. If you use this option,\n"
Packit dd8086
	    "          a CD-ROM device name must be specified.\n"
Packit dd8086
	    "  -p      play the whole CD\n"
Packit dd8086
	    "  -t n    play track >n<\n"
Packit dd8086
	    "  -t a-b  play all tracks between a and b (inclusive)\n"
Packit dd8086
	    "  -L      set volume level\n"
Packit dd8086
	    "  -s      stop playing\n"
Packit dd8086
	    "  -S      list audio subchannel information\n"
Packit dd8086
	    "  -e      eject cdrom\n"
Packit dd8086
            "\n"
Packit dd8086
	    "That's all. Oh, maybe a few words more about the auto-mode. This\n"
Packit dd8086
	    "is the 'dont-touch-any-key' feature. You load a CD, player starts\n"
Packit dd8086
	    "to play it, and when it is done it ejects the CD. Start it that\n"
Packit dd8086
	    "way on a spare console and forget about it...\n"
Packit dd8086
	    "\n"
Packit dd8086
	    "(c) 1997,98 Gerd Knorr <kraxel@goldbach.in-berlin.de>\n"
Packit dd8086
	    "(c) 2005 Rocky Bernstein <rocky@gnu.org>\n"
Packit dd8086
	    , prog, prog);
Packit dd8086
}
Packit dd8086
Packit dd8086
typedef enum {
Packit dd8086
  NO_OP=0,
Packit dd8086
  PLAY_CD=1,
Packit dd8086
  PLAY_TRACK=2,
Packit dd8086
  STOP_PLAYING=3,
Packit dd8086
  EJECT_CD=4,
Packit dd8086
  CLOSE_CD=5,
Packit dd8086
  SET_VOLUME=6,
Packit dd8086
  LIST_SUBCHANNEL=7,
Packit dd8086
} cd_operation_t;
Packit dd8086
Packit dd8086
int
Packit dd8086
main(int argc, char *argv[])
Packit dd8086
{
Packit dd8086
  int  c;
Packit dd8086
  char *h;
Packit dd8086
  int  i_rc = 0;
Packit dd8086
  int  i_volume_level = -1;
Packit dd8086
  cd_operation_t todo = NO_OP; /* operation to do in non-interactive mode */
Packit dd8086
Packit dd8086
  psz_program = strrchr(argv[0],'/');
Packit dd8086
  psz_program = psz_program ? psz_program+1 : argv[0];
Packit dd8086
Packit dd8086
  /* parse options */
Packit dd8086
  while ( 1 ) {
Packit dd8086
    if (-1 == (c = getopt(argc, argv, "aCdehkpL:sSt:vx")))
Packit dd8086
      break;
Packit dd8086
    switch (c) {
Packit dd8086
    case 'v':
Packit dd8086
      b_verbose = true;
Packit dd8086
      break;
Packit dd8086
    case 'd':
Packit dd8086
      debug = 1;
Packit dd8086
      break;
Packit dd8086
    case 'a':
Packit dd8086
      auto_mode = 1;
Packit dd8086
      break;
Packit dd8086
    case 'L':
Packit dd8086
      if (NULL != strchr(optarg,'-')) {
Packit dd8086
	i_volume_level = atoi(optarg);
Packit dd8086
	todo = SET_VOLUME;
Packit dd8086
      }
Packit dd8086
      break;
Packit dd8086
    case 't':
Packit dd8086
      if (NULL != (h = strchr(optarg,'-'))) {
Packit dd8086
	*h = 0;
Packit dd8086
	start_track = atoi(optarg);
Packit dd8086
	stop_track = atoi(h+1)+1;
Packit dd8086
	if (0 == start_track) start_track = 1;
Packit dd8086
	if (1 == stop_track)  stop_track  = CDIO_CDROM_LEADOUT_TRACK;
Packit dd8086
      } else {
Packit dd8086
	start_track = atoi(optarg);
Packit dd8086
	stop_track = start_track+1;
Packit dd8086
	one_track = 1;
Packit dd8086
      }
Packit dd8086
      todo = PLAY_TRACK;
Packit dd8086
      break;
Packit dd8086
    case 'p':
Packit dd8086
      todo = PLAY_CD;
Packit dd8086
      break;
Packit dd8086
    case 'C':
Packit dd8086
      todo = CLOSE_CD;
Packit dd8086
      break;
Packit dd8086
      break;
Packit dd8086
    case 's':
Packit dd8086
      todo = STOP_PLAYING;
Packit dd8086
      break;
Packit dd8086
    case 'S':
Packit dd8086
      todo = LIST_SUBCHANNEL;
Packit dd8086
      break;
Packit dd8086
    case 'e':
Packit dd8086
      todo = EJECT_CD;
Packit dd8086
      break;
Packit dd8086
    case 'h':
Packit dd8086
      usage(psz_program);
Packit dd8086
      exit(1);
Packit dd8086
    default:
Packit dd8086
      usage(psz_program);
Packit dd8086
      exit(1);
Packit dd8086
    }
Packit dd8086
  }
Packit dd8086
Packit dd8086
  if (argc > optind) {
Packit dd8086
    psz_device_global = strdup(argv[optind]);
Packit dd8086
  } else {
Packit dd8086
    char **ppsz_cdda_drives=NULL;
Packit dd8086
    char **ppsz_all_cd_drives = cdio_get_devices_ret(&driver_id);
Packit dd8086
Packit dd8086
    if (!ppsz_all_cd_drives) {
Packit dd8086
      fprintf(stderr, "Can't find a CD-ROM drive\n");
Packit dd8086
      exit(2);
Packit dd8086
    }
Packit dd8086
    ppsz_cdda_drives = cdio_get_devices_with_cap(ppsz_all_cd_drives,
Packit dd8086
						 CDIO_FS_AUDIO, false);
Packit dd8086
    if (!ppsz_cdda_drives || !ppsz_cdda_drives[0]) {
Packit dd8086
      fprintf(stderr, "Can't find a CD-ROM drive with a CD-DA in it\n");
Packit dd8086
      exit(3);
Packit dd8086
    }
Packit dd8086
    psz_device_global = strdup(ppsz_cdda_drives[0]);
Packit dd8086
    cdio_free_device_list(ppsz_all_cd_drives);
Packit dd8086
    cdio_free_device_list(ppsz_cdda_drives);
Packit dd8086
  }
Packit dd8086
Packit dd8086
  if (!b_cd && todo != EJECT_CD) {
Packit dd8086
    cd_close(psz_device_global);
Packit dd8086
  }
Packit dd8086
Packit dd8086
  /* open device */
Packit dd8086
  if (b_verbose)
Packit dd8086
    fprintf(stderr,"open %s... ", psz_device_global);
Packit dd8086
Packit dd8086
  p_cdio_global = cdio_open (psz_device_global, driver_id);
Packit dd8086
Packit dd8086
  if (!p_cdio_global) {
Packit dd8086
    if (b_verbose)
Packit dd8086
      fprintf(stderr, "error: %s\n", strerror(errno));
Packit dd8086
    else
Packit dd8086
      fprintf(stderr, "open %s: %s\n", psz_device_global, strerror(errno));
Packit dd8086
    exit(1);
Packit dd8086
  } else
Packit dd8086
    if (b_verbose) fprintf(stderr,"ok\n");
Packit dd8086
Packit dd8086
  if (EJECT_CD == todo) {
Packit dd8086
    i_rc = cd_eject() ? 0 : 1;
Packit dd8086
  } else {
Packit dd8086
    read_toc(p_cdio_global);
Packit dd8086
    if (!b_cd) {
Packit dd8086
      cd_close(psz_device_global);
Packit dd8086
      read_toc(p_cdio_global);
Packit dd8086
    }
Packit dd8086
    if (b_cd)
Packit dd8086
      switch (todo) {
Packit dd8086
      case NO_OP:
Packit dd8086
	break;
Packit dd8086
      case STOP_PLAYING:
Packit dd8086
	i_rc = cd_stop(p_cdio_global) ? 0 : 1;
Packit dd8086
	break;
Packit dd8086
      case EJECT_CD:
Packit dd8086
	/* Should have been handled above before case statement. gcc
Packit dd8086
	   warns if we don't include this. And with this Coverty
Packit dd8086
	   complains when we do - we can't win, so go with gcc. */
Packit dd8086
	cd_eject();
Packit dd8086
	break;
Packit dd8086
	case PLAY_TRACK:
Packit dd8086
	  /* play just this one track */
Packit dd8086
	  play_track(start_track, stop_track);
Packit dd8086
	  break;
Packit dd8086
	case PLAY_CD:
Packit dd8086
	  play_track(1,CDIO_CDROM_LEADOUT_TRACK);
Packit dd8086
	  break;
Packit dd8086
	case CLOSE_CD:
Packit dd8086
	  i_rc = cdio_close_tray(psz_device_global, NULL) ? 0 : 1;
Packit dd8086
	  break;
Packit dd8086
	case SET_VOLUME:
Packit dd8086
	  {
Packit dd8086
	    cdio_audio_volume_t volume;
Packit dd8086
	    volume.level[0] = i_volume_level;
Packit dd8086
	    i_rc = (DRIVER_OP_SUCCESS == cdio_audio_set_volume(p_cdio_global,
Packit dd8086
							       &volume))
Packit dd8086
	      ? 0 : 1;
Packit dd8086
	    break;
Packit dd8086
	  }
Packit dd8086
	case LIST_SUBCHANNEL:
Packit dd8086
	  if (read_subchannel(p_cdio_global)) {
Packit dd8086
	    if (sub.audio_status == CDIO_MMC_READ_SUB_ST_PAUSED ||
Packit dd8086
		sub.audio_status == CDIO_MMC_READ_SUB_ST_PLAY) {
Packit dd8086
	      {
Packit dd8086
		printf("track %2d - %02x:%02x (%02x:%02x abs) ",
Packit dd8086
		       sub.track, sub.rel_addr.m, sub.rel_addr.s,
Packit dd8086
		       sub.abs_addr.m, sub.abs_addr.s);
Packit dd8086
	      }
Packit dd8086
	    }
Packit dd8086
	    printf("drive state: %s\n",
Packit dd8086
		   mmc_audio_state2str(sub.audio_status));
Packit dd8086
	  } else {
Packit dd8086
	    i_rc = 1;
Packit dd8086
	  }
Packit dd8086
	  break;
Packit dd8086
      }
Packit dd8086
      else {
Packit dd8086
	fprintf(stderr,"no CD in drive (%s)\n", psz_device_global);
Packit dd8086
      }
Packit dd8086
  }
Packit dd8086
Packit dd8086
  oops("bye", i_rc);
Packit dd8086
Packit dd8086
  return 0; /* keep compiler happy */
Packit dd8086
}