SDL-1.2.14
[sdl_omap.git] / src / audio / ums / SDL_umsaudio.c
1 /*
2     SDL - Simple DirectMedia Layer
3     Copyright (C) 1997-2009 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 */
77 static int Audio_Available(void)
78 {
79     return 1;
80 }
81
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
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
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
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
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
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 \f
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
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
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
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
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
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
398 static UMSAudioDevice_ReturnCode UADInitialize(_THIS)
399 {
400     return UMSAudioDevice_initialize( this->hidden->umsdev,
401                                       this->hidden->ev );
402 }
403
404 static UMSAudioDevice_ReturnCode UADStart(_THIS)
405 {
406     return UMSAudioDevice_start( this->hidden->umsdev,
407                                  this->hidden->ev );
408 }
409
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
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
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
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
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
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
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
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
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
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
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
527 static UMSAudioDevice_ReturnCode UADStop(_THIS)
528 {
529     return UMSAudioDevice_stop( this->hidden->umsdev,
530                                 this->hidden->ev );
531 }
532
533 static UMSAudioDevice_ReturnCode UADClose(_THIS)
534 {
535     return UMSAudioDevice_close( this->hidden->umsdev,
536                                  this->hidden->ev );
537 }
538
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