src/audio/ums/SDL_umsaudio.c
author Ryan C. Gordon <icculus@icculus.org>
Thu, 24 Aug 2006 12:10:46 +0000
changeset 1982 3b4ce57c6215
parent 1895 c121d94672cb
child 2041 4a32d186f35b
permissions -rw-r--r--
First shot at new audio data types (int32 and float32).

Notable changes:
- Converters between types are autogenerated. Instead of making multiple
passes over the data with seperate filters for endianess, size, signedness,
etc, converting between data types is always one specialized filter. This
simplifies SDL_BuildAudioCVT(), which otherwise had a million edge cases
with the new types, and makes the actually conversions more CPU cache
friendly. Left a stub for adding specific optimized versions of these
routines (SSE/MMX/Altivec, assembler, etc)
- Autogenerated converters are built by SDL/src/audio/sdlgenaudiocvt.pl. This
does not need to be run unless tweaking the code, and thus doesn't need
integration into the build system.
- Went through all the drivers and tried to weed out all the "Uint16"
references that are better specified with the new SDL_AudioFormat typedef.
- Cleaned out a bunch of hardcoded bitwise magic numbers and replaced them
with new SDL_AUDIO_* macros.
- Added initial float32 and int32 support code. Theoretically, existing
drivers will push these through converters to get the data they want to
feed to the hardware.

