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