/***********************************************************************
***
***     CDTV Audio Examples by Carl Sassenrath (10-Dec-90)
***
***     Copyright (C) 1990 Commodore International, Ltd.
***     Permission granted to use for your CDTV programs.
***
************************************************************************
***
***     This file contains examples of how to perform a few simple
***     CD Audio commands on CDTV.  It was cranked out in a few
***     hours to help illustrate a few effects... and it all seems
***     to work (could use a little tweaking maybe). Sorry, but I have
***     not had the chance to convert it from Manx C to Lattice C, so
***     if you do, please send me a copy.  If not, I will get too it
***     RSN...  -Carl-
***
***     Send comments, problems, etc. to:
***     	Sassenrath, P.O. Box 1510, Ukiah, CA 95482
***     	Phone: (707) 462-4878  FAX: (707) 462-4879
***     	CBM VAX: carl
***
***********************************************************************
***
***	Modified for Lattice-C(ver5.05)	by Kaori Kuwata (4-Jan-91)
***
***********************************************************************/
#include <exec/types.h>
#include <exec/io.h>
#include <devices/cdtv.h>
#include <proto/all.h>
#include <stdio.h>
#include <stdlib.h>

#define FANCY

extern struct IOStdReq *CreateStdIO();
extern struct MsgPort  *CreatePort();
struct IOStdReq *IOReq1 = NULL;
struct IOStdReq *IOReq2 = NULL;
struct MsgPort  *IOPort = NULL;

int    Track;
struct CDTOC   Toc[100];
char   Buffer[10*2048];

char   AssertFail[] = "Assertion failed (MUST)";

#define MUST(expr)  if(!(expr)) Quit(AssertFail);

VOID Init(VOID);
VOID Quit(char *);
VOID DoIOR(struct IOStdReq *,int,long,long,APTR);
VOID SendIOR(struct IOStdReq *,int,long,long,APTR);
CDPOS AddMSF(CDPOS, CDPOS);
VOID ReadTOC(VOID);
VOID Play(VOID);
VOID PlayBurst(VOID);
VOID PlayBounce(VOID);
VOID PlayFastFwd(VOID);
VOID PlayAltFastFwd(VOID);
VOID PlayShow(VOID);
VOID PlayAbort(VOID);
VOID PlayFade(VOID);
VOID PlayMixedMode(VOID);


/***********************************************************************
***
***  Main
***
***********************************************************************/
VOID main(int argc,char *argv[])
{

  printf("CDTV Audio Example (3-Jan-91)\n");

  Init();

  ReadTOC();	/* Read CD table of contents */

  DoIOR(IOReq1,CD_MUTE,0x7fff,1,0);	/* Set to full volume */

  Play();
  PlayBurst();
  PlayBounce();
  PlayFastFwd();
  PlayAltFastFwd();
  PlayShow();
  PlayAbort();
  PlayFade();
  PlayMixedMode();

  Quit(0);

}

/***********************************************************************
***
***  Init -- initialize program and structures
***
***********************************************************************/
VOID Init(VOID)
{

  MUST(IOPort = CreatePort(0,0));
  MUST(IOReq1 = CreateStdIO(IOPort));
  MUST(IOReq2 = CreateStdIO(IOPort));

  if(OpenDevice("cdtv.device",0,IOReq1,0))
    Quit("CDTV Device will not open");

  *IOReq2 = *IOReq1;	/* Note: structure copy */

}

/***********************************************************************
***
***  Quit -- exit program and clean-up.  Return an error in needed.
***
***********************************************************************/
VOID Quit(char *s)	/* error message */
{

  if(IOReq1->io_Device) CloseDevice(IOReq1);
  if(IOReq1) DeleteStdIO(IOReq1);
  if(IOReq2) DeleteStdIO(IOReq2);
  if(IOPort) DeletePort(IOPort);
  if(s)
    {
    printf("\nERROR: %s\n",s);
    exit(40);
    }
  else exit(0);

}

/***********************************************************************
***
***  DoIOR -- execute a device command
***
***********************************************************************/
VOID DoIOR(struct IOStdReq *req,int cmd,long off,long len,APTR data)
{

  req->io_Command = cmd;
  req->io_Offset = off;
  req->io_Length = len;
  req->io_Data  = data;
  if(DoIO(req)) Quit("DoIO command error");

}

/***********************************************************************
***
***  SendIOR -- asynchronously execute a device command
***
***********************************************************************/
VOID SendIOR(struct IOStdReq *req,int cmd,long off,long len,APTR data)
{

  req->io_Command = cmd;
  req->io_Offset = off;
  req->io_Length = len;
  req->io_Data = data;
  SendIO(req);

}

