/* * This file is part of DisOrder. * Copyright (C) 2007 Richard Kettlewell * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ /** @file clients/playrtp-coreaudio.c * @brief RTP player - Core Audio support */ #include "common.h" #if HAVE_COREAUDIO_AUDIOHARDWARE_H #include #include #include "mem.h" #include "log.h" #include "vector.h" #include "heap.h" #include "playrtp.h" /** @brief Callback from Core Audio */ static OSStatus adioproc (AudioDeviceID attribute((unused)) inDevice, const AudioTimeStamp attribute((unused)) *inNow, const AudioBufferList attribute((unused)) *inInputData, const AudioTimeStamp attribute((unused)) *inInputTime, AudioBufferList *outOutputData, const AudioTimeStamp attribute((unused)) *inOutputTime, void attribute((unused)) *inClientData) { UInt32 nbuffers = outOutputData->mNumberBuffers; AudioBuffer *ab = outOutputData->mBuffers; uint32_t samples_available; pthread_mutex_lock(&lock); while(nbuffers > 0) { float *samplesOut = ab->mData; size_t samplesOutLeft = ab->mDataByteSize / sizeof (float); while(samplesOutLeft > 0) { const struct packet *p = playrtp_next_packet(); if(p && contains(p, next_timestamp)) { /* This packet is ready to play */ const uint32_t packet_end = p->timestamp + p->nsamples; const uint32_t offset = next_timestamp - p->timestamp; const uint16_t *ptr = (void *)(p->samples_raw + offset); samples_available = packet_end - next_timestamp; if(samples_available > samplesOutLeft) samples_available = samplesOutLeft; next_timestamp += samples_available; samplesOutLeft -= samples_available; if(dump_buffer) { size_t n; for(n = 0; n < samples_available; ++n) { dump_buffer[dump_index++] = (int16_t)ntohs(ptr[n]); dump_index %= dump_size; } } while(samples_available-- > 0) *samplesOut++ = (int16_t)ntohs(*ptr++) * (0.5 / 32767); /* We don't bother junking the packet - that'll be dealt with next time * round */ } else { /* No packet is ready to play (and there might be no packet at all) */ samples_available = p ? p->timestamp - next_timestamp : samplesOutLeft; if(samples_available > samplesOutLeft) samples_available = samplesOutLeft; //info("infill by %"PRIu32, samples_available); /* Conveniently the buffer is 0 to start with */ next_timestamp += samples_available; samplesOut += samples_available; samplesOutLeft -= samples_available; if(dump_buffer) { size_t n; for(n = 0; n < samples_available; ++n) { dump_buffer[dump_index++] = 0; dump_index %= dump_size; } } } } ++ab; --nbuffers; } pthread_mutex_unlock(&lock); return 0; } void playrtp_coreaudio(void) { OSStatus status; UInt32 propertySize; AudioDeviceID adid; AudioStreamBasicDescription asbd; /* If this looks suspiciously like libao's macosx driver there's an * excellent reason for that... */ /* TODO report errors as strings not numbers */ propertySize = sizeof adid; status = AudioHardwareGetProperty(kAudioHardwarePropertyDefaultOutputDevice, &propertySize, &adid); if(status) fatal(0, "AudioHardwareGetProperty: %d", (int)status); if(adid == kAudioDeviceUnknown) fatal(0, "no output device"); propertySize = sizeof asbd; status = AudioDeviceGetProperty(adid, 0, false, kAudioDevicePropertyStreamFormat, &propertySize, &asbd); if(status) fatal(0, "AudioHardwareGetProperty: %d", (int)status); D(("mSampleRate %f", asbd.mSampleRate)); D(("mFormatID %08lx", asbd.mFormatID)); D(("mFormatFlags %08lx", asbd.mFormatFlags)); D(("mBytesPerPacket %08lx", asbd.mBytesPerPacket)); D(("mFramesPerPacket %08lx", asbd.mFramesPerPacket)); D(("mBytesPerFrame %08lx", asbd.mBytesPerFrame)); D(("mChannelsPerFrame %08lx", asbd.mChannelsPerFrame)); D(("mBitsPerChannel %08lx", asbd.mBitsPerChannel)); D(("mReserved %08lx", asbd.mReserved)); if(asbd.mFormatID != kAudioFormatLinearPCM) fatal(0, "audio device does not support kAudioFormatLinearPCM"); status = AudioDeviceAddIOProc(adid, adioproc, 0); if(status) fatal(0, "AudioDeviceAddIOProc: %d", (int)status); pthread_mutex_lock(&lock); for(;;) { /* Wait for the buffer to fill up a bit */ playrtp_fill_buffer(); /* Start playing now */ info("Playing..."); next_timestamp = pheap_first(&packets)->timestamp; active = 1; status = AudioDeviceStart(adid, adioproc); if(status) fatal(0, "AudioDeviceStart: %d", (int)status); /* Wait until the buffer empties out */ while(nsamples >= minbuffer || (nsamples > 0 && contains(pheap_first(&packets), next_timestamp))) pthread_cond_wait(&cond, &lock); /* Stop playing for a bit until the buffer re-fills */ status = AudioDeviceStop(adid, adioproc); if(status) fatal(0, "AudioDeviceStop: %d", (int)status); active = 0; /* Go back round */ } } #endif /* Local Variables: c-basic-offset:2 comment-column:40 fill-column:79 indent-tabs-mode:nil End: */