Copyright ©1995 by NeXT Computer, Inc.  All Rights Reserved.




snddriver_dsp_boot(), snddriver_dsp_reset()

SUMMARY Start the DSP

DECLARED IN sound/snddriver_client.h

SYNOPSIS kern_return_t snddriver_dsp_boot(port_t commandPort, int *bootImage, int imageSize, int priority)

kern_return_t snddriver_dsp_reset(port_t commandPort, int priority)

DESCRIPTION snddriver_dsp_boot() enqueues a command to boot the DSP.  The arguments are as follows:

commandPort is the DSP command port, as retrieved by snddriver_get_dsp_cmd_port().
bootImage is a pointer to a DSP program image that's downloaded to the DSP (program memory location 0x0) and immediately executed.  The image is created by reading a ".lod" file that's assembled from DSP56001 assembly code.
imageSize is the size of the DSP boot image, in bytes.  The image must not exceed 512 words (24-bit DSP words right-justified within 32-bit integers).
priority is one of the three priority constants SNDDRIVER_LOW_PRIORITY, SNDDRIVER_MED_PRIORITY, or SNDDRIVER_HIGH_PRIORITY.  The sound driver sorts the commands in its DSP command queue according to priority.

Booting the DSP clears neither external memory nor on-chip data memory.

snddriver_dsp_reset() puts the DSP in its reset state.  By this it's meant that the DSP's execution is immediately halted and a bootstrap program is awaited.  Booting the DSP automatically resets it, thus you don't need to call this function before calling snddriver_boot_dsp().

RETURN Returns an error code:  0 on success, nonzero on failure.



snddriver_dsp_dma_read() See snddriver_dsp_dma_write()



snddriver_dsp_dma_write(), snddriver_dsp_dma_read()

SUMMARY Transfer data to and from the DSP via DMA

DECLARED IN sound/snddriver_client.h

SYNOPSIS kern_return_t snddriver_dsp_dma_write(port_t commandPort, int elementCount, int dataFormat, pointer_t data)

kern_return_t snddriver_dsp_dma_read(port_t commandPort, int elementCount, int dataFormat, pointer_t data)

DESCRIPTION These functions enqueue commands that perform application-initiated DMA transfers to and from the DSP.  You must include complex DMA protocol to use these functions.  The arguments to the two functions are similar:

commandPort is the DSP command port, as retrieved by snddriver_get_dsp_cmd_port().
elementCount is the number of data elements to send during each transfer.
dataFormat is an integer constant that describes the size and packing of an individual data element.  These are

DSP_MODE8 1 byte per element
DSP_MODE16 2 bytes per element
DSP_MODE24 3 bytes per element
DSP_MODE32 3 bytes per element, right-justified in 4
DSP_MODE2416 2 bytes per element, packed and right-justified in 4

data is a pointer to the data that you're transferring.

There are three rules regarding the size and alignment of a DMA transfer buffer:

The size in bytes of a single DMA transfer buffer, reckoned as elementCount * bytes-per-element, must be a multiple of 16. Note that bytes-per-element isn't given directly as an argument.
The data must be "quad-aligned"; in other words, the starting address (data) must be a multiple of 16.
All the data in a transfer buffer must lie on the same page of virtual memory.

If you're writing data, the snddriver_dsp_dma_write() function enqueues a command to send the data to the DSP and then immediately returns.  snddriver_dsp_dma_read(), on the other hand, waits until it has read the prescribed amount of data and returns with data filled.  DMA-transfer commands are always enqueued with high priority.

RETURN Returns an error code:  0 on success, nonzero on failure.

SEE ALSO snddriver_dsp_read(), snddriver_dsp_write(), snddriver_dsp_protocol()



snddriver_dsp_host_cmd()

SUMMARY Enqueue a DSP command

DECLARED IN sound/snddriver_client.h

SYNOPSIS kern_return_t snddriver_dsp_host_cmd(port_t commandPort, u_int hostCommand, u_int priority)

DESCRIPTION snddriver_dsp_host_cmd() enqueues a command on the sound driver's DSP command queue that interrupts the DSP and causes it to execute one of 32 interrupt routines (or host commands).  Its arguments are as follows:

commandPort is the DSP command port, as retrieved by snddriver_get_dsp_cmd_port().
hostCommand is an integer that represents the host command you want to execute.  The first 22 host commands are already defined (or reserved).  The host commands provided by NeXT are represented by constants (prefix "DSP_hc_") that are defined in /usr/include/nextdev/snd_dsp.h.  Creating your own host command requires a familiarity with DSP programming that lies beyond the scope of this description.
priority is one of the three priority constants SNDDRIVER_LOW_PRIORITY, SNDDRIVER_MED_PRIORITY, or SNDDRIVER_HIGH_PRIORITY.  The sound driver sorts the commands in its DSP command queue according to priority.

When the DSP receives a host command, it sets the HC flag in the Command Vector Register.  After executing the command, the DSP clears the flag.  You should always precede a call to snddriver_dsp_host_cmd() with a call to snddriver_dspcmd_req_condition() that waits for HC to clear in order to avoid overwriting a previously requested, but as yet unexecuted, host command:

/* CVR_HC is defined in <nextdev/snd_dspreg.h> */
err = snddriver_dspcmd_req_condition(commandPort, CVR_HC, 0, ...);
if (err != 0)
. . .

/* Now enqueue the host command request. */
err = snddriver_dsp_host_cmd(...);
if (err != 0)
. . .

RETURN Returns an error code:  0 on success, nonzero on failure.

SEE ALSO snddriver_dspcmd_req_condition()



snddriver_dsp_protocol()

SUMMARY Set the sound driver's protocol vis-a-vis the DSP

DECLARED IN sound/snddriver_client.h

SYNOPSIS kern_return_t snddriver_dsp_protocol(port_t devicePort, port_t ownerPort, int protocol)

DESCRIPTION snddriver_dsp_protocol() lets you establish the manner in which the sound driver communicates with the DSP; specifically, it determines whether to create 0, 1, or 2 DSP-reply buffers and whether DSP interrupts are enabled.  The existence of the DSP-reply buffers determines whether you can use streams to transfer data.

The function's first two arguments are the sound driver device port and the DSP owner port, as acquired through SNDAcquire().

protocol is the heart of the matter:  It's a code that represents the protocol that you wish to establish.  There are two ways to create the appropriate protocol:  If you're using streams to access the DSP, then you should pass the protocol variable that's modified by calls to snddriver_stream_setup(), as explained (with an example) in the description of that function. Alternatively--or in addition to the foregoing--you can create a protocol code by or'ing the following DSP protocol constants:

