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.