summaryrefslogtreecommitdiffstats
path: root/media-sound/mpg123/files/mpg123-osx.diff
blob: a58fffd147b7772db4838bf283b20f2231b08cf6 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
diff -Naur mpg123-orig/Makefile mpg123/Makefile
--- mpg123-orig/Makefile	Sun Apr 10 13:35:56 2005
+++ mpg123/Makefile	Sun Apr 10 13:40:03 2005
@@ -54,6 +54,7 @@
 	@echo "make netbsd         NetBSD"
 	@echo "make openbsd        OpenBSD"
 	@echo "make mint           MiNT on Atari"
+	@echo "make macos	   MacOSX
 	@echo "make generic        try this one if your system isn't listed above"
 	@echo ""
 	@echo "Please read the file INSTALL for additional information."
@@ -149,6 +150,16 @@
 	@echo "Please read the file INSTALL for additional information."
 	@echo ""
 
+macos-help:
+	@echo ""
+	@echo "There are several Mac OS X flavours. Choose one:"
+	@echo ""
+	@echo "make macos"
+	@echo "make macos-esd"
+	@echo ""
+	@echo "Please read the file INSTALL for additional information."
+	@echo ""
+
 linux-devel:
 	$(MAKE) OBJECTS='decode_i386.o dct64_i386.o audio_oss.o' \
          LDFLAGS= \
@@ -759,6 +770,24 @@
 			-DI386_ASSEM -DREAL_IS_FLOAT -DUSE_MMAP -DOSS \
 			-DDONT_CATCH_SIGNALS -DNAS' \
 		mpg123-make
+
+macos:
+	$(MAKE) CC=cc LDFLAGS='$(LDFLAGS)' AUDIO_LIB='-framework CoreAudio' \
+		OBJECTS='decode.o dct64.o audio_macosx.o term.o' \
+		CFLAGS='$(CFLAGS) -DINET6 -DTERM_CONTROL -DMAC_OS_X -Wall -O2 -DPPC_ENDIAN' \
+		mpg123-make
+
+macos-generic:
+	$(MAKE) CC=cc LDFLAGS='$(LDFLAGS)' AUDIO_LIB='-framework CoreAudio' \
+		OBJECTS='decode.o dct64.o audio_macosx.o term.o' \
+		CFLAGS='$(CFLAGS) -DINET6 -DTERM_CONTROL -DMAC_OS_X -Wall -O2 -DPPC_ENDIAN' \
+		mpg123-make
+
+macos-esd:
+	$(MAKE) CC=cc LDFLAGS='$(LDFLAGS)' AUDIO_LIB='-lesd -laudiofile' \
+		OBJECTS='decode.o dct64.o audio_esd.o' \
+		CFLAGS='$(CFLAGS) -O -DDARWIN -Wall -O2 -DPPC_ENDIAN -DUSE_ESD' \
+		mpg123-make	
 
 mint:
 	$(MAKE)  LDFLAGS= \
