ftp.nice.ch/pub/next/unix/audio/sndutil.1.3.s.tar.gz#/sndutil-1.3/sndtools/dsptosnd.c

This is dsptosnd.c in view mode; [Download] [Up]

/*
 * dsptosnd.c
 *	Convert a DSP commands file to a 16-bit linear soundfile.
 */
#import <libc.h>
#import <c.h>
#import <mach/mach.h>
#import <mach/mach_init.h>
#import <mach/mach_error.h>
#import <mach/cthreads.h>
#import <stdlib.h>
#import <stdio.h>
#import <sound/sound.h>
#import <sound/sounddriver.h>
#import <sound/snddriver_client.h>

#define READ_TAG 1
#define WRITE_TAG 2
#define FLUSH_TAG 3
#define READ_BUFFER_SIZE 4096	/* samples */

#define VIEW_SIZE 4

static int done = 0;
static unsigned short *read_data;
static int total_bytes_read = 0;
static int bytes_read = 0;
static int sd_err, s_err;
static int read_count = 0;
static int skipping_zeros = 1;

typedef enum {
	SD_NO_ERROR	= 100,	// non-error ack.
	SD_BAD_PORT	= 101,	// message sent to wrong port
	SD_BAD_MSG	= 102,	// unknown message id
	SD_BAD_PARM	= 103,	// bad parameter list in message
	SD_NO_MEMORY	= 104,	// can't allocate memory (record)
	SD_PORT_BUSY	= 105,	// access req'd to existing excl access port
	SD_NOT_OWNER	= 106,	// must be owner to do this
	SD_BAD_CHAN	= 107,	// dsp channel hasn't been inited
	SD_SEARCH	= 108,	// couldn't find requested resource
	SD_NODATA	= 109,	// can't send data commands to dsp in this mode
	SD_NOPAGER	= 110,	// can't allocate from external pager (record).
	SD_NOTALIGNED	= 111,	// bad data alignment.
	SD_BAD_HOST_PRIV = 112,	// bad host privilege port passed.
	SD_BAD_PROTO 	= 113	// can't do requested operation in cur protocol
} SDError;

#define SD_MAX_ERROR 	113

static char *sd_error_list[] = {
	"sound success",
	"sound message sent to wrong port",
	"unknown sound message id",
	"bad parameter list in sound message",
	"can't allocate memory for recording",
	"sound service in use",
	"sound service requires ownership",
	"DSP channel not initialized",
	"can't find requested sound resource",
	"bad DSP mode for sending data commands",
	"external pager support not implemented",
	"sound data not properly aligned",
	"bad host provilege port passed",
	"can't do requested operation in extant DSP protocol"
};

/*
 * Error code to string conversion
 */
extern char *mach_error_string();

char *sd_error_string(int error)
{
    if (error <= 0)
      return mach_error_string(error);
    else if (error >= SD_NO_ERROR && error <= SD_MAX_ERROR)
      return sd_error_list[error-SD_NO_ERROR];
    else
      return SNDSoundError(error);
}

void sd_error(char *msg,int error)
{
    char *errtype;
    if (error <= 0)
      errtype = "mach";
    else if (error >= SD_NO_ERROR && error <= SD_MAX_ERROR)
      errtype = "snddriver";
    else
      errtype = "sound";
    fprintf(stderr,"%s\n\t%s error:%s\n",msg,errtype,
	    sd_error_string(error));
}

void sd_error_fail(char *msg,int sd_err) 
{
    if (sd_err != 0) {
	sd_error(msg, sd_err);
	exit(1);
    }
}

static void read_completed(void *arg, int tag, void *p, int nbytes)
/* This gets called when the entire result array has been read from the DSP. */
{
    if (tag == READ_TAG) {
	int i;
	read_data = (unsigned short *)p;
#ifdef DEBUG
	printf("got a sound buffer from the DSP\n");
#endif
	if (skipping_zeros) {
	    for (i=0; i<nbytes/2; i++) {
		if (read_data[i] != 0) {
		    skipping_zeros = 0;
		    bytes_read = nbytes - i*2;
		    total_bytes_read += bytes_read;
		    break;
		}
	    }
	    return;
	}
	bytes_read = nbytes;
	total_bytes_read += bytes_read;
	if (total_bytes_read >= read_count*sizeof(short)
	    && read_data[READ_BUFFER_SIZE-1] == 0
	    && read_data[READ_BUFFER_SIZE-2] == 0
	    && read_data[READ_BUFFER_SIZE-3] == 0
	    && read_data[READ_BUFFER_SIZE-4] == 0)
	  done++;
	/* The test for trailing zeros is a bit of a crock, but 
	   it seems to be necessary.  If the DSP does not 
	   revert to sending zeros, we're hosed. (It always should.) */
    }
}

typedef struct {
    int	sampleCount;
    int	dspBufSize;
    int	soundoutBufSize;
    int	reserved;
} commandsSubHeader;

