chiark / gitweb /
init script always address local instance of daemon
[disorder] / clients / playrtp-coreaudio.c
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 */