This is DACPlayer.m in view mode; [Download] [Up]
/* * DACPlayer.m * Implementation of an object to play sound over the soundout device. * Author: Robert D. Poor, NeXT Technical Support * Copyright 1989 NeXT, Inc. Next, Inc. is furnishing this software * for example purposes only and assumes no liability for its use. * * Edit history (most recent edits first) * * 06-Dec-89 Rob Poor: Created. * * End of edit history */ #import <mach.h> #import <stdio.h> #import <servers/netname.h> #import <appkit/Application.h> #import <appkit/Panel.h> #import <sound/accesssound.h> #import <sound/sounddriver.h> #import <sound/soundstruct.h> #import "DACPlayer.h" #import "errors.h" extern int done; /* * HandleDACMessage is called courtesy of the DPSAddPort mechanism * whenever a message arrives from the sound driver. userData is bound * to the DACPlayer object itself. */ static void HandleDACMessage(msg_header_t *msg, void *userData) { int k_err; snddriver_handlers_t *handlers; handlers = [(DACPlayer *)userData msgHandlers]; /* handlers = (DACPlayer *)userData->msgHandlers; */ k_err = snddriver_reply_handler(msg, handlers); check_snddriver_error(k_err,"Cannot parse message from DAC"); } /* * Following are the routines that snddriver_reply_handler will * dispatch to. Note that in each case, the DACPlayer object itself * will be passed as the first argument. */ static void DACCompletedMsg(void *arg, int tag) { [(id)arg handleCompleted]; } /* * The dispatch table that snddriver_reply_handler uses. */ const static snddriver_handlers_t dacHandlers = { (void *)0, (int) 0, NULL, /* DACStartedMsg */ DACCompletedMsg, NULL, /* DACAbortedMsg */ NULL, /* DACPausedMsg */ NULL, /* DACResumedMsg */ NULL, /* DACOverflowMsg */ NULL, NULL, NULL, NULL }; @implementation DACPlayer:Player /* * Create a new DACPlayer object. */ + new { self = [super new]; msgHandlers = dacHandlers; /* Copy the static structure */ msgHandlers.arg = self; /* Install self as arg to handler functions */ samplingRate = SND_RATE_HIGH; return self; } - prepare { port_t arbitration_port; int i, r, protocol; [self stop]; /* make sure playing has stopped first. */ /* get the device port for the sound driver on the local machine */ r = netname_look_up(name_server_port,"","sound", &devicePort); check_mach_error(r,"netname lookup failure"); /* try to become owner of the sound out resource */ r = port_allocate(task_self(), &ownerPort); check_mach_error(r,"Cannot allocate owner port"); arbitration_port = ownerPort; r = snddriver_set_sndout_owner_port(devicePort,ownerPort,&arbitration_port); check_snddriver_error(r,"Cannot become owner of sound-out resources"); /* * Tell the delegate (if any) that we are about to start playing. * Call it here in case the delegate wants to set the samplingRate. */ if (delegate && [delegate respondsTo:@selector(willPlay:)]) { [delegate willPlay :self]; } /* set up the DMA read stream */ protocol = 0; r = snddriver_stream_setup(devicePort, ownerPort, ((samplingRate == SND_RATE_HIGH)? SNDDRIVER_STREAM_TO_SNDOUT_44: SNDDRIVER_STREAM_TO_SNDOUT_22), READ_BUF_SIZE, BYTES_PER_16BIT, LOW_WATER, HIGH_WATER, &protocol, /* ignored for sndout */ &streamPort); check_snddriver_error(r,"Cannot set up stream to sound-out"); /* allocate a port for the replies */ r = port_allocate(task_self(),&replyPort); check_mach_error(r,"Cannot allocate reply port"); /* Start the DMA stream in a paused state */ r = snddriver_stream_control(streamPort,0,SNDDRIVER_PAUSE_STREAM); check_snddriver_error(r,"can't do initial pause"); /* * Queue up the initial DMA buffers before starting. */ /* * Setting the state to PLA_RUNNING is a hack for the benefit of * updateStream. Currently, updateStream will enqueue a region iff * the state = PLA_RUNNING. We use this mechanism so that the * delegate can stop the player from within the playData::: method. * Perhaps we should design a better way... */ playerState = PLA_RUNNING; bytesPlayed = 0; regionIndex = 0; for (i=0;i<regionCount;i++) { [self updateStream]; } playerState = PLA_PAUSED; DPSAddPort(replyPort, HandleDACMessage, /* function to call */ MSG_SIZE_MAX, self, /* first arg to HandleDACMessage */ NX_RUNMODALTHRESHOLD /* priority */ ); return self; } - run { int r; if (playerState == PLA_RUNNING) { return nil; } else if (playerState == PLA_STOPPED) { [self prepare]; } /* else playerState == PLA_PAUSED */ /* Resume the DMA stream. */ r = snddriver_stream_control(streamPort,0,SNDDRIVER_RESUME_STREAM); check_snddriver_error(r,"Can't resume the DMA stream"); playerState = PLA_RUNNING; return self; } - pause { int r; if (playerState == PLA_PAUSED) { return nil; } else if (playerState == PLA_STOPPED) { return [self prepare]; } /* else playerState == PLA_RUNNING */ r = snddriver_stream_control(streamPort, WRITE_TAG, SNDDRIVER_PAUSE_STREAM); check_snddriver_error(r,"Call to pause stream failed"); playerState = PLA_PAUSED; return self; } - stop { int r; if (playerState == PLA_STOPPED) return nil; playerState = PLA_STOPPED; /* flush any outstanding buffers */ r = snddriver_stream_control(streamPort, WRITE_TAG, SNDDRIVER_ABORT_STREAM); check_snddriver_error(r,"Couldn't abort stream"); DPSRemovePort(replyPort); r = port_deallocate(task_self(),ownerPort); check_mach_error(r,"Couldn't deallocate ownerPort"); /* * Tell the delegate (if any) that we stopped playing. */ if (delegate && [delegate respondsTo:@selector(didPlay:)]) { [delegate didPlay :self]; } return self; } /* * Get and Set the sampling rate. */ - (double)samplingRate { return samplingRate; } - setSamplingRate :(double)sampling_rate { if ((sampling_rate == SND_RATE_LOW) || (sampling_rate == SND_RATE_HIGH)) { samplingRate = sampling_rate; } else { NXRunAlertPanel("Alert", "sampling rate must be %f or %f", NULL,NULL,NULL,SND_RATE_LOW,SND_RATE_HIGH); samplingRate = SND_RATE_LOW; } return self; } - wait { msg_header_t *reply_msg; int k_err; reply_msg = (msg_header_t *)malloc(MSG_SIZE_MAX); reply_msg->msg_size = MSG_SIZE_MAX; reply_msg->msg_local_port = replyPort; while(!done) { 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); } [self updateStream]; } } /* * Internal methods. */ - (snddriver_handlers_t *)msgHandlers { return &msgHandlers; } /* * handleCompleted is called whenever we get a completed message * from the driver. */ - handleCompleted { return [self updateStream]; } /* * updateStream is called whenever we get a 'region completed' message from * the driver. (It is also called at initialization time.) updateStream * allocates a buffer, passes the buffer to the delegate, and enqueue the * buffer in the write stream. */ - updateStream { int r; char *currentRegion; currentRegion = (char *)regions[regionIndex++]; if (regionIndex == regionCount) regionIndex = 0; bytesPlayed += regionSize; /* let the delegate have its way with the buffer */ if (delegate && [delegate respondsTo:@selector(playData:::)]) { [delegate playData :self :currentRegion :regionSize]; } /* The delegate may have stopped the recorder. Quit now if so... */ if (playerState != PLA_RUNNING) return nil; /* enqueue the buffer in the DMA stream to the DACs */ r = snddriver_stream_start_writing (streamPort, /* port */ currentRegion, /* data to be played */ regionSize/sizeof(short), /* number of samples to play */ WRITE_TAG, /* tag for this region */ FALSE, /* preempt */ FALSE, /* deallocate on completion*/ FALSE, /* send msg when started */ TRUE, /* send msg when completed */ FALSE, /* send msg when aborted */ FALSE, /* send msg when paused */ FALSE, /* send msg when resumed */ FALSE, /* send msg when overflowed */ replyPort /* port for the above messages */ ); check_snddriver_error(r,"Cannot enqueue write request"); return self; } @end
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.