#define	DSP_COMMANDS_SEND_TIMEOUT (1000)

static int s_kill_dsp_commands_thread;

typedef struct {
    port_t cmd_port;
    msg_header_t *message;
    int size;
} dsp_commands_struct;

static any_t dsp_commands_thread(any_t args)
{
    int err;
    int count = 0;
    msg_header_t *msg;
    dsp_commands_struct *info;
    
    info = (dsp_commands_struct *)args;
    msg = info->message;
    while (count < info->size) {
        msg->msg_remote_port = info->cmd_port;
        msg->msg_local_port = PORT_NULL;
        err = msg_send(msg, SEND_TIMEOUT, DSP_COMMANDS_SEND_TIMEOUT);
	while (err == SEND_TIMED_OUT) {
	    if (s_kill_dsp_commands_thread)
	        break;
            err = msg_send(msg, SEND_TIMEOUT, DSP_COMMANDS_SEND_TIMEOUT);
	}
#ifdef DEBUG
	if (err != KERN_SUCCESS)
            printf("dsp commands thread msg_send error %d\n", err);
#endif
        if (s_kill_dsp_commands_thread)
	    break;
        count += msg->msg_size;
        msg = (msg_header_t *) ((char *)msg + msg->msg_size);
    }
    free(args);
    cthread_exit(0);
    return NULL;
}

static int play_dsp_commands(int tag, SNDSoundStruct *s,
			     port_t cmd_port, port_t reply_port)
{
    dsp_commands_struct *args;
    int err;
    
    args = (dsp_commands_struct *)malloc(sizeof(dsp_commands_struct));
    if (!args)
        return SND_ERR_KERNEL;
    
    args->cmd_port = cmd_port;
    args->message = (msg_header_t *) ((char *)s + s->dataLocation +
				      sizeof(commandsSubHeader));
    args->size = s->dataSize - sizeof(commandsSubHeader);
    s_kill_dsp_commands_thread = FALSE;
    cthread_detach(cthread_fork(dsp_commands_thread, (any_t)args));
    err = snddriver_dspcmd_req_msg(cmd_port, reply_port);
    return err? SND_ERR_CANNOT_PLAY : SND_ERR_NONE;
}

static void fail(char *s, char *s2)
{
    printf("*** %s%s\n\n",s,s2);
    exit(1);                        /* Exit, indicating error */
}

static int s_allocPort(port_t *portP)
/* 
 * Allocate Mach port.
 */
{
    int ec;
    ec = port_allocate(task_self(), portP);
    if (ec != KERN_SUCCESS)
      fail("port_allocate failed",NULL);
    return 0;
}

