chiark / gitweb /
Reduce track choice neophilia a bit
[disorder] / clients / playrtp-coreaudio.c
CommitLineData
c593cf7c 1/*
2 * This file is part of DisOrder.
3 * Copyright (C) 2007 Richard Kettlewell
4 *
e7eb3a27 5 * This program is free software: you can redistribute it and/or modify
c593cf7c 6 * it under the terms of the GNU General Public License as published by
e7eb3a27 7 * the Free Software Foundation, either version 3 of the License, or
c593cf7c 8 * (at your option) any later version.
9 *
e7eb3a27
RK
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.
14 *
c593cf7c 15 * You should have received a copy of the GNU General Public License
e7eb3a27 16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
c593cf7c 17 */
18/** @file clients/playrtp-coreaudio.c
19 * @brief RTP player - Core Audio support
20 */
21
05b75f8d 22#include "common.h"
c593cf7c 23
24#if HAVE_COREAUDIO_AUDIOHARDWARE_H
c593cf7c 25#include <pthread.h>
26#include <CoreAudio/AudioHardware.h>
27
28#include "mem.h"
29#include "log.h"
30#include "vector.h"
31#include "heap.h"
32#include "playrtp.h"
33
34/** @brief Callback from Core Audio */
35static 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;
46
47 pthread_mutex_lock(&lock);
48 while(nbuffers > 0) {
49 float *samplesOut = ab->mData;
50 size_t samplesOutLeft = ab->mDataByteSize / sizeof (float);
51
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);
59
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;
e9b635a3
RK
65 if(dump_buffer) {
66 size_t n;
67
68 for(n = 0; n < samples_available; ++n) {
69 dump_buffer[dump_index++] = (int16_t)ntohs(ptr[n]);
70 dump_index %= dump_size;
71 }
72 }
c593cf7c 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
76 * round */
77 } else {
78 /* No packet is ready to play (and there might be no packet at all) */
79 samples_available = p ? p->timestamp - next_timestamp
80 : samplesOutLeft;
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;
abf849cf
RK
88 if(dump_buffer) {
89 size_t n;
90
91 for(n = 0; n < samples_available; ++n) {
92 dump_buffer[dump_index++] = 0;
93 dump_index %= dump_size;
94 }
95 }
c593cf7c 96 }
97 }
98 ++ab;
99 --nbuffers;
100 }
101 pthread_mutex_unlock(&lock);
102 return 0;
103}
104
105void playrtp_coreaudio(void) {
106 OSStatus status;
107 UInt32 propertySize;
108 AudioDeviceID adid;
109 AudioStreamBasicDescription asbd;
110
111 /* If this looks suspiciously like libao's macosx driver there's an
112 * excellent reason for that... */
113
114 /* TODO report errors as strings not numbers */
115 propertySize = sizeof adid;
116 status = AudioHardwareGetProperty(kAudioHardwarePropertyDefaultOutputDevice,
117 &propertySize, &adid);
118 if(status)
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);
126 if(status)
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);
140 if(status)
141 fatal(0, "AudioDeviceAddIOProc: %d", (int)status);
142 pthread_mutex_lock(&lock);
143 for(;;) {
144 /* Wait for the buffer to fill up a bit */
145 playrtp_fill_buffer();
146 /* Start playing now */
147 info("Playing...");
148 next_timestamp = pheap_first(&packets)->timestamp;
149 active = 1;
150 status = AudioDeviceStart(adid, adioproc);
151 if(status)
152 fatal(0, "AudioDeviceStart: %d", (int)status);
153 /* Wait until the buffer empties out */
154 while(nsamples >= minbuffer
155 || (nsamples > 0
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);
160 if(status)
161 fatal(0, "AudioDeviceStop: %d", (int)status);
162 active = 0;
163 /* Go back round */
164 }
165}
166
167#endif
168
169/*
170Local Variables:
171c-basic-offset:2
172comment-column:40
173fill-column:79
174indent-tabs-mode:nil
175End:
176*/