SNDDRIVER_DSP_PROTO_RAW represents the barest protocol.  The sound driver makes no assumptions about how the DSP is being used:  No DSP-reply buffers are created and the DSP can't interrupt the host.  You can't use streams in raw protocol; to transfer data, you use the snddriver_dsp_write() and snddriver_dsp_read() functions.

All the other protocols create at least one DSP-reply buffer and allow DSP interrupts, thus allowing you to transfer data through a stream:

SNDDRIVER_DSP_PROTO_DSPMSG ("DSP-message") creates a buffer that can hold 512 DSP-reply messages.  A message from the DSP (as it lies in the reply buffer) is a 24-bit word right-justified in 32 bits.  To receive the contents of this buffer, you enqueue a request through snddriver_dspcmd_req_msg().
SNDDRIVER_DSP_PROTO_DSPERR ("DSP-error") creates an additional 512-message DSP-reply buffer that collects error messages sent from the DSP.  An error message is identified as having its MSB (bit 23) set.  You can request the contents of the error buffer through snddriver_dspcmd_req_err().
SNDDRIVER_DSP_PROTO_C_DMA ("complex DMA") implies DSP message mode (a single DSP-reply buffer is created) and allows DSP-initiated DMA transfers.
SNDDRIVER_DSP_PROTO_HFABORT ("host flag abort") causes the driver to take note if the DSP aborts.  (The DSP indicates that it has aborted by setting HF2 and HF3.)

To get the documented behavior from these protocols, you must include SNDDRIVER_DSP_PROTO_RAW.

Note:  A protocol of 0 produces Release 1.0 behavior; this is roughly equivalent to a combination of DSP message, DSP error, and host flag abort modes.

RETURN Returns an error code:  0 on success, nonzero on failure.

SEE ALSO snddriver_stream_setup()



snddriver_dsp_read() See snddriver_dsp_write()

snddriver_dsp_read_data() See snddriver_dsp_write()

snddriver_dsp_read_messages() See snddriver_dsp_write()

snddriver_dsp_reset() See snddriver_dsp_boot()



snddriver_dsp_set_flags()

SUMMARY Set the DSP host flags

DECLARED IN sound/snddriver_client.h

SYNOPSIS kern_return_t snddriver_dsp_set_flags(port_t commandPort, u_int flagMask, u_int flagValue, u_int priority)

DESCRIPTION snddriver_dsp_set_flags() enqueues a command to modify one or both of the DSP host interface flags HF0 (host flag 0) and HF1 (host flag 1).

The flagMask argument defines which of the host flags you want to affect.  The flags are represented by the constants SNDDRIVER_ICR_HF0 and SNDDRIVER_ICR_HF1.  You can set both flags at the same time by or'ing these two constants. (ICR stands for "Interrupt Control Register"; this is the register to which the host flags belong.)

flagValue is the value to which you're setting the flag(s).  A host flag can be either on or off, states that are also referred to as "set" and "cleared".  To set a flag, you pass its constant identifier; to clear it, you pass 0.  The following examples illustrate this concept:

/* Set HF0 (turn it on). */
snddriver_dsp_set_flags(..., SNDDRIVER_ICR_HF0,
SNDDRIVER_ICR_HF0,...)

/* Clear HF1. */
snddriver_dsp_set_flags(..., SNDDRIVER_ICR_HF1,0,...)

/* Set both flags. */
snddriver_dsp_set_flags(...,
SNDDRIVER_ICR_HF0 | SNDDRIVER_ICR_HF1,
SNDDRIVER_ICR_HF0 | SNDDRIVER_ICR_HF1, ...)

/* Set HF0 and clear HF1. */
snddriver_dsp_set_flags(...,
SNDDRIVER_ICR_HF0 | SNDDRIVER_ICR_HF1,
SNDDRIVER_ICR_HF0, ...)

/* Clear both flags. */
snddriver_dsp_set_flags(...,
SNDDRIVER_ICR_HF0 | SNDDRIVER_ICR_HF1, 0,...)

The other two arguments, commandPort and priority, are the DSP command port and command-queue priority, respectively. The DSP command port is retrieved through snddriver_dsp_cmd_port(); you set the priority to one of SNDDRIVER_HIGH_PRIORITY, SNDDRIVER_MED_PRIORITY, or SNDDRIVER_LOW_PRIORITY.

RETURN Returns an error code:  0 on success, nonzero on failure.

SEE ALSO snddriver_dspcmd_req_condition()



snddriver_dsp_write(), snddriver_dsp_read(), snddriver_dsp_read_data(), snddriver_dsp_read_messages()

SUMMARY Transfer data to and from the DSP

DECLARED IN sound/snddriver_client.h

SYNOPSIS kern_return_t snddriver_dsp_write(port_t commandPort, void *buffer, int elementCount, int elementSize, int priority)

kern_return_t snddriver_dsp_read(port_t commandPort, void *buffer, int elementCount, int elementSize, int priority)
kern_return_t snddriver_dsp_read_messages(port_t commandPort, void *buffer, int elementCount, int elementSize, int priority)
kern_return_t snddriver_dsp_read_data(port_t commandPort, void **buffer, int elementCount, int elementSize, int priority)

DESCRIPTION snddriver_dsp_write() enqueues a command to perform a one-shot, application-initiated data transfer to the DSP; snddriver_dsp_read() brings data back from the DSP in a like manner.  You generally use these functions if you have a small amount of data to transfer or if the transfers are infrequent enough that the overhead of the obvious alternative--setting up a DMA stream--would be exorbitant.

The other two functions, snddriver_dsp_read_messages() and snddriver_dsp_read_data() are auxiliary to snddriver_dsp_read().  When you call snddriver_dsp_read(), it, in turn, calls one of the auxiliary functions; which of the two functions it calls depends on the current DSP protocol, as described below.  You can call these functions yourself by-passing snddriver_dsp_read(), although you should adhere to the same protocol rules that snddriver_dsp_read() obeys.

The arguments to all four functions are similar:

commandPort is the DSP command port, as retrieved through snddriver_get_dsp_cmd_port().
buffer, as used by snddriver_dsp_write(), is a pointer to the data you want to send to the DSP.  For the snddriver_dsp_read...() functions, it's a pointer to the location where you want the retrieved data to be stored.  Note that for snddriver_dsp_read_data(), buffer is the address of a pointer; this allows the function to allocate memory for the data if you haven't allocated it yourself.
elementCount and elementSize are the number of data elements to transfer and the size, in bytes, of a single element, respectively.
priority is an integer used to sort the command on the DSP command queue.  The sound driver defines three priorities represented by the constants SNDDRIVER_LOW_PRIORITY, SNDDRIVER_MED_PRIORITY, and SNDDRIVER_HIGH_PRIORITY.  You normally set all application-initiated data transfers to low priority, thus reserving medium and high priority for operations that need to jump to the head of the DSP command queue.