void main(int argc,char **argv) {
    SNDSoundStruct *sndin, *sndout;
    short *outPtr, *ptr;
    FILE *outstream;
    int outCount, width, nChans, nSampsToWrite;
    port_t dsp_dev_port=0, dsp_owner_port=0, dsp_cmd_port=0, dsp_reply_port=0;
    port_t dsp_read_port=0;
    commandsSubHeader *subheader;
    int dmasize, required_access, priority, preempt;

    int protocol;
    kern_return_t k_err;
    snddriver_handlers_t handlers = { 0, 0, 0, 0, 0, 0, 0, 0, read_completed};
    msg_header_t *reply_msg;
    int low_water = 0;
    int high_water = 0;
    int read_width = 2;/* bytes per sample */
    
    s_allocPort(&dsp_reply_port);

    if (argc != 3) {
	fprintf(stderr,
		"%s - convert a DSP commands file to a 16-bit linear soundfile.\n",
		argv[0]);
	fprintf(stderr, "Usage: dsptosnd dspcommands.snd soundfile.snd\n");
	exit(1);
    }

    if (SNDReadSoundfile(*++argv,&sndin))
      fail("could not open input dsp commands file ",*argv);
    if (sndin->dataFormat != SND_FORMAT_DSP_COMMANDS)
      fail("Can only accept dsp-commands format soundfiles on input","");
    subheader = (commandsSubHeader *)((char *)sndin + sndin->dataLocation);
    nChans = sndin->channelCount;
    
    SNDAlloc(&sndout,
	     subheader->sampleCount * sizeof(short),
	     SND_FORMAT_LINEAR_16,
	     sndin->samplingRate,
	     nChans,4);

    SNDGetDataPointer(sndout, (char **)(&outPtr), &outCount, &width);
    outCount /= nChans;		/* to get sample frames */
    
    if (NULL == (outstream = fopen(*++argv,"w")))
      fail("could not open output file ",*argv);
    if (SNDWriteHeader(fileno(outstream),sndout))
      fail("could not write output file ",*argv);
    
    /*
     * get the device port for the sound/dsp driver on the local machine
     * and try to become owner of the dsp resource.  Note that it fails
     * if you are logged into a remote machine which is not a "public sound
     * server" (as set in the UNIX section of the Preferences application).
     */
    required_access = SND_ACCESS_DSP;
    priority = 0;
    preempt = 0;
    s_err = SNDAcquire(required_access, priority, preempt,
			-1, NULL_NEGOTIATION_FUN, (void *)0,
			&dsp_dev_port, &dsp_owner_port);
    if (s_err != 0) {
	fprintf(stderr,"*** Could not acquire DSP\n");
	exit(1);
    }

    /*
     * get the command port
     */
    if(snddriver_get_dsp_cmd_port(dsp_dev_port,dsp_owner_port,&dsp_cmd_port))
      fail("Could not get the DSP command port",NULL);

    dmasize = subheader->dspBufSize; 

    /*
     * set up the reading and writing streams, and set the DSP protocol
     */
    protocol = SNDDRIVER_DSP_PROTO_DSPMSG;

    sd_err = snddriver_stream_setup(dsp_dev_port, dsp_owner_port,
				     SNDDRIVER_DMA_STREAM_FROM_DSP,
				     dmasize, read_width,
				     low_water, high_water,
				     &protocol, &dsp_read_port);
    sd_error_fail("Cannot set up stream from DSP", sd_err);
    
    sd_err = snddriver_dsp_protocol(dsp_dev_port, dsp_owner_port, protocol);
    sd_error_fail("Cannot set up DSP protocol", sd_err);

    /*
     * allocate a port for the replies
     */
    k_err = port_allocate(task_self(),&dsp_reply_port);
    if (k_err != KERN_SUCCESS) {
	mach_error("Cannot allocate reply port", k_err);
	exit(1);
    }

    sd_err = snddriver_dsp_reset(dsp_cmd_port, SNDDRIVER_HIGH_PRIORITY);
    sd_error_fail("Cannot reset DSP", sd_err);

    if(play_dsp_commands(0, sndin, dsp_cmd_port, dsp_reply_port)) 
      fail("Could not play DSP commands file",NULL);

/*** FIXME: The DSP boots up sending zeros to sound out in order to
  fill up the soundout buffers.  Unfortunately, we have no soundout
  buffers in this situation and the leading zeros end up going to disk.
  The workaround here is to discard zeros until data is seen. This means
  you get a different result using "playscore -w".
***/

    skipping_zeros = 1;
    read_count = subheader->sampleCount;

    /*
     * enqueue region read request, asking only for a completion message
     */
    sd_err = snddriver_stream_start_reading(dsp_read_port,0,
					    READ_BUFFER_SIZE,
					    READ_TAG,
					    0,1,0,0,0,0, dsp_reply_port);
    sd_error_fail("Cannot enqueue read request", sd_err);
    /*
     * enqueue second region read request (for double buffering).
     */
    sd_err = snddriver_stream_start_reading(dsp_read_port,0,
					    READ_BUFFER_SIZE,
					    READ_TAG,
					    0,1,0,0,0,0, dsp_reply_port);
    sd_error_fail("Cannot enqueue read request", sd_err);

    /*
     * receive reply messages (the completion messages) and dispatch.
     */
    reply_msg = (msg_header_t *)malloc(MSG_SIZE_MAX);
    done = 0;

    while (!done) {

	/*
	 * We now sleep in msg_receive() until all data is received 
	 * from the DSP.
	 */

	reply_msg->msg_size = MSG_SIZE_MAX;
	reply_msg->msg_local_port = dsp_reply_port;
	k_err = msg_receive(reply_msg, MSG_OPTION_NONE, 0);
	if (k_err != KERN_SUCCESS) {
	        mach_error("Cannot receive message from DSP", k_err);
		    exit(1);
	    }
    
	/*
	 * dispatch to the appropriate handler
	 */
	sd_err = snddriver_reply_handler(reply_msg,&handlers);
	sd_error_fail("Cannot parse message from DSP", sd_err);

	if (bytes_read > 0) {
	  nSampsToWrite = bytes_read / sizeof(short);
	  ptr = read_data + READ_BUFFER_SIZE - nSampsToWrite;
	  SNDSwapHostToSound(ptr, ptr, nSampsToWrite / nChans,
			     nChans, SND_FORMAT_LINEAR_16);
	  fwrite(&read_data[READ_BUFFER_SIZE - nSampsToWrite],
		 sizeof(short), nSampsToWrite, outstream);
	}

	/*
	 * Queue up the next read.
	 */
	sd_err = snddriver_stream_start_reading(dsp_read_port,0,
						READ_BUFFER_SIZE,
						READ_TAG,
						0,1,0,0,0,0, dsp_reply_port);
	sd_error_fail("Cannot enqueue read request", sd_err);

    }
    
    
    SNDFree(sndin);
    fclose(outstream);
    SNDFree(sndout);
    
    /*
     * Data received in the read_handler should be deallocated
     */
    vm_deallocate(task_self(),(pointer_t)read_data,bytes_read);
    
    exit(0);
}


These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.