2 * This file is part of DisOrder.
3 * Copyright (C) 2007 Richard Kettlewell
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 3 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 /** @file clients/playrtp-coreaudio.c
19 * @brief RTP player - Core Audio support
24 #if HAVE_COREAUDIO_AUDIOHARDWARE_H
26 #include <CoreAudio/AudioHardware.h>
34 /** @brief Callback from Core Audio */
35 static OSStatus adioproc
36 (AudioDeviceID
attribute((unused
)) inDevice
,
37 const AudioTimeStamp
attribute((unused
)) *inNow
,
38 const AudioBufferList
attribute((unused
)) *inInputData
,
39 const AudioTimeStamp
attribute((unused
)) *inInputTime
,
40 AudioBufferList
*outOutputData
,
41 const AudioTimeStamp
attribute((unused
)) *inOutputTime
,
42 void attribute((unused
)) *inClientData
) {
43 UInt32 nbuffers
= outOutputData
->mNumberBuffers
;
44 AudioBuffer
*ab
= outOutputData
->mBuffers
;
45 uint32_t samples_available
;
47 pthread_mutex_lock(&lock
);
49 float *samplesOut
= ab
->mData
;
50 size_t samplesOutLeft
= ab
->mDataByteSize
/ sizeof (float);
52 while(samplesOutLeft
> 0) {
53 const struct packet
*p
= playrtp_next_packet();
54 if(p
&& contains(p
, next_timestamp
)) {
55 /* This packet is ready to play */
56 const uint32_t packet_end
= p
->timestamp
+ p
->nsamples
;
57 const uint32_t offset
= next_timestamp
- p
->timestamp
;
58 const uint16_t *ptr
= (void *)(p
->samples_raw
+ offset
);
60 samples_available
= packet_end
- next_timestamp
;
61 if(samples_available
> samplesOutLeft
)
62 samples_available
= samplesOutLeft
;
63 next_timestamp
+= samples_available
;
64 samplesOutLeft
-= samples_available
;
68 for(n
= 0; n
< samples_available
; ++n
) {
69 dump_buffer
[dump_index
++] = (int16_t)ntohs(ptr
[n
]);
70 dump_index
%= dump_size
;
73 while(samples_available
-- > 0)
74 *samplesOut
++ = (int16_t)ntohs(*ptr
++) * (0.5 / 32767);
75 /* We don't bother junking the packet - that'll be dealt with next time
78 /* No packet is ready to play (and there might be no packet at all) */
79 samples_available
= p ? p
->timestamp
- next_timestamp
81 if(samples_available
> samplesOutLeft
)
82 samples_available
= samplesOutLeft
;
83 //info("infill by %"PRIu32, samples_available);
84 /* Conveniently the buffer is 0 to start with */
85 next_timestamp
+= samples_available
;
86 samplesOut
+= samples_available
;
87 samplesOutLeft
-= samples_available
;
91 for(n
= 0; n
< samples_available
; ++n
) {
92 dump_buffer
[dump_index
++] = 0;
93 dump_index
%= dump_size
;
101 pthread_mutex_unlock(&lock
);
105 void playrtp_coreaudio(void) {
109 AudioStreamBasicDescription asbd
;
111 /* If this looks suspiciously like libao's macosx driver there's an
112 * excellent reason for that... */
114 /* TODO report errors as strings not numbers */
115 propertySize
= sizeof adid
;
116 status
= AudioHardwareGetProperty(kAudioHardwarePropertyDefaultOutputDevice
,
117 &propertySize
, &adid
);
119 fatal(0, "AudioHardwareGetProperty: %d", (int)status
);
120 if(adid
== kAudioDeviceUnknown
)
121 fatal(0, "no output device");
122 propertySize
= sizeof asbd
;
123 status
= AudioDeviceGetProperty(adid
, 0, false,
124 kAudioDevicePropertyStreamFormat
,
125 &propertySize
, &asbd
);
127 fatal(0, "AudioHardwareGetProperty: %d", (int)status
);
128 D(("mSampleRate %f", asbd
.mSampleRate
));
129 D(("mFormatID %08lx", asbd
.mFormatID
));
130 D(("mFormatFlags %08lx", asbd
.mFormatFlags
));
131 D(("mBytesPerPacket %08lx", asbd
.mBytesPerPacket
));
132 D(("mFramesPerPacket %08lx", asbd
.mFramesPerPacket
));
133 D(("mBytesPerFrame %08lx", asbd
.mBytesPerFrame
));
134 D(("mChannelsPerFrame %08lx", asbd
.mChannelsPerFrame
));
135 D(("mBitsPerChannel %08lx", asbd
.mBitsPerChannel
));
136 D(("mReserved %08lx", asbd
.mReserved
));
137 if(asbd
.mFormatID
!= kAudioFormatLinearPCM
)
138 fatal(0, "audio device does not support kAudioFormatLinearPCM");
139 status
= AudioDeviceAddIOProc(adid
, adioproc
, 0);
141 fatal(0, "AudioDeviceAddIOProc: %d", (int)status
);
142 pthread_mutex_lock(&lock
);
144 /* Wait for the buffer to fill up a bit */
145 playrtp_fill_buffer();
146 /* Start playing now */
148 next_timestamp
= pheap_first(&packets
)->timestamp
;
150 status
= AudioDeviceStart(adid
, adioproc
);
152 fatal(0, "AudioDeviceStart: %d", (int)status
);
153 /* Wait until the buffer empties out */
154 while(nsamples
>= minbuffer
156 && contains(pheap_first(&packets
), next_timestamp
)))
157 pthread_cond_wait(&cond
, &lock
);
158 /* Stop playing for a bit until the buffer re-fills */
159 status
= AudioDeviceStop(adid
, adioproc
);
161 fatal(0, "AudioDeviceStop: %d", (int)status
);