Of these functions, snddriver_dsp_write() is most straightforward:  When it's called, a transfer-data-to-the-DSP command is sorted (by priority) into the DSP command queue.  If, when its turn comes, the command can't be executed, the driver simply pushes it back on the queue and tries again.  No other commands of equal or lower priority can be executed while a frustrated write command is sitting on top of the queue.  Note, however, that higher priority commands will get through.

As mentioned earlier, snddriver_dsp_read() calls one of its two auxiliary functions as determined by the current DSP protocol:

If your application is in raw protocol, then snddriver_dsp_read_data() is used to read data from the DSP transmit registers.
If DSP message protocol is included, snddriver_dsp_read_messages() is used to read data from the DSP-reply buffer.

The difference between the two mechanisms is generally transparent such that you can call snddriver_read_data() without regard for the current protocol.  However, the manner in which the underlying functions handles incomplete reads is significant: If the read can't be completed (typically because the DSP hasn't generated enough data), snddriver_dsp_read_data() blocks the DSP command queue in the fashion of snddriver_dsp_write().  In the same situation, snddriver_dsp_read_messages() waits for more data without blocking the command queue.  Thus snddriver_dsp_read_messages() can safely be called from a separate thread at any time.  This isn't true of snddriver_dsp_read_data(); you should be scrupulous about ensuring that sufficient data has been processed by the DSP before you attempt to read it through this function (or through snddriver_dsp_read() while in raw protocol).

RETURN Returns an error code:  0 on success, nonzero on failure.

SEE ALSO snddriver_dsp_dma_read(), snddriver_dsp_dma_write()



snddriver_dspcmd_req_condition()

SUMMARY Request a DSP host interface register condition

DECLARED IN sound/snddriver_client.h

SYNOPSIS kern_return_t snddriver_dspcmd_req_condition(port_t commandPort, u_int registerMask, u_int conditionFlags, int priority, port_t replyPort)

DESCRIPTION snddriver_dspcmd_req_condition() does two things:  It causes the DSP command queue to block until the specified host interface register condition is true, and it registers a request for an asynchronous message to be sent to replyPort when the condition is fulfilled.  The function returns immediately.

You specify a condition through a combination of the registerMask and conditionFlags arguments:

registerMask specifies the host interface registers (actually, the bits therein) that you're interested in.  It's created by or'ing the register-bit constants defined in nextdev/snd_dspregs.h.  A subset of these are also defined as sound driver constants in sound/snddriver_client.h.
conditionFlags encodes the states of the register bits that define a satisfied condition.  To specify that you want a register bit set, you or the register-bit constant that represents it; if you want it clear, you exclude the constant.  If you want all the specified bits to be clear, set conditionFlags to 0.

In the following example, the command queue is blocked until HF0 is set and HF1 is clear (both flags are in the Interrupt Control Register):

/* Block until HF0 is set and HF1 is clear. */
snddriver_dspcmd_req_condition(...,
SNDDRIVER_ICR_HF0 | SNDDRIVER_ICR_HF1,
SNDDRIVER_ICR_HF2, ...)

The condition request is sorted into the DSP command queue according to priority, which must be one of SNDDRIVER_LOW_PRIORITY, SNDDRIVER_MED_PRIORITY, or SNDDRIVER_HIGH_PRIORITY.

The message that's sent to the reply port when the condition is fulfilled contains the value of the host interface register.  By setting the registerMask argument to 0, you can use the snddriver_dspcmd_req_condition() function to simply poll for this value.

RETURN Returns an error code:  0 on success, nonzero on failure.

SEE ALSO snddriver_dsp_set_flags()



snddriver_dspcmd_req_err() See snddriver_dspcmd_req_msg()



snddriver_dspcmd_req_msg(), snddriver_dspcmd_req_err()

SUMMARY Request the contents of the DSP-reply buffers

DECLARED IN sound/snddriver_client.h

SYNOPSIS kern_return_t snddriver_dspcmd_req_msg(port_t commandPort, port_t replyPort)

kern_return_t snddriver_dspcmd_req_err(port_t commandPort, port_t replyPort)

DESCRIPTION The snddriver_dspcmd_req_msg() and snddriver_dspcmd_req_err() functions are part of the mechanism by which your application retrieves messages from the sound driver's DSP-reply buffers.  They request that the contents of the appropriate buffer (as described below) be sent in a Mach message to replyPort, a valid port that must already be allocated.  Simply requesting a message is only half of the story:  You then have to receive the message that's been sent, usually by sitting in a msg_receive() loop.  You typically process the Mach messages that these functions induce by passing the messages to the snddriver_reply_handler() function.

The utility of these functions depends on your application's DSP protocol:

You should never use these functions in raw protocol since the sound driver doesn't create any DSP-reply buffers.
By including DSP message protocol, a single DSP-reply buffer is created in which both error and non-error messages are stored; thus ...req_msg() is of use, but ...req_err() isn't.
DSP error protocol deems that two buffers be created, one for error messages and the other for non-error messages.  Both functions are useful in this protocol.

DSP protocol and how to set it is explained in the description of the snddriver_set_dsp_protocol() function.  For both functions, the commandPort argument is the DSP command port as retrieved by snddriver_get_dsp_cmd_port().

RETURN Returns an error code:  0 on success, nonzero on failure.

SEE ALSO snddriver_set_dsp_protocol(), snddriver_reply_handler()



snddriver_get_device_parms() See snddriver_set_device_parms()



snddriver_get_dsp_cmd_port()

SUMMARY Get the DSP command port

DECLARED IN sound/snddriver_client.h

SYNOPSIS kern_return_t snddriver_get_dsp_cmd_port(port_t devicePort, port_t ownerPort, port_t *commandPort)

DESCRIPTION snddriver_get_dsp_cmd_port() attempts to get the DSP command port, the port through which the sound driver issues commands to the DSP.  If it's successful, the port is returned in the commandPort argument, which needn't have been previously allocated.

The first two arguments, devicePort and ownerPort, are the sound driver device port and the DSP owner port, as acquired through SNDAcquire().

The DSP command port is required as an argument by almost all sound driver functions that communicate with the DSP.  The one notable exception, for which you don't have to get the command port as it's gotten implicitly when needed, is if you send and retrieve DSP data via streams after having booted the DSP through the SNDBootDSP() sound library function.  But even in this case getting the command port as a reflex to getting the DSP owner port won't serve you ill.

RETURN Returns an error code:  0 on success, nonzero on failure.



snddriver_get_volume() See snddriver_set_device_parms()



snddriver_new_device_port()

SUMMARY Reallocate the sound driver device port

DECLARED IN sound/snddriver_client.h