Still TODO:
- Optimize and debug new converters.
- Update the CoreAudio backend to accept float32 data directly.
- Other backends, too?
- SDL_LoadWAV() needs to be updated to support int32 and float32 .wav files
(both of which exist and can be generated by 'sox' for testing purposes).
- Update the mixer to handle new datatypes.
- Optionally update SDL_sound and SDL_mixer, etc.
     1 /*
     2     SDL - Simple DirectMedia Layer
     3     Copyright (C) 1997-2006 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,
    54                                          long flags);
    55 static UMSAudioDevice_ReturnCode UADClose(_THIS);
    56 static UMSAudioDevice_ReturnCode UADGetBitsPerSample(_THIS, long *bits);
    57 static UMSAudioDevice_ReturnCode UADSetBitsPerSample(_THIS, long bits);
    58 static UMSAudioDevice_ReturnCode UADSetSampleRate(_THIS, long rate,
    59                                                   long *set_rate);
    60 static UMSAudioDevice_ReturnCode UADSetByteOrder(_THIS, string byte_order);
    61 static UMSAudioDevice_ReturnCode UADSetAudioFormatType(_THIS, string fmt);
    62 static UMSAudioDevice_ReturnCode UADSetNumberFormat(_THIS, string fmt);
    63 static UMSAudioDevice_ReturnCode UADInitialize(_THIS);
    64 static UMSAudioDevice_ReturnCode UADStart(_THIS);
    65 static UMSAudioDevice_ReturnCode UADStop(_THIS);
    66 static UMSAudioDevice_ReturnCode UADSetTimeFormat(_THIS,
    67                                                   UMSAudioTypes_TimeFormat
    68                                                   fmt);
    69 static UMSAudioDevice_ReturnCode UADWriteBuffSize(_THIS, long *buff_size);
    70 static UMSAudioDevice_ReturnCode UADWriteBuffRemain(_THIS, long *buff_size);
    71 static UMSAudioDevice_ReturnCode UADWriteBuffUsed(_THIS, long *buff_size);
    72 static UMSAudioDevice_ReturnCode UADSetDMABufferSize(_THIS, long bytes,
    73                                                      long *bytes_ret);
    74 static UMSAudioDevice_ReturnCode UADSetVolume(_THIS, long volume);
    75 static UMSAudioDevice_ReturnCode UADSetBalance(_THIS, long balance);
    76 static UMSAudioDevice_ReturnCode UADSetChannels(_THIS, long channels);
    77 static UMSAudioDevice_ReturnCode UADPlayRemainingData(_THIS, boolean block);
    78 static UMSAudioDevice_ReturnCode UADEnableOutput(_THIS, string output,
    79                                                  long *left_gain,
    80                                                  long *right_gain);
    81 static UMSAudioDevice_ReturnCode UADWrite(_THIS, UMSAudioTypes_Buffer * buff,
    82                                           long samples,
    83                                           long *samples_written);
    84 
    85 /* Audio driver bootstrap functions */
    86 static int
    87 Audio_Available(void)
    88 {
    89     return 1;
    90 }
    91 
    92 static void
    93 Audio_DeleteDevice(_THIS)
    94 {
    95     if (this->hidden->playbuf._buffer)
    96         SDL_free(this->hidden->playbuf._buffer);
    97     if (this->hidden->fillbuf._buffer)
    98         SDL_free(this->hidden->fillbuf._buffer);
    99     _somFree(this->hidden->umsdev);
   100     SDL_free(this->hidden);
   101     SDL_free(this);
   102 }
   103 
   104 static SDL_AudioDevice *
   105 Audio_CreateDevice(int devindex)
   106 {
   107     SDL_AudioDevice *this;
   108 
   109     /*
   110      * Allocate and initialize management storage and private management
   111      * storage for this SDL-using library.
   112      */
   113     this = (SDL_AudioDevice *) SDL_malloc(sizeof(SDL_AudioDevice));
   114     if (this) {
   115         SDL_memset(this, 0, (sizeof *this));
   116         this->hidden = (struct SDL_PrivateAudioData *)
   117             SDL_malloc((sizeof *this->hidden));
   118     }
   119     if ((this == NULL) || (this->hidden == NULL)) {
   120         SDL_OutOfMemory();
   121         if (this) {
   122             SDL_free(this);
   123         }
   124         return (0);
   125     }
   126     SDL_memset(this->hidden, 0, (sizeof *this->hidden));
   127 #ifdef DEBUG_AUDIO
   128     fprintf(stderr, "Creating UMS Audio device\n");
   129 #endif
   130 
   131     /*
   132      * Calls for UMS env initialization and audio object construction.
   133      */
   134     this->hidden->ev = somGetGlobalEnvironment();
   135     this->hidden->umsdev = UMSAudioDeviceNew();
   136 
   137     /*
   138      * Set the function pointers.
   139      */
   140     this->OpenAudio = UMS_OpenAudio;
   141     this->WaitAudio = NULL;     /* we do blocking output */
   142     this->PlayAudio = UMS_PlayAudio;
   143     this->GetAudioBuf = UMS_GetAudioBuf;
   144     this->CloseAudio = UMS_CloseAudio;
   145     this->free = Audio_DeleteDevice;
   146 
   147 #ifdef DEBUG_AUDIO
   148     fprintf(stderr, "done\n");
   149 #endif
   150     return this;
   151 }
   152 
   153 AudioBootStrap UMS_bootstrap = {
   154     UMS_DRIVER_NAME, "AUX UMS audio",
   155     Audio_Available, Audio_CreateDevice
   156 };
   157 
   158 static Uint8 *
   159 UMS_GetAudioBuf(_THIS)
   160 {
   161 #ifdef DEBUG_AUDIO
   162     fprintf(stderr, "enter UMS_GetAudioBuf\n");
   163 #endif
   164     return this->hidden->fillbuf._buffer;
   165 /*
   166     long                      bufSize;
   167     UMSAudioDevice_ReturnCode rc;
   168 
   169     rc = UADSetTimeFormat(this, UMSAudioTypes_Bytes );
   170     rc = UADWriteBuffSize(this,  bufSize );
   171 */
   172 }
   173 
   174 static void
   175 UMS_CloseAudio(_THIS)
   176 {
   177     UMSAudioDevice_ReturnCode rc;
   178 
   179 #ifdef DEBUG_AUDIO
   180     fprintf(stderr, "enter UMS_CloseAudio\n");
   181 #endif
   182     rc = UADPlayRemainingData(this, TRUE);
   183     rc = UADStop(this);
   184     rc = UADClose(this);
   185 }
   186 
   187 static void
   188 UMS_PlayAudio(_THIS)
   189 {
   190     UMSAudioDevice_ReturnCode rc;
   191     long samplesToWrite;
   192     long samplesWritten;
   193     UMSAudioTypes_Buffer swpbuf;
   194 
   195 #ifdef DEBUG_AUDIO
   196     fprintf(stderr, "enter UMS_PlayAudio\n");
   197 #endif
   198     samplesToWrite =
   199         this->hidden->playbuf._length / this->hidden->bytesPerSample;
   200     do {
   201         rc = UADWrite(this, &this->hidden->playbuf,
   202                       samplesToWrite, &samplesWritten);
   203         samplesToWrite -= samplesWritten;
   204 
   205         /* rc values: UMSAudioDevice_Success
   206          *            UMSAudioDevice_Failure
   207          *            UMSAudioDevice_Preempted
   208          *            UMSAudioDevice_Interrupted
   209          *            UMSAudioDevice_DeviceError
   210          */
   211         if (rc == UMSAudioDevice_DeviceError) {
   212 #ifdef DEBUG_AUDIO
   213             fprintf(stderr, "Returning from PlayAudio with devices error\n");
   214 #endif
   215             return;
   216         }
   217     }
   218     while (samplesToWrite > 0);
   219 
   220     SDL_LockAudio();
   221     SDL_memcpy(&swpbuf, &this->hidden->playbuf, sizeof(UMSAudioTypes_Buffer));
   222     SDL_memcpy(&this->hidden->playbuf, &this->hidden->fillbuf,
   223                sizeof(UMSAudioTypes_Buffer));
   224     SDL_memcpy(&this->hidden->fillbuf, &swpbuf, sizeof(UMSAudioTypes_Buffer));
   225     SDL_UnlockAudio();
   226 
   227 #ifdef DEBUG_AUDIO
   228     fprintf(stderr, "Wrote audio data and swapped buffer\n");
   229 #endif
   230 }
   231 
   232 #if 0
   233 //      /* Set the DSP frequency */
   234 //      value = spec->freq;
   235 //      if ( ioctl(this->hidden->audio_fd, SOUND_PCM_WRITE_RATE, &value) < 0 ) {
   236 //              SDL_SetError("Couldn't set audio frequency");
   237 //              return(-1);
   238 //      }
   239 //      spec->freq = value;
   240 #endif
   241 
   242 static int
   243 UMS_OpenAudio(_THIS, SDL_AudioSpec * spec)
   244 {
   245     char *audiodev = "/dev/paud0";
   246     long lgain;
   247     long rgain;
   248     long outRate;
   249     long outBufSize;
   250     long bitsPerSample;
   251     long samplesPerSec;
   252     long success;
   253     SDL_AudioFormat test_format;
   254     int frag_spec;
   255     UMSAudioDevice_ReturnCode rc;
   256 
   257 #ifdef DEBUG_AUDIO
   258     fprintf(stderr, "enter UMS_OpenAudio\n");
   259 #endif
   260     rc = UADOpen(this, audiodev, "PLAY", UMSAudioDevice_BlockingIO);
   261     if (rc != UMSAudioDevice_Success) {
   262         SDL_SetError("Couldn't open %s: %s", audiodev, strerror(errno));
   263         return -1;
   264     }
   265 
   266     rc = UADSetAudioFormatType(this, "PCM");
   267 
   268     success = 0;
   269     test_format = SDL_FirstAudioFormat(spec->format);
   270     do {
   271 #ifdef DEBUG_AUDIO
   272         fprintf(stderr, "Trying format 0x%4.4x\n", test_format);
   273 #endif
   274         switch (test_format) {
   275         case AUDIO_U8:
   276 /* from the mac code: better ? */
   277 /* sample_bits = spec->size / spec->samples / spec->channels * 8; */
   278             success = 1;
   279             bitsPerSample = 8;
   280             rc = UADSetSampleRate(this, spec->freq << 16, &outRate);
   281             rc = UADSetByteOrder(this, "MSB");  /* irrelevant */
   282             rc = UADSetNumberFormat(this, "UNSIGNED");
   283             break;
   284         case AUDIO_S8:
   285             success = 1;
   286             bitsPerSample = 8;
   287             rc = UADSetSampleRate(this, spec->freq << 16, &outRate);
   288             rc = UADSetByteOrder(this, "MSB");  /* irrelevant */
   289             rc = UADSetNumberFormat(this, "SIGNED");
   290             break;
   291         case AUDIO_S16LSB:
   292             success = 1;
   293             bitsPerSample = 16;
   294             rc = UADSetSampleRate(this, spec->freq << 16, &outRate);
   295             rc = UADSetByteOrder(this, "LSB");
   296             rc = UADSetNumberFormat(this, "SIGNED");
   297             break;
   298         case AUDIO_S16MSB:
   299             success = 1;
   300             bitsPerSample = 16;
   301             rc = UADSetSampleRate(this, spec->freq << 16, &outRate);
   302             rc = UADSetByteOrder(this, "MSB");
   303             rc = UADSetNumberFormat(this, "SIGNED");
   304             break;
   305         case AUDIO_U16LSB:
   306             success = 1;
   307             bitsPerSample = 16;
   308             rc = UADSetSampleRate(this, spec->freq << 16, &outRate);
   309             rc = UADSetByteOrder(this, "LSB");
   310             rc = UADSetNumberFormat(this, "UNSIGNED");
   311             break;
   312         case AUDIO_U16MSB:
   313             success = 1;
   314             bitsPerSample = 16;
   315             rc = UADSetSampleRate(this, spec->freq << 16, &outRate);
   316             rc = UADSetByteOrder(this, "MSB");
   317             rc = UADSetNumberFormat(this, "UNSIGNED");
   318             break;
   319         default:
   320             break;
   321         }
   322         if (!success) {
   323             test_format = SDL_NextAudioFormat();
   324         }
   325     }
   326     while (!success && test_format);
   327 
   328     if (success == 0) {
   329         SDL_SetError("Couldn't find any hardware audio formats");
   330         return -1;
   331     }
   332 
   333     spec->format = test_format;
   334 
   335     for (frag_spec = 0; (0x01 << frag_spec) < spec->size; ++frag_spec);
   336     if ((0x01 << frag_spec) != spec->size) {
   337         SDL_SetError("Fragment size must be a power of two");
   338         return -1;
   339     }
   340     if (frag_spec > 2048)
   341         frag_spec = 2048;
   342 
   343     this->hidden->bytesPerSample = (bitsPerSample / 8) * spec->channels;
   344     samplesPerSec = this->hidden->bytesPerSample * outRate;
   345 
   346     this->hidden->playbuf._length = 0;
   347     this->hidden->playbuf._maximum = spec->size;
   348     this->hidden->playbuf._buffer = (unsigned char *) SDL_malloc(spec->size);
   349     this->hidden->fillbuf._length = 0;
   350     this->hidden->fillbuf._maximum = spec->size;
   351     this->hidden->fillbuf._buffer = (unsigned char *) SDL_malloc(spec->size);
   352 
   353     rc = UADSetBitsPerSample(this, bitsPerSample);
   354     rc = UADSetDMABufferSize(this, frag_spec, &outBufSize);
   355     rc = UADSetChannels(this, spec->channels);  /* functions reduces to mono or stereo */
   356 
   357     lgain = 100;                /*maximum left input gain */
   358     rgain = 100;                /*maimum right input gain */
   359     rc = UADEnableOutput(this, "LINE_OUT", &lgain, &rgain);
   360     rc = UADInitialize(this);
   361     rc = UADStart(this);
   362     rc = UADSetVolume(this, 100);
   363     rc = UADSetBalance(this, 0);
   364 
   365     /* We're ready to rock and roll. :-) */
   366     return 0;
   367 }
   368 
   369 
   370 static UMSAudioDevice_ReturnCode
   371 UADGetBitsPerSample(_THIS, long *bits)
   372 {
   373     return UMSAudioDevice_get_bits_per_sample(this->hidden->umsdev,
   374                                               this->hidden->ev, bits);
   375 }
   376 
   377 static UMSAudioDevice_ReturnCode
   378 UADSetBitsPerSample(_THIS, long bits)
   379 {
   380     return UMSAudioDevice_set_bits_per_sample(this->hidden->umsdev,
   381                                               this->hidden->ev, bits);
   382 }
   383 
   384 static UMSAudioDevice_ReturnCode
   385 UADSetSampleRate(_THIS, long rate, long *set_rate)
   386 {
   387     /* from the mac code: sample rate = spec->freq << 16; */
   388     return UMSAudioDevice_set_sample_rate(this->hidden->umsdev,
   389                                           this->hidden->ev, rate, set_rate);
   390 }
   391 
   392 static UMSAudioDevice_ReturnCode
   393 UADSetByteOrder(_THIS, string byte_order)
   394 {
   395     return UMSAudioDevice_set_byte_order(this->hidden->umsdev,
   396                                          this->hidden->ev, byte_order);
   397 }
   398 
   399 static UMSAudioDevice_ReturnCode
   400 UADSetAudioFormatType(_THIS, string fmt)
   401 {
   402     /* possible PCM, A_LAW or MU_LAW */
   403     return UMSAudioDevice_set_audio_format_type(this->hidden->umsdev,
   404                                                 this->hidden->ev, fmt);
   405 }
   406 
   407 static UMSAudioDevice_ReturnCode
   408 UADSetNumberFormat(_THIS, string fmt)
   409 {
   410     /* possible SIGNED, UNSIGNED, or TWOS_COMPLEMENT */
   411     return UMSAudioDevice_set_number_format(this->hidden->umsdev,
   412                                             this->hidden->ev, fmt);
   413 }
   414 
   415 static UMSAudioDevice_ReturnCode
   416 UADInitialize(_THIS)
   417 {
   418     return UMSAudioDevice_initialize(this->hidden->umsdev, this->hidden->ev);
   419 }
   420 
   421 static UMSAudioDevice_ReturnCode
   422 UADStart(_THIS)
   423 {
   424     return UMSAudioDevice_start(this->hidden->umsdev, this->hidden->ev);
   425 }
   426 
   427 static UMSAudioDevice_ReturnCode
   428 UADSetTimeFormat(_THIS, UMSAudioTypes_TimeFormat fmt)
   429 {
   430     /*
   431      * Switches the time format to the new format, immediately.
   432      * possible UMSAudioTypes_Msecs, UMSAudioTypes_Bytes or UMSAudioTypes_Samples
   433      */
   434     return UMSAudioDevice_set_time_format(this->hidden->umsdev,
   435                                           this->hidden->ev, fmt);
   436 }
   437 
   438 static UMSAudioDevice_ReturnCode
   439 UADWriteBuffSize(_THIS, long *buff_size)
   440 {
   441     /*
   442      * returns write buffer size in the current time format
   443      */
   444     return UMSAudioDevice_write_buff_size(this->hidden->umsdev,
   445                                           this->hidden->ev, buff_size);
   446 }
   447 
   448 static UMSAudioDevice_ReturnCode
   449 UADWriteBuffRemain(_THIS, long *buff_size)
   450 {
   451     /*
   452      * returns amount of available space in the write buffer
   453      * in the current time format
   454      */
   455     return UMSAudioDevice_write_buff_remain(this->hidden->umsdev,
   456                                             this->hidden->ev, buff_size);
   457 }
   458 
   459 static UMSAudioDevice_ReturnCode
   460 UADWriteBuffUsed(_THIS, long *buff_size)
   461 {
   462     /*
   463      * returns amount of filled space in the write buffer
   464      * in the current time format
   465      */
   466     return UMSAudioDevice_write_buff_used(this->hidden->umsdev,
   467                                           this->hidden->ev, buff_size);
   468 }
   469 
   470 static UMSAudioDevice_ReturnCode
   471 UADSetDMABufferSize(_THIS, long bytes, long *bytes_ret)
   472 {
   473     /*
   474      * Request a new DMA buffer size, maximum requested size 2048.
   475      * Takes effect with next initialize() call.
   476      * Devices may or may not support DMA.
   477      */
   478     return UMSAudioDevice_set_DMA_buffer_size(this->hidden->umsdev,
   479                                               this->hidden->ev,
   480                                               bytes, bytes_ret);
   481 }
   482 
   483 static UMSAudioDevice_ReturnCode
   484 UADSetVolume(_THIS, long volume)
   485 {
   486     /*
   487      * Set the volume.
   488      * Takes effect immediately.
   489      */
   490     return UMSAudioDevice_set_volume(this->hidden->umsdev,
   491                                      this->hidden->ev, volume);
   492 }
   493 
   494 static UMSAudioDevice_ReturnCode
   495 UADSetBalance(_THIS, long balance)
   496 {
   497     /*
   498      * Set the balance.
   499      * Takes effect immediately.
   500      */
   501     return UMSAudioDevice_set_balance(this->hidden->umsdev,
   502                                       this->hidden->ev, balance);
   503 }
   504 
   505 static UMSAudioDevice_ReturnCode
   506 UADSetChannels(_THIS, long channels)
   507 {
   508     /*
   509      * Set mono or stereo.
   510      * Takes effect with next initialize() call.
   511      */
   512     if (channels != 1)
   513         channels = 2;
   514     return UMSAudioDevice_set_number_of_channels(this->hidden->umsdev,
   515                                                  this->hidden->ev, channels);
   516 }
   517 
   518 static UMSAudioDevice_ReturnCode
   519 UADOpen(_THIS, string device, string mode, long flags)
   520 {
   521     return UMSAudioDevice_open(this->hidden->umsdev,
   522                                this->hidden->ev, device, mode, flags);
   523 }
   524 
   525 static UMSAudioDevice_ReturnCode
   526 UADWrite(_THIS, UMSAudioTypes_Buffer * buff,
   527          long samples, long *samples_written)
   528 {
   529     return UMSAudioDevice_write(this->hidden->umsdev,
   530                                 this->hidden->ev,
   531                                 buff, samples, samples_written);
   532 }
   533 
   534 static UMSAudioDevice_ReturnCode
   535 UADPlayRemainingData(_THIS, boolean block)
   536 {
   537     return UMSAudioDevice_play_remaining_data(this->hidden->umsdev,
   538                                               this->hidden->ev, block);
   539 }
   540 
   541 static UMSAudioDevice_ReturnCode
   542 UADStop(_THIS)
   543 {
   544     return UMSAudioDevice_stop(this->hidden->umsdev, this->hidden->ev);
   545 }
   546 
   547 static UMSAudioDevice_ReturnCode
   548 UADClose(_THIS)
   549 {
   550     return UMSAudioDevice_close(this->hidden->umsdev, this->hidden->ev);
   551 }
   552 
   553 static UMSAudioDevice_ReturnCode
   554 UADEnableOutput(_THIS, string output, long *left_gain, long *right_gain)
   555 {
   556     return UMSAudioDevice_enable_output(this->hidden->umsdev,
   557                                         this->hidden->ev,
   558                                         output, left_gain, right_gain);
   559 }
   560 
   561 /* vi: set ts=4 sw=4 expandtab: */