コード例 #1
0
ファイル: audio_nas.c プロジェクト: AzagraMac/PS2_SDK
static
int config(struct audio_config *config)
{
  unsigned int bitdepth;
  int format = 0;
  AuDeviceID device;
  AuElement elements[2];

  bitdepth = config->precision & ~7;
  if (bitdepth == 0 || bitdepth > 16)
    bitdepth = 16;

  switch (config->precision = bitdepth) {
  case 8:
    format    = AuFormatLinearSigned8;
    audio_pcm = audio_pcm_s8;
    break;

  case 16:
# if defined(WORDS_BIGENDIAN)
    format    = AuFormatLinearSigned16MSB;
    audio_pcm = audio_pcm_s16be;
# else
    format    = AuFormatLinearSigned16LSB;
    audio_pcm = audio_pcm_s16le;
# endif
    break;
  }

  device = getdevice(server, &config->channels);
  if (device == AuNone) {
    audio_error = _("could not find an output device");
    return -1;
  }

  AuMakeElementImportClient(&elements[0], config->speed, format,
			    config->channels, AuTrue,
			    config->speed * 2, config->speed, 0, 0);
  AuMakeElementExportDevice(&elements[1], 0, device, config->speed,
			    AuUnlimitedSamples, 0, 0);
  AuSetElements(server, flow, AuTrue, 2, elements, 0);

  AuRegisterEventHandler(server, AuEventHandlerIDMask, 0, flow,
			 eventhandler, 0);

  AuStartFlow(server, flow, 0);

  return 0;
}
コード例 #2
0
static int nas_open(WINE_WAVEOUT* wwo) {
    AuElement elements[3];

    if (!wwo->AuServ)
       return 0;

    if (!nas_finddev(wwo))
       return 0;

    if (!(wwo->AuFlow = AuCreateFlow(wwo->AuServ, NULL)))
       return 0;

    wwo->BufferSize = FRAG_SIZE * FRAG_COUNT;

    AuMakeElementImportClient(&elements[0], wwo->format.wf.nSamplesPerSec,
           wwo->format.wBitsPerSample == 16 ? AuFormatLinearSigned16LSB : AuFormatLinearUnsigned8,
           wwo->format.wf.nChannels, AuTrue, wwo->BufferSize, wwo->BufferSize / 2, 0, NULL);

    AuMakeElementExportDevice(&elements[1], 0, wwo->AuDev, wwo->format.wf.nSamplesPerSec,
                              AuUnlimitedSamples, 0, NULL);

    AuSetElements(wwo->AuServ, wwo->AuFlow, AuTrue, 2, elements, NULL);

    AuRegisterEventHandler(wwo->AuServ, AuEventHandlerIDMask, 0, wwo->AuFlow,
                           event_handler, (AuPointer) wwo);


    wwo->PlayedTotal = 0;
    wwo->WrittenTotal = 0;
    wwo->open = 1;

    wwo->BufferUsed = 0;
    wwo->writeBytes = 0;
    wwo->freeBytes = 0;
    wwo->sendBytes = 0;
    wwo->SoundBuffer = NULL;
    wwo->FlowStarted = 0;

    AuStartFlow(wwo->AuServ, wwo->AuFlow, NULL);
    AuPauseFlow(wwo->AuServ, wwo->AuFlow, NULL);
    wwo->FlowStarted = 1;

    return 1;
}
コード例 #3
0
ファイル: SDL_nasaudio.c プロジェクト: bohwaz/ozex
static int NAS_OpenAudio(_THIS, SDL_AudioSpec *spec)
{
	AuElement elms[3];
	int buffer_size;
	Uint16 test_format, format;

	this->hidden->mixbuf = NULL;

	/* Try for a closest match on audio format */
	format = 0;
	for ( test_format = SDL_FirstAudioFormat(spec->format);
						! format && test_format; ) {
		format = sdlformat_to_auformat(test_format);

		if (format == AuNone) {
			test_format = SDL_NextAudioFormat();
		}
	}
	if ( format == 0 ) {
		SDL_SetError("Couldn't find any hardware audio formats");
		return(-1);
	}
	spec->format = test_format;

	this->hidden->aud = AuOpenServer("", 0, NULL, 0, NULL, NULL);
	if (this->hidden->aud == 0)
	{
		SDL_SetError("Couldn't open connection to NAS server");
		return (-1);
	}
	
	this->hidden->dev = find_device(this, spec->channels);
	if ((this->hidden->dev == AuNone) || (!(this->hidden->flow = AuCreateFlow(this->hidden->aud, NULL)))) {
		AuCloseServer(this->hidden->aud);
		this->hidden->aud = 0;
		SDL_SetError("Couldn't find a fitting playback device on NAS server");
		return (-1);
	}
	
	buffer_size = spec->freq;
	if (buffer_size < 4096)
		buffer_size = 4096; 

	if (buffer_size > 32768)
		buffer_size = 32768; /* So that the buffer won't get unmanageably big. */

	/* Calculate the final parameters for this audio specification */
	SDL_CalculateAudioSpec(spec);

	this2 = this->hidden;

	AuMakeElementImportClient(elms, spec->freq, format, spec->channels, AuTrue,
				buffer_size, buffer_size / 4, 0, NULL);
	AuMakeElementExportDevice(elms+1, 0, this->hidden->dev, spec->freq,
				AuUnlimitedSamples, 0, NULL);
	AuSetElements(this->hidden->aud, this->hidden->flow, AuTrue, 2, elms, NULL);
	AuRegisterEventHandler(this->hidden->aud, AuEventHandlerIDMask, 0, this->hidden->flow,
				event_handler, (AuPointer) NULL);

	AuStartFlow(this->hidden->aud, this->hidden->flow, NULL);

	/* Allocate mixing buffer */
	this->hidden->mixlen = spec->size;
	this->hidden->mixbuf = (Uint8 *)SDL_AllocAudioMem(this->hidden->mixlen);
	if ( this->hidden->mixbuf == NULL ) {
		return(-1);
	}
	memset(this->hidden->mixbuf, spec->silence, spec->size);

	/* Get the parent process id (we're the parent of the audio thread) */
	this->hidden->parent = getpid();

	/* We're ready to rock and roll. :-) */
	return(0);
}
コード例 #4
0
static int init(struct xmp_context *ctx)
{
	struct xmp_options *o = &ctx->o;
	int channels, rate, format, buf_samples;
	int duration, gain, watermark;
	char *server;
	AuDeviceID device = AuNone;
	AuElement element[2];
	char *token, **parm;
	int i;

	duration = 2;
	gain = 100;
	server = NULL;
	watermark = 10;
	channels = 2;
	rate = o->freq;

	parm_init();
	chkparm1("duration", duration = atoi(token));
	chkparm1("gain", gain = atoi(token));
	chkparm1("server", server = token);
	chkparm1("watermark", watermark = atoi(token));
	parm_end();

	if (o->resol == 8) {
		format = o->outfmt & XMP_FMT_UNS ?
		    AuFormatLinearUnsigned8 : AuFormatLinearSigned8;
	} else {
		if (o->big_endian) {
			format = o->outfmt & XMP_FMT_UNS ?
				AuFormatLinearUnsigned16MSB :
				AuFormatLinearSigned16MSB;
		} else {
			format = o-> outfmt & XMP_FMT_UNS ?
				AuFormatLinearUnsigned16LSB :
				AuFormatLinearSigned16LSB;
		}
	}

	if (o->outfmt & XMP_FMT_MONO)
		channels = 1;

	info.aud = AuOpenServer(server, 0, NULL, 0, NULL, NULL);
	if (!info.aud) {
		fprintf(stderr, "xmp: drv_nas: can't connect to server %s\n",
			server ? server : "");
		return XMP_ERR_DINIT;
	}

	for (i = 0; i < AuServerNumDevices(info.aud); i++) {
		if (((AuDeviceKind(AuServerDevice(info.aud, i)) ==
		     AuComponentKindPhysicalOutput) &&
		     AuDeviceNumTracks(AuServerDevice(info.aud, i)) ==
		     channels)) {
			device =
			    AuDeviceIdentifier(AuServerDevice(info.aud, i));
			break;
		}
	}

	info.da = AuGetDeviceAttributes(info.aud, device, NULL);
	if (!info.da) {
		fprintf(stderr, "xmp: drv_nas: can't get device attributes\n");
		AuCloseServer(info.aud);
		return XMP_ERR_DINIT;
	}

	AuDeviceGain(info.da) = AuFixedPointFromSum(gain, 0);
	AuSetDeviceAttributes(info.aud, AuDeviceIdentifier(info.da),
			      AuCompDeviceGainMask, info.da, NULL);

	info.flow = AuCreateFlow(info.aud, NULL);
	if (!info.flow) {
		fprintf(stderr, "xmp: drv_nas: can't create flow\n");
		AuCloseServer(info.aud);
		return XMP_ERR_DINIT;
	}

	buf_samples = rate * duration;

	AuMakeElementImportClient(&element[0], rate, format, channels, AuTrue,
				  buf_samples,
				  (AuUint32) (buf_samples * watermark / 100), 0,
				  NULL);

	AuMakeElementExportDevice(&element[1], 0, device, rate,
				  AuUnlimitedSamples, 0, NULL);

	AuSetElements(info.aud, info.flow, AuTrue, 2, element, NULL);

	AuRegisterEventHandler(info.aud, AuEventHandlerIDMask, 0, info.flow,
			       nas_event, (AuPointer) & info);

	info.buf_size = buf_samples * channels * AuSizeofFormat(format);
	info.buf = (char *)malloc(info.buf_size);
	info.buf_cnt = 0;
	info.data_sent = AuFalse;
	info.finished = AuFalse;

	AuStartFlow(info.aud, info.flow, NULL);

	return xmp_smix_on(ctx);
}
コード例 #5
0
ファイル: SDL_nasaudio.c プロジェクト: JohnCrash/SDLnanovg
static int
NAS_OpenDevice(_THIS, void *handle, const char *devname, int iscapture)
{
    AuElement elms[3];
    int buffer_size;
    SDL_AudioFormat test_format, format;

    /* Initialize all variables that we clean on shutdown */
    this->hidden = (struct SDL_PrivateAudioData *)
        SDL_malloc((sizeof *this->hidden));
    if (this->hidden == NULL) {
        return SDL_OutOfMemory();
    }
    SDL_memset(this->hidden, 0, (sizeof *this->hidden));

    /* Try for a closest match on audio format */
    format = 0;
    for (test_format = SDL_FirstAudioFormat(this->spec.format);
         !format && test_format;) {
        format = sdlformat_to_auformat(test_format);
        if (format == AuNone) {
            test_format = SDL_NextAudioFormat();
        }
    }
    if (format == 0) {
        NAS_CloseDevice(this);
        return SDL_SetError("NAS: Couldn't find any hardware audio formats");
    }
    this->spec.format = test_format;

    this->hidden->aud = NAS_AuOpenServer("", 0, NULL, 0, NULL, NULL);
    if (this->hidden->aud == 0) {
        NAS_CloseDevice(this);
        return SDL_SetError("NAS: Couldn't open connection to NAS server");
    }

    this->hidden->dev = find_device(this, this->spec.channels);
    if ((this->hidden->dev == AuNone)
        || (!(this->hidden->flow = NAS_AuCreateFlow(this->hidden->aud, 0)))) {
        NAS_CloseDevice(this);
        return SDL_SetError("NAS: Couldn't find a fitting device on NAS server");
    }

    buffer_size = this->spec.freq;
    if (buffer_size < 4096)
        buffer_size = 4096;

    if (buffer_size > 32768)
        buffer_size = 32768;    /* So that the buffer won't get unmanageably big. */

    /* Calculate the final parameters for this audio specification */
    SDL_CalculateAudioSpec(&this->spec);

    this2 = this->hidden;

    AuMakeElementImportClient(elms, this->spec.freq, format,
                              this->spec.channels, AuTrue, buffer_size,
                              buffer_size / 4, 0, NULL);
    AuMakeElementExportDevice(elms + 1, 0, this->hidden->dev, this->spec.freq,
                              AuUnlimitedSamples, 0, NULL);
    NAS_AuSetElements(this->hidden->aud, this->hidden->flow, AuTrue, 2, elms,
                      NULL);
    NAS_AuRegisterEventHandler(this->hidden->aud, AuEventHandlerIDMask, 0,
                               this->hidden->flow, event_handler,
                               (AuPointer) NULL);

    NAS_AuStartFlow(this->hidden->aud, this->hidden->flow, NULL);

    /* Allocate mixing buffer */
    this->hidden->mixlen = this->spec.size;
    this->hidden->mixbuf = (Uint8 *) SDL_AllocAudioMem(this->hidden->mixlen);
    if (this->hidden->mixbuf == NULL) {
        NAS_CloseDevice(this);
        return SDL_OutOfMemory();
    }
    SDL_memset(this->hidden->mixbuf, this->spec.silence, this->spec.size);

    /* We're ready to rock and roll. :-) */
    return 0;
}
コード例 #6
0
static int sound_Start(int frameCount, int samplesPerSec, int stereo0, int semaIndex0)
{
  AuElement elements[2];  /* first is a client element, second is
			     a device output element */
  AuDeviceID device;        /* ID of the device to play to */
  

  /* open the server */
  DPRINTF("opening server\n");
  server = AuOpenServer(NULL, 0, NULL, 0, NULL, NULL);
  if(server == NULL) {
    DPRINTF("failed to open audio server\n");
    return false;
  }

  /* XXX should check the protocol version! */

  /* record requested info */
  semaIndex = semaIndex0;
  stereo = stereo0;
  sampleRate= samplesPerSec;
  
  /* pick a device to play to */ 
  device = choose_nas_device(server, samplesPerSec, stereo, 0);
  if(device == AuNone) {
    DPRINTF("no available device on the server!\n");
    AuCloseServer(server);
    server = NULL;
    return false;
  }

  /* set up output parameters */
  fmtBytes=2;
  fmtSigned=1;
  fmtStereo=stereo;
  fmtIsBigendian=0;
  recording=0;
  


  /* create a flow to write on */
  DPRINTF("creating flow\n");
  flow = AuCreateFlow(server, NULL);


  /* create client and device elements to play with */
  DPRINTF("creating elements(%d,%d)\n",
	 frameCount, frameCount / 4);
  AuMakeElementImportClient(&elements[0],
			    samplesPerSec,
			    AuFormatLinearSigned16LSB,  /* XXX this should be chosen based on the platform */
			    stereo ? 2 : 1,
			    AuTrue,
			    2*frameCount,   /* max: 2 buffers */
			    frameCount,   /* low */
			    0, NULL);
	
  AuMakeElementExportDevice(&elements[1],
			    0,
			    device,
			    samplesPerSec,
			    AuUnlimitedSamples,
			    0, NULL);

  /* set up the flow with these elements */
  AuSetElements(server,	flow,
		AuTrue,
		2, elements,
		NULL);

  /* start her up */
  DPRINTF("starting flow\n");
  AuStartFlow(server, flow, NULL);
  AuFlush(server);
  

  /* initialize the space indication */
  bytesAvail = 0;

  
  /* arrange to be informed when events come in from the server */
  aioEnable(AuServerConnectionNumber(server), 0, AIO_EXT);
  aioHandle(AuServerConnectionNumber(server), handleAudioEvents, AIO_R);



  return true;
}
コード例 #7
0
ファイル: ao_nas.c プロジェクト: has207/libao
int ao_plugin_open(ao_device *device, ao_sample_format *format)
{
    ao_nas_internal *internal = (ao_nas_internal *) device->internal;
    unsigned char nas_format;
    AuElement elms[2];

    /* get format */
    switch (format->bits)
    {
    case 8  :
        nas_format = AuFormatLinearUnsigned8;
        break;
    case 16 :
        if (device->machine_byte_format == AO_FMT_BIG)
            nas_format = AuFormatLinearSigned16MSB;
        else
            nas_format = AuFormatLinearSigned16LSB;
        break;
    default :
        return 0;
    }

    /* open server */
    internal->aud = AuOpenServer(internal->host, 0, 0, 0, 0, 0);
    if (!internal->aud)
        return 0; /* Could not contact NAS server */

    /* find physical output device */
    {
        int i;
        for (i = 0; i < AuServerNumDevices(internal->aud); i++)
            if ((AuDeviceKind(AuServerDevice(internal->aud, i)) ==
                    AuComponentKindPhysicalOutput) &&
                    (AuDeviceNumTracks(AuServerDevice(internal->aud, i)) ==
                     device->output_channels))
                break;

        if ((i == AuServerNumDevices(internal->aud)) ||
                (!(internal->flow = AuCreateFlow(internal->aud, 0)))) {
            /* No physical output device found or flow creation failed. */
            AuCloseServer(internal->aud);
            return 0;
        }
        internal->dev = AuDeviceIdentifier(AuServerDevice(internal->aud, i));
    }

    /* set up flow */
    AuMakeElementImportClient(&elms[0], format->rate,
                              nas_format, device->output_channels, AuTrue,
                              internal->buf_size, internal->buf_size / 2,
                              0, 0);
    AuMakeElementExportDevice(&elms[1], 0, internal->dev,
                              format->rate, AuUnlimitedSamples, 0, 0);
    AuSetElements(internal->aud, internal->flow, AuTrue, 2, elms, 0);
    AuStartFlow(internal->aud, internal->flow, 0);

    device->driver_byte_format = AO_FMT_NATIVE;

    if(!device->inter_matrix) {
        /* set up out matrix such that users are warned about > stereo playback */
        if(device->output_channels<=2)
            device->inter_matrix=strdup("L,R");
        //else no matrix, which results in a warning
    }

    return 1;
}
コード例 #8
0
ファイル: nassink.c プロジェクト: pli3/gst-plugins-bad
static gboolean
gst_nas_sink_prepare (GstAudioSink * asink, GstRingBufferSpec * spec)
{
    GstNasSink *sink = GST_NAS_SINK (asink);
    AuElement elements[2];
    AuUint32 buf_samples;
    unsigned char format;

    format = gst_nas_sink_sink_get_format (spec);
    if (format == 0) {
        GST_ELEMENT_ERROR (sink, RESOURCE, SETTINGS, (NULL),
                           ("Unable to get format %d", spec->format));
        return FALSE;
    }
    GST_DEBUG_OBJECT (sink, "Format: %d %d\n", spec->format, format);

    sink->flow = AuGetScratchFlow (sink->audio, NULL);
    if (sink->flow == 0) {
        GST_DEBUG_OBJECT (sink, "couldn't get flow");
        return FALSE;
    }

    buf_samples = spec->rate * NAS_SOUND_PORT_DURATION;
    /*
       spec->segsize = gst_util_uint64_scale (buf_samples * spec->bytes_per_sample,
       spec->latency_time, GST_SECOND / GST_USECOND);
       spec->segsize -= spec->segsize % spec->bytes_per_sample;
       spec->segtotal = spec->buffer_time / spec->latency_time;
     */
    spec->segsize = buf_samples * spec->bytes_per_sample;
    spec->segtotal = 1;

    memset (spec->silence_sample, 0, spec->bytes_per_sample);
    GST_DEBUG_OBJECT (sink, "Bytes per sample %d", spec->bytes_per_sample);

    GST_DEBUG_OBJECT (sink, "Rate %d Format %d tracks %d bufs %d %d/%d w %d",
                      spec->rate, format, spec->channels, (gint) buf_samples, spec->segsize,
                      spec->segtotal, spec->width);
    AuMakeElementImportClient (&elements[0],      /* element */
                               spec->rate,               /* rate */
                               format,                   /* format */
                               spec->channels,           /* number of tracks */
                               AuTrue,                   /* discart */
                               buf_samples,              /* max samples */
                               (AuUint32) (buf_samples / 100 * AuSoundPortLowWaterMark),
                               /* low water mark */
                               0,                        /* num actions */
                               NULL);

    sink->device = NAS_getDevice (sink->audio, spec->channels);
    if (sink->device == AuNone) {
        GST_DEBUG_OBJECT (sink, "no device with %i tracks found", spec->channels);
        return FALSE;
    }

    AuMakeElementExportDevice (&elements[1],      /* element */
                               0,                        /* input */
                               sink->device,             /* device */
                               spec->rate,               /* rate */
                               AuUnlimitedSamples,       /* num samples */
                               0,                        /* num actions */
                               NULL);                    /* actions */

    AuSetElements (sink->audio,   /* server */
                   sink->flow,               /* flow ID */
                   AuTrue,                   /* clocked */
                   2,                        /* num elements */
                   elements,                 /* elements */
                   NULL);

    AuRegisterEventHandler (sink->audio,  /* server */
                            AuEventHandlerIDMask,     /* value mask */
                            0,                        /* type */
                            sink->flow,               /* flow ID */
                            NAS_EventHandler,         /* callback */
                            (AuPointer) sink);        /* data */

    AuStartFlow (sink->audio, sink->flow, NULL);

    return TRUE;
}
コード例 #9
0
ファイル: nas.c プロジェクト: dirker/mpg123
/* 0 on error */
static int nas_createFlow(out123_handle *ao)
{
    AuDeviceID      device = AuNone;
    AuElement       elements[2];
    unsigned char   format;
    AuUint32        buf_samples;
    int             i;
 

    switch(ao->format) {
    case MPG123_ENC_SIGNED_16:
    default:
		if (((char) *(short *)"x")=='x') /* ugly, but painless */
			format = AuFormatLinearSigned16LSB; /* little endian */
		else
		format = AuFormatLinearSigned16MSB; /* big endian */
        break;
    case MPG123_ENC_UNSIGNED_8:
        format = AuFormatLinearUnsigned8;
        break;
    case MPG123_ENC_SIGNED_8:
        format = AuFormatLinearSigned8;
        break;
    case MPG123_ENC_ULAW_8:
        format = AuFormatULAW8;
        break;
    }
    /* look for an output device */
    for (i = 0; i < AuServerNumDevices(info.aud); i++)
       if (((AuDeviceKind(AuServerDevice(info.aud, i)) ==
              AuComponentKindPhysicalOutput) &&
             AuDeviceNumTracks(AuServerDevice(info.aud, i))
             ==  ao->channels )) {
            device = AuDeviceIdentifier(AuServerDevice(info.aud, i));
            break;
       }
    if (device == AuNone) {
       error1("Couldn't find an output device providing %d channels.", ao->channels);
       return 0;
    }

    /* set gain */
    if(ao->gain >= 0) {
        info.da = AuGetDeviceAttributes(info.aud, device, NULL);
        if ((info.da)!=NULL) {
            AuDeviceGain(info.da) = AuFixedPointFromSum(ao->gain, 0);
            AuSetDeviceAttributes(info.aud, AuDeviceIdentifier(info.da),
                                  AuCompDeviceGainMask, info.da, NULL);
        }
        else
            error("audio/gain: setable Volume/PCM-Level not supported");
    }
    
    if (!(info.flow = AuCreateFlow(info.aud, NULL))) {
        error("Couldn't create flow");
        return 0;
    }

    buf_samples = ao->rate * NAS_SOUND_PORT_DURATION;

    AuMakeElementImportClient(&elements[0],        /* element */
                              (unsigned short) ao->rate,
                                                   /* rate */
                              format,              /* format */
                              ao->channels,        /* channels */
                              AuTrue,              /* ??? */
                              buf_samples,         /* max samples */
                              (AuUint32) (buf_samples / 100
                                  * NAS_SOUND_LOW_WATER_MARK),
                                                   /* low water mark */
                              0,                   /* num actions */
                              NULL);               /* actions */
    AuMakeElementExportDevice(&elements[1],        /* element */
                              0,                   /* input */
                              device,              /* device */
                              (unsigned short) ao->rate,
                                                   /* rate */
                              AuUnlimitedSamples,  /* num samples */
                              0,                   /* num actions */
                              NULL);               /* actions */
    AuSetElements(info.aud,                        /* Au server */
                  info.flow,                       /* flow ID */
                  AuTrue,                          /* clocked */
                  2,                               /* num elements */
                  elements,                        /* elements */
                  NULL);                           /* return status */

    AuRegisterEventHandler(info.aud,               /* Au server */
                           AuEventHandlerIDMask,   /* value mask */
                           0,                      /* type */
                           info.flow,              /* id */
                           nas_eventHandler,       /* callback */
                           (AuPointer) &info);     /* data */

    info.buf_size = buf_samples * ao->channels * AuSizeofFormat(format);
    info.buf = (char *) malloc(info.buf_size);
    if (info.buf == NULL) {
        error1("Unable to allocate input/output buffer of size %ld",
             (long)info.buf_size);
        return 0;
    }
    info.buf_cnt = 0;
    info.data_sent = AuFalse;
    info.finished = AuFalse;
    
    AuStartFlow(info.aud,                          /* Au server */
                info.flow,                         /* id */
                NULL);                             /* status */
    return 1; /* success */
}
コード例 #10
0
ファイル: sound-nas.c プロジェクト: hroptatyr/sxemacs
static int
sound_nas_play(audio_job_t aj)
{
	/* stream stuff */
	Lisp_Media_Stream *ms;
	media_substream *mss;
	/* thread stuff */
	media_thread_play_state mtp;
	/* device stuff */
	Lisp_Object device;
	Lisp_Audio_Device *lad = NULL;
	sound_nas_data_t *snd = NULL;
	/* nas stuff */
	AuElement elms[3];
	AuStatus as;
	int bsize = 0;
	/* subthread stuff */
	sound_nas_aj_data_t _snsd, *snsd = &_snsd;
	sxe_mse_volume_args _volargs, *volargs = &_volargs;
	sxe_mse_rerate_args _rrargs, *rrargs = &_rrargs;
	/* cache stuff */
	int alloced_myself = 0;

	SOUND_UNPACK_MT(aj, device, ms, mss, lad, snd, snsd->mtap);

	/* refuse to do anything if the AuServer pointer is not set */
	if (snd->aud == NULL) {
		NAS_DEBUG_C("b0rked connection, gute Nacht!\n");
		return 0;
	}

	/* install error handlers before anything else */
	AuSetErrorHandler(snd->aud, nas_error_handler);
	AuSetIOErrorHandler(snd->aud, nas_IOerror_handler);

	/* find physical output device */
	snsd->dev = nas_find_device(snd->aud, snsd->mtap->channels);

	if (snsd->dev == AuNone ||
	    !(snsd->flow = AuCreateFlow(snd->aud, NULL))) {
		/* No physical output device found or flow creation failed. */
		NAS_DEBUG_C("no physical devices for this stream\n");
		return 0;
	}

	/* A system call interrupted with a SIGALRM or SIGIO
	   comes back here */
	if (SETJMP(nas_server_sig)) {
		NAS_CRITICAL("Caught the lethal signal.\n");
		snd->aud = NULL;
		sound_nas_finish(snd);
		goto uhoh;
	}

	/* init the snsd */
	snsd->samplerate = 0;
	snsd->framesize = (snsd->channels = snsd->mtap->channels)
		* sizeof(int16_t);
	snsd->msf = MEDIA_SAMPLE_FORMAT(sxe_msf_S16);
	snsd->coe_ch_cnt = 0;
	snsd->resolution =
		(snsd->mtap->samplerate * MTPSTATE_REACT_TIME) / 1000000;
	bsize = (snsd->resolution + NAS_FRAG_SIZE - 1) & ~(NAS_FRAG_SIZE-1);

	snsd->snd = snd;
	snsd->buffer_size = bsize;
	snsd->writepos = snsd->readpos = 0;
	snsd->overfill = snsd->underrun = 0;

	/* set up flow */
	AuMakeElementImportClient(elms+0, snsd->mtap->samplerate,
				  AuFormatLinearSigned16LSB,
				  snsd->mtap->channels,
				  AuTrue,
				  bsize, bsize / 2,
				  0, NULL);
	snsd->gain = AuFixedPointFromFraction(1, 1);
	AuMakeElementMultiplyConstant(elms+1, 0, snsd->gain);
	AuMakeElementExportDevice(elms+2, 0, snsd->dev,
				  snsd->mtap->samplerate,
				  AuUnlimitedSamples, 0, NULL);
	AuSetElements(snd->aud, snsd->flow, AuTrue, 3, elms, &as);

	if (as != AuSuccess) {
		NAS_DEBUG_C("play(): AuSetElements failed\n");
		return 0;
	}

#if 0				/* atm we insist on having stereo access */
	/* the channel effect */
	ADD_MEDIA_SAMPLE_EFFECT(
		snsd->coe_chain, snsd->coe_ch_cnt,
		MEDIA_SAMPLE_EFFECT(sxe_mse_2ch_to_1ch), NULL);
#endif

	/* the volume effect */
	ADD_MEDIA_SAMPLE_EFFECT(
		snsd->coe_chain, snsd->coe_ch_cnt,
		MEDIA_SAMPLE_EFFECT(sxe_mse_volume), volargs);
	volargs->num_channels = snsd->channels;
	snsd->volargs = volargs;

	/* the rerate effect */
	ADD_MEDIA_SAMPLE_EFFECT(
		snsd->coe_chain, snsd->coe_ch_cnt,
		MEDIA_SAMPLE_EFFECT(sxe_mse_rerate), rrargs);
	rrargs->num_channels = snsd->channels;
	rrargs->srcrate = rrargs->tgtrate = 1;
	snsd->rrargs = rrargs;

	AuRegisterEventHandler(snd->aud, AuEventHandlerIDMask |
			       AuEventHandlerTypeMask,
			       AuEventTypeElementNotify, snsd->flow,
			       nas_event_handler, (AuPointer)aj);

	/* rewind the stream */
	media_stream_meth(ms, rewind)(mss);

	/* play chunks of the stream */
	SXE_MUTEX_LOCK(&aj->mtx);
	if (aj->buffer_alloc_size < SOUND_MAX_AUDIO_FRAME_SIZE) {
		alloced_myself = 1;
		aj->buffer = xmalloc_atomic(SOUND_MAX_AUDIO_FRAME_SIZE);
		aj->buffer_alloc_size = SOUND_MAX_AUDIO_FRAME_SIZE;
	}
	SXE_MUTEX_UNLOCK(&aj->mtx);
	snsd->mtp = MTPSTATE_STOP;
	snsd->volume = -1;
	audio_job_device_data(aj) = snsd;

	/* prefill the buffer */
	NAS_DEBUG_S("prefill the buffer: %d\n", 4*bsize);
	if (!nas_fill(aj, 4*bsize))
		goto uhoh;

	while (aj->play_state != MTPSTATE_STOP) {

#if 0
		if (aj->volume != snsd->volume) {
			AuElementParameters aep;
			NAS_DEBUG_S("Setting volume.\n");
			snsd->volume = aj->volume;
			snsd->gain = AU_FIXED_POINT_SCALE*(snsd->volume)/127;
			aep.parameters[AuParmsMultiplyConstantConstant] =
				snsd->gain;
			aep.flow = snd->flow;
			aep.element_num = 1;
			aep.num_parameters = AuParmsMultiplyConstant;

			AuSetElementParameters(snd->aud, 1, &aep, &as);
			if (as != AuSuccess) {
				NAS_DEBUG_S("Setting volume failed.\n");
			}
		}
#endif

#ifdef EF_USE_ASYNEQ
		/* events for me audio-job? */
		if (audio_job_queue(aj)) {
			sound_nas_handle_aj_events(aj);
		}
#endif

		SXE_MUTEX_LOCK(&aj->mtx);
		mtp = aj->play_state;
		SXE_MUTEX_UNLOCK(&aj->mtx);
		switch (mtp) {
		case MTPSTATE_RUN:
			if (snsd->mtp != mtp) {
				NAS_DEBUG("ah, gotta work again\n");
				AuStartFlow(snd->aud, snsd->flow, &as);
				if (as != AuSuccess) {
					NAS_DEBUG_C("play(): "
						    "AuStartFlow failed\n");
					aj->play_state = MTPSTATE_STOP;
				}
			}
			nas_empty_event_queue(snsd);
			usleep(snsd->resolution);
			break;
		case MTPSTATE_PAUSE:
			if (snsd->mtp != mtp) {
				NAS_DEBUG("sleeping for %d\n",
					  snsd->resolution);
				AuStopFlow(snd->aud, snsd->flow, &as);
			}
			usleep(snsd->resolution);
			break;

		case MTPSTATE_UNKNOWN:
		case MTPSTATE_STOP:
		case NUMBER_OF_MEDIA_THREAD_PLAY_STATES:
		default:
			NAS_DEBUG("ACK, quit\n");
			AuStopFlow(snd->aud, snsd->flow, &as);
			SXE_MUTEX_LOCK(&aj->mtx);
			aj->play_state = MTPSTATE_STOP;
			SXE_MUTEX_UNLOCK(&aj->mtx);
			break;
		}
		snsd->mtp = mtp;
	}

uhoh:
	/* -- Close and shutdown -- */
	SXE_MUTEX_LOCK(&aj->mtx);
	if (alloced_myself && aj->buffer) {
		xfree(aj->buffer);
	}
	aj->buffer = NULL;
	aj->buffer_alloc_size = 0;
	SXE_MUTEX_UNLOCK(&aj->mtx);

	return 1;
}