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