chiark / gitweb /
split up queue.c to remove -ldl dependency from clients
[disorder] / clients / playrtp-coreaudio.c
CommitLineData
c593cf7c 1/*
2 * This file is part of DisOrder.
3 * Copyright (C) 2007 Richard Kettlewell
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
18 * USA
19 */
20/** @file clients/playrtp-coreaudio.c
21 * @brief RTP player - Core Audio support
22 */
23
24#include <config.h>
25
26#if HAVE_COREAUDIO_AUDIOHARDWARE_H
27#include "types.h"
28
29#include <assert.h>
30#include <pthread.h>
31#include <CoreAudio/AudioHardware.h>
32
33#include "mem.h"
34#include "log.h"
35#include "vector.h"
36#include "heap.h"
37#include "playrtp.h"
38
39/** @brief Callback from Core Audio */
40static OSStatus adioproc
41 (AudioDeviceID attribute((unused)) inDevice,
42 const AudioTimeStamp attribute((unused)) *inNow,
43 const AudioBufferList attribute((unused)) *inInputData,
44 const AudioTimeStamp attribute((unused)) *inInputTime,
45 AudioBufferList *outOutputData,
46 const AudioTimeStamp attribute((unused)) *inOutputTime,
47 void attribute((unused)) *inClientData) {
48 UInt32 nbuffers = outOutputData->mNumberBuffers;
49 AudioBuffer *ab = outOutputData->mBuffers;
50 uint32_t samples_available;
51
52 pthread_mutex_lock(&lock);
53 while(nbuffers > 0) {
54 float *samplesOut = ab->mData;
55 size_t samplesOutLeft = ab->mDataByteSize / sizeof (float);
56
57 while(samplesOutLeft > 0) {
58 const struct packet *p = playrtp_next_packet();
59 if(p && contains(p, next_timestamp)) {
60 /* This packet is ready to play */
61 const uint32_t packet_end = p->timestamp + p->nsamples;
62 const uint32_t offset = next_timestamp - p->timestamp;
63 const uint16_t *ptr = (void *)(p->samples_raw + offset);
64
65 samples_available = packet_end - next_timestamp;
66 if(samples_available > samplesOutLeft)
67 samples_available = samplesOutLeft;
68 next_timestamp += samples_available;
69 samplesOutLeft -= samples_available;
70 while(samples_available-- > 0)
71 *samplesOut++ = (int16_t)ntohs(*ptr++) * (0.5 / 32767);
72 /* We don't bother junking the packet - that'll be dealt with next time
73 * round */
74 } else {
75 /* No packet is ready to play (and there might be no packet at all) */
76 samples_available = p ? p->timestamp - next_timestamp
77 : samplesOutLeft;
78 if(samples_available > samplesOutLeft)
79 samples_available = samplesOutLeft;
80 //info("infill by %"PRIu32, samples_available);
81 /* Conveniently the buffer is 0 to start with */
82 next_timestamp += samples_available;
83 samplesOut += samples_available;
84 samplesOutLeft -= samples_available;
85 }
86 }
87 ++ab;
88 --nbuffers;
89 }
90 pthread_mutex_unlock(&lock);
91 return 0;
92}
93
94void playrtp_coreaudio(void) {
95 OSStatus status;
96 UInt32 propertySize;
97 AudioDeviceID adid;
98 AudioStreamBasicDescription asbd;
99
100 /* If this looks suspiciously like libao's macosx driver there's an
101 * excellent reason for that... */
102
103 /* TODO report errors as strings not numbers */
104 propertySize = sizeof adid;
105 status = AudioHardwareGetProperty(kAudioHardwarePropertyDefaultOutputDevice,
106 &propertySize, &adid);
107 if(status)
108 fatal(0, "AudioHardwareGetProperty: %d", (int)status);
109 if(adid == kAudioDeviceUnknown)
110 fatal(0, "no output device");
111 propertySize = sizeof asbd;
112 status = AudioDeviceGetProperty(adid, 0, false,
113 kAudioDevicePropertyStreamFormat,
114 &propertySize, &asbd);
115 if(status)
116 fatal(0, "AudioHardwareGetProperty: %d", (int)status);
117 D(("mSampleRate %f", asbd.mSampleRate));
118 D(("mFormatID %08lx", asbd.mFormatID));
119 D(("mFormatFlags %08lx", asbd.mFormatFlags));
120 D(("mBytesPerPacket %08lx", asbd.mBytesPerPacket));
121 D(("mFramesPerPacket %08lx", asbd.mFramesPerPacket));
122 D(("mBytesPerFrame %08lx", asbd.mBytesPerFrame));
123 D(("mChannelsPerFrame %08lx", asbd.mChannelsPerFrame));
124 D(("mBitsPerChannel %08lx", asbd.mBitsPerChannel));
125 D(("mReserved %08lx", asbd.mReserved));
126 if(asbd.mFormatID != kAudioFormatLinearPCM)
127 fatal(0, "audio device does not support kAudioFormatLinearPCM");
128 status = AudioDeviceAddIOProc(adid, adioproc, 0);
129 if(status)
130 fatal(0, "AudioDeviceAddIOProc: %d", (int)status);
131 pthread_mutex_lock(&lock);
132 for(;;) {
133 /* Wait for the buffer to fill up a bit */
134 playrtp_fill_buffer();
135 /* Start playing now */
136 info("Playing...");
137 next_timestamp = pheap_first(&packets)->timestamp;
138 active = 1;
139 status = AudioDeviceStart(adid, adioproc);
140 if(status)
141 fatal(0, "AudioDeviceStart: %d", (int)status);
142 /* Wait until the buffer empties out */
143 while(nsamples >= minbuffer
144 || (nsamples > 0
145 && contains(pheap_first(&packets), next_timestamp)))
146 pthread_cond_wait(&cond, &lock);
147 /* Stop playing for a bit until the buffer re-fills */
148 status = AudioDeviceStop(adid, adioproc);
149 if(status)
150 fatal(0, "AudioDeviceStop: %d", (int)status);
151 active = 0;
152 /* Go back round */
153 }
154}
155
156#endif
157
158/*
159Local Variables:
160c-basic-offset:2
161comment-column:40
162fill-column:79
163indent-tabs-mode:nil
164End:
165*/