/* MPEG/WAVE Sound library (C) 1997 by Jung woo-jae */ // Nasplayer.cc // Playing raw audio over Network Audio System (by NCD) // By Willem (willem@stack.nl) #include "config.h" #ifdef WANT_NAS #include "mpegsound.h" #include "mpegsound_locals.h" #include #include NASplayer::NASplayer(AuServer *aud) { this->aud = aud; dev = AuNone; format = AuNone; flow = 0; samplerate = 0; channels = 0; buffer_ms = 2500; req_size = 0; } NASplayer::~NASplayer() { if (aud) { if (flow) AuDestroyFlow(aud, flow, 0); AuCloseServer(aud); } } NASplayer *NASplayer::opendevice(char *server) { AuServer *aud; char *return_status; aud = AuOpenServer(server, 0, 0, 0, 0, &return_status); if (!aud) { debug("NASplayer:: AuOpenServer(%s) failed: %s\n", server, return_status); free(return_status); //should we? return NULL; } return new NASplayer(aud); } void NASplayer::abort(void) { if (!aud || !flow) return; AuStopFlow(aud, flow, 0); AuHandleEvents(aud); req_size = 0; AuStartFlow(aud, flow, 0); } bool NASplayer::setsoundtype(int stereo, int samplesize, int speed) { int changed = 0; unsigned char newf = AuNone; if (samplesize == 16) newf = AuFormatLinearSigned16LSB; //else newf = AuFormatLinearSigned8; else newf = AuFormatLinearUnsigned8; stereo = stereo ? 2 : 1; if (stereo != channels) { channels = stereo; changed++; } if (samplerate != speed) { samplerate = speed; changed++; } if (newf != format) { format = newf; changed++; } if (changed) return resetsoundtype(); return true; } static AuBool static_event_handler(AuServer *, AuEvent *ev, AuEventHandlerRec *hnd) { NASplayer *np = (NASplayer *)(hnd->data); return np->event_handler(ev); } bool NASplayer::resetsoundtype(void) { if (!aud) return seterrorcode(SOUND_ERROR_DEVOPENFAIL); if (flow) { AuUnregisterEventHandler(aud, evhnd); AuDestroyFlow(aud, flow, 0); } flow = 0; int i = 0; while(true) { if (i >= AuServerNumDevices(aud)) return seterrorcode(SOUND_ERROR_DEVCTRLERROR); if ((AuDeviceKind(AuServerDevice(aud, i)) == AuComponentKindPhysicalOutput) && AuDeviceNumTracks(AuServerDevice(aud, i)) == channels) { dev = AuDeviceIdentifier(AuServerDevice(aud, i)); break; } i++; } flow = AuCreateFlow(aud, 0); if (!flow) return seterrorcode(SOUND_ERROR_DEVCTRLERROR); AuElement elms[3]; int buffer_size = (buffer_ms * samplerate) / 1000; if (buffer_size < 8192) buffer_size = 8192; AuMakeElementImportClient(elms, samplerate, format, channels, AuTrue, buffer_size, buffer_size / 2, 0, 0); AuMakeElementExportDevice(elms+1, 0, dev, samplerate, AuUnlimitedSamples, 0, 0); AuSetElements(aud, flow, AuTrue, 2, elms, 0); evhnd = AuRegisterEventHandler(aud, AuEventHandlerIDMask, 0, flow, static_event_handler, (AuPointer) this); AuStartFlow(aud, flow, 0); req_size = 0; return true; } //non-blocking putblock, may do partial writes int NASplayer::putblock_nt(void *buffer, int size) { int writeSize; AuHandleEvents(aud); //check for events if (!req_size) return 0; //no space to write (yet) writeSize = req_size; if (req_size > size) { writeSize = size; } req_size -= writeSize; AuWriteElement(aud, flow, 0, writeSize, buffer, AuFalse, 0); return writeSize; } bool NASplayer::putblock(void *buffer, int size) { while (size > req_size) { USLEEP(3); AuHandleEvents(aud); } req_size -= size; AuWriteElement(aud, flow, 0, size, buffer, AuFalse, 0); return true; } int NASplayer::getblocksize(void) { return 2048; } int NASplayer::setvolume(int volume) { if (!aud) return 0; AuDeviceAttributes *da = AuGetDeviceAttributes(aud, dev, NULL); if (!da) return 0; if (volume > 100) volume = 100; if (volume > 0) { AuDeviceGain(da) = AuFixedPointFromSum(volume, 0); AuSetDeviceAttributes(aud, AuDeviceIdentifier(da), AuCompDeviceGainMask, da, NULL); AuFreeDeviceAttributes(aud, 1, da); da = AuGetDeviceAttributes(aud, dev, NULL); } return AuFixedPointRoundUp(AuDeviceGain(da)); } AuBool NASplayer::event_handler(AuEvent *ev) { switch (ev->type) { case AuEventTypeElementNotify: AuElementNotifyEvent *event = (AuElementNotifyEvent *)ev; switch (event->kind) { case AuElementNotifyKindLowWater: req_size += event->num_bytes; break; case AuElementNotifyKindState: switch (event->cur_state) { case AuStatePause: if (event->reason != AuReasonUser) req_size += event->num_bytes; break; } } } return AuTrue; } #endif /*\ WANT_NAS \*/