diff -Naur mpg123-orig/audio_macosx.c mpg123/audio_macosx.c
--- mpg123-orig/audio_macosx.c	Wed Dec 31 19:00:00 1969
+++ mpg123/audio_macosx.c	Sun Apr 10 13:47:14 2005
@@ -0,0 +1,337 @@
+/*- This is a 80 chars line, to have pretty formatted comments ---------------*/
+
+/* audio_macosx.c, originally written by Guillaume Outters
+ * to contact the author, please mail to: guillaume.outters@free.fr
+ *
+ * This file is some quick pre-alpha patch to allow me to have some music for my
+ * long working days, and it does it well. But it surely isn't a final version;
+ * as Mac OS X requires at least a G3, I'm not sure it will be useful to
+ * implement downsampling.
+ *
+ * Mac OS X audio works by asking you to fill its buffer, and, to complicate a
+ * bit, you must provide it with floats. In order not to patch too much mpg123,
+ * we'll accept signed short (mpg123 "approved" format) and transform them into
+ * floats as soon as received. Let's say this way calculations are faster.
+ *
+ * As we don't have some /dev/audio device with blocking write, we'll have to
+ * stop mpg123 before it does too much work, while we are waiting our dump proc
+ * to be called. I wanted to use semaphores, but they still need an
+ * implementation from Apple before I can do anything. So we'll block using a
+ * sleep and wake up on SIGUSR2.
+ * Version 0.2: now I use named semaphores (which are implemented AND work).
+ * Preprocessor flag MOSX_USES_SEM (defined at the beginning of this file)
+ * enables this behaviour.
+ *
+ * In order always to have a ready buffer to be dumped when the routine gets
+ * called, we have a "buffer loop" of NUMBER_BUFFERS buffers. mpg123 fills it
+ * on one extremity ('to'), playProc reads it on another point ('from'). 'to'
+ * blocks when it arrives on 'from' (having filled the whole circle of buffers)
+ * and 'from' blocks when no data is available. As soon as it has emptied a
+ * buffer, if mpg123 is sleeping, it awakes it quite brutaly (SIGUSR2) to tell
+ * it to fill the buffer. */
+
+#ifndef MOSX_USES_SEM
+#define MOSX_USES_SEM 1	/* Semaphores or sleep()/kill()? I would say semaphores, but this is just my advice, after all */
+#endif
+#ifndef MOSX_SEM_V2
+#define MOSX_SEM_V2 1
+#endif
+
+#include "mpg123.h"
+#include <CoreAudio/AudioHardware.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#if MOSX_USES_SEM
+#include <semaphore.h>
+#endif
+
+struct aBuffer
+{
+	float * buffer;
+	long size;
+	
+	float * ptr;	/* Where in the buffer are we? */
+	long remaining;
+	
+	struct aBuffer * next;
+};
+typedef struct aBuffer aBuffer;
+
+struct anEnv
+{
+	long size;
+	short * debut;
+	short * ptr;
+	AudioDeviceID device;
+	char play;
+	
+	/* Intermediate buffers */
+	
+	#if MOSX_USES_SEM
+	sem_t * semaphore;
+	#else
+	char wait;	/* mpg123 is waiting (due to the semaphore) to show the world its power; let's free him! */
+	pid_t pid;
+	#endif
+	aBuffer * from;	/* Current buffers */
+	aBuffer * to;
+};
+
+static struct anEnv env;
+
+#define ENV ((struct anEnv *)inClientData)
+#define NUMBER_BUFFERS 16	/* Tried with 3 buffers, but then any little window move is sufficient to stop the sound. Here we have 1.5 seconds music buffered */
+
+void destroyBuffers()
+{
+	aBuffer * ptr;
+	aBuffer * ptr2;
+	
+	ptr = env.to->next;
+	env.to->next = NULL;
+	while(ptr)
+	{
+		ptr2 = ptr->next;
+		if(ptr->buffer) free(ptr->buffer);
+		free(ptr);
+		ptr = ptr2;
+	}
+}
+
+void initBuffers()
+{
+	long m;
+	aBuffer ** ptrptr;
+	
+	ptrptr = &env.to;
+	for(m = 0; m < NUMBER_BUFFERS; m++)
+	{
+		*ptrptr = malloc(sizeof(aBuffer));
+		(*ptrptr)->size = 0;
+		(*ptrptr)->remaining = 0;
+		(*ptrptr)->buffer = NULL;
+		ptrptr = &(*ptrptr)->next;
+		#if MOSX_USES_SEM
+		sem_post(env.semaphore);	/* This buffer is ready for filling (of course, it is empty!) */ 
+		#endif
+	}
+	*ptrptr = env.from = env.to;
+}
+
+int fillBuffer(aBuffer * b, short * source, long size)
+{
+	float * dest;
+	
+	if(b->remaining)	/* Non empty buffer, must still be playing */
+		return(-1);
+	if(b->size != size)	/* Hey! What's that? Coudn't this buffer size be fixed once (well, perhaps we just didn't allocate it yet) */
+	{
+		if(b->buffer) free(b->buffer);
+		b->buffer = malloc(size * sizeof(float));
+		b->size = size;
+	}
+	
+	dest = b->buffer;
+	while(size--)
+		//*dest++ = ((*source++) + 32768) / 65536.0;
+		*dest++ = (*source++) / 32768.0;
+	
+	b->ptr = b->buffer;
+	b->remaining = b->size;	/* Do this at last; we shouldn't show the buffer is full before it is effectively */
+	
+	#ifdef DEBUG_MOSX
+	printf("."); fflush(stdout);
+	#endif
+	
+	return(0);
+}
+
+OSStatus playProc(AudioDeviceID inDevice, const AudioTimeStamp * inNow, const AudioBufferList * inInputData, const AudioTimeStamp * inInputTime, AudioBufferList * outOutputData, const AudioTimeStamp * inOutputTime, void * inClientData)
+{
+	long m, n, o;
+	float * dest;
+	
+	for(o = 0; o < outOutputData->mNumberBuffers; o++)
+	{
+		m = outOutputData->mBuffers[o].mDataByteSize / sizeof(float);	/* What we have to fill */
+		dest = outOutputData->mBuffers[o].mData;
+		
+		while(m > 0)
+		{
+			if( (n = ENV->from->remaining) <= 0 )	/* No more bytes in the current read buffer! */
+			{
+				while( (n = ENV->from->remaining) <= 0)
+					usleep(2000);	/* Let's wait a bit for the results... */
+			}
+						
+			/* We dump what we can */
+			
+			if(n > m) n = m;	/* In fact, just the necessary should be sufficient (I think) */
+			
+			memcpy(dest, ENV->from->ptr, n * sizeof(float));
+			
+			/* Let's remember all done work */
+			
+			m -= n;
+			ENV->from->ptr += n;
+			if( (ENV->from->remaining -= n) <= 0)	/* ... and tell mpg123 there's a buffer to fill */
+			{
+				#if MOSX_USES_SEM
+				sem_post(ENV->semaphore);
+				#else
+				if(ENV->wait)
+				{
+					kill(ENV->pid, SIGUSR2);
+					ENV->wait = 0;
+				}
+				#endif
+				ENV->from = ENV->from->next;
+			}
+		}
+	}
+	
+	return (0); 
+}
+
+#if ! MOSX_USES_SEM
+void start(int n)
+{
+	signal(SIGUSR2, start);
+}
+#endif
+
+int audio_open(struct audio_info_struct *ai)
+{
+	long size;
+	AudioStreamBasicDescription format;
+	#if MOSX_USES_SEM
+	char s[10];
+	long m;
+	#endif
+	/*float vol;
+	OSStatus e;*/
+	
+	/* Where did that default audio output go? */
+	
+	size = sizeof(env.device);
+	if(AudioHardwareGetProperty(kAudioHardwarePropertyDefaultOutputDevice, &size, &env.device)) return(-1);
+		
+	/* Hmmm, let's choose PCM format */
+	
+	size = sizeof(format);
+	if(AudioDeviceGetProperty(env.device, 0, 0, kAudioDevicePropertyStreamFormat, &size, &format)) return(-1);
+	if(format.mFormatID != kAudioFormatLinearPCM) return(-1);
+	
+	/* Let's test volume, which doesn't seem to work (but this is only an alpha, remember?) */
+	
+	/*vol = 0.5;
+	size = sizeof(vol);
+	if(e = AudioDeviceSetProperty(env.device, NULL, 0, 0, 'volm', size, &vol)) { printf("Didn't your mother ever tell you not to hear music so loudly?\n"); return(-1); } */
+	
+	/* Let's init our environment */
+	
+	env.size = 0;
+	env.debut = NULL;
+	env.ptr = NULL;
+	env.play = 0;
+	
+	#if MOSX_USES_SEM
+	strcpy(s, "/mpg123-0000");
+	do
+	{
+		for(m = 10;; m--)
+			if( (s[m]++) <= '9')
+				break;
+			else
+				s[m] = '0';
+	} while( (env.semaphore = sem_open(s, O_CREAT | O_EXCL, 0644, 0)) == (sem_t *)SEM_FAILED);
+	#else
+	env.pid = getpid();
+	env.wait = 0;
+	signal(SIGUSR2, start);
+	#endif
+	
+	initBuffers();
+		
+	/* And prepare audio launching */
+	
+	if(AudioDeviceAddIOProc(env.device, playProc, &env)) return(-1);
+	
+	return(0);
+}
+
+int audio_reset_parameters(struct audio_info_struct *ai)
+{
+	return 0;
+}
+
+int audio_rate_best_match(struct audio_info_struct *ai)
+{
+	return 0;
+}
+
+int audio_set_rate(struct audio_info_struct *ai)
+{
+	return 0;
+}
+
+int audio_set_channels(struct audio_info_struct *ai)
+{
+	return 0;
+}
+
+int audio_set_format(struct audio_info_struct *ai)
+{
+	return 0;
+}
+
+int audio_get_formats(struct audio_info_struct *ai)
+{
+	return AUDIO_FORMAT_SIGNED_16;
+}
+
+int audio_play_samples(struct audio_info_struct *ai,unsigned char *buf,int len)
+{
+	/* We have to calm down mpg123, else he wouldn't hesitate to drop us another buffer (which would be the same, in fact) */
+	
+	#if MOSX_USES_SEM && MOSX_SEM_V2	/* Suddenly, I have some kind of doubt: HOW COULD IT WORK??? */
+	while(sem_wait(env.semaphore)){}	/* We just have to wait a buffer fill request */
+	fillBuffer(env.to, (short *)buf, len / sizeof(short));
+	#else
+	while(fillBuffer(env.to, (short *)buf, len / sizeof(short)) < 0)	/* While the next buffer to write is not empty, we wait a bit... */
+	{
+		#ifdef DEBUG_MOSX
+		printf("|"); fflush(stdout);
+		#endif
+		#if MOSX_USES_SEM
+		sem_wait(env.semaphore);	/* This is a bug. I should wait for the semaphore once per empty buffer (and not each time fillBuffers() returns -1). See MOSX_SEM_V2; this was a too quickly modified part when I implemented MOSX_USES_SEM. */
+		#else
+		env.wait = 1;
+		sleep(3600);	/* This should be sufficient, shouldn't it? */
+		#endif
+	}
+	#endif
+	env.to = env.to->next;
+	
+	/* And we lauch action if not already done */
+	
+	if(!env.play)
+	{
+		if(AudioDeviceStart(env.device, playProc)) return(-1);
+		env.play = 1;
+	}
+	
+	return len;
+}
+
+int audio_close(struct audio_info_struct *ai)
+{
+	AudioDeviceStop(env.device, playProc);	/* No matter the error code, we want to close it (by brute force if necessary) */
+	AudioDeviceRemoveIOProc(env.device, playProc);
+	destroyBuffers();
+	#if MOSX_USES_SEM
+	sem_close(env.semaphore);
+	#endif
+	return 0;
+}