chiark / gitweb /
Move track sorting to its own function. Only choose_populate() uses
[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
05b75f8d 24#include "common.h"
c593cf7c 25
26#if HAVE_COREAUDIO_AUDIOHARDWARE_H
c593cf7c 27#include <pthread.h>
28#include <CoreAudio/AudioHardware.h>
29
30#include "mem.h"
31#include "log.h"
32#include "vector.h"
33#include "heap.h"
34#include "playrtp.h"
35
36/** @brief Callback from Core Audio */
37static OSStatus adioproc
38 (AudioDeviceID attribute((unused)) inDevice,
39 const AudioTimeStamp attribute((unused)) *inNow,
40 const AudioBufferList attribute((unused)) *inInputData,
41 const AudioTimeStamp attribute((unused)) *inInputTime,
42 AudioBufferList *outOutputData,
43 const AudioTimeStamp attribute((unused)) *inOutputTime,
44 void attribute((unused)) *inClientData) {
45 UInt32 nbuffers = outOutputData->mNumberBuffers;
46 AudioBuffer *ab = outOutputData->mBuffers;
47 uint32_t samples_available;
48
49 pthread_mutex_lock(&lock);
50 while(nbuffers > 0) {
51 float *samplesOut = ab->mData;
52 size_t samplesOutLeft = ab->mDataByteSize / sizeof (float);
53
54 while(samplesOutLeft > 0) {
55 const struct packet *p = playrtp_next_packet();
56 if(p && contains(p, next_timestamp)) {
57 /* This packet is ready to play */
58 const uint32_t packet_end = p->timestamp + p->nsamples;
59 const uint32_t offset = next_timestamp - p->timestamp;
60 const uint16_t *ptr = (void *)(p->samples_raw + offset);
61
62 samples_available = packet_end - next_timestamp;
63 if(samples_available > samplesOutLeft)
64 samples_available = samplesOutLeft;
65 next_timestamp += samples_available;
66 samplesOutLeft -= samples_available;
e9b635a3
RK
67 if(dump_buffer) {
68 size_t n;
69
70 for(n = 0; n < samples_available; ++n) {
71 dump_buffer[dump_index++] = (int16_t)ntohs(ptr[n]);
72 dump_index %= dump_size;
73 }
74 }
c593cf7c 75 while(samples_available-- > 0)
76 *samplesOut++ = (int16_t)ntohs(*ptr++) * (0.5 / 32767);
77 /* We don't bother junking the packet - that'll be dealt with next time
78 * round */
79 } else {
80 /* No packet is ready to play (and there might be no packet at all) */
81 samples_available = p ? p->timestamp - next_timestamp
82 : samplesOutLeft;
83 if(samples_available > samplesOutLeft)
84 samples_available = samplesOutLeft;
85 //info("infill by %"PRIu32, samples_available);
86 /* Conveniently the buffer is 0 to start with */
87 next_timestamp += samples_available;
88 samplesOut += samples_available;
89 samplesOutLeft -= samples_available;
abf849cf
RK
90 if(dump_buffer) {
91 size_t n;
92
93 for(n = 0; n < samples_available; ++n) {
94 dump_buffer[dump_index++] = 0;
95 dump_index %= dump_size;
96 }
97 }
c593cf7c 98 }
99 }
100 ++ab;
101 --nbuffers;
102 }
103 pthread_mutex_unlock(&lock);
104 return 0;
105}
106
107void playrtp_coreaudio(void) {
108 OSStatus status;
109 UInt32 propertySize;
110 AudioDeviceID adid;
111 AudioStreamBasicDescription asbd;
112
113 /* If this looks suspiciously like libao's macosx driver there's an
114 * excellent reason for that... */
115
116 /* TODO report errors as strings not numbers */
117 propertySize = sizeof adid;
118 status = AudioHardwareGetProperty(kAudioHardwarePropertyDefaultOutputDevice,
119 &propertySize, &adid);
120 if(status)
121 fatal(0, "AudioHardwareGetProperty: %d", (int)status);
122 if(adid == kAudioDeviceUnknown)
123 fatal(0, "no output device");
124 propertySize = sizeof asbd;
125 status = AudioDeviceGetProperty(adid, 0, false,
126 kAudioDevicePropertyStreamFormat,
127 &propertySize, &asbd);
128 if(status)
129 fatal(0, "AudioHardwareGetProperty: %d", (int)status);
130 D(("mSampleRate %f", asbd.mSampleRate));
131 D(("mFormatID %08lx", asbd.mFormatID));
132 D(("mFormatFlags %08lx", asbd.mFormatFlags));
133 D(("mBytesPerPacket %08lx", asbd.mBytesPerPacket));
134 D(("mFramesPerPacket %08lx", asbd.mFramesPerPacket));
135 D(("mBytesPerFrame %08lx", asbd.mBytesPerFrame));
136 D(("mChannelsPerFrame %08lx", asbd.mChannelsPerFrame));
137 D(("mBitsPerChannel %08lx", asbd.mBitsPerChannel));
138 D(("mReserved %08lx", asbd.mReserved));
139 if(asbd.mFormatID != kAudioFormatLinearPCM)
140 fatal(0, "audio device does not support kAudioFormatLinearPCM");
141 status = AudioDeviceAddIOProc(adid, adioproc, 0);
142 if(status)
143 fatal(0, "AudioDeviceAddIOProc: %d", (int)status);
144 pthread_mutex_lock(&lock);
145 for(;;) {
146 /* Wait for the buffer to fill up a bit */
147 playrtp_fill_buffer();
148 /* Start playing now */
149 info("Playing...");
150 next_timestamp = pheap_first(&packets)->timestamp;
151 active = 1;
152 status = AudioDeviceStart(adid, adioproc);
153 if(status)
154 fatal(0, "AudioDeviceStart: %d", (int)status);
155 /* Wait until the buffer empties out */
156 while(nsamples >= minbuffer
157 || (nsamples > 0
158 && contains(pheap_first(&packets), next_timestamp)))
159 pthread_cond_wait(&cond, &lock);
160 /* Stop playing for a bit until the buffer re-fills */
161 status = AudioDeviceStop(adid, adioproc);
162 if(status)
163 fatal(0, "AudioDeviceStop: %d", (int)status);
164 active = 0;
165 /* Go back round */
166 }
167}
168
169#endif
170
171/*
172Local Variables:
173c-basic-offset:2
174comment-column:40
175fill-column:79
176indent-tabs-mode:nil
177End:
178*/