SYNOPSIS kern_return_t snddriver_new_device_port(port_t devicePort, port_t superuserPort, port_t *newDevicePort)

DESCRIPTION This function deallocates the sound driver device port devicePort, as previously acquired through SNDAcquire(), then allocates a new port to the device which it returns as newDevicePort.  When the old device port is deallocated, so, too, are all its resource owner ports and sound streams; thus any currently operating sound driver tasks, such as recording and playing sounds, are aborted.  Because of the ruthlessness of this act, you must be the UNIX superuser to call this function, as verified by the superuserPort argument, for which you should pass the return value of host_priv_self().  The new device port's registration with regard to the Network Name Server is the same as that of the old; in other words, if the old port had been registered (through netname_check_in()), the new one will be registered automatically.

RETURN Returns an error code:  0 on success, nonzero on failure.



snddriver_reply_handler()

SUMMARY Respond to asynchronous sound driver messages

DECLARED IN sound/snddriver_client.h

SYNOPSIS kern_return_t snddriver_reply_handler(msg_header_t *reply, snddriver_handlers_t *handlers)

DESCRIPTION snddriver_reply_handler() helps your application respond to asynchronous sound driver messages.  The function is designed around the snddriver_handlers structure, which provides a correspondence between the sound driver messages and a list of C functions that you provide.  When you receive a message from the sound driver, you pass the message and a snddriver_handlers structure to snddriver_reply_handler() which then executes the handler function that corresponds to the message.

The definition of the snddriver_handlers structure (typedef'd, for convenience, as snddriver_handlers_t) reveals the nature of the functions that you can register as reply handlers:

typedef struct snddriver_handlers {
void                     *arg;
int                       timeout;
sndreply_tagged_t         started;
sndreply_tagged_t         completed;
sndreply_tagged_t         aborted;
sndreply_tagged_t         paused;
sndreply_tagged_t         resumed;
sndreply_tagged_t         overflow;
sndreply_recorded_data_t  recorded_data;
sndreply_dsp_cond_true_t  condition_true;
sndreply_dsp_msg_t        dsp_message;
sndreply_dsp_msg_t        dsp_error;
} snddriver_handlers_t;

The structure's arg field is a value that's passed to the reply handlers when they're called by snddriver_reply_handler(); you can set it to whatever value best suits your application, but keep in mind that the value must fit within the size of a pointer (four bytes).  The timeout field is currently unused.

The final ten fields are the heart of the structure:  Each corresponds to a particular sound driver message.  The first six of these correspond to messages that indicate a change in the state of a stream ("stream-state" messages); in other words, the sound driver sends a specific message when a stream starts processing data, when it completes its processing, when it aborts, and so on.  By setting a field to a particular function, you register that function as the handler for the message to which the field corresponds.  For example, to establish a function named handleStreamStart() as the function that's executed when your application receives a stream-started message from the sound driver, you would do the following:

/* Create a snddriver_handlers_t and register the
* function handleStreamStart() (which we'll assume already
* exists) to process stream-started messages.
*/
snddriver_handlers_t replyHandlers;
replyHandlers.started = handleStreamStart;

While this registers handleStreamStart() as the handler for stream-started messages, you must also tell the sound driver that you actually want such messages sent to your application.  To do this, you set the msgStarted boolean argument to true when you call snddriver_stream_start_reading() or snddriver_stream_start_writing().  Analogous msg... message flags exist for the other five stream-state messages.

When the sound driver sends a stream-state message to your application, it sends it to the port that you specify as the last argument (replyPort) to snddriver_stream_start_reading() or snddriver_stream_start_writing().  To receive the message, you create a msg_header_t structure, set its local_port field to the stream's reply port, and then wait for the message to arrive by sitting in a message receive (msg_receive()) loop.  After so capturing the message, you then pass it, along with your handler structure, to snddriver_reply_handler().  This is demonstrated by the example below.

Notice, from the definition of snddriver_handlers, that the six stream-state handlers are all of type sndreply_tagged_t.  This type represents a two-argument function protocol that's defined as

typedef void (*sndreply_tagged_t) (void *arg, int tag);

The functions that you register to handle the stream-state messages must adhere to this protocol.  The values of the arguments are set by snddriver_reply_handler():

arg is given the value of the arg field of the snddriver_handlers structure in which the function is registered.  As mentioned earlier, you can set the structure's arg field to a (four-byte) value that suits the needs of your application.
tag is the region-identifying tag that you provide as an argument to snddriver_stream_start_writing() or snddriver_stream_start_reading().

The seventh of the ten snddriver_handlers handler fields--recorded_data--also applies to streams.  However, unlike the fist six, which are optional, recorded_data is essential when you're reading data from a stream.  Its importance arises from the way that the sound driver handles read data:  It keeps the data in the kernel's virtual memory until you ask to bring it into your application.  The only way to bring this data back is to supply a recorded_data handler that does so.  The following program excerpt demonstrates a typical way to achieve this effect.  In the example, details such as acquiring the sound driver and sound resource owner ports are omitted.  The read stream shown here is anonymous--the code can be used equally well for a stream that reads from sound-in or from the DSP:

/* The code shown in the example requires the following header
files  */
#import <sound/snddriver_client.h>
#import <mach.h>

/* Define a read stream tag, a read pointer, and a byte count
variable. */
#define READ_TAG 1
static short *readData;
static int readCount;

/* Create a recorded_data handler; the function's protocol is
* explained following the example.
*/
static void read_completed(void *arg, int tag, void *kernelData,
int size)
{
/* Make sure this is the read stream. */
if (tag == READ_TAG) {
readData = (short *)kernelData;
readCount = size;
}
}

main()
{
/* Define a read port, a reply port, and a reply structure. */
port_t readPort, replyPort;
snddriver_handlers_t replyHandlers;

/* Allocate a Mach message header. msg_header_t and MSG_SIZE_MAX
* (and msg_receive, below) are defined in mach.h.
*/
msg_header_t *reply_msg = (msg_header_t *)malloc(MSG_SIZE_MAX);

/* Create an error-check variable. */
int err;

/* Allocate the reply port. */
err = port_allocate(task_self(), &replyPort);
if (err != 0)
. . .

/* Set the recorded_data handler. */
replyHandlers.recorded_data = read_completed;

/* Set the amount of data you want to read; for the purposes of
* this example, an arbitrary amount is specified.
*/
readCount = 1024;

/* Here, a number of activities -- such as acquiring the sound
* driver port and sound resource owner port, setting up a read
* stream through snddriver_stream_setup(), and (possibly)
* booting the DSP and sending it data -- are omitted.
*/
. . .

/* Enqueue a read request.  The six 0 arguments are the message
* request flags.
*/
err = snddriver_stream_start_reading(readPort, 0, readCount,
READ_TAG, 0,0,0,0,0,0, replyPort);
if (err != 0)
.  .  .

/* Sit in a message-receive loop. */
while(1) {
/* Set up the reply message.  This must be done inside the
* loop since msg_receive() may change the message header.
*/
replyMsg->msg_size = MSG_SIZE_MAX;
replyMsg->msg_local_port = replyPort;
err = msg_receive(replyMsg, MSG_OPTION_NONE, 0);
if (err != 0)
. . .
}

/* Dispatch the message to the reply handlers.*/
err = snddriver_reply_handler(replyMsg, &replyHandlers);
if (err != 0)
. . .
/* Provide a means to break out of the loop. */
. . .
}
}