/***********************************************************************
***
***  AddMSF -- add two Min:Sec:Frm values
***
***********************************************************************/
CDPOS AddMSF(CDPOS arg1,CDPOS arg2)
{

  arg1.MSF.Frame += arg2.MSF.Frame;
  if(arg1.MSF.Frame >= 75)
    {
    arg1.MSF.Frame  -= 75;
    arg1.MSF.Second++;
    }

  arg1.MSF.Second += arg2.MSF.Second;
  if(arg1.MSF.Second >= 60)
    {
    arg1.MSF.Second -= 60;
    arg1.MSF.Minute++;
    }

  arg1.MSF.Minute += arg2.MSF.Minute;

  return(arg1);

}

/***********************************************************************
***
***  ReadTOC -- read entire CD table of contents
***
***********************************************************************/
VOID ReadTOC(VOID)
{

  char s[20];
  SHORT i;

  DoIOR(IOReq1,CD_TOCMSF,0,100,&Toc[0]);

  printf("CD is %02d:%02d:%02d long ",Toc[0].Position.MSF.Minute,
				      Toc[0].Position.MSF.Second,
				      Toc[0].Position.MSF.Frame);
  printf("with tracks %d to %d\n",Toc[0].Track,Toc[0].LastTrack);

  DoIOR(IOReq1,CD_ISROM,0,0,0);
  printf("It %s CD-ROM data\n",	IOReq1->io_Actual ? "has" : "does not have");

/*--------------------------------------------------------------------*/
#ifdef FANCY
  for(i = 0;i<=Toc[0].LastTrack;i++)
    {
    printf("Track[%02d]  Type[%02x]  LSN[%ld]  %02d:%02d:%02d\n",
		i,Toc[i].AddrCtrl,Toc[i].Position.LSN,
				  Toc[i].Position.MSF.Minute,
				  Toc[i].Position.MSF.Second,
				  Toc[i].Position.MSF.Frame);
    }
#endif
/*--------------------------------------------------------------------*/

  if(IOReq1->io_Actual && Toc[0].LastTrack <= 1)
    Quit("No audio tracks\n");

  if(Toc[0].LastTrack <= 2)
    Quit("Not enough tracks\n");

/*--------------------------------------------------------------------*/
#ifdef FANCY
  printf("Input Track number you want to play ->");
  gets(s);
  printf("\n");
  Track = atoi(s);
#else
  Track = ((Toc[0].LastTrack >= 6) ? 7 : 2);
#endif
/*--------------------------------------------------------------------*/

}

/***********************************************************************
***
***  Example Functions
***
***********************************************************************/
VOID Play(VOID)
{

  CDPOS tm;

  printf("Play 20 seconds of track %d...\n",Track);

  tm.Raw = TOMSF(0,20,0);

  DoIOR(IOReq1,CD_PLAYMSF,Toc[Track].Position.Raw,
		AddMSF(Toc[Track].Position,tm).Raw,0);

}

VOID PlayBurst(VOID)
{

  int i;
  CDPOS start, stop;
  CDPOS tm;

  printf("Play 20 short sequential bursts...\n");
  start.Raw = Toc[Track].Position.Raw;

  tm.Raw = TOMSF(0,0,19);

  for(i = 0; i < 40; i++)
    {
    stop = AddMSF(start,tm);
    DoIOR(IOReq1,CD_PLAYMSF,start.Raw,stop.Raw,0);
    start.Raw = stop.Raw;
  }

}

VOID PlayBounce(VOID)
{

  int i;
  CDPOS start, stop;
  CDPOS tm;

  printf("Bounce the 'needle' on the 'record'...\n");

  tm.Raw = TOMSF(0,6,0);
  start = AddMSF(Toc[Track].Position,tm);
  tm.Raw = TOMSF(0,0,6);
  stop = AddMSF(start,tm);

  for(i = 0; i < 30; i++)
    {
    DoIOR(IOReq1,CD_PLAYMSF,start.Raw,stop.Raw,0);
    }

}

VOID PlayFastFwd(VOID)
{

  int i;
  CDPOS start, stop;
  CDPOS tm;

  printf("Play fast forward...\n");
  start.Raw = Toc[Track].Position.Raw;

  for(i = 0; i < 40; i++)
    {
    tm.Raw = TOMSF(0,0,19);
    stop = AddMSF(start,tm);
    DoIOR(IOReq1,CD_PLAYMSF,start.Raw,stop.Raw,0);
    tm.Raw = TOMSF(0,1,0);
    start = AddMSF(start,tm);
    }

}

