home *** CD-ROM | disk | FTP | other *** search
- /*
- * Audio Handler
- *
- * Copyright (c) 1992,1995 by Martin Brenner, martin@ego.oche.de
- *
- * THIS INFORMATION IS PROVIDED "AS IS"; NO WARRANTIES ARE MADE.
- * ALL USE IS AT YOUR OWN RISK, AND NO LIABILITY OR
- * RESPONSIBILITY IS ASSUMED.
- *
- * This handler is partially based on port-handler by Andy Finkel.
- * Some insight about DOS and the SAS C Compiler was gained from
- * the Guru Book by Ralph Babel.
- *
- * For each new invocation of this handler a new process is started,
- * just like the console handler. Options after the colon ':' are
- * evaluated and Audio output is done with double buffering.
- *
- * This is an example for a handler where incoming DOS packets are
- * NOT the same length as messages sent to the exec device. Instead,
- * data is buffered and sent to the device au bloc.
- *
- * $Header: Work:Progs/aud/RCS/audio.c,v 1.4 1995/08/01 03:04:48 martin Exp martin $
- *
- * $Log: audio.c,v $
- * Revision 1.4 1995/08/01 03:04:48 martin
- * Bug fixes for version 1.0 beta
- *
- * Revision 1.3 1995/07/19 10:04:04 martin
- * first release version, 1.0 beta
- *
- * Revision 1.2 1995/07/04 20:00:33 martin
- * almost completely rewritten, now finally working
- *
- * Revision 1.1 92/12/22 17:01:47 martin
- * Initial revision
- *
- *
- *
- */
-
- #include <exec/memory.h>
- #include <exec/lists.h>
- #include <exec/errors.h>
- #include <dos/dos.h>
- #include <dos/dosextens.h>
- #include <dos/filehandler.h>
- #include <devices/audio.h>
- /* Hack, because registerized BeginIO() doesn't seem to exist in small.lib */
- #define BeginIO __stdargs BeginIO
- #include <proto/exec.h>
- #undef BeginIO
-
- #include "audio.h"
-
- /* debugging on the serial port */
- #define DEBUG
- #define DPRINTF KPrintF
-
- #define QTOUPPER(c) ((c)>='a'&&(c)<='z'?(c)-'a'+'A':(c))
-
- #undef BADDR
- #define BADDR(x) ((CPTR)((LONG)x << 2))
-
- /*
- * global vars
- *
- * since several invocations (processes) of the handler use the SAME
- * global data segment, process specific data MUST be allocated or
- * created on the stack. ExecBase can safely be global, though.
- */
- extern struct ExecBase *__far AbsExecBase;
- struct ExecBase *SysBase;
-
- /* Prototypes */
- BOOL checkReadyQueue(struct AudioState *as);
- BOOL checkIOBHandling(struct AudioState *as);
- void swapPtrs(struct AudioState *as);
- void allocIO(struct IOAudio *iob, struct AudioOptions *ao);
- void freeIO(struct IOAudio *iob, struct AudioOptions *ao);
- void lockIO(struct IOAudio *iob, struct AudioOptions *ao);
- void abortIO(struct IOAudio *iob, struct AudioOptions *ao);
- void writeIO(struct IOAudio *iob, struct AudioBuffer *buffer, struct AudioOptions *ao);
- void returnpkt(struct DosPacket *packet, LONG res1, LONG res2, struct AudioOptions *ao);
- int strncmpi(char *str1, char *str2, int n);
- BOOL parseArgs(struct AudioOptions *ao, int len, char *str);
- LONG getLong(int *len, char **ptr);
-
- /*
- * quark()
- *
- * entry point for the handler.
- */
- void __saveds quark(void)
- {
- UBYTE *openstring; /* BCPL String to Handler Device Name */
- BOOL error = 1;
-
- struct AudioState state; /* This struct contains the complete information about Audio IO */
-
- struct DeviceNode *devnode; /* our device node is passed in parm pkt Arg3 */
- struct DosPacket *packet; /* temporary packet handle, as received from the calling process */
- struct MsgPort *devport; /* MessagePort of handler process */
- struct Process *process; /* Process ID of handler process */
- struct Message *message; /* temporary message handle */
-
- /* First, initialize SysBase, so we may call exec functions */
- SysBase = AbsExecBase;
-
- process = (struct Process *)FindTask(NULL);
- devport = &process->pr_MsgPort;
-
- WaitPort(devport); /* Wait for startup message */
- message = (struct Message *)GetMsg(devport);
- packet = (struct DosPacket *) message->mn_Node.ln_Name;
-
- openstring = (UBYTE *)BADDR(packet->dp_Arg1);
- devnode = (struct DeviceNode *)BADDR(packet->dp_Arg3);
-
- /* Set default options, currently any channel */
- state.AS_Options.AO_Priority = 0;
- state.AS_Options.AO_NumChannels = 4;
- state.AS_Options.AO_Channels = "\x1\x2\x4\x8";
- state.AS_Options.AO_BufferSize = AUDIOBUFSIZE;
- state.AS_Options.AO_Period = DEFAULT_PERIOD;
- state.AS_Options.AO_Volume = DEFAULT_VOLUME;
- state.AS_Options.AO_Debug = 0;
-
- /* Seek for ':' and interpret everything after as options */
- parseArgs(&(state.AS_Options), openstring[0], openstring+1);
-
- #ifdef DEBUG
- if (state.AS_Options.AO_Debug)
- DPRINTF("S%ld", state.AS_Options.AO_Debug);
- #endif
-
- /*
- * create several IO blocks, Messageport and open the device
- * we dont create another port, but use our own port
- */
- state.AS_IO1 = (struct IOAudio *)AllocMem(sizeof(struct IOAudio), MEMF_PUBLIC|MEMF_CLEAR);
- if (!state.AS_IO1)
- goto handler_exit;
- state.AS_IO2 = (struct IOAudio *)AllocMem(sizeof(struct IOAudio), MEMF_PUBLIC|MEMF_CLEAR);
- if (!state.AS_IO2)
- goto handler_exit;
- state.AS_AllocIO = (struct IOAudio *)AllocMem(sizeof(struct IOAudio), MEMF_PUBLIC|MEMF_CLEAR);
- if (!state.AS_AllocIO)
- goto handler_exit;
- state.AS_LockIO = (struct IOAudio *)AllocMem(sizeof(struct IOAudio), MEMF_PUBLIC|MEMF_CLEAR);
- if (!state.AS_LockIO)
- goto handler_exit;
-
- state.AS_CurrentIO = state.AS_IO1;
- state.AS_NextIO = state.AS_IO2;
-
- state.AS_AllocIO->ioa_Request.io_Message.mn_ReplyPort = devport;
-
- /* link a dummy packet */
- state.AS_DummyPacket.dp_Type = ACTION_WRITE_RETURN;
- state.AS_AllocIO->ioa_Request.io_Message.mn_Node.ln_Name = (UBYTE *)&(state.AS_DummyPacket);
-
- state.AS_BytesCopied = 0; /* A counter for bytes copied from DOS packet to Audio buffer */
-
- /* allocate chip mem for audio double buffer, initialize all buffer variables */
- state.AS_Buffer1.AB_Data = AllocMem(state.AS_Options.AO_BufferSize, MEMF_CHIP);
- state.AS_Buffer1.AB_Size = state.AS_Options.AO_BufferSize;
- state.AS_Buffer1.AB_End = 0;
- state.AS_Buffer1.AB_InUse = 0;
- if (!state.AS_Buffer1.AB_Data)
- goto handler_exit;
- state.AS_Buffer2.AB_Data = AllocMem(state.AS_Options.AO_BufferSize, MEMF_CHIP);
- state.AS_Buffer2.AB_Size = state.AS_Options.AO_BufferSize;
- state.AS_Buffer2.AB_End = 0;
- state.AS_Buffer2.AB_InUse = 0;
- if (!state.AS_Buffer2.AB_Data)
- goto handler_exit;
-
- /* Current buffer is buffer currently played */
- /* Next buffer is buffer currently being filled with sound data */
- state.AS_CurrentBuffer = &(state.AS_Buffer1);
- state.AS_NextBuffer = &(state.AS_Buffer2);
-
- /* Initialize the DOS packet input queue */
- NewList(&(state.AS_ReadyQueue));
-
- /* Open audio.device, DON'T allocate any channels yet */
- if (error = OpenDevice(AUDIONAME, 0, (struct IORequest *)state.AS_AllocIO, 0)) {
- returnpkt(packet, DOSFALSE, ERROR_OBJECT_IN_USE, &(state.AS_Options));
- goto handler_exit;
- }
-
- state.AS_AllocState = ALLOCSTATE_UNUSED;
- state.AS_LockState = LOCKSTATE_UNUSED;
- state.AS_OpenForOutput = FALSE;
-
- /* devnode->dn_Task = devport; nope, start one task for each invocation */
-
- /* Finished with startup parameter packet...send back...*/
- returnpkt(packet, DOSTRUE, packet->dp_Res2, &(state.AS_Options));
-
- /*
- * The Main Event Loop:
- *
- * We have the following incoming messages:
- *
- * ACTION_FINDOUTPUT user process opens our file handle for output
- * ACTION_WRITE play sound request coming from the user process
- * ACTION_END the file handle is closed -> we will be closed
- * ACTION_IS_FILESYSTEM is the handler a filesystem? NO
- * return from ADCMD_ALLOCATE coming from the audio.device
- * return from CMD_WRITE coming from the audio.device
- * return from ADCMD_FREE coming from the audio.device
- * the last three will be labeled with ACTION_WRITE_RETURN on our message port.
- * Therefore, a dummy packet is linked with the IO-Requests.
- *
- * ADCMD_ALLOCATE might block if a channel is not available
- * ACTION_WRITE may block, if the buffer is full
- * ACTION_END may block, if there is still sound playing
- *
- * Audio return messages have a dummy dos packet in their ln_Name field,
- * which has a type of ACTION_WRITE_RETURN.
- */
-
- do {
- WaitPort(devport);
- /* evaluate incoming packets */
- while ((message = (struct Message *) GetMsg(devport)) != 0) {
- packet = (struct DosPacket *) message->mn_Node.ln_Name;
- switch (packet->dp_Type) {
-
- case ACTION_FINDOUTPUT:
- /* The device is opened here. We try to allocate requested channel(s) */
- state.AS_FileHandle = (struct FileHandle *) BADDR(packet->dp_Arg1);
- #ifdef DEBUG
- if (state.AS_Options.AO_Debug)
- DPRINTF("F%ld", state.AS_Options.AO_Debug);
- #endif
- if (state.AS_OpenForOutput) {
- returnpkt(packet, DOSFALSE, ERROR_OBJECT_IN_USE, &(state.AS_Options));
- }
- else {
- state.AS_OpenForOutput = DOSTRUE;
- state.AS_FileHandle->fh_Port = (struct MsgPort *)DOSTRUE; /* Interactive */
- state.AS_FileHandle->fh_Arg1 = 42; /* handler-private info */
- returnpkt(packet, DOSTRUE, 0L, &(state.AS_Options));
- }
- break;
-
- case ACTION_END: /* Close request */
- state.AS_OpenForOutput = FALSE;
- #ifdef DEBUG
- if (state.AS_Options.AO_Debug)
- DPRINTF("E%ld", state.AS_Options.AO_Debug);
- #endif
- returnpkt(packet, DOSTRUE, 0L, &(state.AS_Options));
- break;
-
- case ACTION_WRITE: /* Write Request */
- /* put new write request in the waiting queue */
- AddTail(&(state.AS_ReadyQueue), (struct Node *)message);
- #ifdef DEBUG
- if (state.AS_Options.AO_Debug)
- DPRINTF("W%ld", state.AS_Options.AO_Debug);
- #endif
- if (state.AS_AllocState == ALLOCSTATE_UNUSED) {
- /* allocate channel(s) */
- /* request new key */
- state.AS_AllocIO->ioa_AllocKey = 0;
- allocIO(state.AS_AllocIO, &(state.AS_Options));
- state.AS_AllocState = ALLOCSTATE_ALLOCATING;
- }
- break;
-
- case ACTION_IS_FILESYSTEM: /* Is handler a filesystem? */
- /* It isn't! */
- #ifdef DEBUG
- if (state.AS_Options.AO_Debug)
- DPRINTF("I%ld", state.AS_Options.AO_Debug);
- #endif
- returnpkt(packet, DOSFALSE, 0L, &(state.AS_Options));
- break;
-
- case ACTION_WRITE_RETURN:
- /* IO Request came back - check which one */
- switch (((struct IORequest *)message)->io_Command) {
- case ADCMD_ALLOCATE:
- /* allocation has finished, initialize iobs with key */
- *state.AS_IO1 = *state.AS_AllocIO;
- *state.AS_IO2 = *state.AS_AllocIO;
- *state.AS_LockIO = *state.AS_AllocIO;
- state.AS_AllocState = ALLOCSTATE_ALLOCATED;
- #ifdef DEBUG
- if (state.AS_Options.AO_Debug)
- DPRINTF("ra%ld", state.AS_Options.AO_Debug);
- #endif
- /* lock the allocated channel */
- lockIO(state.AS_LockIO, &(state.AS_Options));
- state.AS_LockState = LOCKSTATE_LOCKED;
- break;
- case CMD_WRITE:
- /* check, which buffer is free again */
- if ((struct IOAudio *)message == state.AS_IO1) {
- state.AS_Buffer1.AB_InUse = 0;
- if (state.AS_IO1->ioa_Request.io_Error != IOERR_ABORTED)
- state.AS_Buffer1.AB_End = 0;
- }
- else {
- state.AS_Buffer2.AB_InUse = 0;
- if (state.AS_IO2->ioa_Request.io_Error != IOERR_ABORTED)
- state.AS_Buffer2.AB_End = 0;
- }
- #ifdef DEBUG
- if (state.AS_Options.AO_Debug)
- DPRINTF("rw%ld", state.AS_Options.AO_Debug);
- #endif
- break;
- case ADCMD_LOCK:
- /* LOCK replied */
- #ifdef DEBUG
- if (state.AS_Options.AO_Debug)
- DPRINTF("rl%ld", state.AS_Options.AO_Debug);
- #endif
- if (state.AS_LockIO->ioa_Request.io_Error == ADIOERR_CHANNELSTOLEN) {
- /* Channel stolen, set allocate flag */
- state.AS_AllocState = ALLOCSTATE_STOLEN;
- /*
- * If NextBuffer is in use, it is currently played,
- * and CurrentBuffer is sent, but not yet played by the
- * audio.device, so it is safe to AbortIO it without
- * losing sound. Anyway, we wait until both io requests
- * are inactive before we send the ADCMD_FREE request.
- */
- if (state.AS_NextBuffer->AB_InUse)
- abortIO(state.AS_CurrentIO, &(state.AS_Options));
- /*
- * swap, so Current becomes Next to be sent as next after
- * channel reallocation.
- */
- swapPtrs(&state);
- }
- state.AS_LockState = LOCKSTATE_UNUSED;
- break;
- case ADCMD_FREE:
- /* Allocation has ended, set state */
- state.AS_AllocState = ALLOCSTATE_UNUSED;
- #ifdef DEBUG
- if (state.AS_Options.AO_Debug)
- DPRINTF("rf%ld", state.AS_Options.AO_Debug);
- #endif
- /* Check if we still have data to send, if yes, the FREE */
- /* was a response to free stolen channels */
- if (!IsListEmpty(&(state.AS_ReadyQueue))
- || state.AS_Buffer1.AB_End || state.AS_Buffer2.AB_End) {
- /* try to allocate the channels again */
- /* use old key */
- allocIO(state.AS_AllocIO, &(state.AS_Options));
- state.AS_AllocState = ALLOCSTATE_ALLOCATING;
- }
- break;
- }
- break;
- default:
- #ifdef DEBUG
- if (state.AS_Options.AO_Debug)
- DPRINTF("P%ld?%ld", packet->dp_Type, state.AS_Options.AO_Debug);
- #endif
- returnpkt(packet, DOSFALSE, ERROR_ACTION_NOT_KNOWN, &(state.AS_Options));
- break;
- }
- }
-
- /* If there is stealing and audio channel is no longer used, free it */
- if (state.AS_AllocState == ALLOCSTATE_STOLEN
- && !state.AS_Buffer1.AB_InUse && !state.AS_Buffer2.AB_InUse) {
- /* Free Channel */
- freeIO(state.AS_AllocIO, &(state.AS_Options));
- state.AS_AllocState = ALLOCSTATE_FREEING;
- }
-
- /* As long as there is activity, try handling input and output */
- while (checkReadyQueue(&state) || checkIOBHandling(&state))
- ;
-
- /* Is device being closed? */
- if (!state.AS_OpenForOutput && IsListEmpty(&(state.AS_ReadyQueue))
- && state.AS_AllocState == ALLOCSTATE_ALLOCATED
- && !state.AS_Buffer1.AB_InUse && !state.AS_Buffer2.AB_InUse
- && !state.AS_Buffer1.AB_End && !state.AS_Buffer2.AB_End) {
- /* Free Channel */
- freeIO(state.AS_AllocIO, &(state.AS_Options));
- state.AS_AllocState = ALLOCSTATE_FREEING;
- }
- } while (state.AS_OpenForOutput || !IsListEmpty(&(state.AS_ReadyQueue))
- || state.AS_AllocState != ALLOCSTATE_UNUSED
- || state.AS_LockState != LOCKSTATE_UNUSED);
-
- handler_exit:
- if (!error)
- CloseDevice((struct IORequest *)state.AS_AllocIO);
- if (state.AS_IO1)
- FreeMem(state.AS_IO1, sizeof(struct IOAudio));
- if (state.AS_IO2)
- FreeMem(state.AS_IO2, sizeof(struct IOAudio));
- if (state.AS_AllocIO)
- FreeMem(state.AS_AllocIO, sizeof(struct IOAudio));
- if (state.AS_LockIO)
- FreeMem(state.AS_LockIO, sizeof(struct IOAudio));
- if (state.AS_Buffer1.AB_Data)
- FreeMem(state.AS_Buffer1.AB_Data, state.AS_Buffer1.AB_Size);
- if (state.AS_Buffer2.AB_Data)
- FreeMem(state.AS_Buffer2.AB_Data, state.AS_Buffer2.AB_Size);
-
- /* end of main handler code */
- #ifdef DEBUG
- if (state.AS_Options.AO_Debug)
- DPRINTF("X%ld\n", state.AS_Options.AO_Debug);
- #endif
- }
- /*
- * checkReadyQueue()
- *
- * check, if there are still incoming packets not already handled
- */
- BOOL checkReadyQueue(struct AudioState *as)
- {
- struct Message *msg = (struct Message *)as->AS_ReadyQueue.lh_Head;
- BOOL active = FALSE;
-
- if (!as->AS_NextBuffer->AB_InUse && msg->mn_Node.ln_Succ) {
- struct DosPacket *p = (struct DosPacket *) msg->mn_Node.ln_Name;
- UBYTE *src, *dst;
- LONG restlen, packetlen;
-
- /* try to fit write request into current 'new'ábuffer */
- restlen = as->AS_NextBuffer->AB_Size - as->AS_NextBuffer->AB_End;
- if (restlen) { /* Buffer is not yet full */
- packetlen = p->dp_Arg3 - as->AS_BytesCopied;
- src = (UBYTE *)p->dp_Arg2 + as->AS_BytesCopied;
- dst = as->AS_NextBuffer->AB_Data + as->AS_NextBuffer->AB_End;
- if (packetlen <= restlen) {
- /* copy whole packet and reply */
- LONG i;
- for (i = packetlen; i; --i)
- *dst++ = *src++;
- Remove(&(msg->mn_Node));
- returnpkt(p, p->dp_Arg3, 0, &(as->AS_Options));
- as->AS_BytesCopied = 0;
- as->AS_NextBuffer->AB_End += packetlen;
- }
- else {
- /* Buffer is full */
- LONG i;
- for (i = restlen; i; --i)
- *dst++ = *src++;
- as->AS_BytesCopied += restlen;
- as->AS_NextBuffer->AB_End = as->AS_NextBuffer->AB_Size;
- }
- active = TRUE;
- }
- }
- return active;
- }
-
-
- /*
- * checkIOBHandling()
- *
- * check if a buffer can be sent to the audio.device
- */
- BOOL checkIOBHandling(struct AudioState *as)
- {
- BOOL active = FALSE;
-
- if (as->AS_AllocState == ALLOCSTATE_ALLOCATED) {
- /* check if NextBuffer can be sent to the audio.device, either */
- /* if the buffer is full, or the ReadyQueue is empty and the device is closing */
- if (!as->AS_NextBuffer->AB_InUse
- && (as->AS_NextBuffer->AB_End == as->AS_NextBuffer->AB_Size
- || as->AS_NextBuffer->AB_End > 0 && (!as->AS_OpenForOutput)
- && IsListEmpty(&(as->AS_ReadyQueue)))) {
- /* send the buffer off */
- writeIO(as->AS_NextIO, as->AS_NextBuffer, &(as->AS_Options));
- as->AS_NextBuffer->AB_InUse = 1;
- /* swap buffers */
- swapPtrs(as);
- active = TRUE;
- }
- }
- return active;
- }
-
- /*
- * swapPtrs()
- *
- * swap two pointers
- */
- void swapPtrs(struct AudioState *as)
- {
- struct AudioBuffer *tmpbuf;
- struct IOAudio *tmpio;
-
- tmpbuf = as->AS_NextBuffer;
- as->AS_NextBuffer = as->AS_CurrentBuffer;
- as->AS_CurrentBuffer = tmpbuf;
- tmpio = as->AS_NextIO;
- as->AS_NextIO = as->AS_CurrentIO;
- as->AS_CurrentIO = tmpio;
- }
-
- /*
- * allocIO()
- *
- * allocate one or more audio channels.
- */
- void allocIO(struct IOAudio *iob, struct AudioOptions *ao)
- {
- iob->ioa_Request.io_Message.mn_Node.ln_Pri = ao->AO_Priority;
- iob->ioa_Request.io_Command = ADCMD_ALLOCATE;
- iob->ioa_Request.io_Flags = 0;
- iob->ioa_Data = ao->AO_Channels;
- iob->ioa_Length = ao->AO_NumChannels;
- BeginIO((struct IORequest *)iob);
- #ifdef DEBUG
- if (ao->AO_Debug)
- DPRINTF("a%ld", ao->AO_Debug);
- #endif
- }
-
- /*
- * lockIO()
- *
- * lock one or more audio channels.
- */
- void lockIO(struct IOAudio *iob, struct AudioOptions *ao)
- {
- iob->ioa_Request.io_Command = ADCMD_LOCK;
- iob->ioa_Request.io_Flags = 0;
- BeginIO((struct IORequest *)iob);
- #ifdef DEBUG
- if (ao->AO_Debug)
- DPRINTF("l%ld", ao->AO_Debug);
- #endif
- }
-
- /*
- * freeIO()
- *
- * allocate one or more audio channels.
- */
- void freeIO(struct IOAudio *iob, struct AudioOptions *ao)
- {
- iob->ioa_Request.io_Command = ADCMD_FREE;
- iob->ioa_Request.io_Flags = 0;
- BeginIO((struct IORequest *)iob);
- #ifdef DEBUG
- if (ao->AO_Debug)
- DPRINTF("f%ld", ao->AO_Debug);
- #endif
- }
-
- /*
- * writeIO()
- *
- * allocate one or more audio channels.
- */
- void writeIO(struct IOAudio *iob, struct AudioBuffer *buffer, struct AudioOptions *ao)
- {
- iob->ioa_Request.io_Message.mn_Node.ln_Pri = ao->AO_Priority;
- iob->ioa_Request.io_Command = CMD_WRITE;
- iob->ioa_Request.io_Flags = ADIOF_PERVOL;
- iob->ioa_Data = buffer->AB_Data;
- iob->ioa_Length = buffer->AB_End;
- iob->ioa_Period = ao->AO_Period;
- iob->ioa_Volume = ao->AO_Volume;
- iob->ioa_Cycles = 1;
- BeginIO((struct IORequest *)iob);
- #ifdef DEBUG
- if (ao->AO_Debug)
- DPRINTF("w%ld", ao->AO_Debug);
- #endif
- }
-
- /*
- * abortIO()
- *
- * Abort a CMD_WRITE request
- */
- void abortIO(struct IOAudio *iob, struct AudioOptions *ao)
- {
- #ifdef DEBUG
- if (ao->AO_Debug)
- DPRINTF("bw%ld", ao->AO_Debug);
- #endif
- AbortIO((struct IORequest *)iob);
- }
-
- /*
- * returnpkt()
- *
- * return a packet to calling DOS process.
- */
- void returnpkt(struct DosPacket *packet, LONG res1, LONG res2, struct AudioOptions *ao)
- {
- struct Message *message = packet->dp_Link;
- struct MsgPort *replyport = packet->dp_Port;
- struct Process *process = (struct Process *)FindTask(NULL);
-
- packet->dp_Res1 = res1;
- packet->dp_Res2 = res2;
- packet->dp_Port = &(process->pr_MsgPort);
- message->mn_Node.ln_Name = (char *) packet;
-
- #ifdef DEBUG
- if (ao->AO_Debug)
- DPRINTF("RP%ld", ao->AO_Debug);
- #endif
- PutMsg(replyport,message);
- }
-
- /*
- * parseArgs()
- *
- * parse arguments of the device string (after the colon ':')
- */
- BOOL parseArgs(struct AudioOptions *ao, int len, char *str)
- {
- LONG val;
- while (len && *str != ':') {
- len--, str++;
- }
- if (*str != ':')
- return FALSE;
- len--, str++;
- while (len > 0) {
- if (!strncmpi(str, "PERIOD", 6)) {
- len -= 6, str += 6;
- val = getLong(&len, &str);
- if (val >= 124 && val <= 65535)
- ao->AO_Period = val;
- }
- else if (!strncmpi(str, "FREQUENCY", 9)) {
- len -= 9, str += 9;
- val = getLong(&len, &str);
- if (val >= 55 && val <= 28603)
- ao->AO_Period = CLOCK_CONSTANT_PAL / val;
- }
- else if (!strncmpi(str, "VOLUME", 6)) {
- len -= 6, str += 6;
- val = getLong(&len, &str);
- if (val >= 0 && val <= 64)
- ao->AO_Volume = val;
- }
- else if (!strncmpi(str, "PRIORITY", 8)) {
- len -= 8, str += 8;
- val = getLong(&len, &str);
- if (val >= -128 && val <= 127)
- ao->AO_Priority = val;
- }
- else if (!strncmpi(str, "BUFFER", 6)) {
- len -= 6, str += 6;
- val = getLong(&len, &str);
- if (val >= 0x1000 && val <= 0x40000) {
- val = val / 4; /* 2 buffers */
- ao->AO_BufferSize = val * 2; /* must be even size */
- }
- }
- else if (!strncmpi(str, "CHANNEL", 7)) {
- len -= 7, str += 7;
- val = getLong(&len, &str);
- if (val >= 0 && val <= 3) {
- ao->AO_NumChannels = 1;
- ao->AO_Channels = "\x1\x2\x4\x8" + val;
- }
- }
- else if (!strncmpi(str, "LEFT", 4)) {
- len -= 4, str += 4;
- ao->AO_NumChannels = 2;
- ao->AO_Channels = "\x1\x8";
- }
- else if (!strncmpi(str, "RIGHT", 5)) {
- len -= 5, str += 5;
- ao->AO_NumChannels = 2;
- ao->AO_Channels = "\x2\x4";
- }
- else if (!strncmpi(str, "STEREO", 6)) {
- len -= 6, str += 6;
- ao->AO_NumChannels = 4;
- ao->AO_Channels = "\x3\x5\xA\xC";
- /* but not yet fully implemented! */
- }
- else if (!strncmpi(str, "16BIT", 5)) {
- len -= 5, str += 5;
- /* not implemented! */
- }
- else if (!strncmpi(str, "DEBUG", 5)) {
- len -= 5, str += 5;
- val = getLong(&len, &str);
- ao->AO_Debug = val;
- }
- while (len && *str != '/') /* skip rest (as CON: does, too) */
- len--, str++;
- if (*str == '/')
- len--, str++;
- }
- return TRUE;
- }
-
- /*
- * getLong()
- *
- * convert string to integer, supports decimal, octal and hex
- */
- LONG getLong(int *len, char **ptr)
- {
- LONG val = 0;
- int l = *len;
- char *str = *ptr;
- int sign = 1;
-
- if (l == 0)
- return val;
-
- while (*str == ' ' || *str == '\t')
- str++;
-
- if (*str == '-') {
- sign = -1;
- str++;
- }
- if (*str == '0') {
- l--, str++;
- if (l == 0) {
- *len = l, *ptr = str;
- return val;
- }
- if (QTOUPPER(*str) == 'X') {
- l--, str++;
- while (len > 0) {
- if (*str >= '0' && *str <= '9')
- val = val * 16 + *str - '0';
- else if (QTOUPPER(*str) >= 'A' && QTOUPPER(*str) <= 'F')
- val = val * 16 + QTOUPPER(*str) - 'A';
- else
- break;
- l--, str++;
- }
- }
- else {
- while (len > 0 && *str >= '0' && *str <= '7') {
- val = val * 8 + *str - '0';
- l--, str++;
- }
- }
- }
- else {
- while (len > 0 && *str >= '0' && *str <= '9') {
- val = val * 10 + *str - '0';
- l--, str++;
- }
- }
- *len = l, *ptr = str;
-
- return val * sign;
- }
-
- /*
- * strncmpi()
- *
- * compare two strings, length n, ignorecase
- */
- int strncmpi(char *str1, char *str2, int n)
- {
- UBYTE *astr = str1;
- UBYTE *bstr = str2;
- UBYTE c;
-
- while ((c = QTOUPPER(*astr)) && (c == QTOUPPER(*bstr)) && n)
- astr++, bstr++, n--;
- if (!c || !n)
- return 0;
- if (c < *bstr)
- return -1;
- return 1;
- }
-