As implied by the example, you don't need to tell the sound driver that you want a data-recorded message to be sent to your application; the message is always sent automatically.  The example also illustrates the rule that the reply port used to receive messages while in the msg_receive() loop is that which is specified as the final argument to the snddriver_stream_start_reading() function.

The data type of the recorded_data field dictates the protocol of the function that you design to bring data back to the application.  The type is sndreply_recorded_data_t:

typedef void (*sndreply_recorded_data_t)(void *arg, int tag,
void *kernelData, int size);

The first two arguments, arg and tag, are the same as in the snddreply_tagged_t type.  kernelData is a pointer to the recorded data as it resides in the kernel; size is the size of the recorded data in bytes.

The final three snddriver_handlers fields correspond to messages that are inspired by the DSP:

The condition_true handler is called when a requested DSP host interface register condition comes true.  (More accurately, the handler is called when the message that indicates that the condition is true is passed to snddriver_reply_handler().)
dsp_message handles general messages that the sound driver receives from the DSP.
dsp_error does the same for DSP error messages.

For each of these three handlers, there is a corresponding sound driver function that enqueues a request for a condition, a DSP message, or a DSP error message, respectively:

snddriver_dspcmd_req_condition() blocks the DSP command queue until the state of the DSP host interface registers satisfies a requested condition.
snddriver_dspcmd_req_msg() requests that the messages in the DSP-reply buffer be sent to your application.  You must include DSP-message protocol for this to have an effect.
snddriver_dspcmd_req_err() requests that the 512-byte DSP-reply error buffer be sent in a message.  You must include DSP-error protocol for this to have an effect.

As with the snddriver_stream_start...() functions, the three DSP request functions require that you provide a reply port as an argument.  It's to this reply port that the sound driver sends the requested DSP-inspired messages.  A single call to one of these functions causes a single reply message to be sent to your application.  Thus, for each call to snddriver_dspcmd_req_msg(), for example, your application will receive one message from the sound driver.

The condition_true handler is of type sndreply_dsp_cond_true_t:

typedef void (*sndreply_dsp_cond_true_t)(void *arg, u_int mask,
u_int flags, u_int registers);

arg is the value of the arg field.  The next two arguments, mask and flags, are given the values that were passed to snddriver_dspcmd_req_condition() (which also has mask and flags arguments).  registers encodes the current status of the four DSP host interface registers in a single 32-bit vector.  See the description of snddriver_dspcmd_req_condition() for more information on how this works.

The dsp_message and dsp_error are of type sndreply_dsp_msg_t:

typedef void (*sndreply_dsp_msg_t)(void *arg, int *data, int size);

arg is the value of the arg field.  data is a pointer to the contents of the appropriate DSP-message buffer (regular or error, as the handler is dsp_message or dsp_error).  size is the size of the buffer contents, in bytes.

snddriver_reply_handler() ignores messages for which you haven't created and registered a handler function.

RETURN Returns an error code:  0 on success, nonzero on failure.

SEE ALSO snddriver_stream_start_reading(), snddriver_stream_start_writing(), snddriver_dspcmd_req_condition(), snddriver_dspcmd_req_msg(), snddriver_dspcmd_req_err()



snddriver_set_device_parms(), snddriver_get_device_parms(), snddriver_set_volume(), snddriver_get_volume(), snddriver_set_ramp()

SUMMARY Set and get sound playback attributes

DECLARED IN sound/snddriver_client.h

SYNOPSIS kern_return_t snddriver_set_device_parms(port_t devicePort, boolean_t speakerOn, boolean_t filterOn, boolean_t zerofill)

kern_return_t snddriver_get_device_parms(port_t devicePort, boolean_t *speakerOn, boolean_t *filterOn, boolean_t *zerofill)
kern_return_t snddriver_set_volume(port_t devicePort, int leftVolume, int rightVolume)
kern_return_t snddriver_get_volume(port_t devicePort, int *leftVolume, int *rightVolume)
kern_return_t snddriver_set_ramp(port_t devicePort, int rampOn)

DESCRIPTION These functions set and get attributes of the sound playback system.  Each takes, as its first argument, the sound driver device port as acquired through SNDAcquire().  You needn't acquire ownership of sound-out to set the playback attributes.

The NXSoundOut class can be used in place of all these functions.

snddriver_set_device_parms() sets three attributes as specified by the values of its boolean arguments:

