src/audio/SDL_audio.c
author Sam Lantinga <slouken@libsdl.org>
Fri, 17 Sep 2004 13:20:10 +0000
changeset 955 d74fbf56f2f6
parent 942 41a59de7f2ed
child 1190 173c063d4f55
permissions -rw-r--r--
Date: Fri, 25 Jun 2004 13:29:15 +0100
From: "alan buckley"
Subject: Modification for RISC OS version of SDL

Ive attached a zip file with the changes to this email, it contains the
following:

The file sdldiff.txt is the output from cvs diff u. .
The directory thread/riscos contains all the new files to support threading.

Readme.riscos is a new readme file to add.
     1 /*
     2     SDL - Simple DirectMedia Layer
     3     Copyright (C) 1997-2004 Sam Lantinga
     4 
     5     This library is free software; you can redistribute it and/or
     6     modify it under the terms of the GNU Library General Public
     7     License as published by the Free Software Foundation; either
     8     version 2 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     Library General Public License for more details.
    14 
    15     You should have received a copy of the GNU Library General Public
    16     License along with this library; if not, write to the Free
    17     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
    18 
    19     Sam Lantinga
    20     slouken@libsdl.org
    21 */
    22 
    23 #ifdef SAVE_RCSID
    24 static char rcsid =
    25  "@(#) $Id$";
    26 #endif
    27 
    28 /* Allow access to a raw mixing buffer */
    29 #include <stdlib.h>
    30 #include <stdio.h>
    31 #include <string.h>
    32 
    33 #include "SDL.h"
    34 #include "SDL_audio.h"
    35 #include "SDL_timer.h"
    36 #include "SDL_error.h"
    37 #include "SDL_audio_c.h"
    38 #include "SDL_audiomem.h"
    39 #include "SDL_sysaudio.h"
    40 
    41 /* Available audio drivers */
    42 static AudioBootStrap *bootstrap[] = {
    43 #ifdef OPENBSD_AUDIO_SUPPORT
    44 	&OPENBSD_AUDIO_bootstrap,
    45 #endif
    46 #ifdef OSS_SUPPORT
    47 	&DSP_bootstrap,
    48 	&DMA_bootstrap,
    49 #endif
    50 #ifdef ALSA_SUPPORT
    51 	&ALSA_bootstrap,
    52 #endif
    53 #ifdef QNXNTOAUDIO_SUPPORT
    54 	&QNXNTOAUDIO_bootstrap,
    55 #endif
    56 #ifdef SUNAUDIO_SUPPORT
    57 	&SUNAUDIO_bootstrap,
    58 #endif
    59 #ifdef DMEDIA_SUPPORT
    60 	&DMEDIA_bootstrap,
    61 #endif
    62 #ifdef ARTSC_SUPPORT
    63 	&ARTSC_bootstrap,
    64 #endif
    65 #ifdef ESD_SUPPORT
    66 	&ESD_bootstrap,
    67 #endif
    68 #ifdef NAS_SUPPORT
    69 	&NAS_bootstrap,
    70 #endif
    71 #ifdef ENABLE_DIRECTX
    72 	&DSOUND_bootstrap,
    73 #endif
    74 #ifdef ENABLE_WINDIB
    75 	&WAVEOUT_bootstrap,
    76 #endif
    77 #ifdef __BEOS__
    78 	&BAUDIO_bootstrap,
    79 #endif
    80 #ifdef MACOSX
    81 	&COREAUDIO_bootstrap,
    82 #endif
    83 #if defined(macintosh) || TARGET_API_MAC_CARBON
    84 	&SNDMGR_bootstrap,
    85 #endif
    86 #ifdef _AIX
    87 	&Paud_bootstrap,
    88 #endif
    89 #ifdef ENABLE_AHI
    90 	&AHI_bootstrap,
    91 #endif
    92 #ifdef MMEAUDIO_SUPPORT
    93 	&MMEAUDIO_bootstrap,
    94 #endif
    95 #ifdef MINTAUDIO_SUPPORT
    96 	&MINTAUDIO_GSXB_bootstrap,
    97 	&MINTAUDIO_MCSN_bootstrap,
    98 	&MINTAUDIO_STFA_bootstrap,
    99 	&MINTAUDIO_XBIOS_bootstrap,
   100 	&MINTAUDIO_DMA8_bootstrap,
   101 #endif
   102 #ifdef DISKAUD_SUPPORT
   103 	&DISKAUD_bootstrap,
   104 #endif
   105 #ifdef ENABLE_DC
   106 	&DCAUD_bootstrap,
   107 #endif
   108 #ifdef DRENDERER_SUPPORT
   109 	&DRENDERER_bootstrap,
   110 #endif
   111 	NULL
   112 };
   113 SDL_AudioDevice *current_audio = NULL;
   114 
   115 /* Various local functions */
   116 int SDL_AudioInit(const char *driver_name);
   117 void SDL_AudioQuit(void);
   118 
   119 #ifdef ENABLE_AHI
   120 static int audio_configured = 0;
   121 #endif
   122 
   123 /* The general mixing thread function */
   124 int SDL_RunAudio(void *audiop)
   125 {
   126 	SDL_AudioDevice *audio = (SDL_AudioDevice *)audiop;
   127 	Uint8 *stream;
   128 	int    stream_len;
   129 	void  *udata;
   130 	void (*fill)(void *userdata,Uint8 *stream, int len);
   131 	int    silence;
   132 #ifdef ENABLE_AHI
   133 	int started = 0;
   134 
   135 /* AmigaOS NEEDS that the audio driver is opened in the thread that uses it! */
   136 
   137 	D(bug("Task audio started audio struct:<%lx>...\n",audiop));
   138 
   139 	D(bug("Before Openaudio..."));
   140 	if(audio->OpenAudio(audio, &audio->spec)==-1)
   141 	{
   142 		D(bug("Open audio failed...\n"));
   143 		return(-1);
   144 	}
   145 	D(bug("OpenAudio...OK\n"));
   146 #endif
   147 
   148 	/* Perform any thread setup */
   149 	if ( audio->ThreadInit ) {
   150 		audio->ThreadInit(audio);
   151 	}
   152 	audio->threadid = SDL_ThreadID();
   153 
   154 	/* Set up the mixing function */
   155 	fill  = audio->spec.callback;
   156 	udata = audio->spec.userdata;
   157 
   158 #ifdef ENABLE_AHI
   159 	audio_configured = 1;
   160 
   161 	D(bug("Audio configured... Checking for conversion\n"));
   162 	SDL_mutexP(audio->mixer_lock);
   163 	D(bug("Semaphore obtained...\n"));
   164 #endif
   165 
   166 	if ( audio->convert.needed ) {
   167 		if ( audio->convert.src_format == AUDIO_U8 ) {
   168 			silence = 0x80;
   169 		} else {
   170 			silence = 0;
   171 		}
   172 		stream_len = audio->convert.len;
   173 	} else {
   174 		silence = audio->spec.silence;
   175 		stream_len = audio->spec.size;
   176 	}
   177 	stream = audio->fake_stream;
   178 
   179 #ifdef ENABLE_AHI
   180 	SDL_mutexV(audio->mixer_lock);
   181 	D(bug("Entering audio loop...\n"));
   182 #endif
   183 
   184 
   185 	/* Loop, filling the audio buffers */
   186 	while ( audio->enabled ) {
   187 
   188 		/* Wait for new current buffer to finish playing */
   189 		if ( stream == audio->fake_stream ) {
   190 			SDL_Delay((audio->spec.samples*1000)/audio->spec.freq);
   191 		} else {
   192 #ifdef ENABLE_AHI
   193 			if ( started > 1 )
   194 #endif
   195 			audio->WaitAudio(audio);
   196 		}
   197 
   198 		/* Fill the current buffer with sound */
   199 		if ( audio->convert.needed ) {
   200 			if ( audio->convert.buf ) {
   201 				stream = audio->convert.buf;
   202 			} else {
   203 				continue;
   204 			}
   205 		} else {
   206 			stream = audio->GetAudioBuf(audio);
   207 			if ( stream == NULL ) {
   208 				stream = audio->fake_stream;
   209 			}
   210 		}
   211 		memset(stream, silence, stream_len);
   212 
   213 		if ( ! audio->paused ) {
   214 			SDL_mutexP(audio->mixer_lock);
   215 			(*fill)(udata, stream, stream_len);
   216 			SDL_mutexV(audio->mixer_lock);
   217 		}
   218 
   219 		/* Convert the audio if necessary */
   220 		if ( audio->convert.needed ) {
   221 			SDL_ConvertAudio(&audio->convert);
   222 			stream = audio->GetAudioBuf(audio);
   223 			if ( stream == NULL ) {
   224 				stream = audio->fake_stream;
   225 			}
   226 			memcpy(stream, audio->convert.buf,
   227 			               audio->convert.len_cvt);
   228 		}
   229 
   230 		/* Ready current buffer for play and change current buffer */
   231 		if ( stream != audio->fake_stream ) {
   232 			audio->PlayAudio(audio);
   233 #ifdef ENABLE_AHI
   234 /* AmigaOS don't have to wait the first time audio is played! */
   235 			started++;
   236 #endif
   237 		}
   238 	}
   239 	/* Wait for the audio to drain.. */
   240 	if ( audio->WaitDone ) {
   241 		audio->WaitDone(audio);
   242 	}
   243 
   244 #ifdef ENABLE_AHI
   245 	D(bug("WaitAudio...Done\n"));
   246 
   247 	audio->CloseAudio(audio);
   248 
   249 	D(bug("CloseAudio..Done, subtask exiting...\n"));
   250 	audio_configured = 0;
   251 #endif
   252 	return(0);
   253 }
   254 
   255 static void SDL_LockAudio_Default(SDL_AudioDevice *audio)
   256 {
   257 	if ( audio->thread && (SDL_ThreadID() == audio->threadid) ) {
   258 		return;
   259 	}
   260 	SDL_mutexP(audio->mixer_lock);
   261 }
   262 
   263 static void SDL_UnlockAudio_Default(SDL_AudioDevice *audio)
   264 {
   265 	if ( audio->thread && (SDL_ThreadID() == audio->threadid) ) {
   266 		return;
   267 	}
   268 	SDL_mutexV(audio->mixer_lock);
   269 }
   270 
   271 int SDL_AudioInit(const char *driver_name)
   272 {
   273 	SDL_AudioDevice *audio;
   274 	int i = 0, idx;
   275 
   276 	/* Check to make sure we don't overwrite 'current_audio' */
   277 	if ( current_audio != NULL ) {
   278 		SDL_AudioQuit();
   279 	}
   280 
   281 	/* Select the proper audio driver */
   282 	audio = NULL;
   283 	idx = 0;
   284 #ifdef unix
   285 	if ( (driver_name == NULL) && (getenv("ESPEAKER") != NULL) ) {
   286 		/* Ahem, we know that if ESPEAKER is set, user probably wants
   287 		   to use ESD, but don't start it if it's not already running.
   288 		   This probably isn't the place to do this, but... Shh! :)
   289 		 */
   290 		for ( i=0; bootstrap[i]; ++i ) {
   291 			if ( strcmp(bootstrap[i]->name, "esd") == 0 ) {
   292 				const char *esd_no_spawn;
   293 
   294 				/* Don't start ESD if it's not running */
   295 				esd_no_spawn = getenv("ESD_NO_SPAWN");
   296 				if ( esd_no_spawn == NULL ) {
   297 					putenv("ESD_NO_SPAWN=1");
   298 				}
   299 				if ( bootstrap[i]->available() ) {
   300 					audio = bootstrap[i]->create(0);
   301 					break;
   302 				}
   303 #ifdef linux	/* No unsetenv() on most platforms */
   304 				if ( esd_no_spawn == NULL ) {
   305 					unsetenv("ESD_NO_SPAWN");
   306 				}
   307 #endif
   308 			}
   309 		}
   310 	}
   311 #endif /* unix */
   312 	if ( audio == NULL ) {
   313 		if ( driver_name != NULL ) {
   314 #if 0	/* This will be replaced with a better driver selection API */
   315 			if ( strrchr(driver_name, ':') != NULL ) {
   316 				idx = atoi(strrchr(driver_name, ':')+1);
   317 			}
   318 #endif
   319 			for ( i=0; bootstrap[i]; ++i ) {
   320 				if (strncmp(bootstrap[i]->name, driver_name,
   321 				            strlen(bootstrap[i]->name)) == 0) {
   322 					if ( bootstrap[i]->available() ) {
   323 						audio=bootstrap[i]->create(idx);
   324 						break;
   325 					}
   326 				}
   327 			}
   328 		} else {
   329 			for ( i=0; bootstrap[i]; ++i ) {
   330 				if ( bootstrap[i]->available() ) {
   331 					audio = bootstrap[i]->create(idx);
   332 					if ( audio != NULL ) {
   333 						break;
   334 					}
   335 				}
   336 			}
   337 		}
   338 		if ( audio == NULL ) {
   339 			SDL_SetError("No available audio device");
   340 #if 0 /* Don't fail SDL_Init() if audio isn't available.
   341          SDL_OpenAudio() will handle it at that point.  *sigh*
   342        */
   343 			return(-1);
   344 #endif
   345 		}
   346 	}
   347 	current_audio = audio;
   348 	if ( current_audio ) {
   349 		current_audio->name = bootstrap[i]->name;
   350 		if ( !current_audio->LockAudio && !current_audio->UnlockAudio ) {
   351 			current_audio->LockAudio = SDL_LockAudio_Default;
   352 			current_audio->UnlockAudio = SDL_UnlockAudio_Default;
   353 		}
   354 	}
   355 	return(0);
   356 }
   357 
   358 char *SDL_AudioDriverName(char *namebuf, int maxlen)
   359 {
   360 	if ( current_audio != NULL ) {
   361 		strncpy(namebuf, current_audio->name, maxlen-1);
   362 		namebuf[maxlen-1] = '\0';
   363 		return(namebuf);
   364 	}
   365 	return(NULL);
   366 }
   367 
   368 int SDL_OpenAudio(SDL_AudioSpec *desired, SDL_AudioSpec *obtained)
   369 {
   370 	SDL_AudioDevice *audio;
   371 
   372 	/* Start up the audio driver, if necessary */
   373 	if ( ! current_audio ) {
   374 		if ( (SDL_InitSubSystem(SDL_INIT_AUDIO) < 0) ||
   375 		     (current_audio == NULL) ) {
   376 			return(-1);
   377 		}
   378 	}
   379 	audio = current_audio;
   380 
   381 	if (audio->opened) {
   382 		SDL_SetError("Audio device is already opened");
   383 		return(-1);
   384 	}
   385 
   386 	/* Verify some parameters */
   387 	if ( desired->callback == NULL ) {
   388 		SDL_SetError("SDL_OpenAudio() passed a NULL callback");
   389 		return(-1);
   390 	}
   391 	switch ( desired->channels ) {
   392 	    case 1:	/* Mono */
   393 	    case 2:	/* Stereo */
   394 	    case 4:	/* surround */
   395 	    case 6:	/* surround with center and lfe */
   396 		break;
   397 	    default:
   398 		SDL_SetError("1 (mono) and 2 (stereo) channels supported");
   399 		return(-1);
   400 	}
   401 
   402 #if defined(macintosh) || (defined(__riscos__) && !defined(DISABLE_THREADS))
   403 	/* FIXME: Need to implement PPC interrupt asm for SDL_LockAudio() */
   404 #else
   405 #if defined(__MINT__) && !defined(ENABLE_THREADS)
   406 	/* Uses interrupt driven audio, without thread */
   407 #else
   408 	/* Create a semaphore for locking the sound buffers */
   409 	audio->mixer_lock = SDL_CreateMutex();
   410 	if ( audio->mixer_lock == NULL ) {
   411 		SDL_SetError("Couldn't create mixer lock");
   412 		SDL_CloseAudio();
   413 		return(-1);
   414 	}
   415 #endif /* __MINT__ */
   416 #endif /* macintosh */
   417 
   418 	/* Calculate the silence and size of the audio specification */
   419 	SDL_CalculateAudioSpec(desired);
   420 
   421 	/* Open the audio subsystem */
   422 	memcpy(&audio->spec, desired, sizeof(audio->spec));
   423 	audio->convert.needed = 0;
   424 	audio->enabled = 1;
   425 	audio->paused  = 1;
   426 
   427 #ifndef ENABLE_AHI
   428 
   429 /* AmigaOS opens audio inside the main loop */
   430 	audio->opened = audio->OpenAudio(audio, &audio->spec)+1;
   431 
   432 	if ( ! audio->opened ) {
   433 		SDL_CloseAudio();
   434 		return(-1);
   435 	}
   436 #else
   437 	D(bug("Locking semaphore..."));
   438 	SDL_mutexP(audio->mixer_lock);
   439 
   440 	audio->thread = SDL_CreateThread(SDL_RunAudio, audio);
   441 	D(bug("Created thread...\n"));
   442 
   443 	if ( audio->thread == NULL ) {
   444 		SDL_mutexV(audio->mixer_lock);
   445 		SDL_CloseAudio();
   446 		SDL_SetError("Couldn't create audio thread");
   447 		return(-1);
   448 	}
   449 
   450 	while(!audio_configured)
   451 		SDL_Delay(100);
   452 #endif
   453 
   454 	/* If the audio driver changes the buffer size, accept it */
   455 	if ( audio->spec.samples != desired->samples ) {
   456 		desired->samples = audio->spec.samples;
   457 		SDL_CalculateAudioSpec(desired);
   458 	}
   459 
   460 	/* Allocate a fake audio memory buffer */
   461 	audio->fake_stream = SDL_AllocAudioMem(audio->spec.size);
   462 	if ( audio->fake_stream == NULL ) {
   463 		SDL_CloseAudio();
   464 		SDL_OutOfMemory();
   465 		return(-1);
   466 	}
   467 
   468 	/* See if we need to do any conversion */
   469 	if ( obtained != NULL ) {
   470 		memcpy(obtained, &audio->spec, sizeof(audio->spec));
   471 	} else if ( desired->freq != audio->spec.freq ||
   472                     desired->format != audio->spec.format ||
   473 	            desired->channels != audio->spec.channels ) {
   474 		/* Build an audio conversion block */
   475 		if ( SDL_BuildAudioCVT(&audio->convert,
   476 			desired->format, desired->channels,
   477 					desired->freq,
   478 			audio->spec.format, audio->spec.channels,
   479 					audio->spec.freq) < 0 ) {
   480 			SDL_CloseAudio();
   481 			return(-1);
   482 		}
   483 		if ( audio->convert.needed ) {
   484 			audio->convert.len = desired->size;
   485 			audio->convert.buf =(Uint8 *)SDL_AllocAudioMem(
   486 			   audio->convert.len*audio->convert.len_mult);
   487 			if ( audio->convert.buf == NULL ) {
   488 				SDL_CloseAudio();
   489 				SDL_OutOfMemory();
   490 				return(-1);
   491 			}
   492 		}
   493 	}
   494 
   495 #ifndef ENABLE_AHI
   496 	/* Start the audio thread if necessary */
   497 	switch (audio->opened) {
   498 		case  1:
   499 			/* Start the audio thread */
   500 			audio->thread = SDL_CreateThread(SDL_RunAudio, audio);
   501 			if ( audio->thread == NULL ) {
   502 				SDL_CloseAudio();
   503 				SDL_SetError("Couldn't create audio thread");
   504 				return(-1);
   505 			}
   506 			break;
   507 
   508 		default:
   509 			/* The audio is now playing */
   510 			break;
   511 	}
   512 #else
   513 	SDL_mutexV(audio->mixer_lock);
   514 	D(bug("SDL_OpenAudio USCITA...\n"));
   515 
   516 #endif
   517 
   518 	return(0);
   519 }
   520 
   521 SDL_audiostatus SDL_GetAudioStatus(void)
   522 {
   523 	SDL_AudioDevice *audio = current_audio;
   524 	SDL_audiostatus status;
   525 
   526 	status = SDL_AUDIO_STOPPED;
   527 	if ( audio && audio->enabled ) {
   528 		if ( audio->paused ) {
   529 			status = SDL_AUDIO_PAUSED;
   530 		} else {
   531 			status = SDL_AUDIO_PLAYING;
   532 		}
   533 	}
   534 	return(status);
   535 }
   536 
   537 void SDL_PauseAudio (int pause_on)
   538 {
   539 	SDL_AudioDevice *audio = current_audio;
   540 
   541 	if ( audio ) {
   542 		audio->paused = pause_on;
   543 	}
   544 }
   545 
   546 void SDL_LockAudio (void)
   547 {
   548 	SDL_AudioDevice *audio = current_audio;
   549 
   550 	/* Obtain a lock on the mixing buffers */
   551 	if ( audio && audio->LockAudio ) {
   552 		audio->LockAudio(audio);
   553 	}
   554 }
   555 
   556 void SDL_UnlockAudio (void)
   557 {
   558 	SDL_AudioDevice *audio = current_audio;
   559 
   560 	/* Release lock on the mixing buffers */
   561 	if ( audio && audio->UnlockAudio ) {
   562 		audio->UnlockAudio(audio);
   563 	}
   564 }
   565 
   566 void SDL_CloseAudio (void)
   567 {
   568 	SDL_QuitSubSystem(SDL_INIT_AUDIO);
   569 }
   570 
   571 void SDL_AudioQuit(void)
   572 {
   573 	SDL_AudioDevice *audio = current_audio;
   574 
   575 	if ( audio ) {
   576 		audio->enabled = 0;
   577 		if ( audio->thread != NULL ) {
   578 			SDL_WaitThread(audio->thread, NULL);
   579 		}
   580 		if ( audio->mixer_lock != NULL ) {
   581 			SDL_DestroyMutex(audio->mixer_lock);
   582 		}
   583 		if ( audio->fake_stream != NULL ) {
   584 			SDL_FreeAudioMem(audio->fake_stream);
   585 		}
   586 		if ( audio->convert.needed ) {
   587 			SDL_FreeAudioMem(audio->convert.buf);
   588 
   589 		}
   590 #ifndef ENABLE_AHI
   591 		if ( audio->opened ) {
   592 			audio->CloseAudio(audio);
   593 			audio->opened = 0;
   594 		}
   595 #endif
   596 		/* Free the driver data */
   597 		audio->free(audio);
   598 		current_audio = NULL;
   599 	}
   600 }
   601 
   602 #define NUM_FORMATS	6
   603 static int format_idx;
   604 static int format_idx_sub;
   605 static Uint16 format_list[NUM_FORMATS][NUM_FORMATS] = {
   606  { AUDIO_U8, AUDIO_S8, AUDIO_S16LSB, AUDIO_S16MSB, AUDIO_U16LSB, AUDIO_U16MSB },
   607  { AUDIO_S8, AUDIO_U8, AUDIO_S16LSB, AUDIO_S16MSB, AUDIO_U16LSB, AUDIO_U16MSB },
   608  { AUDIO_S16LSB, AUDIO_S16MSB, AUDIO_U16LSB, AUDIO_U16MSB, AUDIO_U8, AUDIO_S8 },
   609  { AUDIO_S16MSB, AUDIO_S16LSB, AUDIO_U16MSB, AUDIO_U16LSB, AUDIO_U8, AUDIO_S8 },
   610  { AUDIO_U16LSB, AUDIO_U16MSB, AUDIO_S16LSB, AUDIO_S16MSB, AUDIO_U8, AUDIO_S8 },
   611  { AUDIO_U16MSB, AUDIO_U16LSB, AUDIO_S16MSB, AUDIO_S16LSB, AUDIO_U8, AUDIO_S8 },
   612 };
   613 
   614 Uint16 SDL_FirstAudioFormat(Uint16 format)
   615 {
   616 	for ( format_idx=0; format_idx < NUM_FORMATS; ++format_idx ) {
   617 		if ( format_list[format_idx][0] == format ) {
   618 			break;
   619 		}
   620 	}
   621 	format_idx_sub = 0;
   622 	return(SDL_NextAudioFormat());
   623 }
   624 
   625 Uint16 SDL_NextAudioFormat(void)
   626 {
   627 	if ( (format_idx == NUM_FORMATS) || (format_idx_sub == NUM_FORMATS) ) {
   628 		return(0);
   629 	}
   630 	return(format_list[format_idx][format_idx_sub++]);
   631 }
   632 
   633 void SDL_CalculateAudioSpec(SDL_AudioSpec *spec)
   634 {
   635 	switch (spec->format) {
   636 		case AUDIO_U8:
   637 			spec->silence = 0x80;
   638 			break;
   639 		default:
   640 			spec->silence = 0x00;
   641 			break;
   642 	}
   643 	spec->size = (spec->format&0xFF)/8;
   644 	spec->size *= spec->channels;
   645 	spec->size *= spec->samples;
   646 }