static sqInt sound_AvailableSpace(void) { PRINTF(); struct audio_info info; int avail; if (auFd < 0) return 0; if (ioctl(auFd, AUDIO_GETINFO, &info)) { perror("AUDIO_GETINFO"); sound_Stop(); } avail= auBufBytes * (info.play.eof - auBuffersPlayed + 2); PRINTF(("auBufBytes=%d, info.play.eof=%d, auBuffersPlayed=%d, avail=%d", auBufBytes, info.play.eof, auBuffersPlayed, avail)); return avail; }
// start up sound output. // static sqInt sound_Start(sqInt frameCount, sqInt samplesPerSec, sqInt stereo, sqInt semaIndex) { Stream *s= 0; debugf("snd_Start frames: %d samplesPerSec: %d stereo: %d semaIndex: %d\n", frameCount, samplesPerSec, stereo, semaIndex); if (output) // there might be a change of sample rate sound_Stop(); if ((s= Stream_new(0))) // 0utput { if (( Stream_setFormat(s, frameCount, samplesPerSec, stereo)) && Stream_startSema(s, semaIndex)) { output= s; return 1; } Stream_delete(s); } return primitiveFail(); }
static int sound_StopRecording(void) { return sound_Stop(); }
/* StartRecording: open the device for recording. XXX this routine is almost identical to snd_Start(). The two should be factored into a single function! */ static int sound_StartRecording(int desiredSamplesPerSec, int stereo0, int semaIndex0) { AuElement elements[2]; /* elements for the NAS flow to assemble: element 0 = physical input element 1 = client export */ AuDeviceID device; /* physical device ID to use */ DPRINTF("StartRecording\n"); sound_Stop(); 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 check protocol version of the server */ semaIndex= semaIndex0; stereo= stereo0; sampleRate= desiredSamplesPerSec; device= choose_nas_device(server, desiredSamplesPerSec, stereo, 1); if(device == AuNone) { DPRINTF("no available device on the server!\n"); AuCloseServer(server); server = NULL; return false; } /* record format info */ fmtBytes=2; fmtSigned=1; fmtStereo=stereo; fmtIsBigendian=0; recording=1; /* create a flow to read from */ DPRINTF("creating flow\n"); flow = AuCreateFlow(server, NULL); /* create client and device elements to record with */ DPRINTF("creating elements\n"); AuMakeElementImportDevice(&elements[0], desiredSamplesPerSec, /* XXX should use the actual sampling rate of device */ device, AuUnlimitedSamples, 0, NULL); AuMakeElementExportClient(&elements[1], 0, desiredSamplesPerSec, AuFormatLinearSigned16LSB, /* XXX this should be chosen based on the platform */ stereo ? 2 : 1, AuTrue, 1000000, /* was AuUnlimitedSamples */ 1000, /* water mark: go ahead and send frequently! */ 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), NULL, AIO_EXT); aioHandle(AuServerConnectionNumber(server), handleAudioEvents, AIO_W); return true; }
/* Process audio events from the NAS server. The same routine is used whether we are recording or playing back */ static void handleAudioEvents(int fd, void *data, int flags) { if(!server) { DPRINTF( "handleAudioEvents called while unconnected!\n"); return; } /* read events once */ AuEventsQueued(server, AuEventsQueuedAfterReading); /* then loop through the read queue */ while(AuEventsQueued(server, AuEventsQueuedAlready)) { AuEvent event; AuNextEvent(server, AuTrue, &event); DPRINTF("event of type %d\n", event.type); switch(event.type) { case 0: { AuErrorEvent *errEvent = (AuErrorEvent *) &event; char errdesc[1000]; AuGetErrorText(server, errEvent->error_code, errdesc, sizeof(errdesc)); fprintf(stderr, "audio error: %s\n", errdesc); sound_Stop(); return; /* return, not break, so that we don't process the now-closed server any longer! */ } case AuEventTypeElementNotify: { AuElementNotifyEvent *enEvent = (AuElementNotifyEvent *)&event; switch(enEvent->kind) { case AuElementNotifyKindLowWater: DPRINTF("low water event\n"); bytesAvail += enEvent->num_bytes; break; case AuElementNotifyKindHighWater: DPRINTF("high water event\n"); bytesAvail += enEvent->num_bytes; break; case AuElementNotifyKindState: DPRINTF("state change (%d->%d)\n", enEvent->prev_state, enEvent->cur_state); bytesAvail += enEvent->num_bytes; if(enEvent->cur_state == AuStatePause) { /* if the flow has stopped, then arrange for it to get started again */ /* XXX there is probably a more intelligent place to do this, in case there is a real reason it has paused */ DPRINTF("unpausing\n"); AuStartFlow(server, flow, NULL); AuFlush(server); } break; } } } } if(bytesAvail > 0) { DPRINTF("bytesAvail: %d\n", bytesAvail); signalSemaphoreWithIndex(semaIndex); } aioHandle(fd, handleAudioEvents, flags & AIO_RW); }
static sqInt sound_Start(sqInt frameCount, sqInt samplesPerSec, sqInt stereo, sqInt semaIndex) { PRINTF(("(frameCount=%d, samplesPerSec=%d, stereo=%d, semaIndex=%d)", frameCount, samplesPerSec, stereo, semaIndex)); int bytesPerFrame= (stereo ? 4 : 2); struct audio_info info; int err; if (auFd != -1) sound_Stop(); auPlaySemaIndex= semaIndex; fmtStereo= stereo; auBufBytes= bytesPerFrame * frameCount; if ((auFd= open("/dev/audio", O_WRONLY|O_NONBLOCK)) == -1) { perror("/dev/audio"); return false; } PRINTF(("auFd=%d", auFd)); if ((auCtlFd= open("/dev/audioctl", O_WRONLY|O_NONBLOCK)) == -1) { perror("/dev/audioctl"); return false; } PRINTF(("auCtlFd=%d", auCtlFd)); /* set up device */ if (ioctl(auFd, AUDIO_GETINFO, &info)) { perror("AUDIO_GETINFO"); goto closeAndFail; } info.play.gain= 255; info.play.precision= 16; info.play.encoding= AUDIO_ENCODING_LINEAR; info.play.channels= fmtStereo ? 2 : 1; info.play.sample_rate= samplesPerSec; auBuffersPlayed= info.play.eof; while ((err= ioctl(auFd, AUDIO_SETINFO, &info)) && errno == EINTR) ; if (err) { perror("AUDIO_SETINFO"); goto closeAndFail; } action.sa_handler= auHandle; sigemptyset(&action.sa_mask); action.sa_flags= 0; action.sa_flags|= SA_RESTART; if (sigaction(SIGIO, &action, &oldAction) < 0) /* On Solaris, SIGIO == SIGPOLL */ { perror("sigaction(SIGIO, auHandle)"); } if (ioctl(auCtlFd, I_SETSIG, S_MSG)) /* send SIGIO whenever EOF arrives */ { perror("ioctl(auFd, I_SETSIG, S_MSG)"); } return true; closeAndFail: close(auFd); auFd= -1; close(auCtlFd); auCtlFd= -1; return false; }