The internal speaker is turned on or off as speakerOn is true or false.  Calling the function with alternating true and false speakerOn values is equivalent to toggling the Mute key (Command Mute) on the keyboard.
Similarly, the value of filterOn turns the de-emphasis filter on or off.  The filter can be controlled from the keyboard by toggling the louder key while holding down the Command key (this isn't marked on the keyboard).  In addition, the de-emphasis filter is automatically turned on when a de-emphasis format sound is played and returned to its previous state when the sound is done playing.
During playback, low sampling rate (22.05 kHz) sounds are converted to the high sampling rate (44.1 kHz) as they are sent to the DAC (which converts data at 44.1 kHz only).  To do this, the sound driver emits an extra sample for every existing sample in the sound data.  The value of zerofill determines whether these extra samples are set to 0 (true) or if they're copies of the existing samples (false).  In almost all cases, copying the samples is preferable, since zerofilling results in a decrease in power.  Note that you can't toggle this attribute from the keyboard.  Also, keep in mind that CODEC rate sounds are converted to 22.05 kHz before being sent to the DAC and so are also affected by the state of zerofill.

snddriver_get_device_parms() returns, by reference in its final three arguments, the values of the attributes described above.

snddriver_set_volume() sets the volume of the internal speaker and similarly adjusts the signal that's sent to the stereo headphone jack (the signal to the line-out jacks is unaffected).  The two channels of the stereo signal are set independent of each other, specified as the values of leftVolume and rightVolume.  The volume of the internal speaker is the sum of these two values.  Volume values are integers in the range 1 to 43, inclusive, where the unit is equal to 2 decibels.  A volume of 1 is inaudible and 43 is full blast.  An argument value outside this range will yield some unexpected volume within the range.  You can also adjust playback volume by pressing the volume keys on the keyboard.  Each discrete tap on a volume key increments or decrements both the left and the right volume settings by 1.

snddriver_get_volume() returns the left and right playback volumes by reference in leftVolume and rightVolume, respectively.

By default, sounds are ramped during playback:  The first few samples are ramped up from zero and the last samples are ramped down.  This helps prevent clicks at the beginnings and ends of sounds.  snddriver_set_ramp() enables or disables this feature as its rampOn argument is nonzero or zero.  You almost always want ramping enabled; the one obvious case in which it's undesirable is if you're chaining a series of separate sounds that are meant to be played seamlessly, one immediately after the other.  In this case, ramping will cause annoying amplitude dips at each seam.

RETURN Returns an error code:  0 on success, nonzero on failure.

SEE ALSO SNDSetVolume(), + setVolume:: (Sound), setAttenuationLeft:right: (NXSoundOut)



snddriver_set_dsp_owner_port(), snddriver_set_sndin_owner_port(), snddriver_set_sndout_owner_port()

SUMMARY Acquire ownership of sound resources

DECLARED IN sound/snddriver_client.h

SYNOPSIS kern_return_t snddriver_set_dsp_owner_port(port_t devicePort, port_t ownerPort, port_t *negotiationPort)

kern_return_t snddriver_set_sndin_owner_port(port_t devicePort, port_t ownerPort, port_t *negotiationPort)
kern_return_t snddriver_set_sndout_owner_port(port_t devicePort, port_t ownerPort, port_t *negotiationPort)

DESCRIPTION These functions try to acquire ownership of the DSP, sound-in, or sound-out by setting the resource's owner port to a port that you supply.  They duplicate part of the functionality provided by SNDAcquire(); the latter should, in most cases, be used to the exclusion of these.

The arguments are the same for all three functions:

devicePort is a valid port to the sound driver device, as acquired through SNDAcquire().
ownerPort is the port that will become the owner port for the requested resource if the function is successful.  You must have already allocated ownerPort through the function port_allocate().
If the function successfully acquires ownership of the resource, then the port pointed to by negotiationPort is registered as the negotiation port for the resource.  However, if the function isn't successful--most likely because ownership of the resource has already been claimed--then the currently registered negotiation port is returned in the negotiationPort argument.  By convention you point negotiationPort to ownerPort before calling these functions, thereby making the owner port accessible to other tasks.  Similarly, if your bid for ownership fails and the current owner has followed this convention, then you can use the port returned in negotiationPort as the owner port for the resource.  Note, however, that if the function call fails, there's no way to determine if the port pointed to by negotiationPort is actually the owner port.  If you want to acquire sole ownership of a resource, set negotiationPort to something other than the ownerPort before calling these functions.  This will ensure that only the caller will have access to the resource (assuming that the function is successful).

A single port can be used to claim ownership of more than one device.  This is sometimes necessary when setting up a multiple-device stream (as explained in snddriver_stream_setup()).  In the following example, the same port attempts to own both the DSP and sound-out:

err = port_allocate(task_self(), &ownerPort)
. . .

/* Acquire ownership of the DSP. */
err=snddriver_set_dsp_owner_port(devPort, ownerPort, &negPort);
. . .

/* Acquire ownership of sound-out. */
err=snddriver_set_sndout_owner_port(devPort,ownerPort,&negPort);

After you've claimed ownership of a resource, you should do something with it.  With sound-in you set up a stream port through which you read (record) data.  This is done by calling the snddriver_stream_setup() and snddriver_stream_start_reading() functions.  Analogously, with sound-out you set up a stream through which you write (playback) data through the snddriver_stream_start_writing() function.

If you claim ownership of the DSP you should also acquire the DSP command port by calling snddriver_get_dsp_cmd_port(). Most of the functions that access the DSP require the command port as an argument.  You can also set up streams to the DSP as you would to sound-in or sound-out.  Successfully setting the DSP's owner port puts the DSP in its reset state.

To relinquish ownership of a resource, you deallocate the owner port by calling port_deallocate():

err = port_deallocate(task_self(), ownerPort);

Deallocating a resource's owner unregisters the resource's negotiation port.  All ports are automatically deallocated when your application exits.

RETURN Returns an error code:  0 on success, nonzero on failure.

SEE ALSO snddriver_stream_setup(), snddriver_get_dsp_cmd_port()



snddriver_set_ramp() See snddriver_set_device_parms()

snddriver_set_sndin_owner_port() See snddriver_set_dsp_owner_port()



snddriver_set_sndout_bufcount(), snddriver_set_sndout_bufsize(), snddriver_stream_ndma()

SUMMARY Configure stream transfer buffers

DECLARED IN sound/snddriver_client.h

SYNOPSIS kern_return_t snddriver_set_sndout_bufcount(port_t devicePort, port_t sndoutPort, int count)

kern_return_t snddriver_set_sndout_bufsize(port_t devicePort, port_t sndoutPort, int size)
kern_return_t snddriver_stream_ndma(port_t streamPort, int regionTag, int count)

DESCRIPTION These functions let you control the number and size of the DMA buffers that are used to transfer data in a stream.

snddriver_set_sndout_bufcount() sets the number of buffers that are used when playing back sounds; the count argument, which must be greater than 0, establishes the buffer count.  Four buffers are used in the default configuration.

snddriver_set_sndout_bufsize() sets the size of the sound-out buffers (in bytes) to the value of the size argument.  This function is needed only if you're using a linked stream to sound-out (see the snddriver_stream_setup() function for more on linked streams).  The value of size must be no greater than vm_page_size, the size of a page of virtual memory; the default is vm_page_size.  If you're writing directly to the sound-out stream--in other words if the stream to sound-out is configured as SNDDRIVER_STREAM_TO_SNDOUT_22 or ...SNDOUT_44--the size of the sound-out buffers is computed from the sampleCount argument to snddriver_stream_setup() and the size set here is ignored.

For both of these functions, the devicePort and sndoutPort arguments are ports to the sound driver device and to sound-out, as acquired through SNDAcquire().

snddriver_stream_ndma() sets, to count, the number of DMA transfer buffers that are used to receive data from sound-in, and to transmit and receive data to and from the DSP.  The DMA buffer count can be set on a region-by-region basis; the stream and region to which a particular setting applies are identified by the streamPort and regionTag arguments, respectively.  Note that in a linked stream to sound-out, the buffer count to and from the DSP is automatically set to the sound-out buffer count (overruling the setting made through this function).

RETURN Returns an error code:  0 on success, nonzero on failure.

SEE ALSO snddriver_stream_setup()



snddriver_set_sndout_bufsize() See snddriver_set_sndout_bufcount()

snddriver_set_sndout_owner_port() See snddriver_set_dsp_owner_port()

snddriver_set_volume() See snddriver_set_device_parms()



snddriver_stream_control(), snddriver_stream_nsamples()

SUMMARY Control and query a stream

DECLARED IN sound/snddriver_client.h

SYNOPSIS kern_return_t snddriver_stream_control(port_t streamPort, int regionTag, int control)

kern_return_t snddriver_stream_nsamples(port_t streamPort, int *byteCount)

DESCRIPTION snddriver_stream_control() provides control over an active stream by allowing you to apply a controlling operation to one or more of the stream's enqueued regions.  The stream and the regions therein are identified by the function's first two arguments:

streamPort is the stream's port, as created by snddriver_stream_setup().
regionTag is the integer identifier that you gave the region (or regions) in a previous call to snddriver_stream_start_writing() or snddriver_stream_start_reading().

A tag value of 0 causes the controlling operation to be applied to all regions enqueued on the stream.

You specify the controlling operation by passing one of the following constants as the control argument:

SNDDRIVER_PAUSE_STREAM causes the stream to pause.  If data is currently being read from or written to the specified region, the read or write is immediately suspended.  If the region isn't yet active, the pause takes effect when the region comes to the top of the stream's queue (it's paused just before the first sample is read or written).
SNDDRIVER_RESUME_STREAM resumes a previously paused stream.
SNDDRIVER_ABORT_STREAM terminates the stream's activity when the specified region comes to the top of the queue; the queue is then cleared.  If the region is currently being acted upon, the stream is terminated immediately.
SNDDRIVER_AWAIT_STREAM is used to retrieve a partially recorded region from a stream that's reading data--normally, you can't retrieve such data until the entire region has been filled.  If the specified region is currently active, a data-recorded message is sent to the reply port that you registered in snddriver_stream_start_reading().  You then pass the message to snddriver_reply_handler() which calls the recorded_data reply handler.  The unrecorded portion of the region continues.  If the specified region isn't currently active, this has no effect.

While you can use any of these four at the same time by or'ing them in control, the only combination that's of use is SNDDRIVER_AWAIT_STREAM or'd with one of the other three.  For example, by setting regionTag to 0 and control to

SNDDRIVER_PAUSE_STREAM | SNDDRIVER_AWAIT_STREAM

you immediately pause the stream and can then bring back data from the current region.

You can request that a stream-paused, stream-resumed, or stream-aborted message be sent to the reply port when you pause, resume, or abort a stream, respectively, by setting the appropriate msg... flag to true in your call to snddriver_stream_start_...().

snddriver_stream_nsamples() returns the number of bytes (not samples, despite the name of the function) that have been read from or written to a particular stream.  The steam is specified by streamPort.  The byte count is returned by reference in the byteCount argument.

RETURN Returns an error code:  0 on success, nonzero on failure.

SEE ALSO snddriver_stream_setup(), snddriver_stream_start_writing(), snddriver_stream_start_writing(), snddriver_reply_handler()



snddriver_stream_nsamples() See snddriver_stream_control()



snddriver_stream_setup()

SUMMARY Configure a sound stream

DECLARED IN sound/snddriver_client.h

SYNOPSIS kern_return_t snddriver_stream_setup(port_t devicePort, port_t ownerPort, int dataPath, int sampleCount, int sampleSize, int lowWater, int highWater, int *protocol, port_t *streamPort)

DESCRIPTION A stream, as it applies to the sound driver, is a path through which an indefinitely long sequence of data passes.  One end of a sound driver stream typically lies in your application's memory, while at the other end is a sound device.  For example, to record a sound from the microphone you create a stream from sound-in to your application.  Analogously, a stream from your application to sound-out is required to play back sound data.  A single stream of data can pass through more than one sound device; for example, you can send data from your application to the DSP from whence it issues directly to sound-out.  Thus you can DSP-process and play your sound data in one motion, without incurring the overhead of bringing the processed data back into your application.

The snddriver_stream_setup() function creates a port to a sound stream.  The port, returned in the streamPort argument, is used as an identifier in subsequent calls to functions that write to, read from, and otherwise control the stream (as listed at the end of this description).

The function's first two arguments are the usual capability ports:  devicePort is a port to the sound driver device, and ownerPort is the owner port for all resources that are touched by the stream, as acquired through SNDAcquire().

You establish the stream's course--the source and destination of its data--by setting dataPath to one of constants listed below. There are two types of data paths:  "simple" and "linked."  The simple data paths (listed below) connect your application to a sound resource:

SNDDRIVER_STREAM_FROM_SNDIN; read samples from the CODEC microphone.
SNDDRIVER_STREAM_TO_SNDOUT_44; write samples to the stereo DAC at the high sampling rate (44.1 kHz).
SNDDRIVER_STREAM_TO_SNDOUT_22; write samples to the stereo DAC at the low sampling rate (22.05 kHz).
SNDDRIVER_DMA_STREAM_TO_DSP; write data via DMA to the DSP.
SNDDRIVER_DMA_STREAM_FROM_DSP; read data via DMA from the DSP.

Six linked data paths connect the DSP directly to sound-out:

SNDDRIVER_STREAM_DSP_TO_SNDOUT_44 and ...SNDOUT_22; DSP-processed samples are sent directly to sound-out at the low or high sampling rate.
SNDDRIVER_STREAM_THROUGH_DSP_TO_SNDOUT_44 and ...SNDOUT_22; data flows from your application to the DSP and thence directly to sound-out at the high or low sampling rate.
SNDDRIVER_DMA_STREAM_THROUGH_DSP_TO_SNDOUT_44 and ...SNDOUT_22; data flows from your application to the DSP and thence directly to sound-out using DMA.

Data is transferred through a stream in buffers.  The sampleCount argument establishes the length of a single transfer buffer in samples (or data elements); the size of a single sample is set by the sampleSize argument.  The maximum size for a transfer buffer (in bytes) is that of a page of virtual memory, as given by the global read-only variable vm_page_size.  Typically, the transfer buffer size is set to this limit:  If, for example, the samples that you're sending through the stream are two bytes wide, then, to follow this convention, you would set sampleCount to vm_page_size/2.  If the stream uses DMA, then the size of a transfer buffer (in bytes) must be a power of 2 greater than or equal to 16.

For some applications--particularly those in which latency is an issue--setting the number of transfer buffers that are used can be as important as setting the size of the buffers.  This is done through the snddriver_set_sndout_bufcount() and snddriver_stream_ndma() functions.

The range of acceptable values for the sampleSize argument depends on the stream's data path:

If you're reading from sound-in into your application, then sampleSize must be set to 1 to accommodate the 8-bit mu-law samples generated by the CODEC microphone input.
If you're writing from your application to sound-out or from the DSP to sound-out , then sampleSize must be 2 since the DAC expects 16-bit interleaved-stereo samples.  Note that while the DAC processes data only at the high sampling rate, the sound driver performs the conversion from low to high for you.  This isn't true for playback of CODEC-rate sounds for which you typically download a sampling-rate conversion program to the DSP, and then create a stream that goes through the DSP and then directly to sound-out.  This is what the SNDStartPlaying() function does, for example.
In all the other paths, your application writes to or reads from the DSP.  Here, sampleSize can be 1, 2, or 4, according to the sample size expected by or produced by your DSP program.

The lowWater and highWater arguments are memory threshold values, measured in bytes, that are inspected by the sound driver.  During an operation such as recording or playback, successive pages of sound data are locked into physical memory (or "wired down") during which time they're read from or written to.  As a page is completed, it's unwired.  The driver tries to maintain at least lowWater bytes of wired-down memory; if the amount drops below this threshold, the driver wires down pages until it reaches the highWater mark.

If your stream touches the DSP, then you need to set the DSP protocol by passing the appropriate value to snddriver_dsp_protocol().  The protocol argument found here helps you create this value:  The function or's the appropriate protocol constants, as determined by the characteristics of the stream that you're setting up, into protocol and returns the new value by reference.  You then pass the variable to snddriver_dsp_protocol().  You should initialize your protocol variable to SNDDRIVER_DSP_PROTO_RAW before calling snddriver_stream_setup(), as shown in the following example:

/* Initialize the protocol variable. */
int protocol = SNDDRIVER_DSP_PROTO_RAW;
int err;

/* Set up a stream to the DSP. */
err = snddriver_stream_setup(..., SNDDRIVER_STREAM_TO_DSP,
.., &protocol, ...);
if (err != 0)
. . .

/* Set up a stream from the DSP. */
err = snddriver_stream_setup(..., SNDDRIVER_STREAM_FROM_DSP,
..., &protocol, ...);
if (err != 0)
. . .

/* Pass the protocol to the sound driver. */
err = snddriver_dsp_protocol(..., protocol);
if (err != 0)
. . .

The protocol constants are described as part of the snddriver_dsp_protocol() function.

Having created a stream, you can read from it, write to it, and control it by passing the port returned in streamPort to the following functions:

snddriver_stream_start_reading() and snddriver_stream_start_writing() read from and write to a stream, respectively. Streams from sound-in or from the DSP can only be read; similarly, streams to sound-out or to the DSP can only be written.
snddriver_stream_control() pauses, resumes, and aborts an active stream.
snddriver_stream_nsamples() measures the amount of data that has passed through the stream.

For sound-in and sound-out, streams are the only way to travel.  This isn't true of the DSP; the sound driver provides a one-shot, non-stream DSP read and write mechanism, embodied in snddriver_dsp_read(), snddriver_dsp_dma_read(), and analogous ...write() functions, that can be more efficient for short data transfers.

RETURN Returns an error code:  0 on success, nonzero on failure.

SEE ALSO snddriver_stream_start_reading(), snddriver_stream_start_writing(), snddriver_set_sndout_bufcount(), snddriver_stream_ndma()



snddriver_stream_start_reading() See snddriver_stream_start_writing()



snddriver_stream_start_writing(), snddriver_stream_start_reading()

SUMMARY Send data to and retrieve data fom a stream

DECLARED IN sound/snddriver_client.h

SYNOPSIS kern_return_t snddriver_stream_start_writing(port_t streamPort, void *data, int sampleCount, int regionTag, boolean_t preempt, boolean_t deallocateWhenDone, boolean_t msgStarted, boolean_t msgCompleted, boolean_t msgAborted, boolean_t msgPaused, boolean_t msgResumed, boolean_t msgUnderrun, port_t replyPort)

kern_return_t snddriver_stream_start_reading(port_t streamPort, char *filename, int sampleCount, int regionTag, boolean_t msgStarted, boolean_t msgCompleted, boolean_t msgAborted, boolean_t msgPaused, boolean_t msgResumed, boolean_t msgOverrun, port_t replyPort)

DESCRIPTION These two functions cause data to be written to or read from a sound stream identified by streamPort, which must have been created by a previous call to snddriver_stream_setup().  The two functions operate in much the same manner:  Each invocation enqueues a single region of data that's operated on (either read from or written to) asynchronously by the sound driver.  However, there's a fundamental difference between the two functions in that ...writing() enqueues a region that you pass as the data argument, while ...reading() stores the data it reads in a region that it allocates itself.  To bring the read data back into your application, you must create and register a reply-handler function that transfers the data when the read is complete.  The mechanism for doing this is explained (and an example given) in the snddriver_reply_handler() function description.

Note:  The ...reading() argument filename--which would imply that the read data is written to a file--is currently unused. Also note that the data buffer you pass to ...writing() is write-protected:  Any changes you make to the data after it's been enqueued won't be seen by the driver.

sampleCount is the number of samples in the region that's being written or read.  If you're writing to the DSP, sampleCount must be a multiple of the sampleCount argument to snddriver_stream_setup().  In all other cases, sampleCount can be any value.

regionTag is an integer that identifies the region.  While you can give each region a distinct tag, you usually create a single tag value for each stream that you set up.  For example, if you have a stream that reads data from sound-in and another that writes to sound-out, you would create two tag values, one for either stream, and then tag each region with the value associated with its stream.

If the preempt flag (...writing() only) is true, the sound driver starts writing data immediately after the current transfer buffer has been completely processed.  When it's finished with the preempting region, the driver returns to its region queue, disregarding the rest of the partially-processed preempted region.

If deallocateWhenDone (...writing() only) is true, the region's data is deallocated after it's written.

The six msg... flags register requests for stream-state messages to be sent asynchronously to the port replyPort.  The first flags, msgStarted and msgCompleted, if true, cause messages to be sent just as the driver begins its first and just after it finishes its last transfer of data from the region, respectively.  The conditions referred to by the next three arguments, msgAborted, msgPaused, and msgResumed, occur as a result of calls to snddriver_stream_control().  The msgUnderrun (for ...writing()) or msgOverrun (for ...reading()) argument, if true, causes a message to be sent if the driver can't transfer data quickly enough to keep up with real time.  In general this is only signficant if data is being read from sound-in or written to sound-out:  Underrun results in brief pauses in playback; overrun causes incoming samples to be lost.  You normally process the asynchronous messages that you receive by passing them to the snddriver_reply_handler() function.

RETURN Returns an error code:  0 on success, nonzero on failure.

SEE ALSO snddriver_reply_handler, snddriver_stream_setup