home *** CD-ROM | disk | FTP | other *** search
- ;/*
- SC PARAMS=REGISTER NMINC MCCONS STREQ NOWVRET STRMERGE NOSTKCHK UTILLIB OPTIMIZE OPTTIME OPTINLINE MODIFIED IGNORE=73 CDDA.c
- SLINK CDDA.o TO CDDA LIB LIB:sc.lib LIB:amiga.lib SC SD ND NOICONS
- Quit
- */
-
- /*
- ** CDDA - A program to replay digital audio data read from a
- ** Sony CD-ROM drive.
- **
- ** Copyright © 1993-1994 by Olaf `Olsen' Barthel
- ** This is a freeware release.
- */
-
- /* Avoid a nasty type conflict in older include files. */
-
- #define CheckIO foo
-
- /* System includes */
-
- #include <devices/scsidisk.h>
- #include <devices/trackdisk.h>
- #include <devices/audio.h>
-
- #include <exec/execbase.h>
- #include <exec/memory.h>
-
- #include <dos/dosextens.h>
- #include <dos/dostags.h>
- #include <dos/rdargs.h>
- #include <dos/dosasl.h>
-
- #include <hardware/cia.h>
-
- #include <clib/utility_protos.h>
- #include <clib/exec_protos.h>
- #include <clib/dos_protos.h>
- #include <clib/alib_protos.h>
- #include <clib/macros.h>
-
- #include <pragmas/utility_pragmas.h>
- #include <pragmas/exec_pragmas.h>
- #include <pragmas/dos_pragmas.h>
-
- #include <string.h>
-
- /* Get the CheckIO prototype right. */
-
- #undef CheckIO
-
- struct IORequest *CheckIO(struct IORequest *);
-
- /* The CIA A base address. */
-
- #ifndef ciaa
- extern struct CIA __far ciaa;
- #endif /* ciaa */
-
- /* Command line template. */
-
- #define TEMPLATE "DEVICE/K,UNIT/K/N,TRACK/N,FROM/K/N,TO/K/N,NUM/K/N,MAX/K/N,VERBOSE/S"
-
- /* The argument vector offsets. */
-
- enum { ARG_DEVICE,ARG_UNIT,ARG_TRACK,ARG_FROM,ARG_TO,ARG_NUM,ARG_MAX,ARG_VERBOSE,
-
- ARGCOUNT
- };
-
- /* Player task priority and stack size. */
-
- #define CHILD_PRI 5
- #define CHILD_STACK 8192
-
- /* Default SCSI device name and unit number. */
-
- #define SCSI_DEVICE "scsi.device"
- #define SCSI_UNIT 2
-
- /* The signal mask associated with a MsgPort. */
-
- #define PORTMASK(p) (1L << ((struct MsgPort *)(p)) -> mp_SigBit)
-
- /* Some signal masks. */
-
- #define SIG_HANDSHAKE SIGF_SINGLE
- #define SIG_KILL SIGBREAKF_CTRL_C
- #define SIG_SYNC SIGBREAKF_CTRL_F
- #define SIG_CHILD PORTMASK(ChildPort)
-
- /* The size of an audio data buffer and the
- * number of blocks per packet (= 1 second of data).
- */
-
- #define BUFFER_SIZE 22050
- #define BLOCKS_PER_PKT 75
-
- /* Amiga audio channel allocation bits. */
-
- #define LEFT0F 1
- #define RIGHT0F 2
- #define RIGHT1F 4
- #define LEFT1F 8
-
- /* SCSI command `test unit ready'. */
-
- struct TestUnitReady
- {
- UBYTE Command,
- Pad,
- Reserved[3],
- Control;
- };
-
- /* SCSI command `inquiry'. */
-
- struct Inquiry
- {
- UBYTE Command,
- Pad,
- PageCode,
- Reserved,
- AllocationLength,
- Control;
- };
-
- /* The data structure to be filled by the inquiry command. */
-
- struct InquiryData
- {
- UBYTE PeripheralType,
- DeviceTypeModifier,
- Version,
- Reserved[5],
- Vendor[8],
- Product[16],
- Revision[4];
- };
-
- /* SCSI command `read table of contents'. */
-
- struct ReadTOC
- {
- UBYTE Command,
- Pad,
- Reserved[4],
- StartingTrack;
- UBYTE Alloc1,Alloc2;
- UBYTE Control;
- };
-
- /* The data structure to be filled in by the
- * readtoc command.
- */
-
- struct FullTOC
- {
- UWORD DataLength;
- UBYTE FirstTrack,
- LastTrack;
-
- struct
- {
- UBYTE Reserved1,
- Control,
- TrackNumber,
- Reserved2;
- ULONG Address;
- } TOC[100];
- };
-
- /* Sony vendor unique command to read digital
- * audio data.
- */
-
- struct ReadCDDA
- {
- UBYTE Command,
- Pad;
- ULONG Address,
- Count;
- UBYTE SubCode,
- Control;
- };
-
- /* Digital audio data, as returned by the corresponding command. */
-
- struct CDDASector
- {
- struct CDDASample
- {
- UBYTE LeftLSB,
- LeftMSB,
-
- RightLSB,
- RightMSB;
- } Sample[588];
- };
-
- /* Handler->player task communications structure. */
-
- struct AudioMessage
- {
- struct Message Message;
- LONG NumBlocks;
- struct CDDASector *Data;
- };
-
- /* Global library bases. */
-
- struct ExecBase *SysBase;
- struct DosLibrary *DOSBase;
- struct Library *UtilityBase;
-
- /* Command line arguments. */
-
- struct RDArgs *ArgsPtr;
- STRPTR *Arg;
-
- /* Audio data. */
-
- struct IOAudio *AudioControl,
- *AudioLeft,
- *AudioRight;
- struct MsgPort *AudioPort;
- BYTE *ThisLeft,
- *NextLeft,
- *ThisRight,
- *NextRight,
- *AudioData;
- ULONG Rate;
- LONG FillCounter = 0,
- TotalPending = 0;
- BOOL AudioUsed = FALSE,
- AudioActive = FALSE,
- Verbose,
- LED;
-
- /* Current sector and total number of sectors to read. */
-
- LONG Index,Total;
-
- /* SCSI device control data. */
-
- struct MsgPort *SCSIPort;
- struct IOExtTD *SCSIRequest;
-
- struct SCSICmd SCSICmd;
- UBYTE SenseBuffer[256];
-
- /* Data to be read from the device. */
-
- struct FullTOC FullTOC;
- struct InquiryData InquiryData;
-
- /* Block queue access semaphore and number of
- * packets in the queue.
- */
-
- struct SignalSemaphore QueueSemaphore;
- LONG QueueCount;
-
- /* Handler and player task data. */
-
- struct Task *Child;
- struct MsgPort *ChildPort;
- struct Process *Father;
- BOOL FatherWaiting;
-
- /* Program version ID. */
-
- STRPTR VersionTag = "\0$VER: CDDA 1.3 (14.1.94)";
-
- /* Prototypes for this module. */
-
- LONG __saveds Main(VOID);
- VOID SCSIExit(VOID);
- BOOL SCSIInit(STRPTR Device,LONG Unit);
- VOID AudioExit(VOID);
- BOOL AudioInit(VOID);
- VOID DeleteAudioMessage(struct AudioMessage *Message);
- struct AudioMessage * CreateAudioMessage(LONG NumBlocks);
- VOID StartAudio(VOID);
- VOID ProcessAudio(VOID);
- VOID __saveds ChildTask(VOID);
- VOID CloseAll(VOID);
- BOOL OpenAll(VOID);
- BYTE ReadCDDASectors(APTR Buffer,LONG From,LONG Count);
- BYTE TestUnitReady(VOID);
- BYTE ReadTOC(VOID);
- BYTE Inquire(VOID);
-
- /* Main():
- *
- * The program entry point. No compiler startup code.
- */
-
- LONG __saveds
- Main()
- {
- LONG Result = RETURN_FAIL;
-
- /* Open the resources. */
-
- if(OpenAll())
- {
- LONG Blocks,MaxQueueSize;
- struct AudioMessage *Message;
- BOOL Stop = FALSE,
- FirstMessage = TRUE;
-
- /* Everything went fine so far. */
-
- Result = RETURN_OK;
-
- /* Get the maximum block queue size. */
-
- if(Arg[ARG_MAX])
- MaxQueueSize = *(LONG *)Arg[ARG_MAX];
- else
- MaxQueueSize = 3;
-
- /* Safety check. */
-
- if(MaxQueueSize < 1)
- MaxQueueSize = 1;
-
- /* Print the banner message. */
-
- FPrintf(Output(),"\33[1mCDDA\33[0m © Copyright 1993-1994 by Olaf `Olsen' Barthel, \33[4mAll rights reserved\33[0m\n");
-
- /* Play the tracks. */
-
- while(Total > 0)
- {
- /* Stop playing? */
-
- if(CheckSignal(SIG_KILL))
- {
- Stop = TRUE;
-
- break;
- }
-
- /* Gain access to the block queue... */
-
- ObtainSemaphore(&QueueSemaphore);
-
- /* More blocks in the queue than there
- * should be?
- */
-
- if(QueueCount > MaxQueueSize)
- {
- /* Wait for the player task
- * to make room.
- */
-
- Forbid();
-
- ReleaseSemaphore(&QueueSemaphore);
-
- SetSignal(0,SIG_HANDSHAKE);
-
- FatherWaiting = TRUE;
-
- Wait(SIG_HANDSHAKE);
-
- FatherWaiting = FALSE;
-
- Permit();
- }
- else
- ReleaseSemaphore(&QueueSemaphore);
-
- /* Allocate another packet. */
-
- if(Message = CreateAudioMessage(Blocks = MIN(BLOCKS_PER_PKT,Total)))
- {
- /* Read the data, we may need more than one
- * attempt if the drive feels unable to supply
- * the requested data immediately.
- */
-
- while(ReadCDDASectors(Message -> Data,Index,Blocks))
- {
- /* Stop here? */
-
- if(CheckSignal(SIG_KILL))
- {
- Stop = TRUE;
-
- break;
- }
-
- /* If in verbose mode, tell the user that
- * we are retrying to read the data.
- */
-
- if(Verbose)
- {
- if(FirstMessage)
- {
- Printf("CDDA: Rereading sector #%ld\n",Index);
-
- FirstMessage = FALSE;
- }
- else
- Printf("\033[ACDDA: Rereading sector #%ld\033[K\n",Index);
- }
- }
-
- /* Read request aborted? */
-
- if(Stop)
- {
- /* Clean up and exit. */
-
- DeleteAudioMessage(Message);
-
- break;
- }
- else
- {
- /* Add the data to the queue. */
-
- PutMsg(ChildPort,&Message -> Message);
-
- Total -= Blocks;
- Index += Blocks;
- }
- }
- else
- {
- /* Stop here? */
-
- if(CheckSignal(SIG_KILL))
- {
- Stop = TRUE;
-
- break;
- }
-
- /* If in verbose mode tell the user that
- * we will need to retry the memory
- * allocation.
- */
-
- if(Verbose)
- {
- if(FirstMessage)
- {
- Printf("CDDA: Retrying memory allocation for sector #%ld\n",Index);
-
- FirstMessage = FALSE;
- }
- else
- Printf("\033[ACDDA: Retrying memory allocation for sector #%ld\033[K\n",Index);
- }
-
- /* Wait for the player task to make room. */
-
- Forbid();
-
- SetSignal(0,SIG_HANDSHAKE);
-
- FatherWaiting = TRUE;
-
- Wait(SIG_HANDSHAKE);
-
- FatherWaiting = FALSE;
-
- Permit();
- }
- }
-
- /* Issue the break message if necessary. */
-
- if(Stop)
- PrintFault(ERROR_BREAK,"CDDA");
- else
- {
- /* Resync with player task, we
- * are about to exit cleanly.
- * In this case we want the player
- * task to process the remaining
- * audio data before we proceed to
- * tell it to shut down.
- */
-
- Forbid();
-
- SetSignal(0,SIG_HANDSHAKE);
-
- Signal(Child,SIG_SYNC);
-
- Wait(SIG_HANDSHAKE);
-
- Permit();
- }
- }
-
- CloseAll();
-
- return(Result);
- }
-
- /* SCSIExit():
- *
- * Cleans up the scsi device data.
- */
-
- VOID
- SCSIExit()
- {
- if(SCSIRequest)
- {
- if(SCSIRequest -> iotd_Req . io_Device)
- CloseDevice(SCSIRequest);
-
- DeleteIORequest(SCSIRequest);
-
- SCSIRequest = NULL;
- }
-
- if(SCSIPort)
- {
- DeleteMsgPort(SCSIPort);
-
- SCSIPort = NULL;
- }
- }
-
- /* SCSIInit(STRPTR Device,LONG Unit):
- *
- * Sets up the scsi device data.
- */
-
- BOOL
- SCSIInit(STRPTR Device,LONG Unit)
- {
- if(!(SCSIPort = CreateMsgPort()))
- return(FALSE);
-
- if(!(SCSIRequest = (struct IOExtTD *)CreateIORequest(SCSIPort,sizeof(struct IOExtTD))))
- return(FALSE);
-
- if(OpenDevice(Device,Unit,SCSIRequest,NULL))
- return(FALSE);
-
- return(TRUE);
- }
-
- /* AudioExit():
- *
- * Cleans up the audio device data.
- */
-
- VOID
- AudioExit()
- {
- /* Turn the power LED (= alias filter) back on/off. */
-
- Disable();
-
- if(LED)
- ciaa . ciapra &= ~CIAF_LED;
- else
- ciaa . ciapra |= CIAF_LED;
-
- Enable();
-
- /* Clean up the left audio channel data. */
-
- if(AudioLeft)
- {
- if(AudioLeft -> ioa_Request . io_Device && AudioUsed)
- {
- if(!CheckIO(AudioLeft))
- {
- AbortIO(AudioLeft);
-
- WaitIO(AudioLeft);
- }
- else
- GetMsg(AudioPort);
- }
-
- FreeVec(AudioLeft);
-
- AudioLeft = NULL;
- }
-
- /* Clean up the right audio channel data. */
-
- if(AudioRight)
- {
- if(AudioRight -> ioa_Request . io_Device && AudioUsed)
- {
- if(!CheckIO(AudioRight))
- {
- AbortIO(AudioRight);
-
- WaitIO(AudioRight);
- }
- else
- GetMsg(AudioPort);
- }
-
- FreeVec(AudioRight);
-
- AudioRight = NULL;
- }
-
- /* Free the control request. */
-
- if(AudioControl)
- {
- if(AudioControl -> ioa_Request . io_Device)
- CloseDevice(AudioControl);
-
- DeleteIORequest(AudioControl);
-
- AudioControl = NULL;
- }
-
- /* Take care of the rest. */
-
- if(AudioPort)
- {
- DeleteMsgPort(AudioPort);
-
- AudioPort = NULL;
- }
-
- if(AudioData)
- {
- FreeVec(AudioData);
-
- AudioData = NULL;
- }
- }
-
- /* AudioInit():
- *
- * Initialize the audio device driver data.
- */
-
- BOOL
- AudioInit()
- {
- /* Audio channel allocation map which should
- * allocate two different stereo channels.
- */
-
- STATIC UBYTE AllocationMap[] =
- {
- LEFT0F | RIGHT0F,
- LEFT0F | RIGHT1F,
- LEFT1F | RIGHT0F,
- LEFT1F | RIGHT1F
- };
-
- /* Turn off the power LED (= alias filter). */
-
- Disable();
-
- if(ciaa . ciapra & CIAF_LED)
- LED = FALSE;
- else
- {
- LED = TRUE;
-
- ciaa . ciapra |= CIAF_LED;
- }
-
- Enable();
-
- /* Allocate the data. */
-
- if(!(AudioData = (BYTE *)AllocVec(BUFFER_SIZE * 4,MEMF_CHIP | MEMF_CLEAR)))
- return(FALSE);
-
- if(!(AudioPort = CreateMsgPort()))
- return(FALSE);
-
- if(!(AudioControl = (struct IOAudio *)CreateIORequest(AudioPort,sizeof(struct IOAudio))))
- return(FALSE);
-
- if(!(AudioLeft = (struct IOAudio *)AllocVec(sizeof(struct IOAudio),MEMF_PUBLIC)))
- return(FALSE);
-
- if(!(AudioRight = (struct IOAudio *)AllocVec(sizeof(struct IOAudio),MEMF_PUBLIC)))
- return(FALSE);
-
- /* Prepare for initialization. */
-
- AudioControl -> ioa_Request . io_Message . mn_Node . ln_Pri = 127;
- AudioControl -> ioa_Request . io_Command = ADCMD_ALLOCATE;
- AudioControl -> ioa_Request . io_Flags = ADIOF_NOWAIT | ADIOF_PERVOL;
- AudioControl -> ioa_Data = AllocationMap;
- AudioControl -> ioa_Length = sizeof(AllocationMap);
-
- /* Open the audio device driver. */
-
- if(OpenDevice(AUDIONAME,NULL,AudioControl,NULL))
- return(FALSE);
-
- /* Choose the right replay rate for 22,050 samples per second. */
-
- Rate = (SysBase -> ex_EClockFrequency * 5) / 22050;
-
- /* Clone the audio control request. */
-
- CopyMem(AudioControl,AudioLeft, sizeof(struct IOAudio));
- CopyMem(AudioControl,AudioRight,sizeof(struct IOAudio));
-
- /* Split the channels. */
-
- AudioLeft -> ioa_Request . io_Unit = (struct Unit *)((ULONG)AudioLeft -> ioa_Request . io_Unit & (LEFT0F | LEFT1F));
- AudioRight -> ioa_Request . io_Unit = (struct Unit *)((ULONG)AudioRight -> ioa_Request . io_Unit & (RIGHT0F | RIGHT1F));
-
- /* Set up the double buffering data. */
-
- ThisLeft = AudioData;
- NextLeft = ThisLeft + BUFFER_SIZE;
- ThisRight = NextLeft + BUFFER_SIZE;
- NextRight = ThisRight + BUFFER_SIZE;
-
- return(TRUE);
- }
-
- /* DeleteAudioMessage(struct AudioMessage *Message):
- *
- * Delete an AudioMessage, clean up the associated data.
- */
-
- VOID
- DeleteAudioMessage(struct AudioMessage *Message)
- {
- FreeVec(Message);
-
- ObtainSemaphore(&QueueSemaphore);
-
- QueueCount--;
-
- ReleaseSemaphore(&QueueSemaphore);
-
- /* Wake up the handler process. */
-
- if(FatherWaiting)
- Signal(Father,SIG_HANDSHAKE);
- }
-
- /* CreateAudioMessage(LONG NumBlocks):
- *
- * Create an AudioMessage, i.e. a message with auxilary data
- * space attached.
- */
-
- struct AudioMessage *
- CreateAudioMessage(LONG NumBlocks)
- {
- struct AudioMessage *Message;
-
- if(Message = (struct AudioMessage *)AllocVec(sizeof(struct AudioMessage) + 15 + NumBlocks * sizeof(struct CDDASector),MEMF_ANY | MEMF_PUBLIC))
- {
- ULONG Place = ((ULONG)(Message + 1) + 15) & ~15;
-
- Message -> Message . mn_Length = sizeof(struct AudioMessage) + 15 + NumBlocks * sizeof(struct CDDASector);
- Message -> NumBlocks = NumBlocks;
- Message -> Data = (struct CDDASector *)Place;
-
- ObtainSemaphore(&QueueSemaphore);
-
- QueueCount++;
-
- ReleaseSemaphore(&QueueSemaphore);
- }
-
- return(Message);
- }
-
- /* StartAudio():
- *
- * Set up the audio device requests to play the currently
- * available data and start them simultaneously.
- */
-
- VOID
- StartAudio()
- {
- /* So nobody will interrupt us. */
-
- Forbid();
-
- AudioLeft -> ioa_Request . io_Command = CMD_WRITE;
- AudioLeft -> ioa_Request . io_Flags = ADIOF_PERVOL;
- AudioLeft -> ioa_Period = Rate;
- AudioLeft -> ioa_Volume = 64;
- AudioLeft -> ioa_Cycles = 1;
- AudioLeft -> ioa_Data = ThisLeft;
- AudioLeft -> ioa_Length = FillCounter;
-
- /* Swap the buffers (double buffering). */
-
- ThisLeft = NextLeft;
- NextLeft = AudioLeft -> ioa_Data;
-
- AudioRight -> ioa_Request . io_Command = CMD_WRITE;
- AudioRight -> ioa_Request . io_Flags = ADIOF_PERVOL;
- AudioRight -> ioa_Period = Rate;
- AudioRight -> ioa_Volume = 64;
- AudioRight -> ioa_Cycles = 1;
- AudioRight -> ioa_Data = ThisRight;
- AudioRight -> ioa_Length = FillCounter;
-
- /* Swap the buffers (double buffering). */
-
- ThisRight = NextRight;
- NextRight = AudioRight -> ioa_Data;
-
- /* Reset the audio data fill counter. */
-
- FillCounter = 0;
-
- /* Stop playing any sound (= ^S). */
-
- AudioControl -> ioa_Request . io_Command = CMD_STOP;
- AudioControl -> ioa_Period = Rate;
-
- BeginIO(AudioControl);
- WaitIO(AudioControl);
-
- /* Prepare to start both audio device requests,
- * the CMD_STOP command will keep them from getting
- * satisfied immediately.
- */
-
- BeginIO(AudioLeft);
- BeginIO(AudioRight);
-
- /* Start playing both channels simultaneously (= ^Q). */
-
- AudioControl -> ioa_Request . io_Command = CMD_START;
- AudioControl -> ioa_Period = Rate;
-
- BeginIO(AudioControl);
- WaitIO(AudioControl);
-
- /* Remember current state, so the cleanup code will
- * know what to do.
- */
-
- AudioUsed = AudioActive = TRUE;
-
- Permit();
- }
-
- /* ProcessAudio():
- *
- * Process incoming audio data messages.
- */
-
- VOID
- ProcessAudio()
- {
- struct AudioMessage *Message;
- LONG i,j;
- WORD Left,Right;
-
- /* Loop until done. */
-
- while(Message = (struct AudioMessage *)GetMsg(ChildPort))
- {
- /* Process the single data blocks. */
-
- for(i = 0 ; i < Message -> NumBlocks ; i++)
- {
- /* Process the samples. */
-
- for(j = 0 ; j < 588 ; j++)
- {
- /* Well, a bit of explanation might be necessary
- * to tell you why this code is discarding every
- * second data block. Each incoming message
- * supplies data for about one second of music or
- * sound to be replayed. This accounts for
- * 44,100 samples per second for each data packet.
- * Unfortunately, the highest replay rate the Amiga
- * audio hardware supports at this time of writing
- * is about 30,000 samples per second. Instead of
- * scaling the data packet size down to the maximum
- * the amount of data is halved, thus reducing the
- * replay rate required to 22,050 samples per second.
- */
-
- if(j & 1)
- {
- /* Convert the digital audio data (little endian,
- * sixteen bits per sample, signed).
- */
-
- Left = (((WORD)Message -> Data[i] . Sample[j] . LeftMSB) << 8) | Message -> Data[i] . Sample[j] . LeftLSB;
- Right = (((WORD)Message -> Data[i] . Sample[j] . RightMSB) << 8) | Message -> Data[i] . Sample[j] . RightLSB;
-
- /* Scale the sixteen bit values down to eight bits. */
-
- ThisLeft[FillCounter] = Left >> 8;
- ThisRight[FillCounter] = Right >> 8;
-
- /* One more byte in the buffer... */
-
- FillCounter++;
- }
- }
- }
-
- /* Clean up. */
-
- DeleteAudioMessage(Message);
-
- /* Audio output still active? */
-
- if(AudioActive)
- {
- WaitIO(AudioLeft);
- WaitIO(AudioRight);
-
- AudioActive = FALSE;
- }
-
- /* Are we to stop here? */
-
- if(SetSignal(0,0) & SIG_KILL)
- break;
- else
- {
- /* Replay the data if available. */
-
- if(FillCounter)
- StartAudio();
- }
- }
- }
-
- /* ChildTask():
- *
- * The audio player task.
- */
-
- VOID __saveds
- ChildTask()
- {
- /* Remember task address. */
-
- Child = SysBase -> ThisTask;
-
- /* Create communications port. */
-
- if(ChildPort = CreateMsgPort())
- {
- struct AudioMessage *Message;
-
- /* Initialize the audio device. */
-
- if(AudioInit())
- {
- ULONG Signals;
- BOOL Terminate = FALSE;
-
- /* Wait for handshake signal. */
-
- Signal(Father,SIG_HANDSHAKE);
-
- /* Loop until finished or stopped. */
-
- for(;;)
- {
- /* Wait for a signal... */
-
- Signals = Wait(SIG_CHILD | SIG_KILL | SIG_SYNC);
-
- /* The last track to play? */
-
- if(Signals & SIG_SYNC)
- Terminate = TRUE;
-
- /* Stop it, now! */
-
- if(Signals & SIG_KILL)
- break;
-
- /* Process audio data. */
-
- if(Signals & SIG_CHILD)
- ProcessAudio();
-
- /* Wake up the handler task. */
-
- if(FatherWaiting)
- Signal(Father,SIG_SYNC);
-
- /* Wait until track is replayed? */
-
- if(Terminate)
- {
- if(!QueueCount)
- {
- if(AudioActive)
- {
- WaitIO(AudioLeft);
- WaitIO(AudioRight);
- }
-
- break;
- }
- }
- }
-
- /* Clean up. */
-
- if(Terminate)
- {
- Forbid();
-
- Signal(Father,SIG_HANDSHAKE);
-
- Child = NULL;
- }
- }
-
- /* Get rid of queued messages. */
-
- while(Message = (struct AudioMessage *)GetMsg(ChildPort))
- DeleteAudioMessage(Message);
-
- /* Free the audio resources. */
-
- AudioExit();
-
- /* Delete the communications port. */
-
- DeleteMsgPort(ChildPort);
-
- ChildPort = NULL;
- }
-
- /* Finish the rest. */
-
- if(Child)
- {
- Forbid();
-
- Signal(Father,SIG_HANDSHAKE);
-
- Child = NULL;
- }
- }
-
- /* CloseAll():
- *
- * The big cleanup routine.
- */
-
- VOID
- CloseAll()
- {
- /* Shut down the scsi device driver. */
-
- SCSIExit();
-
- /* Remove the player task. */
-
- if(Child)
- {
- Signal(Child,SIG_KILL);
-
- Wait(SIG_HANDSHAKE);
- }
-
- /* Free the command line reader data. */
-
- if(ArgsPtr)
- {
- FreeDosObject(DOS_RDARGS,ArgsPtr);
-
- FreeArgs(ArgsPtr);
-
- ArgsPtr = NULL;
- }
-
- /* Free the command line argument data. */
-
- if(Arg)
- {
- FreeVec(Arg);
-
- Arg = NULL;
- }
-
- /* Close the libraries. */
-
- if(UtilityBase)
- {
- CloseLibrary(UtilityBase);
-
- UtilityBase = NULL;
- }
-
- if(DOSBase)
- {
- CloseLibrary(DOSBase);
-
- DOSBase = NULL;
- }
- }
-
- /* OpenAll():
- *
- * Open the resources required and complain if anything
- * goes wrong.
- */
-
- BOOL
- OpenAll()
- {
- STRPTR Device;
- LONG Unit,i;
- BOOL GotIt = FALSE;
-
- /* Most important ;-) */
-
- SysBase = *(struct ExecBase **)4;
-
- /* So we know who we are. */
-
- Father = (struct Process *)SysBase -> ThisTask;
-
- /* Called from Workbench? Oops... */
-
- if(!Father -> pr_CLI)
- {
- struct Message *Message;
-
- /* Wait for startup message. */
-
- WaitPort(&Father -> pr_MsgPort);
-
- /* Pick it up. */
-
- Message = GetMsg(&Father -> pr_MsgPort);
-
- Forbid();
-
- /* Return it and stop here. */
-
- ReplyMsg(Message);
-
- return(FALSE);
- }
-
- /* Open the libraries. */
-
- if(!(DOSBase = (struct DosLibrary *)OpenLibrary("dos.library",37)))
- return(FALSE);
-
- if(!(UtilityBase = OpenLibrary("utility.library",37)))
- {
- Printf("CDDA: Could not open utility.library v37.\n");
-
- return(FALSE);
- }
-
- /* Set up the semaphore. */
-
- InitSemaphore(&QueueSemaphore);
-
- /* Allocate command line reader data. */
-
- if(!(Arg = (STRPTR *)AllocVec(sizeof(STRPTR) * ARGCOUNT,MEMF_ANY | MEMF_CLEAR)))
- {
- PrintFault(ERROR_NO_FREE_STORE,"CDDA");
-
- return(FALSE);
- }
-
- if(!(ArgsPtr = AllocDosObjectTags(DOS_RDARGS,TAG_DONE)))
- {
- PrintFault(IoErr(),"CDDA");
-
- return(FALSE);
- }
-
- /* Just a bit of documentation. */
-
- ArgsPtr -> RDA_ExtHelp = "\nThis program replays digital audio data directly off a compact disc\n"
- "using the Amiga audio hardware (in stereo where available). A special\n"
- "type of CD-ROM drive is required since this program makes use of a\n"
- "vendor unique command supported by Sony drives, such as the CDU-8003A\n"
- "or the Apple CD-300 drive.\n\n"
- "Please note that due to the work involved reading and converting the\n"
- "audio data music may not always play smoothly. The program will also\n"
- "consume large amounts of chip memory in order to supply the player\n"
- "task with a contiguous stream of data (the maximum number of sound\n"
- "queue packets can be adjusted using a command line parameter). Your\n"
- "system may also experience heavy task loading since the incoming data\n"
- "(more than 88K bytes per second) must be handled quickly and\n"
- "efficiently in order to avoid `pops' and delays.\n\n"
- "The command line template looks like this:\n\n"
- "DEVICE/K,UNIT/K/N,TRACK/N,FROM/K/N,TO/K/N,NUM/K/N,MAX/K/N,VERBOSE/S\n\n"
- "DEVICE This parameter requires a keyword. By default the program will\n"
- " address `scsi.device' as the device driver which controls your\n"
- " CD-ROM drive. You can override this by using the following\n"
- " command line option:\n\n"
- " DEVICE <device name>\n\n"
- " Example: DEVICE gvpscsi.device\n\n"
- "UNIT This parameter requires a keyword. By default the program will\n"
- " expect your CD-ROM drive to respond to device unit number 2.\n"
- " You can override this by using the following command line\n"
- " options:\n\n"
- " UNIT <unit number>\n\n"
- " Example: UNIT 3\n\n"
- "TRACK This parameter requires a keyword. Each data/music track on\n"
- " the CD has a unique number assigned which ranges from 1 to 99.\n"
- " This is how you choose which track to replay.\n\n"
- " Example: TRACK 1\n\n"
- "FROM This parameter requires a keyword. The CD consists of several\n"
- " sectors, each of which can be addressed independently. Several\n"
- " sectors form a track, 75 sectors of data make up one second of\n"
- " audio data. This is how you choose which sectors to replay,\n"
- " note that this parameter requires the \"TO\" or the \"NUM\"\n"
- " parameter.\n\n"
- " Example: FROM 1 TO 600\n\n"
- "TO This parameter requires a keyword. It works both with the\n"
- " \"TRACK\" and \"FROM\" parameters.\n\n"
- " Example: TRACK 1 TO 5 Plays tracks to 1 to 5\n"
- " FROM 1 TO 600 Plays sectors 1 to 600\n\n"
- "NUM This parameter requires a keyword. It works both with the\n"
- " \"TRACK\" and \"FROM\" parameters.\n\n"
- " Example: TRACK 3 NUM 7 Plays tracks to 3 to 10\n"
- " FROM 2 NUM 4 Plays sectors 2 to 6\n\n"
- "MAX This parameter requires a keyword. In order to feed a\n"
- " contiguous stream of data to the player task incoming audio\n"
- " data is queued. The maximum number of data packets queued is\n"
- " set using this parameter. By default the number of packets is\n"
- " set to 3. Each packet requires about 176K bytes, so take care.\n\n"
- " Example: MAX 5\n\n"
- "VERBOSE This command line option will enable some more or less helpful\n"
- " progress messages, may not be of any use at all.\n\n"
- " Example: VERBOSE\n\n"
- "That's all folks! This program and the accompanying source code are\n"
- "freeware. Respect copyright, encourage creativity.\n\n"
- TEMPLATE;
-
- /* Read the command line arguments. */
-
- if(!ReadArgs(TEMPLATE,(LONG *)Arg,ArgsPtr))
- {
- PrintFault(IoErr(),"CDDA");
-
- return(FALSE);
- }
-
- /* Did we get what we need? */
-
- if((!Arg[ARG_TRACK] && !Arg[ARG_FROM]) || (Arg[ARG_FROM] && !Arg[ARG_TO] && !Arg[ARG_NUM]))
- {
- PrintFault(ERROR_REQUIRED_ARG_MISSING,"CDDA");
-
- return(FALSE);
- }
-
- /* Get the scsi device name. */
-
- if(Arg[ARG_DEVICE])
- Device = Arg[ARG_DEVICE];
- else
- Device = SCSI_DEVICE;
-
- /* Get the scsi device unit number. */
-
- if(Arg[ARG_UNIT])
- Unit = *(LONG *)Arg[ARG_UNIT];
- else
- Unit = SCSI_UNIT;
-
- /* Verbose operation? */
-
- if(Arg[ARG_VERBOSE])
- Verbose = TRUE;
- else
- Verbose = FALSE;
-
- /* Create the player task. */
-
- Forbid();
-
- if(CreateTask("« CDDA Player task »",CHILD_PRI,ChildTask,CHILD_STACK))
- {
- SetSignal(0,SIG_HANDSHAKE);
-
- Wait(SIG_HANDSHAKE);
- }
-
- Permit();
-
- if(!Child)
- {
- Printf("CDDA: Failed to launch player task.\n");
-
- return(FALSE);
- }
-
- /* Set up the scsi device driver. */
-
- if(!SCSIInit(Device,Unit))
- {
- Printf("CDDA: Failed to open \"%s\" unit #%ld.\n",Device,Unit);
-
- return(FALSE);
- }
-
- /* Get things going, after a medium is removed/inserted
- * the first command will always fail.
- */
-
- TestUnitReady();
-
- /* Inquire device parameters. */
-
- if(Inquire())
- {
- Printf("CDDA: Cannot read device parameters of \"%s\" unit #%ld.\n",Device,Unit);
-
- return(FALSE);
- }
-
- /* We need a device to conform to the SCSI-2 specs, or we
- * will be unable to read the table of contents.
- */
-
- if((InquiryData . Version & 0x07) < 2)
- {
- Printf("CDDA: CD-ROM device must conform to the SCSI-2 specification..\n");
-
- return(FALSE);
- }
-
- /* Is this really a CD-ROM drive? */
-
- if((InquiryData . PeripheralType & 0x1F) != 0x05)
- {
- Printf("CDDA: \"%s\" unit #%ld is not a CD-ROM device.\n",Device,Unit);
-
- return(FALSE);
- }
-
- /* Is there a CD present in the drive? */
-
- if(TestUnitReady())
- {
- Printf("CDDA: No CD in \"%s\" unit #%ld\n",Device,Unit);
-
- return(FALSE);
- }
-
- /* Read the table of contents. */
-
- if(ReadTOC())
- {
- Printf("CDDA: Failed to read table of contents.\n");
-
- return(FALSE);
- }
-
- /* Track number given? */
-
- if(Arg[ARG_TRACK])
- {
- LONG Track = *(LONG *)Arg[ARG_TRACK],To;
-
- /* Track number too low? */
-
- if(Track < FullTOC . FirstTrack)
- {
- Printf("CDDA: The first available track is #%ld, not #%ld.\n",FullTOC . FirstTrack,Track);
-
- return(FALSE);
- }
-
- /* Track number too high? */
-
- if(Track > FullTOC . LastTrack)
- {
- Printf("CDDA: There are only %ld tracks on this CD, not %ld.\n",FullTOC . LastTrack,Track);
-
- return(FALSE);
- }
-
- /* Stop track given? */
-
- if(Arg[ARG_TO])
- To = *(LONG *)Arg[ARG_TO];
- else
- {
- /* Number of tracks to replay given? */
-
- if(Arg[ARG_NUM])
- To = Track + *(LONG *)Arg[ARG_NUM] - 1;
- else
- To = Track;
- }
-
- /* Make sure that the stop value makes sense. */
-
- if(To < Track)
- To = Track;
-
- if(To > FullTOC . LastTrack)
- To = FullTOC . LastTrack;
-
- /* Find the correct sector offsets. */
-
- for(i = 0 ; i < FullTOC . LastTrack - FullTOC . FirstTrack + 1 ; i++)
- {
- if(FullTOC . TOC[i] . TrackNumber == Track)
- {
- Index = FullTOC . TOC[i] . Address;
- Total = FullTOC . TOC[i + (To - Track) + 1] . Address - FullTOC . TOC[i] . Address;
- GotIt = TRUE;
-
- break;
- }
- }
-
- /* Successful? */
-
- if(!GotIt)
- {
- Printf("CDDA: Failed to find track #%ld.\n",*(LONG *)Arg[ARG_TRACK]);
-
- return(FALSE);
- }
- }
- else
- {
- /* Get the first sector number to play. */
-
- Index = *(LONG *)Arg[ARG_FROM];
-
- /* Are we to replay a number of sectors
- * or are we to replay the audio data
- * between a start and a stop sector?
- */
-
- if(Arg[ARG_NUM])
- Total = *(LONG *)Arg[ARG_NUM];
- else
- Total = *(LONG *)Arg[ARG_TO] - Index;
- }
-
- GotIt = FALSE;
-
- /* Verify the offsets given. */
-
- for(i = FullTOC . LastTrack - FullTOC . FirstTrack ; i >= 0 ; i--)
- {
- /* Is this the track we want? */
-
- if(FullTOC . TOC[i] . Address <= Index)
- {
- LONG Size;
-
- /* No chance to replay raw data, this could
- * be a CD-ROM track.
- */
-
- if(FullTOC . TOC[i] . Control & (1 << 2))
- {
- Printf("CDDA: Can only replay digital audio data.\n");
-
- return(FALSE);
- }
-
- /* No quadraphonics, please. */
-
- if(FullTOC . TOC[i] . Control & (1 << 3))
- {
- Printf("CDDA: Can only replay two channel audio data.\n");
-
- return(FALSE);
- }
-
- GotIt = TRUE;
-
- /* Calculate the number of sectors involved. */
-
- Size = FullTOC . TOC[i + 1] . Address - FullTOC . TOC[i] . Address;
-
- /* Too many requested? */
-
- if(Total > Size)
- {
- if(Verbose)
- Printf("CDDA: Too many sectors requested, truncating.\n");
-
- Total = Size;
- }
-
- break;
- }
- }
-
- if(!GotIt)
- {
- Printf("CDDA: Failed to find sector #%ld.\n",Index);
-
- return(FALSE);
- }
-
- return(TRUE);
- }
-
- /* ReadCDDASectors(APTR Buffer,LONG From,LONG Count):
- *
- * Read digital audio data.
- */
-
- BYTE
- ReadCDDASectors(APTR Buffer,LONG From,LONG Count)
- {
- STATIC struct ReadCDDA ReadCDDA;
-
- /* Set up the read command. */
-
- ReadCDDA . Command = 0xD8;
- ReadCDDA . Pad = 0;
- ReadCDDA . Address = From;
- ReadCDDA . Count = Count;
- ReadCDDA . SubCode = 0;
- ReadCDDA . Control = 0;
-
- /* Set up the SCSI direct command. */
-
- SCSIRequest -> iotd_Req . io_Command = HD_SCSICMD;
- SCSIRequest -> iotd_Req . io_Data = &SCSICmd;
- SCSIRequest -> iotd_Req . io_Length = sizeof(struct SCSICmd);
-
- SCSICmd . scsi_Data = Buffer;
- SCSICmd . scsi_Length = Count * sizeof(struct CDDASector);
- SCSICmd . scsi_Actual = 0;
- SCSICmd . scsi_Command = (UBYTE *)&ReadCDDA;
- SCSICmd . scsi_CmdLength = sizeof(struct ReadCDDA);
- SCSICmd . scsi_CmdActual = 0;
- SCSICmd . scsi_Flags = SCSIF_READ | SCSIF_AUTOSENSE;
- SCSICmd . scsi_SenseData = SenseBuffer;
- SCSICmd . scsi_SenseLength = 255;
- SCSICmd . scsi_SenseActual = 0;
-
- return(DoIO(SCSIRequest));
- }
-
- /* TestUnitReady():
- *
- * Check to see whether the unit thinks it is ready
- * to receive and process commands or not.
- */
-
- BYTE
- TestUnitReady()
- {
- STATIC struct TestUnitReady TestUnitReady;
-
- /* Set up the test command (all zeroes). */
-
- memset(&TestUnitReady,0,sizeof(struct TestUnitReady));
-
- /* Set up the SCSI direct command. */
-
- SCSIRequest -> iotd_Req . io_Command = HD_SCSICMD;
- SCSIRequest -> iotd_Req . io_Data = &SCSICmd;
- SCSIRequest -> iotd_Req . io_Length = sizeof(struct SCSICmd);
-
- SCSICmd . scsi_Length = 0;
- SCSICmd . scsi_Actual = 0;
- SCSICmd . scsi_Command = (UBYTE *)&TestUnitReady;
- SCSICmd . scsi_CmdLength = sizeof(struct TestUnitReady);
- SCSICmd . scsi_CmdActual = 0;
- SCSICmd . scsi_Flags = SCSIF_READ | SCSIF_AUTOSENSE;
- SCSICmd . scsi_SenseData = SenseBuffer;
- SCSICmd . scsi_SenseLength = 255;
- SCSICmd . scsi_SenseActual = 0;
-
- return(DoIO(SCSIRequest));
- }
-
- /* ReadTOC():
- *
- * Read the table of contents.
- */
-
- BYTE
- ReadTOC()
- {
- STATIC struct ReadTOC ReadTOC;
-
- /* Set up the read command. */
-
- memset(&ReadTOC,0,sizeof(struct ReadTOC));
-
- ReadTOC . Command = 0x43;
- ReadTOC . Alloc1 = sizeof(struct FullTOC) >> 8;
- ReadTOC . Alloc2 = sizeof(struct FullTOC) & 0xFF;
-
- /* Set up the SCSI direct command. */
-
- SCSIRequest -> iotd_Req . io_Command = HD_SCSICMD;
- SCSIRequest -> iotd_Req . io_Data = &SCSICmd;
- SCSIRequest -> iotd_Req . io_Length = sizeof(struct SCSICmd);
-
- SCSICmd . scsi_Data = (UWORD *)&FullTOC;
- SCSICmd . scsi_Length = sizeof(struct FullTOC);
- SCSICmd . scsi_Actual = 0;
- SCSICmd . scsi_Command = (UBYTE *)&ReadTOC;
- SCSICmd . scsi_CmdLength = sizeof(struct ReadTOC);
- SCSICmd . scsi_CmdActual = 0;
- SCSICmd . scsi_Flags = SCSIF_READ | SCSIF_AUTOSENSE;
- SCSICmd . scsi_SenseData = SenseBuffer;
- SCSICmd . scsi_SenseLength = 255;
- SCSICmd . scsi_SenseActual = 0;
-
- return(DoIO(SCSIRequest));
- }
-
- /* Inquire():
- *
- * Inquire hard-coded device parameters.
- */
-
- BYTE
- Inquire()
- {
- STATIC struct Inquiry Inquiry;
-
- /* Set up the inquiry command. */
-
- memset(&Inquiry,0,sizeof(struct Inquiry));
-
- Inquiry . Command = 0x12;
- Inquiry . AllocationLength = sizeof(struct InquiryData);
-
- /* Set up the SCSI direct command. */
-
- SCSIRequest -> iotd_Req . io_Command = HD_SCSICMD;
- SCSIRequest -> iotd_Req . io_Data = &SCSICmd;
- SCSIRequest -> iotd_Req . io_Length = sizeof(struct SCSICmd);
-
- SCSICmd . scsi_Data = (UWORD *)&InquiryData;
- SCSICmd . scsi_Length = sizeof(struct InquiryData);
- SCSICmd . scsi_Actual = 0;
- SCSICmd . scsi_Command = (UBYTE *)&Inquiry;
- SCSICmd . scsi_CmdLength = sizeof(struct Inquiry);
- SCSICmd . scsi_CmdActual = 0;
- SCSICmd . scsi_Flags = SCSIF_READ | SCSIF_AUTOSENSE;
- SCSICmd . scsi_SenseData = SenseBuffer;
- SCSICmd . scsi_SenseLength = 255;
- SCSICmd . scsi_SenseActual = 0;
-
- return(DoIO(SCSIRequest));
- }
-