chiark / gitweb /
Start rewriting Disobedience choose tab using native tree. Much
[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 "common.h"
25
26 #if HAVE_COREAUDIO_AUDIOHARDWARE_H
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 */
37 static 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;
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         }
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;
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         }
98       }
99     }
100     ++ab;
101     --nbuffers;
102   }
103   pthread_mutex_unlock(&lock);
104   return 0;
105 }
106
107 void 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 /*
172 Local Variables:
173 c-basic-offset:2
174 comment-column:40
175 fill-column:79
176 indent-tabs-mode:nil
177 End:
178 */