src/audio/SDL_audiocvt.c
author Sam Lantinga <slouken@libsdl.org>
Sun, 04 Jan 2004 16:49:27 +0000
changeset 769 b8d311d90021
parent 297 f6ffac90895c
child 942 41a59de7f2ed
permissions -rw-r--r--
Updated copyright information for 2004 (Happy New Year!)
     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 /* Functions for audio drivers to perform runtime conversion of audio format */
    29 
    30 #include <stdio.h>
    31 
    32 #include "SDL_error.h"
    33 #include "SDL_audio.h"
    34 
    35 
    36 /* Effectively mix right and left channels into a single channel */
    37 void SDL_ConvertMono(SDL_AudioCVT *cvt, Uint16 format)
    38 {
    39 	int i;
    40 	Sint32 sample;
    41 
    42 #ifdef DEBUG_CONVERT
    43 	fprintf(stderr, "Converting to mono\n");
    44 #endif
    45 	switch (format&0x8018) {
    46 
    47 		case AUDIO_U8: {
    48 			Uint8 *src, *dst;
    49 
    50 			src = cvt->buf;
    51 			dst = cvt->buf;
    52 			for ( i=cvt->len_cvt/2; i; --i ) {
    53 				sample = src[0] + src[1];
    54 				if ( sample > 255 ) {
    55 					*dst = 255;
    56 				} else {
    57 					*dst = sample;
    58 				}
    59 				src += 2;
    60 				dst += 1;
    61 			}
    62 		}
    63 		break;
    64 
    65 		case AUDIO_S8: {
    66 			Sint8 *src, *dst;
    67 
    68 			src = (Sint8 *)cvt->buf;
    69 			dst = (Sint8 *)cvt->buf;
    70 			for ( i=cvt->len_cvt/2; i; --i ) {
    71 				sample = src[0] + src[1];
    72 				if ( sample > 127 ) {
    73 					*dst = 127;
    74 				} else
    75 				if ( sample < -128 ) {
    76 					*dst = -128;
    77 				} else {
    78 					*dst = sample;
    79 				}
    80 				src += 2;
    81 				dst += 1;
    82 			}
    83 		}
    84 		break;
    85 
    86 		case AUDIO_U16: {
    87 			Uint8 *src, *dst;
    88 
    89 			src = cvt->buf;
    90 			dst = cvt->buf;
    91 			if ( (format & 0x1000) == 0x1000 ) {
    92 				for ( i=cvt->len_cvt/4; i; --i ) {
    93 					sample = (Uint16)((src[0]<<8)|src[1])+
    94 					         (Uint16)((src[2]<<8)|src[3]);
    95 					if ( sample > 65535 ) {
    96 						dst[0] = 0xFF;
    97 						dst[1] = 0xFF;
    98 					} else {
    99 						dst[1] = (sample&0xFF);
   100 						sample >>= 8;
   101 						dst[0] = (sample&0xFF);
   102 					}
   103 					src += 4;
   104 					dst += 2;
   105 				}
   106 			} else {
   107 				for ( i=cvt->len_cvt/4; i; --i ) {
   108 					sample = (Uint16)((src[1]<<8)|src[0])+
   109 					         (Uint16)((src[3]<<8)|src[2]);
   110 					if ( sample > 65535 ) {
   111 						dst[0] = 0xFF;
   112 						dst[1] = 0xFF;
   113 					} else {
   114 						dst[0] = (sample&0xFF);
   115 						sample >>= 8;
   116 						dst[1] = (sample&0xFF);
   117 					}
   118 					src += 4;
   119 					dst += 2;
   120 				}
   121 			}
   122 		}
   123 		break;
   124 
   125 		case AUDIO_S16: {
   126 			Uint8 *src, *dst;
   127 
   128 			src = cvt->buf;
   129 			dst = cvt->buf;
   130 			if ( (format & 0x1000) == 0x1000 ) {
   131 				for ( i=cvt->len_cvt/4; i; --i ) {
   132 					sample = (Sint16)((src[0]<<8)|src[1])+
   133 					         (Sint16)((src[2]<<8)|src[3]);
   134 					if ( sample > 32767 ) {
   135 						dst[0] = 0x7F;
   136 						dst[1] = 0xFF;
   137 					} else
   138 					if ( sample < -32768 ) {
   139 						dst[0] = 0x80;
   140 						dst[1] = 0x00;
   141 					} else {
   142 						dst[1] = (sample&0xFF);
   143 						sample >>= 8;
   144 						dst[0] = (sample&0xFF);
   145 					}
   146 					src += 4;
   147 					dst += 2;
   148 				}
   149 			} else {
   150 				for ( i=cvt->len_cvt/4; i; --i ) {
   151 					sample = (Sint16)((src[1]<<8)|src[0])+
   152 					         (Sint16)((src[3]<<8)|src[2]);
   153 					if ( sample > 32767 ) {
   154 						dst[1] = 0x7F;
   155 						dst[0] = 0xFF;
   156 					} else
   157 					if ( sample < -32768 ) {
   158 						dst[1] = 0x80;
   159 						dst[0] = 0x00;
   160 					} else {
   161 						dst[0] = (sample&0xFF);
   162 						sample >>= 8;
   163 						dst[1] = (sample&0xFF);
   164 					}
   165 					src += 4;
   166 					dst += 2;
   167 				}
   168 			}
   169 		}
   170 		break;
   171 	}
   172 	cvt->len_cvt /= 2;
   173 	if ( cvt->filters[++cvt->filter_index] ) {
   174 		cvt->filters[cvt->filter_index](cvt, format);
   175 	}
   176 }
   177 
   178 
   179 /* Duplicate a mono channel to both stereo channels */
   180 void SDL_ConvertStereo(SDL_AudioCVT *cvt, Uint16 format)
   181 {
   182 	int i;
   183 
   184 #ifdef DEBUG_CONVERT
   185 	fprintf(stderr, "Converting to stereo\n");
   186 #endif
   187 	if ( (format & 0xFF) == 16 ) {
   188 		Uint16 *src, *dst;
   189 
   190 		src = (Uint16 *)(cvt->buf+cvt->len_cvt);
   191 		dst = (Uint16 *)(cvt->buf+cvt->len_cvt*2);
   192 		for ( i=cvt->len_cvt/2; i; --i ) {
   193 			dst -= 2;
   194 			src -= 1;
   195 			dst[0] = src[0];
   196 			dst[1] = src[0];
   197 		}
   198 	} else {
   199 		Uint8 *src, *dst;
   200 
   201 		src = cvt->buf+cvt->len_cvt;
   202 		dst = cvt->buf+cvt->len_cvt*2;
   203 		for ( i=cvt->len_cvt; i; --i ) {
   204 			dst -= 2;
   205 			src -= 1;
   206 			dst[0] = src[0];
   207 			dst[1] = src[0];
   208 		}
   209 	}
   210 	cvt->len_cvt *= 2;
   211 	if ( cvt->filters[++cvt->filter_index] ) {
   212 		cvt->filters[cvt->filter_index](cvt, format);
   213 	}
   214 }
   215 
   216 /* Convert 8-bit to 16-bit - LSB */
   217 void SDL_Convert16LSB(SDL_AudioCVT *cvt, Uint16 format)
   218 {
   219 	int i;
   220 	Uint8 *src, *dst;
   221 
   222 #ifdef DEBUG_CONVERT
   223 	fprintf(stderr, "Converting to 16-bit LSB\n");
   224 #endif
   225 	src = cvt->buf+cvt->len_cvt;
   226 	dst = cvt->buf+cvt->len_cvt*2;
   227 	for ( i=cvt->len_cvt; i; --i ) {
   228 		src -= 1;
   229 		dst -= 2;
   230 		dst[1] = *src;
   231 		dst[0] = 0;
   232 	}
   233 	format = ((format & ~0x0008) | AUDIO_U16LSB);
   234 	cvt->len_cvt *= 2;
   235 	if ( cvt->filters[++cvt->filter_index] ) {
   236 		cvt->filters[cvt->filter_index](cvt, format);
   237 	}
   238 }
   239 /* Convert 8-bit to 16-bit - MSB */
   240 void SDL_Convert16MSB(SDL_AudioCVT *cvt, Uint16 format)
   241 {
   242 	int i;
   243 	Uint8 *src, *dst;
   244 
   245 #ifdef DEBUG_CONVERT
   246 	fprintf(stderr, "Converting to 16-bit MSB\n");
   247 #endif
   248 	src = cvt->buf+cvt->len_cvt;
   249 	dst = cvt->buf+cvt->len_cvt*2;
   250 	for ( i=cvt->len_cvt; i; --i ) {
   251 		src -= 1;
   252 		dst -= 2;
   253 		dst[0] = *src;
   254 		dst[1] = 0;
   255 	}
   256 	format = ((format & ~0x0008) | AUDIO_U16MSB);
   257 	cvt->len_cvt *= 2;
   258 	if ( cvt->filters[++cvt->filter_index] ) {
   259 		cvt->filters[cvt->filter_index](cvt, format);
   260 	}
   261 }
   262 
   263 /* Convert 16-bit to 8-bit */
   264 void SDL_Convert8(SDL_AudioCVT *cvt, Uint16 format)
   265 {
   266 	int i;
   267 	Uint8 *src, *dst;
   268 
   269 #ifdef DEBUG_CONVERT
   270 	fprintf(stderr, "Converting to 8-bit\n");
   271 #endif
   272 	src = cvt->buf;
   273 	dst = cvt->buf;
   274 	if ( (format & 0x1000) != 0x1000 ) { /* Little endian */
   275 		++src;
   276 	}
   277 	for ( i=cvt->len_cvt/2; i; --i ) {
   278 		*dst = *src;
   279 		src += 2;
   280 		dst += 1;
   281 	}
   282 	format = ((format & ~0x9010) | AUDIO_U8);
   283 	cvt->len_cvt /= 2;
   284 	if ( cvt->filters[++cvt->filter_index] ) {
   285 		cvt->filters[cvt->filter_index](cvt, format);
   286 	}
   287 }
   288 
   289 /* Toggle signed/unsigned */
   290 void SDL_ConvertSign(SDL_AudioCVT *cvt, Uint16 format)
   291 {
   292 	int i;
   293 	Uint8 *data;
   294 
   295 #ifdef DEBUG_CONVERT
   296 	fprintf(stderr, "Converting audio signedness\n");
   297 #endif
   298 	data = cvt->buf;
   299 	if ( (format & 0xFF) == 16 ) {
   300 		if ( (format & 0x1000) != 0x1000 ) { /* Little endian */
   301 			++data;
   302 		}
   303 		for ( i=cvt->len_cvt/2; i; --i ) {
   304 			*data ^= 0x80;
   305 			data += 2;
   306 		}
   307 	} else {
   308 		for ( i=cvt->len_cvt; i; --i ) {
   309 			*data++ ^= 0x80;
   310 		}
   311 	}
   312 	format = (format ^ 0x8000);
   313 	if ( cvt->filters[++cvt->filter_index] ) {
   314 		cvt->filters[cvt->filter_index](cvt, format);
   315 	}
   316 }
   317 
   318 /* Toggle endianness */
   319 void SDL_ConvertEndian(SDL_AudioCVT *cvt, Uint16 format)
   320 {
   321 	int i;
   322 	Uint8 *data, tmp;
   323 
   324 #ifdef DEBUG_CONVERT
   325 	fprintf(stderr, "Converting audio endianness\n");
   326 #endif
   327 	data = cvt->buf;
   328 	for ( i=cvt->len_cvt/2; i; --i ) {
   329 		tmp = data[0];
   330 		data[0] = data[1];
   331 		data[1] = tmp;
   332 		data += 2;
   333 	}
   334 	format = (format ^ 0x1000);
   335 	if ( cvt->filters[++cvt->filter_index] ) {
   336 		cvt->filters[cvt->filter_index](cvt, format);
   337 	}
   338 }
   339 
   340 /* Convert rate up by multiple of 2 */
   341 void SDL_RateMUL2(SDL_AudioCVT *cvt, Uint16 format)
   342 {
   343 	int i;
   344 	Uint8 *src, *dst;
   345 
   346 #ifdef DEBUG_CONVERT
   347 	fprintf(stderr, "Converting audio rate * 2\n");
   348 #endif
   349 	src = cvt->buf+cvt->len_cvt;
   350 	dst = cvt->buf+cvt->len_cvt*2;
   351 	switch (format & 0xFF) {
   352 		case 8:
   353 			for ( i=cvt->len_cvt; i; --i ) {
   354 				src -= 1;
   355 				dst -= 2;
   356 				dst[0] = src[0];
   357 				dst[1] = src[0];
   358 			}
   359 			break;
   360 		case 16:
   361 			for ( i=cvt->len_cvt/2; i; --i ) {
   362 				src -= 2;
   363 				dst -= 4;
   364 				dst[0] = src[0];
   365 				dst[1] = src[1];
   366 				dst[2] = src[0];
   367 				dst[3] = src[1];
   368 			}
   369 			break;
   370 	}
   371 	cvt->len_cvt *= 2;
   372 	if ( cvt->filters[++cvt->filter_index] ) {
   373 		cvt->filters[cvt->filter_index](cvt, format);
   374 	}
   375 }
   376 
   377 /* Convert rate down by multiple of 2 */
   378 void SDL_RateDIV2(SDL_AudioCVT *cvt, Uint16 format)
   379 {
   380 	int i;
   381 	Uint8 *src, *dst;
   382 
   383 #ifdef DEBUG_CONVERT
   384 	fprintf(stderr, "Converting audio rate / 2\n");
   385 #endif
   386 	src = cvt->buf;
   387 	dst = cvt->buf;
   388 	switch (format & 0xFF) {
   389 		case 8:
   390 			for ( i=cvt->len_cvt/2; i; --i ) {
   391 				dst[0] = src[0];
   392 				src += 2;
   393 				dst += 1;
   394 			}
   395 			break;
   396 		case 16:
   397 			for ( i=cvt->len_cvt/4; i; --i ) {
   398 				dst[0] = src[0];
   399 				dst[1] = src[1];
   400 				src += 4;
   401 				dst += 2;
   402 			}
   403 			break;
   404 	}
   405 	cvt->len_cvt /= 2;
   406 	if ( cvt->filters[++cvt->filter_index] ) {
   407 		cvt->filters[cvt->filter_index](cvt, format);
   408 	}
   409 }
   410 
   411 /* Very slow rate conversion routine */
   412 void SDL_RateSLOW(SDL_AudioCVT *cvt, Uint16 format)
   413 {
   414 	double ipos;
   415 	int i, clen;
   416 
   417 #ifdef DEBUG_CONVERT
   418 	fprintf(stderr, "Converting audio rate * %4.4f\n", 1.0/cvt->rate_incr);
   419 #endif
   420 	clen = (int)((double)cvt->len_cvt / cvt->rate_incr);
   421 	if ( cvt->rate_incr > 1.0 ) {
   422 		switch (format & 0xFF) {
   423 			case 8: {
   424 				Uint8 *output;
   425 
   426 				output = cvt->buf;
   427 				ipos = 0.0;
   428 				for ( i=clen; i; --i ) {
   429 					*output = cvt->buf[(int)ipos];
   430 					ipos += cvt->rate_incr;
   431 					output += 1;
   432 				}
   433 			}
   434 			break;
   435 
   436 			case 16: {
   437 				Uint16 *output;
   438 
   439 				clen &= ~1;
   440 				output = (Uint16 *)cvt->buf;
   441 				ipos = 0.0;
   442 				for ( i=clen/2; i; --i ) {
   443 					*output=((Uint16 *)cvt->buf)[(int)ipos];
   444 					ipos += cvt->rate_incr;
   445 					output += 1;
   446 				}
   447 			}
   448 			break;
   449 		}
   450 	} else {
   451 		switch (format & 0xFF) {
   452 			case 8: {
   453 				Uint8 *output;
   454 
   455 				output = cvt->buf+clen;
   456 				ipos = (double)cvt->len_cvt;
   457 				for ( i=clen; i; --i ) {
   458 					ipos -= cvt->rate_incr;
   459 					output -= 1;
   460 					*output = cvt->buf[(int)ipos];
   461 				}
   462 			}
   463 			break;
   464 
   465 			case 16: {
   466 				Uint16 *output;
   467 
   468 				clen &= ~1;
   469 				output = (Uint16 *)(cvt->buf+clen);
   470 				ipos = (double)cvt->len_cvt/2;
   471 				for ( i=clen/2; i; --i ) {
   472 					ipos -= cvt->rate_incr;
   473 					output -= 1;
   474 					*output=((Uint16 *)cvt->buf)[(int)ipos];
   475 				}
   476 			}
   477 			break;
   478 		}
   479 	}
   480 	cvt->len_cvt = clen;
   481 	if ( cvt->filters[++cvt->filter_index] ) {
   482 		cvt->filters[cvt->filter_index](cvt, format);
   483 	}
   484 }
   485 
   486 int SDL_ConvertAudio(SDL_AudioCVT *cvt)
   487 {
   488 	/* Make sure there's data to convert */
   489 	if ( cvt->buf == NULL ) {
   490 		SDL_SetError("No buffer allocated for conversion");
   491 		return(-1);
   492 	}
   493 	/* Return okay if no conversion is necessary */
   494 	cvt->len_cvt = cvt->len;
   495 	if ( cvt->filters[0] == NULL ) {
   496 		return(0);
   497 	}
   498 
   499 	/* Set up the conversion and go! */
   500 	cvt->filter_index = 0;
   501 	cvt->filters[0](cvt, cvt->src_format);
   502 	return(0);
   503 }
   504 
   505 /* Creates a set of audio filters to convert from one format to another. 
   506    Returns -1 if the format conversion is not supported, or 1 if the
   507    audio filter is set up.
   508 */
   509   
   510 int SDL_BuildAudioCVT(SDL_AudioCVT *cvt,
   511 	Uint16 src_format, Uint8 src_channels, int src_rate,
   512 	Uint16 dst_format, Uint8 dst_channels, int dst_rate)
   513 {
   514 	/* Start off with no conversion necessary */
   515 	cvt->needed = 0;
   516 	cvt->filter_index = 0;
   517 	cvt->filters[0] = NULL;
   518 	cvt->len_mult = 1;
   519 	cvt->len_ratio = 1.0;
   520 
   521 	/* First filter:  Endian conversion from src to dst */
   522 	if ( (src_format & 0x1000) != (dst_format & 0x1000)
   523 	     && ((src_format & 0xff) != 8) ) {
   524 		cvt->filters[cvt->filter_index++] = SDL_ConvertEndian;
   525 	}
   526 	
   527 	/* Second filter: Sign conversion -- signed/unsigned */
   528 	if ( (src_format & 0x8000) != (dst_format & 0x8000) ) {
   529 		cvt->filters[cvt->filter_index++] = SDL_ConvertSign;
   530 	}
   531 
   532 	/* Next filter:  Convert 16 bit <--> 8 bit PCM */
   533 	if ( (src_format & 0xFF) != (dst_format & 0xFF) ) {
   534 		switch (dst_format&0x10FF) {
   535 			case AUDIO_U8:
   536 				cvt->filters[cvt->filter_index++] =
   537 							 SDL_Convert8;
   538 				cvt->len_ratio /= 2;
   539 				break;
   540 			case AUDIO_U16LSB:
   541 				cvt->filters[cvt->filter_index++] =
   542 							SDL_Convert16LSB;
   543 				cvt->len_mult *= 2;
   544 				cvt->len_ratio *= 2;
   545 				break;
   546 			case AUDIO_U16MSB:
   547 				cvt->filters[cvt->filter_index++] =
   548 							SDL_Convert16MSB;
   549 				cvt->len_mult *= 2;
   550 				cvt->len_ratio *= 2;
   551 				break;
   552 		}
   553 	}
   554 
   555 	/* Last filter:  Mono/Stereo conversion */
   556 	if ( src_channels != dst_channels ) {
   557 		while ( (src_channels*2) <= dst_channels ) {
   558 			cvt->filters[cvt->filter_index++] = 
   559 						SDL_ConvertStereo;
   560 			cvt->len_mult *= 2;
   561 			src_channels *= 2;
   562 			cvt->len_ratio *= 2;
   563 		}
   564 		/* This assumes that 4 channel audio is in the format:
   565 		     Left {front/back} + Right {front/back}
   566 		   so converting to L/R stereo works properly.
   567 		 */
   568 		while ( ((src_channels%2) == 0) &&
   569 				((src_channels/2) >= dst_channels) ) {
   570 			cvt->filters[cvt->filter_index++] =
   571 						 SDL_ConvertMono;
   572 			src_channels /= 2;
   573 			cvt->len_ratio /= 2;
   574 		}
   575 		if ( src_channels != dst_channels ) {
   576 			/* Uh oh.. */;
   577 		}
   578 	}
   579 
   580 	/* Do rate conversion */
   581 	cvt->rate_incr = 0.0;
   582 	if ( (src_rate/100) != (dst_rate/100) ) {
   583 		Uint32 hi_rate, lo_rate;
   584 		int len_mult;
   585 		double len_ratio;
   586 		void (*rate_cvt)(SDL_AudioCVT *cvt, Uint16 format);
   587 
   588 		if ( src_rate > dst_rate ) {
   589 			hi_rate = src_rate;
   590 			lo_rate = dst_rate;
   591 			rate_cvt = SDL_RateDIV2;
   592 			len_mult = 1;
   593 			len_ratio = 0.5;
   594 		} else {
   595 			hi_rate = dst_rate;
   596 			lo_rate = src_rate;
   597 			rate_cvt = SDL_RateMUL2;
   598 			len_mult = 2;
   599 			len_ratio = 2.0;
   600 		}
   601 		/* If hi_rate = lo_rate*2^x then conversion is easy */
   602 		while ( ((lo_rate*2)/100) <= (hi_rate/100) ) {
   603 			cvt->filters[cvt->filter_index++] = rate_cvt;
   604 			cvt->len_mult *= len_mult;
   605 			lo_rate *= 2;
   606 			cvt->len_ratio *= len_ratio;
   607 		}
   608 		/* We may need a slow conversion here to finish up */
   609 		if ( (lo_rate/100) != (hi_rate/100) ) {
   610 #if 1
   611 			/* The problem with this is that if the input buffer is
   612 			   say 1K, and the conversion rate is say 1.1, then the
   613 			   output buffer is 1.1K, which may not be an acceptable
   614 			   buffer size for the audio driver (not a power of 2)
   615 			*/
   616 			/* For now, punt and hope the rate distortion isn't great.
   617 			*/
   618 #else
   619 			if ( src_rate < dst_rate ) {
   620 				cvt->rate_incr = (double)lo_rate/hi_rate;
   621 				cvt->len_mult *= 2;
   622 				cvt->len_ratio /= cvt->rate_incr;
   623 			} else {
   624 				cvt->rate_incr = (double)hi_rate/lo_rate;
   625 				cvt->len_ratio *= cvt->rate_incr;
   626 			}
   627 			cvt->filters[cvt->filter_index++] = SDL_RateSLOW;
   628 #endif
   629 		}
   630 	}
   631 
   632 	/* Set up the filter information */
   633 	if ( cvt->filter_index != 0 ) {
   634 		cvt->needed = 1;
   635 		cvt->src_format = src_format;
   636 		cvt->dst_format = dst_format;
   637 		cvt->len = 0;
   638 		cvt->buf = NULL;
   639 		cvt->filters[cvt->filter_index] = NULL;
   640 	}
   641 	return(cvt->needed);
   642 }