1 /*
2 SDL - Simple DirectMedia Layer
3 Copyright (C) 1997-2012 Sam Lantinga
4
5 This library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Lesser General Public
7 License as published by the Free Software Foundation; either
8 version 2.1 of the License, or (at your option) any later version.
9
10 This library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Lesser General Public License for more details.
14
15 You should have received a copy of the GNU Lesser General Public
16 License along with this library; if not, write to the Free Software
17 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18
19 Carsten Griwodz
20 griff@kom.tu-darmstadt.de
21
22 based on linux/SDL_dspaudio.c by Sam Lantinga
23 */
24 #include "SDL_config.h"
25
26 /* Allow access to a raw mixing buffer */
27
28 #include <errno.h>
29 #include <unistd.h>
30 #include <fcntl.h>
31 #include <sys/types.h>
32 #include <sys/time.h>
33 #include <sys/ioctl.h>
34 #include <sys/stat.h>
35 #include <sys/mman.h>
36
37 #include "SDL_audio.h"
38 #include "../SDL_audio_c.h"
39 #include "../SDL_audiodev_c.h"
40 #include "SDL_umsaudio.h"
41
42 /* The tag name used by UMS audio */
43 #define UMS_DRIVER_NAME "ums"
44
45 #define DEBUG_AUDIO 1
46
47 /* Audio driver functions */
48 static int UMS_OpenAudio(_THIS, SDL_AudioSpec *spec);
49 static void UMS_PlayAudio(_THIS);
50 static Uint8 *UMS_GetAudioBuf(_THIS);
51 static void UMS_CloseAudio(_THIS);
52
53 static UMSAudioDevice_ReturnCode UADOpen(_THIS, string device, string mode, long flags);
54 static UMSAudioDevice_ReturnCode UADClose(_THIS);
55 static UMSAudioDevice_ReturnCode UADGetBitsPerSample(_THIS, long* bits);
56 static UMSAudioDevice_ReturnCode UADSetBitsPerSample(_THIS, long bits);
57 static UMSAudioDevice_ReturnCode UADSetSampleRate(_THIS, long rate, long* set_rate);
58 static UMSAudioDevice_ReturnCode UADSetByteOrder(_THIS, string byte_order);
59 static UMSAudioDevice_ReturnCode UADSetAudioFormatType(_THIS, string fmt);
60 static UMSAudioDevice_ReturnCode UADSetNumberFormat(_THIS, string fmt);
61 static UMSAudioDevice_ReturnCode UADInitialize(_THIS);
62 static UMSAudioDevice_ReturnCode UADStart(_THIS);
63 static UMSAudioDevice_ReturnCode UADStop(_THIS);
64 static UMSAudioDevice_ReturnCode UADSetTimeFormat(_THIS, UMSAudioTypes_TimeFormat fmt );
65 static UMSAudioDevice_ReturnCode UADWriteBuffSize(_THIS, long* buff_size );
66 static UMSAudioDevice_ReturnCode UADWriteBuffRemain(_THIS, long* buff_size );
67 static UMSAudioDevice_ReturnCode UADWriteBuffUsed(_THIS, long* buff_size );
68 static UMSAudioDevice_ReturnCode UADSetDMABufferSize(_THIS, long bytes, long* bytes_ret );
69 static UMSAudioDevice_ReturnCode UADSetVolume(_THIS, long volume );
70 static UMSAudioDevice_ReturnCode UADSetBalance(_THIS, long balance );
71 static UMSAudioDevice_ReturnCode UADSetChannels(_THIS, long channels );
72 static UMSAudioDevice_ReturnCode UADPlayRemainingData(_THIS, boolean block );
73 static UMSAudioDevice_ReturnCode UADEnableOutput(_THIS, string output, long* left_gain, long* right_gain);
74 static UMSAudioDevice_ReturnCode UADWrite(_THIS, UMSAudioTypes_Buffer* buff, long samples, long* samples_written);
75
76 /* Audio driver bootstrap functions */
Audio_Available(void)77 static int Audio_Available(void)
78 {
79 return 1;
80 }
81
Audio_DeleteDevice(_THIS)82 static void Audio_DeleteDevice(_THIS)
83 {
84 if(this->hidden->playbuf._buffer) SDL_free(this->hidden->playbuf._buffer);
85 if(this->hidden->fillbuf._buffer) SDL_free(this->hidden->fillbuf._buffer);
86 _somFree( this->hidden->umsdev );
87 SDL_free(this->hidden);
88 SDL_free(this);
89 }
90
Audio_CreateDevice(int devindex)91 static SDL_AudioDevice *Audio_CreateDevice(int devindex)
92 {
93 SDL_AudioDevice *this;
94
95 /*
96 * Allocate and initialize management storage and private management
97 * storage for this SDL-using library.
98 */
99 this = (SDL_AudioDevice *)SDL_malloc(sizeof(SDL_AudioDevice));
100 if ( this ) {
101 SDL_memset(this, 0, (sizeof *this));
102 this->hidden = (struct SDL_PrivateAudioData *)SDL_malloc((sizeof *this->hidden));
103 }
104 if ( (this == NULL) || (this->hidden == NULL) ) {
105 SDL_OutOfMemory();
106 if ( this ) {
107 SDL_free(this);
108 }
109 return(0);
110 }
111 SDL_memset(this->hidden, 0, (sizeof *this->hidden));
112 #ifdef DEBUG_AUDIO
113 fprintf(stderr, "Creating UMS Audio device\n");
114 #endif
115
116 /*
117 * Calls for UMS env initialization and audio object construction.
118 */
119 this->hidden->ev = somGetGlobalEnvironment();
120 this->hidden->umsdev = UMSAudioDeviceNew();
121
122 /*
123 * Set the function pointers.
124 */
125 this->OpenAudio = UMS_OpenAudio;
126 this->WaitAudio = NULL; /* we do blocking output */
127 this->PlayAudio = UMS_PlayAudio;
128 this->GetAudioBuf = UMS_GetAudioBuf;
129 this->CloseAudio = UMS_CloseAudio;
130 this->free = Audio_DeleteDevice;
131
132 #ifdef DEBUG_AUDIO
133 fprintf(stderr, "done\n");
134 #endif
135 return this;
136 }
137
138 AudioBootStrap UMS_bootstrap = {
139 UMS_DRIVER_NAME, "AIX UMS audio",
140 Audio_Available, Audio_CreateDevice
141 };
142
UMS_GetAudioBuf(_THIS)143 static Uint8 *UMS_GetAudioBuf(_THIS)
144 {
145 #ifdef DEBUG_AUDIO
146 fprintf(stderr, "enter UMS_GetAudioBuf\n");
147 #endif
148 return this->hidden->fillbuf._buffer;
149 /*
150 long bufSize;
151 UMSAudioDevice_ReturnCode rc;
152
153 rc = UADSetTimeFormat(this, UMSAudioTypes_Bytes );
154 rc = UADWriteBuffSize(this, bufSize );
155 */
156 }
157
UMS_CloseAudio(_THIS)158 static void UMS_CloseAudio(_THIS)
159 {
160 UMSAudioDevice_ReturnCode rc;
161
162 #ifdef DEBUG_AUDIO
163 fprintf(stderr, "enter UMS_CloseAudio\n");
164 #endif
165 rc = UADPlayRemainingData(this, TRUE);
166 rc = UADStop(this);
167 rc = UADClose(this);
168 }
169
UMS_PlayAudio(_THIS)170 static void UMS_PlayAudio(_THIS)
171 {
172 UMSAudioDevice_ReturnCode rc;
173 long samplesToWrite;
174 long samplesWritten;
175 UMSAudioTypes_Buffer swpbuf;
176
177 #ifdef DEBUG_AUDIO
178 fprintf(stderr, "enter UMS_PlayAudio\n");
179 #endif
180 samplesToWrite = this->hidden->playbuf._length/this->hidden->bytesPerSample;
181 do
182 {
183 rc = UADWrite(this, &this->hidden->playbuf,
184 samplesToWrite,
185 &samplesWritten );
186 samplesToWrite -= samplesWritten;
187
188 /* rc values: UMSAudioDevice_Success
189 * UMSAudioDevice_Failure
190 * UMSAudioDevice_Preempted
191 * UMSAudioDevice_Interrupted
192 * UMSAudioDevice_DeviceError
193 */
194 if ( rc == UMSAudioDevice_DeviceError ) {
195 #ifdef DEBUG_AUDIO
196 fprintf(stderr, "Returning from PlayAudio with devices error\n");
197 #endif
198 return;
199 }
200 }
201 while(samplesToWrite>0);
202
203 SDL_LockAudio();
204 SDL_memcpy( &swpbuf, &this->hidden->playbuf, sizeof(UMSAudioTypes_Buffer) );
205 SDL_memcpy( &this->hidden->playbuf, &this->hidden->fillbuf, sizeof(UMSAudioTypes_Buffer) );
206 SDL_memcpy( &this->hidden->fillbuf, &swpbuf, sizeof(UMSAudioTypes_Buffer) );
207 SDL_UnlockAudio();
208
209 #ifdef DEBUG_AUDIO
210 fprintf(stderr, "Wrote audio data and swapped buffer\n");
211 #endif
212 }
213
214 #if 0
215 // /* Set the DSP frequency */
216 // value = spec->freq;
217 // if ( ioctl(this->hidden->audio_fd, SOUND_PCM_WRITE_RATE, &value) < 0 ) {
218 // SDL_SetError("Couldn't set audio frequency");
219 // return(-1);
220 // }
221 // spec->freq = value;
222 #endif
223
UMS_OpenAudio(_THIS,SDL_AudioSpec * spec)224 static int UMS_OpenAudio(_THIS, SDL_AudioSpec *spec)
225 {
226 char* audiodev = "/dev/paud0";
227 long lgain;
228 long rgain;
229 long outRate;
230 long outBufSize;
231 long bitsPerSample;
232 long samplesPerSec;
233 long success;
234 Uint16 test_format;
235 int frag_spec;
236 UMSAudioDevice_ReturnCode rc;
237
238 #ifdef DEBUG_AUDIO
239 fprintf(stderr, "enter UMS_OpenAudio\n");
240 #endif
241 rc = UADOpen(this, audiodev,"PLAY", UMSAudioDevice_BlockingIO);
242 if ( rc != UMSAudioDevice_Success ) {
243 SDL_SetError("Couldn't open %s: %s", audiodev, strerror(errno));
244 return -1;
245 }
246
247 rc = UADSetAudioFormatType(this, "PCM");
248
249 success = 0;
250 test_format = SDL_FirstAudioFormat(spec->format);
251 do
252 {
253 #ifdef DEBUG_AUDIO
254 fprintf(stderr, "Trying format 0x%4.4x\n", test_format);
255 #endif
256 switch ( test_format )
257 {
258 case AUDIO_U8:
259 /* from the mac code: better ? */
260 /* sample_bits = spec->size / spec->samples / spec->channels * 8; */
261 success = 1;
262 bitsPerSample = 8;
263 rc = UADSetSampleRate(this, spec->freq << 16, &outRate );
264 rc = UADSetByteOrder(this, "MSB"); /* irrelevant */
265 rc = UADSetNumberFormat(this, "UNSIGNED");
266 break;
267 case AUDIO_S8:
268 success = 1;
269 bitsPerSample = 8;
270 rc = UADSetSampleRate(this, spec->freq << 16, &outRate );
271 rc = UADSetByteOrder(this, "MSB"); /* irrelevant */
272 rc = UADSetNumberFormat(this, "SIGNED");
273 break;
274 case AUDIO_S16LSB:
275 success = 1;
276 bitsPerSample = 16;
277 rc = UADSetSampleRate(this, spec->freq << 16, &outRate );
278 rc = UADSetByteOrder(this, "LSB");
279 rc = UADSetNumberFormat(this, "SIGNED");
280 break;
281 case AUDIO_S16MSB:
282 success = 1;
283 bitsPerSample = 16;
284 rc = UADSetSampleRate(this, spec->freq << 16, &outRate );
285 rc = UADSetByteOrder(this, "MSB");
286 rc = UADSetNumberFormat(this, "SIGNED");
287 break;
288 case AUDIO_U16LSB:
289 success = 1;
290 bitsPerSample = 16;
291 rc = UADSetSampleRate(this, spec->freq << 16, &outRate );
292 rc = UADSetByteOrder(this, "LSB");
293 rc = UADSetNumberFormat(this, "UNSIGNED");
294 break;
295 case AUDIO_U16MSB:
296 success = 1;
297 bitsPerSample = 16;
298 rc = UADSetSampleRate(this, spec->freq << 16, &outRate );
299 rc = UADSetByteOrder(this, "MSB");
300 rc = UADSetNumberFormat(this, "UNSIGNED");
301 break;
302 default:
303 break;
304 }
305 if ( ! success ) {
306 test_format = SDL_NextAudioFormat();
307 }
308 }
309 while ( ! success && test_format );
310
311 if ( success == 0 ) {
312 SDL_SetError("Couldn't find any hardware audio formats");
313 return -1;
314 }
315
316 spec->format = test_format;
317
318 for ( frag_spec = 0; (0x01<<frag_spec) < spec->size; ++frag_spec );
319 if ( (0x01<<frag_spec) != spec->size ) {
320 SDL_SetError("Fragment size must be a power of two");
321 return -1;
322 }
323 if ( frag_spec > 2048 ) frag_spec = 2048;
324
325 this->hidden->bytesPerSample = (bitsPerSample / 8) * spec->channels;
326 samplesPerSec = this->hidden->bytesPerSample * outRate;
327
328 this->hidden->playbuf._length = 0;
329 this->hidden->playbuf._maximum = spec->size;
330 this->hidden->playbuf._buffer = (unsigned char*)SDL_malloc(spec->size);
331 this->hidden->fillbuf._length = 0;
332 this->hidden->fillbuf._maximum = spec->size;
333 this->hidden->fillbuf._buffer = (unsigned char*)SDL_malloc(spec->size);
334
335 rc = UADSetBitsPerSample(this, bitsPerSample );
336 rc = UADSetDMABufferSize(this, frag_spec, &outBufSize );
337 rc = UADSetChannels(this, spec->channels); /* functions reduces to mono or stereo */
338
339 lgain = 100; /*maximum left input gain*/
340 rgain = 100; /*maimum right input gain*/
341 rc = UADEnableOutput(this, "LINE_OUT",&lgain,&rgain);
342 rc = UADInitialize(this);
343 rc = UADStart(this);
344 rc = UADSetVolume(this, 100);
345 rc = UADSetBalance(this, 0);
346
347 /* We're ready to rock and roll. :-) */
348 return 0;
349 }
350
351
UADGetBitsPerSample(_THIS,long * bits)352 static UMSAudioDevice_ReturnCode UADGetBitsPerSample(_THIS, long* bits)
353 {
354 return UMSAudioDevice_get_bits_per_sample( this->hidden->umsdev,
355 this->hidden->ev,
356 bits );
357 }
358
UADSetBitsPerSample(_THIS,long bits)359 static UMSAudioDevice_ReturnCode UADSetBitsPerSample(_THIS, long bits)
360 {
361 return UMSAudioDevice_set_bits_per_sample( this->hidden->umsdev,
362 this->hidden->ev,
363 bits );
364 }
365
UADSetSampleRate(_THIS,long rate,long * set_rate)366 static UMSAudioDevice_ReturnCode UADSetSampleRate(_THIS, long rate, long* set_rate)
367 {
368 /* from the mac code: sample rate = spec->freq << 16; */
369 return UMSAudioDevice_set_sample_rate( this->hidden->umsdev,
370 this->hidden->ev,
371 rate,
372 set_rate );
373 }
374
UADSetByteOrder(_THIS,string byte_order)375 static UMSAudioDevice_ReturnCode UADSetByteOrder(_THIS, string byte_order)
376 {
377 return UMSAudioDevice_set_byte_order( this->hidden->umsdev,
378 this->hidden->ev,
379 byte_order );
380 }
381
UADSetAudioFormatType(_THIS,string fmt)382 static UMSAudioDevice_ReturnCode UADSetAudioFormatType(_THIS, string fmt)
383 {
384 /* possible PCM, A_LAW or MU_LAW */
385 return UMSAudioDevice_set_audio_format_type( this->hidden->umsdev,
386 this->hidden->ev,
387 fmt );
388 }
389
UADSetNumberFormat(_THIS,string fmt)390 static UMSAudioDevice_ReturnCode UADSetNumberFormat(_THIS, string fmt)
391 {
392 /* possible SIGNED, UNSIGNED, or TWOS_COMPLEMENT */
393 return UMSAudioDevice_set_number_format( this->hidden->umsdev,
394 this->hidden->ev,
395 fmt );
396 }
397
UADInitialize(_THIS)398 static UMSAudioDevice_ReturnCode UADInitialize(_THIS)
399 {
400 return UMSAudioDevice_initialize( this->hidden->umsdev,
401 this->hidden->ev );
402 }
403
UADStart(_THIS)404 static UMSAudioDevice_ReturnCode UADStart(_THIS)
405 {
406 return UMSAudioDevice_start( this->hidden->umsdev,
407 this->hidden->ev );
408 }
409
UADSetTimeFormat(_THIS,UMSAudioTypes_TimeFormat fmt)410 static UMSAudioDevice_ReturnCode UADSetTimeFormat(_THIS, UMSAudioTypes_TimeFormat fmt )
411 {
412 /*
413 * Switches the time format to the new format, immediately.
414 * possible UMSAudioTypes_Msecs, UMSAudioTypes_Bytes or UMSAudioTypes_Samples
415 */
416 return UMSAudioDevice_set_time_format( this->hidden->umsdev,
417 this->hidden->ev,
418 fmt );
419 }
420
UADWriteBuffSize(_THIS,long * buff_size)421 static UMSAudioDevice_ReturnCode UADWriteBuffSize(_THIS, long* buff_size )
422 {
423 /*
424 * returns write buffer size in the current time format
425 */
426 return UMSAudioDevice_write_buff_size( this->hidden->umsdev,
427 this->hidden->ev,
428 buff_size );
429 }
430
UADWriteBuffRemain(_THIS,long * buff_size)431 static UMSAudioDevice_ReturnCode UADWriteBuffRemain(_THIS, long* buff_size )
432 {
433 /*
434 * returns amount of available space in the write buffer
435 * in the current time format
436 */
437 return UMSAudioDevice_write_buff_remain( this->hidden->umsdev,
438 this->hidden->ev,
439 buff_size );
440 }
441
UADWriteBuffUsed(_THIS,long * buff_size)442 static UMSAudioDevice_ReturnCode UADWriteBuffUsed(_THIS, long* buff_size )
443 {
444 /*
445 * returns amount of filled space in the write buffer
446 * in the current time format
447 */
448 return UMSAudioDevice_write_buff_used( this->hidden->umsdev,
449 this->hidden->ev,
450 buff_size );
451 }
452
UADSetDMABufferSize(_THIS,long bytes,long * bytes_ret)453 static UMSAudioDevice_ReturnCode UADSetDMABufferSize(_THIS, long bytes, long* bytes_ret )
454 {
455 /*
456 * Request a new DMA buffer size, maximum requested size 2048.
457 * Takes effect with next initialize() call.
458 * Devices may or may not support DMA.
459 */
460 return UMSAudioDevice_set_DMA_buffer_size( this->hidden->umsdev,
461 this->hidden->ev,
462 bytes,
463 bytes_ret );
464 }
465
UADSetVolume(_THIS,long volume)466 static UMSAudioDevice_ReturnCode UADSetVolume(_THIS, long volume )
467 {
468 /*
469 * Set the volume.
470 * Takes effect immediately.
471 */
472 return UMSAudioDevice_set_volume( this->hidden->umsdev,
473 this->hidden->ev,
474 volume );
475 }
476
UADSetBalance(_THIS,long balance)477 static UMSAudioDevice_ReturnCode UADSetBalance(_THIS, long balance )
478 {
479 /*
480 * Set the balance.
481 * Takes effect immediately.
482 */
483 return UMSAudioDevice_set_balance( this->hidden->umsdev,
484 this->hidden->ev,
485 balance );
486 }
487
UADSetChannels(_THIS,long channels)488 static UMSAudioDevice_ReturnCode UADSetChannels(_THIS, long channels )
489 {
490 /*
491 * Set mono or stereo.
492 * Takes effect with next initialize() call.
493 */
494 if ( channels != 1 ) channels = 2;
495 return UMSAudioDevice_set_number_of_channels( this->hidden->umsdev,
496 this->hidden->ev,
497 channels );
498 }
499
UADOpen(_THIS,string device,string mode,long flags)500 static UMSAudioDevice_ReturnCode UADOpen(_THIS, string device, string mode, long flags)
501 {
502 return UMSAudioDevice_open( this->hidden->umsdev,
503 this->hidden->ev,
504 device,
505 mode,
506 flags );
507 }
508
UADWrite(_THIS,UMSAudioTypes_Buffer * buff,long samples,long * samples_written)509 static UMSAudioDevice_ReturnCode UADWrite(_THIS, UMSAudioTypes_Buffer* buff,
510 long samples,
511 long* samples_written)
512 {
513 return UMSAudioDevice_write( this->hidden->umsdev,
514 this->hidden->ev,
515 buff,
516 samples,
517 samples_written );
518 }
519
UADPlayRemainingData(_THIS,boolean block)520 static UMSAudioDevice_ReturnCode UADPlayRemainingData(_THIS, boolean block )
521 {
522 return UMSAudioDevice_play_remaining_data( this->hidden->umsdev,
523 this->hidden->ev,
524 block);
525 }
526
UADStop(_THIS)527 static UMSAudioDevice_ReturnCode UADStop(_THIS)
528 {
529 return UMSAudioDevice_stop( this->hidden->umsdev,
530 this->hidden->ev );
531 }
532
UADClose(_THIS)533 static UMSAudioDevice_ReturnCode UADClose(_THIS)
534 {
535 return UMSAudioDevice_close( this->hidden->umsdev,
536 this->hidden->ev );
537 }
538
UADEnableOutput(_THIS,string output,long * left_gain,long * right_gain)539 static UMSAudioDevice_ReturnCode UADEnableOutput(_THIS, string output, long* left_gain, long* right_gain)
540 {
541 return UMSAudioDevice_enable_output( this->hidden->umsdev,
542 this->hidden->ev,
543 output,
544 left_gain,
545 right_gain );
546 }
547
548