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 */ |
40 | static 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 | |
94 | void 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 | /* |
159 | Local Variables: |
160 | c-basic-offset:2 |
161 | comment-column:40 |
162 | fill-column:79 |
163 | indent-tabs-mode:nil |
164 | End: |
165 | */ |