Mercurial > SDL_sound_CoreAudio
view audio_convert.c @ 444:188eef49ad0d
Updated.
author | Ryan C. Gordon <icculus@icculus.org> |
---|---|
date | Fri, 31 Jan 2003 05:18:23 +0000 |
parents | fb519e6028e3 |
children | 636796aed4e2 |
line wrap: on
line source
/* SDL - Simple DirectMedia Layer Copyright (C) 1997, 1998, 1999, 2000, 2001 Sam Lantinga This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Sam Lantinga slouken@devolution.com */ /* * This file was derived from SDL's SDL_audiocvt.c and is an attempt to * address the shortcomings of it. * * Perhaps we can adapt some good filters from SoX? */ #if HAVE_CONFIG_H # include <config.h> #endif #include "SDL.h" #include "SDL_sound.h" #define __SDL_SOUND_INTERNAL__ #include "SDL_sound_internal.h" /* Functions for audio drivers to perform runtime conversion of audio format */ /* * Toggle endianness. This filter is, of course, only applied to 16-bit * audio data. */ static void Sound_ConvertEndian(Sound_AudioCVT *cvt, Uint16 *format) { int i; Uint8 *data, tmp; /* SNDDBG(("Converting audio endianness\n")); */ data = cvt->buf; for (i = cvt->len_cvt / 2; i; --i) { tmp = data[0]; data[0] = data[1]; data[1] = tmp; data += 2; } /* for */ *format = (*format ^ 0x1000); } /* Sound_ConvertEndian */ /* * Toggle signed/unsigned. Apparently this is done by toggling the most * significant bit of each sample. */ static void Sound_ConvertSign(Sound_AudioCVT *cvt, Uint16 *format) { int i; Uint8 *data; /* SNDDBG(("Converting audio signedness\n")); */ data = cvt->buf; /* 16-bit sound? */ if ((*format & 0xFF) == 16) { /* Little-endian? */ if ((*format & 0x1000) != 0x1000) ++data; for (i = cvt->len_cvt / 2; i; --i) { *data ^= 0x80; data += 2; } /* for */ } /* if */ else { for (i = cvt->len_cvt; i; --i) *data++ ^= 0x80; } /* else */ *format = (*format ^ 0x8000); } /* Sound_ConvertSign */ /* * Convert 16-bit to 8-bit. This is done by taking the most significant byte * of each 16-bit sample. */ static void Sound_Convert8(Sound_AudioCVT *cvt, Uint16 *format) { int i; Uint8 *src, *dst; /* SNDDBG(("Converting to 8-bit\n")); */ src = cvt->buf; dst = cvt->buf; /* Little-endian? */ if ((*format & 0x1000) != 0x1000) ++src; for (i = cvt->len_cvt / 2; i; --i) { *dst = *src; src += 2; dst += 1; } /* for */ *format = ((*format & ~0x9010) | AUDIO_U8); cvt->len_cvt /= 2; } /* Sound_Convert8 */ /* Convert 8-bit to 16-bit - LSB */ static void Sound_Convert16LSB(Sound_AudioCVT *cvt, Uint16 *format) { int i; Uint8 *src, *dst; /* SNDDBG(("Converting to 16-bit LSB\n")); */ src = cvt->buf + cvt->len_cvt; dst = cvt->buf + cvt->len_cvt * 2; for (i = cvt->len_cvt; i; --i) { src -= 1; dst -= 2; dst[1] = *src; dst[0] = 0; } /* for */ *format = ((*format & ~0x0008) | AUDIO_U16LSB); cvt->len_cvt *= 2; } /* Sound_Convert16LSB */ /* Convert 8-bit to 16-bit - MSB */ static void Sound_Convert16MSB(Sound_AudioCVT *cvt, Uint16 *format) { int i; Uint8 *src, *dst; /* SNDDBG(("Converting to 16-bit MSB\n")); */ src = cvt->buf + cvt->len_cvt; dst = cvt->buf + cvt->len_cvt * 2; for (i = cvt->len_cvt; i; --i) { src -= 1; dst -= 2; dst[0] = *src; dst[1] = 0; } /* for */ *format = ((*format & ~0x0008) | AUDIO_U16MSB); cvt->len_cvt *= 2; } /* Sound_Convert16MSB */ /* Duplicate a mono channel to both stereo channels */ static void Sound_ConvertStereo(Sound_AudioCVT *cvt, Uint16 *format) { int i; /* SNDDBG(("Converting to stereo\n")); */ /* 16-bit sound? */ if ((*format & 0xFF) == 16) { Uint16 *src, *dst; src = (Uint16 *) (cvt->buf + cvt->len_cvt); dst = (Uint16 *) (cvt->buf + cvt->len_cvt * 2); for (i = cvt->len_cvt/2; i; --i) { dst -= 2; src -= 1; dst[0] = src[0]; dst[1] = src[0]; } /* for */ } /* if */ else { Uint8 *src, *dst; src = cvt->buf + cvt->len_cvt; dst = cvt->buf + cvt->len_cvt * 2; for (i = cvt->len_cvt; i; --i) { dst -= 2; src -= 1; dst[0] = src[0]; dst[1] = src[0]; } /* for */ } /* else */ cvt->len_cvt *= 2; } /* Sound_ConvertStereo */ /* Effectively mix right and left channels into a single channel */ static void Sound_ConvertMono(Sound_AudioCVT *cvt, Uint16 *format) { int i; Sint32 sample; Uint8 *u_src, *u_dst; Sint8 *s_src, *s_dst; /* SNDDBG(("Converting to mono\n")); */ switch (*format) { case AUDIO_U8: u_src = cvt->buf; u_dst = cvt->buf; for (i = cvt->len_cvt / 2; i; --i) { sample = u_src[0] + u_src[1]; *u_dst = (sample > 255) ? 255 : sample; u_src += 2; u_dst += 1; } /* for */ break; case AUDIO_S8: s_src = (Sint8 *) cvt->buf; s_dst = (Sint8 *) cvt->buf; for (i = cvt->len_cvt / 2; i; --i) { sample = s_src[0] + s_src[1]; if (sample > 127) *s_dst = 127; else if (sample < -128) *s_dst = -128; else *s_dst = sample; s_src += 2; s_dst += 1; } /* for */ break; case AUDIO_U16MSB: u_src = cvt->buf; u_dst = cvt->buf; for (i = cvt->len_cvt / 4; i; --i) { sample = (Uint16) ((u_src[0] << 8) | u_src[1]) + (Uint16) ((u_src[2] << 8) | u_src[3]); if (sample > 65535) { u_dst[0] = 0xFF; u_dst[1] = 0xFF; } /* if */ else { u_dst[1] = (sample & 0xFF); sample >>= 8; u_dst[0] = (sample & 0xFF); } /* else */ u_src += 4; u_dst += 2; } /* for */ break; case AUDIO_U16LSB: u_src = cvt->buf; u_dst = cvt->buf; for (i = cvt->len_cvt / 4; i; --i) { sample = (Uint16) ((u_src[1] << 8) | u_src[0]) + (Uint16) ((u_src[3] << 8) | u_src[2]); if (sample > 65535) { u_dst[0] = 0xFF; u_dst[1] = 0xFF; } /* if */ else { u_dst[0] = (sample & 0xFF); sample >>= 8; u_dst[1] = (sample & 0xFF); } /* else */ u_src += 4; u_dst += 2; } /* for */ break; case AUDIO_S16MSB: u_src = cvt->buf; u_dst = cvt->buf; for (i = cvt->len_cvt / 4; i; --i) { sample = (Sint16) ((u_src[0] << 8) | u_src[1]) + (Sint16) ((u_src[2] << 8) | u_src[3]); if (sample > 32767) { u_dst[0] = 0x7F; u_dst[1] = 0xFF; } /* if */ else if (sample < -32768) { u_dst[0] = 0x80; u_dst[1] = 0x00; } /* else if */ else { u_dst[1] = (sample & 0xFF); sample >>= 8; u_dst[0] = (sample & 0xFF); } /* else */ u_src += 4; u_dst += 2; } /* for */ break; case AUDIO_S16LSB: u_src = cvt->buf; u_dst = cvt->buf; for (i = cvt->len_cvt / 4; i; --i) { sample = (Sint16) ((u_src[1] << 8) | u_src[0]) + (Sint16) ((u_src[3] << 8) | u_src[2]); if (sample > 32767) { u_dst[1] = 0x7F; u_dst[0] = 0xFF; } /* if */ else if (sample < -32768) { u_dst[1] = 0x80; u_dst[0] = 0x00; } /* else if */ else { u_dst[0] = (sample & 0xFF); sample >>= 8; u_dst[1] = (sample & 0xFF); } /* else */ u_src += 4; u_dst += 2; } /* for */ break; } /* switch */ cvt->len_cvt /= 2; } /* Sound_ConvertMono */ /* Convert rate up by multiple of 2 */ static void Sound_RateMUL2(Sound_AudioCVT *cvt, Uint16 *format) { int i; Uint8 *src, *dst; /* SNDDBG(("Converting audio rate * 2\n")); */ src = cvt->buf + cvt->len_cvt; dst = cvt->buf + cvt->len_cvt*2; /* 8- or 16-bit sound? */ switch (*format & 0xFF) { case 8: for (i = cvt->len_cvt; i; --i) { src -= 1; dst -= 2; dst[0] = src[0]; dst[1] = src[0]; } /* for */ break; case 16: for (i = cvt->len_cvt / 2; i; --i) { src -= 2; dst -= 4; dst[0] = src[0]; dst[1] = src[1]; dst[2] = src[0]; dst[3] = src[1]; } /* for */ break; } /* switch */ cvt->len_cvt *= 2; } /* Sound_RateMUL2 */ /* Convert rate down by multiple of 2 */ static void Sound_RateDIV2(Sound_AudioCVT *cvt, Uint16 *format) { int i; Uint8 *src, *dst; /* SNDDBG(("Converting audio rate / 2\n")); */ src = cvt->buf; dst = cvt->buf; /* 8- or 16-bit sound? */ switch (*format & 0xFF) { case 8: for (i = cvt->len_cvt / 2; i; --i) { dst[0] = src[0]; src += 2; dst += 1; } /* for */ break; case 16: for (i = cvt->len_cvt / 4; i; --i) { dst[0] = src[0]; dst[1] = src[1]; src += 4; dst += 2; } break; } /* switch */ cvt->len_cvt /= 2; } /* Sound_RateDIV2 */ /* Very slow rate conversion routine */ static void Sound_RateSLOW(Sound_AudioCVT *cvt, Uint16 *format) { double ipos; int i, clen; Uint8 *output8; Uint16 *output16; /* SNDDBG(("Converting audio rate * %4.4f\n", 1.0/cvt->rate_incr)); */ clen = (int) ((double) cvt->len_cvt / cvt->rate_incr); if (cvt->rate_incr > 1.0) { /* 8- or 16-bit sound? */ switch (*format & 0xFF) { case 8: output8 = cvt->buf; ipos = 0.0; for (i = clen; i; --i) { *output8 = cvt->buf[(int) ipos]; ipos += cvt->rate_incr; output8 += 1; } /* for */ break; case 16: output16 = (Uint16 *) cvt->buf; clen &= ~1; ipos = 0.0; for (i = clen / 2; i; --i) { *output16 = ((Uint16 *) cvt->buf)[(int) ipos]; ipos += cvt->rate_incr; output16 += 1; } /* for */ break; } /* switch */ } /* if */ else { /* 8- or 16-bit sound */ switch (*format & 0xFF) { case 8: output8 = cvt->buf + clen; ipos = (double) cvt->len_cvt; for (i = clen; i; --i) { ipos -= cvt->rate_incr; output8 -= 1; *output8 = cvt->buf[(int) ipos]; } /* for */ break; case 16: clen &= ~1; output16 = (Uint16 *) (cvt->buf + clen); ipos = (double) cvt->len_cvt / 2; for (i = clen / 2; i; --i) { ipos -= cvt->rate_incr; output16 -= 1; *output16 = ((Uint16 *) cvt->buf)[(int) ipos]; } /* for */ break; } /* switch */ } /* else */ cvt->len_cvt = clen; } /* Sound_RateSLOW */ int Sound_ConvertAudio(Sound_AudioCVT *cvt) { Uint16 format; /* Make sure there's data to convert */ if (cvt->buf == NULL) { __Sound_SetError("No buffer allocated for conversion"); return(-1); } /* if */ /* Return okay if no conversion is necessary */ cvt->len_cvt = cvt->len; if (cvt->filters[0] == NULL) return(0); /* Set up the conversion and go! */ format = cvt->src_format; for (cvt->filter_index = 0; cvt->filters[cvt->filter_index]; cvt->filter_index++) { cvt->filters[cvt->filter_index](cvt, &format); } return(0); } /* Sound_ConvertAudio */ /* * Creates a set of audio filters to convert from one format to another. * Returns -1 if the format conversion is not supported, or 1 if the * audio filter is set up. */ int Sound_BuildAudioCVT(Sound_AudioCVT *cvt, Uint16 src_format, Uint8 src_channels, Uint32 src_rate, Uint16 dst_format, Uint8 dst_channels, Uint32 dst_rate, Uint32 dst_size) { /* Start off with no conversion necessary */ cvt->needed = 0; cvt->filter_index = 0; cvt->filters[0] = NULL; cvt->len_mult = 1; cvt->len_ratio = 1.0; /* First filter: Endian conversion from src to dst */ if ((src_format & 0x1000) != (dst_format & 0x1000) && ((src_format & 0xff) != 8)) { SNDDBG(("Adding filter: Sound_ConvertEndian\n")); cvt->filters[cvt->filter_index++] = Sound_ConvertEndian; } /* if */ /* Second filter: Sign conversion -- signed/unsigned */ if ((src_format & 0x8000) != (dst_format & 0x8000)) { SNDDBG(("Adding filter: Sound_ConvertSign\n")); cvt->filters[cvt->filter_index++] = Sound_ConvertSign; } /* if */ /* Next filter: Convert 16 bit <--> 8 bit PCM. */ if ((src_format & 0xFF) != (dst_format & 0xFF)) { switch (dst_format & 0x10FF) { case AUDIO_U8: SNDDBG(("Adding filter: Sound_Convert8\n")); cvt->filters[cvt->filter_index++] = Sound_Convert8; cvt->len_ratio /= 2; break; case AUDIO_U16LSB: SNDDBG(("Adding filter: Sound_Convert16LSB\n")); cvt->filters[cvt->filter_index++] = Sound_Convert16LSB; cvt->len_mult *= 2; cvt->len_ratio *= 2; break; case AUDIO_U16MSB: SNDDBG(("Adding filter: Sound_Convert16MSB\n")); cvt->filters[cvt->filter_index++] = Sound_Convert16MSB; cvt->len_mult *= 2; cvt->len_ratio *= 2; break; } /* switch */ } /* if */ /* Next filter: Mono/Stereo conversion */ if (src_channels != dst_channels) { while ((src_channels * 2) <= dst_channels) { SNDDBG(("Adding filter: Sound_ConvertStereo\n")); cvt->filters[cvt->filter_index++] = Sound_ConvertStereo; cvt->len_mult *= 2; src_channels *= 2; cvt->len_ratio *= 2; } /* while */ /* This assumes that 4 channel audio is in the format: * Left {front/back} + Right {front/back} * so converting to L/R stereo works properly. */ while (((src_channels % 2) == 0) && ((src_channels / 2) >= dst_channels)) { SNDDBG(("Adding filter: Sound_ConvertMono\n")); cvt->filters[cvt->filter_index++] = Sound_ConvertMono; src_channels /= 2; cvt->len_ratio /= 2; } /* while */ if ( src_channels != dst_channels ) { /* Uh oh.. */; } /* if */ } /* if */ /* Do rate conversion */ cvt->rate_incr = 0.0; if ((src_rate / 100) != (dst_rate / 100)) { Uint32 hi_rate, lo_rate; int len_mult; double len_ratio; void (*rate_cvt)(Sound_AudioCVT *cvt, Uint16 *format); if (src_rate > dst_rate) { hi_rate = src_rate; lo_rate = dst_rate; SNDDBG(("Adding filter: Sound_RateDIV2\n")); rate_cvt = Sound_RateDIV2; len_mult = 1; len_ratio = 0.5; } /* if */ else { hi_rate = dst_rate; lo_rate = src_rate; SNDDBG(("Adding filter: Sound_RateMUL2\n")); rate_cvt = Sound_RateMUL2; len_mult = 2; len_ratio = 2.0; } /* else */ /* If hi_rate = lo_rate*2^x then conversion is easy */ while (((lo_rate * 2) / 100) <= (hi_rate / 100)) { cvt->filters[cvt->filter_index++] = rate_cvt; cvt->len_mult *= len_mult; lo_rate *= 2; cvt->len_ratio *= len_ratio; } /* while */ /* We may need a slow conversion here to finish up */ if ((lo_rate / 100) != (hi_rate / 100)) { if (src_rate < dst_rate) { cvt->rate_incr = (double) lo_rate / hi_rate; cvt->len_mult *= 2; cvt->len_ratio /= cvt->rate_incr; } /* if */ else { cvt->rate_incr = (double) hi_rate / lo_rate; cvt->len_ratio *= cvt->rate_incr; } /* else */ SNDDBG(("Adding filter: Sound_RateSLOW\n")); cvt->filters[cvt->filter_index++] = Sound_RateSLOW; } /* if */ } /* if */ /* Set up the filter information */ if (cvt->filter_index != 0) { cvt->needed = 1; cvt->src_format = src_format; cvt->dst_format = dst_format; cvt->len = 0; cvt->buf = NULL; cvt->filters[cvt->filter_index] = NULL; } /* if */ return(cvt->needed); } /* Sound_BuildAudioCVT */