Subject: Re: is CDIOCPLAYTRACKS broken?
To: None <graphix@iastate.edu>
From: Michael Graff <explorer@flame.org>
List: current-users
Date: 12/01/1995 22:29:41
>  The subject says it all.  I can not seem to get CDIOCPLAYTRACKS to
>actually start playing an audio cd.  CDIOCPLAYBLOCKS works fine though
>I have yet to find a way to determine the number of tracks on the disk.

Here's what I wrote some time ago:

#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>
#include <errno.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/cdio.h>

struct ioc_play_track ipt;
struct ioc_toc_header toc_header;
struct ioc_read_toc_entry toc_entry;

struct cd_toc_entry toc[130];  /* more than enough room I hope! */

typedef struct disk_info {
  char  *name;
  u_int  id;
  u_char tracks;
} DiskInfo;

typedef struct msf {
  u_char minute;
  u_char second;
  u_char frame;
} MSF;

int PlayTracks(int, int, int);
int GetPositionInfo(int, int *, MSF *, MSF *);
int GetMediaCatalog(int);
int SetVolume(int, float, float, float, float);
int OpenDevice(void);
int ReadTrackInfo(int, DiskInfo *);

int
main(int argc, char **argv)
{
  int cd;
  int i;
  int lastsec;
  MSF rel, abs;
  int arg;
  int track;
  int last;

  cd = OpenDevice();

  ReadTrackInfo(cd, NULL);
  GetMediaCatalog(cd);

  SetVolume(cd, 0.80, 0.80, 0.0, 0.0);

  arg = 0;
  track = 0;

  for (;;) {
    if (argc == 1)
      track++;
    else {
      if (++arg == argc)
	exit(1);
      track = atoi(argv[arg]);
    }

    if ((track < toc_header.starting_track)
	|| (track > toc_header.ending_track))
      fprintf(stderr, "\n\nInvalid track %d requested\n\n", track);
    else
      PlayTracks(cd, track, track);

    for (;;) {
      register int status;
      int trk;

      status = GetPositionInfo(cd, &trk, &rel, &abs);
      
      if (status < 0)
	exit(1);

      if ((status == CD_AS_PLAY_COMPLETED) || (status == CD_AS_NO_STATUS)) {
	printf("\nPlay completed.\n");
	break;
      }

      if (last != (rel.minute * 60 + rel.second)) {
	printf("\rTrack %02d Time %02d:%02d  [%02d:%02d]",
	       trk, rel.minute, rel.second, abs.minute, abs.second);
	fflush(stdout);
	last = rel.minute * 60 + rel.second;
      }

      usleep(250000);
    }
  }
}

int
PlayTracks(int cd, int start, int end)
{
  struct ioc_play_blocks ipb;
  
  ipb.blk = (toc[start - 1].addr[0] << 24 | toc[start - 1].addr[1] << 16
	     | toc[start - 1].addr[2] << 8 | toc[start - 1].addr[3]);
  ipb.len = (toc[end].addr[0] << 24 | toc[end].addr[1] << 16
	     | toc[end].addr[2] << 8 | toc[end].addr[3]) - ipb.blk;
  
  if (ioctl(cd, CDIOCPLAYBLOCKS, &ipb) < 0) {
    perror("ioctl CDIOCPLAYBLOCKS");
    return errno;
  }

  return 0;
}

int
GetPositionInfo(int cd, int *track, MSF *rel, MSF *abs)
{
  struct ioc_read_subchannel req;
  struct cd_sub_channel_info sub_channel_info;

  req.address_format = CD_MSF_FORMAT;
  req.data_format = CD_CURRENT_POSITION;
  req.track = 0;
  req.data_len = sizeof(sub_channel_info);
  req.data = &sub_channel_info;

  if (ioctl(cd, CDIOCREADSUBCHANNEL, &req) < 0) {
    perror("iocdl CDIOCREADSUBCHANNEL");
    return errno;
  }

  if (track)
    *track = sub_channel_info.what.position.track_number;

  if (rel) {
    rel->minute = sub_channel_info.what.position.reladdr[1];
    rel->second = sub_channel_info.what.position.reladdr[2];
    rel->frame  = sub_channel_info.what.position.reladdr[3];
  }

  if (abs) {
    abs->minute = sub_channel_info.what.position.absaddr[1];
    abs->second = sub_channel_info.what.position.absaddr[2];
    abs->frame  = sub_channel_info.what.position.absaddr[3];
  }

  return(sub_channel_info.header.audio_status);
}

int
GetMediaCatalog(int cd)
{
  int i;
  
  struct ioc_read_subchannel req;
  struct cd_sub_channel_info sub_channel_info;

  req.address_format = CD_MSF_FORMAT;
  req.data_format = CD_MEDIA_CATALOG;
  req.track = 0;
  req.data_len = sizeof(sub_channel_info);
  req.data = &sub_channel_info;

  if (ioctl(cd, CDIOCREADSUBCHANNEL, &req) < 0) {
    perror("iocdl CDIOCREADSUBCHANNEL");
    return errno;
  }

  for (i = 0 ; i < 15 ; i++)
    fprintf(stderr, "%02x ", sub_channel_info.what.media_catalog.mc_number[i]);
  fprintf(stderr, "\n");

  return 0;
}

int
SetVolume(int cd,        /* fd for cd device */
	  float right,   /* right channel volume (percentage of full) */
	  float left,    /* left channel volume */
	  float other1,  /* those other channels */
	  float other2)
{
  struct ioc_vol vol;

  vol.vol[0] = 255 * right;
  vol.vol[1] = 255 * left;
  vol.vol[2] = 255 * other1;
  vol.vol[3] = 255 * other2;
  
  if (ioctl(cd, CDIOCSETVOL, &vol) < 0) {
    perror("ioctl CDIOCSETVOL");
    return errno;
  }

  return 0;
}

int
OpenDevice(void)
{
  int cd;

  cd = open("/dev/cd0a", O_RDONLY|O_NONBLOCK, 0x0600);

  if (cd < 0) {
    perror("open");
    exit(1);
  }

  return (cd);
}

int
ReadTrackInfo(int cd, DiskInfo *di)
{
  int i;

  if (ioctl(cd, CDIOREADTOCHEADER, &toc_header) < 0) {
    perror("ioctl CDIOREADTOCHEADER");
    return errno;
  }
  
  fprintf(stderr, "Length: %d, start track: %d, ending track: %d\n",
	  toc_header.len, toc_header.starting_track, toc_header.ending_track);
  
  for (i = 1 ; i <= toc_header.ending_track ; i += 30) {
    toc_entry.data = &toc[i-1];
    toc_entry.data_len = sizeof(toc[0]) * 30;
    toc_entry.starting_track = i;
    toc_entry.address_format = CD_LBA_FORMAT;
    
    if (ioctl(cd, CDIOREADTOCENTRYS, &toc_entry) < 0) {
      perror("ioctl CDIOREADTOCENTRYS");
      return errno;
    }
  }
  
  for (i = 0 ; i < toc_header.ending_track + 1 ; i++)
    fprintf(stderr, "control %d, type %d, track %d, lba %d\n",
	    toc[i].control, toc[i].addr_type, toc[i].track,
	    toc[i].addr[0] << 24 | toc[i].addr[1] << 16
	    | toc[i].addr[2] << 8 | toc[i].addr[3]);

  return 0;
}

--Michael

--
Michael Graff <explorer@flame.org>        NetBSD is the way to go!
PGP key on a key-server near you!         Rayshade the world!