1 /* -*- c-file-style: "gnu" -*-
2 TiMidity++ -- MIDI to WAVE converter and player
3 Copyright (C) 1999-2002 Masanao Izumo <mo@goice.co.jp>
4 Copyright (C) 1995 Tuukka Toivonen <tt@cgs.fi>
5 ALSA 0.[56] support by Katsuhiro Ueno <katsu@blue.sky.or.jp>
6
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
11
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with this program; if not, write to the Free Software
19 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20
21 darwin_a.c
22
23 Functions to play sound on the Darwin audio driver
24 by T.Nogami <t-nogami@happy.email.ne.jp>
25 */
26
27 #ifdef HAVE_CONFIG_H
28 #include "config.h"
29 #endif /* HAVE_CONFIG_H */
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <unistd.h>
33 #include <fcntl.h>
34
35 #ifndef NO_STRING_H
36 #include <string.h>
37 #else
38 #include <strings.h>
39 #endif
40
41 #include <CoreAudio/AudioHardware.h>
42
43 #include "timidity.h"
44 #include "common.h"
45 #include "output.h"
46 #include "controls.h"
47 #include "timer.h"
48 #include "instrum.h"
49 #include "playmidi.h"
50 #include "miditrace.h"
51
52 static int open_output(void); /* 0=success, 1=warning, -1=fatal error */
53 static void close_output(void);
54 static int output_data(char *buf, int32 in_bytes);
55 static int acntl(int request, void *arg);
56 static int total_bytes;
57 float output_volume = 1.0;
58
59 int mac_buf_using_num;
60
61 /* export the playback mode */
62
63 /*********************************************************************/
64 #define dpm darwin_play_mode
65
66 PlayMode dpm = {
67 DEFAULT_RATE, PE_16BIT|PE_SIGNED, PF_PCM_STREAM|PF_CAN_TRACE,
68 -1,
69 {0}, /* default: get all the buffer fragments you can */
70 "Mac OS X pcm device", 'd',
71 "",
72 open_output,
73 close_output,
74 output_data,
75 acntl
76 };
77
78 #define FailWithAction(cond, action, handler) \
79 if (cond) { \
80 { action; } \
81 goto handler; \
82 }
83
84 //#define DEBUG_DARWIN_A(x) /*nothing*/
85 #define DEBUG_DARWIN_A(x) ctl->cmsg x;
86
87 /*********************************************************************/
88
89 typedef struct {
90 Boolean soundPlaying;
91 AudioDeviceID device; // the default device
92 UInt32 deviceBufferSize;
93 // bufferSize returned by kAudioDevicePropertyBufferSize
94 AudioStreamBasicDescription deviceFormat;
95 // info about the default device
96 #define BUFNUM 256
97 #define BUFLEN 4096
98 char buffer[BUFNUM][BUFLEN]; //1 buffer size = 4096bytes
99 unsigned short buffer_len[BUFNUM];
100 volatile int currBuf, nextBuf;
101 int samples, get_samples;
102 } appGlobals, *appGlobalsPtr, **appGlobalsHandle;
103
104 static appGlobals globals;
105
106
107 /*********************************************************************/
appIOProc(AudioDeviceID inDevice,const AudioTimeStamp * inNow,const AudioBufferList * inInputData,const AudioTimeStamp * inInputTime,AudioBufferList * outOutputData,const AudioTimeStamp * inOutputTime,void * dummy)108 OSStatus appIOProc (AudioDeviceID inDevice, const AudioTimeStamp* inNow,
109 const AudioBufferList* inInputData,
110 const AudioTimeStamp* inInputTime,
111 AudioBufferList* outOutputData,
112 const AudioTimeStamp* inOutputTime, void* dummy )
113 {
114 int next_curr;
115 int trans_len;
116
117 if( globals.currBuf==globals.nextBuf ){ //end of play
118 outOutputData->mNumberBuffers=0;
119 memset(outOutputData->mBuffers[0].mData, 0, globals.deviceBufferSize);
120 globals.soundPlaying=0;
121 return 0;
122 }
123
124 trans_len = globals.buffer_len[globals.currBuf];
125 if( output_volume==1.0 ){
126 memcpy( outOutputData->mBuffers[0].mData,
127 &globals.buffer[globals.currBuf][0],
128 trans_len); // move data into output data buffer
129 }else{
130 float *src = (float*)&globals.buffer[globals.currBuf][0],
131 *dst = outOutputData->mBuffers[0].mData;
132 int i, quant = trans_len/sizeof(float);
133 for(i=0; i<quant; i++){
134 *dst++ = (*src++)*output_volume;
135 }
136 }
137 outOutputData->mBuffers[0].mDataByteSize = trans_len;
138
139 outOutputData->mNumberBuffers=1;
140 globals.samples += trans_len
141 / globals.deviceFormat.mBytesPerPacket;
142
143 next_curr = globals.currBuf+1;
144 next_curr %= BUFNUM;
145 mac_buf_using_num--;
146 globals.currBuf = next_curr;
147
148 return 0; //no err
149 }
150
151 /*********************************************************************/
init_variable()152 static void init_variable()
153 {
154 globals.samples = globals.get_samples = 0;
155 globals.currBuf = globals.nextBuf = 0;
156 globals.soundPlaying = 0;
157 mac_buf_using_num = 0;
158 }
159
160 /*********************************************************************/
161 /*return value == 0 sucess
162 * == -1 fails
163 */
open_output(void)164 static int open_output(void)
165 {
166 OSStatus err = 0; //no err
167 UInt32 count,
168 bufferSize;
169 AudioDeviceID device = kAudioDeviceUnknown;
170 AudioStreamBasicDescription format;
171
172 // get the default output device for the HAL
173 count = sizeof(globals.device);
174 // it is required to pass the size of the data to be returned
175 err = AudioHardwareGetProperty(kAudioHardwarePropertyDefaultOutputDevice,
176 &count, (void *) &device);
177 if (err != 0) goto Bail;
178
179 // get the buffersize that the default device uses for IO
180 count = sizeof(globals.deviceBufferSize);
181 // it is required to pass the size of the data to be returned
182 err = AudioDeviceGetProperty(device, 0, 0, kAudioDevicePropertyBufferSize,
183 &count, &bufferSize);
184 if (err != 0) goto Bail;
185
186 if( globals.deviceBufferSize>BUFLEN ){
187 fprintf(stderr, "globals.deviceBufferSize NG: %ld\n",
188 globals.deviceBufferSize);
189 exit(1);
190 }
191
192 // get a description of the data format used by the default device
193 count = sizeof(globals.deviceFormat);
194 // it is required to pass the size of the data to be returned
195 err = AudioDeviceGetProperty(device, 0, 0,
196 kAudioDevicePropertyStreamFormat,
197 &count, &format);
198 if (err != 0) goto Bail;
199 FailWithAction(format.mFormatID != kAudioFormatLinearPCM, err = -1, Bail);
200 // bail if the format is not linear pcm
201
202 // everything is ok so fill in these globals
203 globals.device = device;
204 globals.deviceBufferSize = bufferSize;
205 globals.deviceFormat = format;
206 init_variable();
207
208 err = AudioDeviceAddIOProc(globals.device, appIOProc, 0 );
209 // setup our device with an IO proc
210 if (err != 0) goto Bail;
211
212 globals.deviceFormat.mSampleRate = dpm.rate;
213
214 #if 0
215 globals.deviceFormat.mFormatFlags = kLinearPCMFormatFlagIsBigEndian
216 | kLinearPCMFormatFlagIsPacked
217 | kLinearPCMFormatFlagIsSignedInteger;
218 globals.deviceFormat.mBytesPerPacket = 4;
219 globals.deviceFormat.mBytesPerFrame = 4;
220 globals.deviceFormat.mBitsPerChannel = 0x10;
221
222 err = AudioDeviceSetProperty(device, &inWhen, 0, 0,
223 kAudioDevicePropertyStreamFormat,
224 count, &globals.deviceFormat);
225 if (err != 0) goto Bail;
226 #endif
227
228 #if 0
229 fprintf(stderr, "deviceBufferSize = %d\n", globals.deviceBufferSize);
230 fprintf(stderr, "mSampleRate = %g\n", globals.deviceFormat.mSampleRate);
231 fprintf(stderr, "mFormatID = 0x%08x\n", globals.deviceFormat.mFormatID);
232 fprintf(stderr, "mFormatFlags = 0x%08x\n",
233 globals.deviceFormat.mFormatFlags);
234 fprintf(stderr, "mBytesPerPacket = 0x%08x\n",
235 globals.deviceFormat.mBytesPerPacket);
236 fprintf(stderr, "mBytesPerFrame = 0x%08x\n",
237 globals.deviceFormat.mBytesPerFrame);
238 fprintf(stderr, "mBitsPerChannel = 0x%08x\n",
239 globals.deviceFormat.mBitsPerChannel);
240 #endif
241
242 Bail:
243 return (err);
244 }
245
246 /*********************************************************************/
output_data(char * buf,int32 in_bytes)247 static int output_data(char *buf, int32 in_bytes)
248 {
249 OSStatus err = 0;
250 int next_nextbuf, out_bytes;
251 int inBytesPerQuant, max_quant, max_outbytes, out_quant, i;
252 float maxLevel;
253
254
255 //quant : 1 value
256 //packet : 1 pair of quant(stereo data)
257
258 if (dpm.encoding & PE_16BIT){
259 inBytesPerQuant = 2;
260 maxLevel=32768.0;
261 }else if(dpm.encoding & PE_24BIT){
262 inBytesPerQuant = 3;
263 maxLevel=8388608.0;
264 }else{
265 ctl->cmsg(CMSG_ERROR, VERB_NORMAL,
266 "Sorry, not support 8bit sound.");
267 exit(1);
268 }
269
270 max_outbytes = globals.deviceBufferSize;
271 max_quant = max_outbytes / sizeof(float);
272
273
274 redo:
275 out_quant = in_bytes/inBytesPerQuant;
276 out_bytes= out_quant * sizeof(float);
277 if( out_bytes > max_outbytes ){
278 out_bytes = max_outbytes;
279 }
280 out_quant = out_bytes/sizeof(float);
281 out_quant &=0xfffffffe; //trunc to eaven number for stereo
282 out_bytes = out_quant*sizeof(float);
283
284 next_nextbuf = globals.nextBuf+1;
285 next_nextbuf %= BUFNUM;
286
287 while( globals.currBuf==next_nextbuf ){ //queue full
288 usleep(100000); //0.1sec
289 }
290
291 switch( inBytesPerQuant ){
292
293 case 2:
294 for( i=0; i<out_quant; i++){
295 ((float*)(globals.buffer[globals.nextBuf]))[i] =
296 ((short*)buf)[i]/maxLevel;
297 }
298 break;
299
300 case 3:
301 for( i=0; i<out_quant; i++ ){
302 ((float*)(globals.buffer[globals.nextBuf]))[i] =
303 (*(int32*)&buf[i*3]>>8) / maxLevel;
304 }
305 break;
306 }
307
308 globals.buffer_len[globals.nextBuf] = out_bytes;
309
310 if( globals.soundPlaying == 0){
311 err = AudioDeviceStart(globals.device, appIOProc);
312 if (err != 0) goto Bail;
313 // start playing sound through the device
314 globals.soundPlaying = 1; // set the playing status global to true
315
316 }
317
318
319
320 globals.nextBuf = next_nextbuf;
321
322
323 in_bytes -= out_quant*inBytesPerQuant;
324 buf += out_quant*inBytesPerQuant;
325 mac_buf_using_num++;
326 globals.get_samples += out_bytes/globals.deviceFormat.mBytesPerPacket;
327
328 if( in_bytes ){
329 goto redo;
330 }
331
332 Bail:
333 return (err);
334 }
335
336 /*********************************************************************/
close_output(void)337 static void close_output(void)
338 {
339 OSStatus err = 0;
340
341 err = AudioDeviceStop(globals.device, appIOProc);
342 // stop playing sound through the device
343 if (err != 0) goto Bail;
344
345 err = AudioDeviceRemoveIOProc(globals.device, appIOProc);
346 // remove the IO proc from the device
347 if (err != 0) goto Bail;
348
349 globals.soundPlaying = 0;
350 // set the playing status global to false
351 Bail:;
352 }
353
354 /*********************************************************************/
acntl(int request,void * arg)355 static int acntl(int request, void *arg)
356 {
357 switch (request){
358 case PM_REQ_GETFRAGSIZ:
359 if( dpm.encoding & PE_24BIT ){
360 *((int *)arg) = 3072;
361 }else{
362 *((int *)arg) = BUFLEN;
363 }
364 return 0;
365
366
367 case PM_REQ_GETSAMPLES:
368 *((int *)arg) = globals.samples;
369 return 0;
370
371 case PM_REQ_DISCARD:
372 AudioDeviceStop(globals.device, appIOProc);
373 init_variable();
374 return 0;
375
376 case PM_REQ_PLAY_START:
377 init_variable();
378 return 0;
379
380 case PM_REQ_FLUSH:
381 case PM_REQ_OUTPUT_FINISH:
382 while( globals.soundPlaying ){
383 trace_loop();
384 usleep(1000);
385 }
386 init_variable();
387 return 0;
388
389 default:
390 break;
391 }
392 return -1;
393 }
394
395