VOID PlayAltFastFwd(VOID)	/* Note: requires 2 I/O requests! */
{

  int i;
  CDPOS start, stop;
  CDPOS tm;

  printf("Play alternate fast forward...\n");
  start.Raw = Toc[Track].Position.Raw;

  tm.Raw = TOMSF(0,40,0);

  stop = AddMSF(start,tm);
  SendIOR(IOReq1,CD_PLAYMSF,start.Raw,stop.Raw,0);

  tm.Raw = TOMSF(0,0,30);

  for(i = 0; i < 30; i++)
    {
    Delay(50/5);    /* Note: Delay() not too accurate */
    start = AddMSF(start,tm);
    DoIOR(IOReq2,CD_POKEPLAYMSF,start.Raw,stop.Raw,0);
    }

  AbortIO(IOReq1);
  WaitIO(IOReq1);

}

VOID PlayShow(VOID)
{

  CDPOS start, stop;
  struct CDSubQ subq;
  CDPOS tm;

  printf("Play and show track time...\n");
  start.Raw = Toc[Track].Position.Raw;

  tm.Raw = TOMSF(0,20,0);

  stop = AddMSF(start,tm);
  SendIOR(IOReq1,CD_PLAYMSF,start.Raw,stop.Raw,0);

  while(!CheckIO(IOReq1))
    {
    DoIOR(IOReq2,CD_SUBQMSF,0,0,&subq);
    if(subq.Status == SQSTAT_PLAYING)
      {
      printf("%02d:%02d:%02d\n",subq.TrackPosition.MSF.Minute,
				subq.TrackPosition.MSF.Second,
				subq.TrackPosition.MSF.Frame);
      }
    }

  WaitIO(IOReq1);

}

VOID PlayAbort(VOID)
{

  CDPOS tm;

  printf("Play 20 sec, abort after 10...\n");

  tm.Raw = TOMSF(0,20,0);

  SendIOR(IOReq1,CD_PLAYMSF,Toc[Track].Position.Raw,
		AddMSF(Toc[Track].Position,tm).Raw,0);

  Delay(50*10);
  AbortIO(IOReq1);
  WaitIO(IOReq1);
  DoIOR(IOReq1,CD_STOPPLAY,0,0,0); /* only if you want to */

}

VOID PlayFade(VOID)
{

  CDPOS start, stop;
  int i;
  CDPOS tm;

  printf("Play while fading up and down...\n");

  tm.Raw = TOMSF(1,0,0);

  start = AddMSF(Toc[Track].Position,tm);
  stop = AddMSF(start,tm);
  SendIOR(IOReq1,CD_PLAYMSF,start.Raw,stop.Raw,0);

  DoIOR(IOReq2,CD_FADE,0x7fff,0,0);       /* full volume */

  for(i = 0; i < 5; i++)
    {
    DoIOR(IOReq2,CD_FADE,0,150,0);
    Delay(100);
    DoIOR(IOReq2,CD_FADE,0x7fff,150,0);
    Delay(100);
  }

  for(i = 0; i < 10; i++)
    {
    DoIOR(IOReq2,CD_FADE,0,15,0);
    Delay(50/4);
    DoIOR(IOReq2,CD_FADE,0x7fff,15,0);
    Delay(50/4);
    }

  DoIOR(IOReq2,CD_FADE,0,750,0);
  Delay(50*10);

  AbortIO(IOReq1);
  WaitIO(IOReq1);

  DoIOR(IOReq1,CD_STOPPLAY,0,0,0);	/* stop the play for good */
  DoIOR(IOReq2,CD_FADE,0x7fff,0,0);	/* return to full vol	  */

}

VOID PlayMixedMode(VOID)
{

  int i;
  CDPOS start, stop;
  CDPOS tm;

  printf("Read and Play a mixed mode disk...\n");

  DoIOR(IOReq1,CD_ISROM,0,0,0);
  if(!IOReq1->io_Actual)
    {
    printf("Not a mixed mode disk!\n");
    return;
    }

  start.Raw = Toc[Track].Position.Raw;

  tm.Raw = TOMSF(0,1,0);

  for(i = 0; i < 40; i++)
    {
    printf("Reading...\n");
    DoIOR(IOReq1,CD_READ,i*10*2048,6*2048,Buffer);
    if(i==0) printf("Whoops... will fix that glitch\n");
    stop = AddMSF(start,tm);
    printf("Playing...\n");
    DoIOR(IOReq1,CD_PLAYMSF,start.Raw,stop.Raw,0);
    start.Raw = stop.Raw;
    }

}
