src/audio/SDL_mixer.c
author Sam Lantinga
Sat, 09 Nov 2002 06:13:28 +0000
changeset 539 a9e38f3b8e4d
parent 297 f6ffac90895c
child 574 64fe373be3dc
permissions -rw-r--r--
Added MMX audio mixing code for gcc (thanks Stephane!)
     1 /*
     2     SDL - Simple DirectMedia Layer
     3     Copyright (C) 1997, 1998, 1999, 2000, 2001, 2002  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 /* This provides the default mixing callback for the SDL audio routines */
    29 
    30 #include <stdio.h>
    31 #include <stdlib.h>
    32 #include <string.h>
    33 
    34 #include "SDL_audio.h"
    35 #include "SDL_mutex.h"
    36 #include "SDL_timer.h"
    37 #include "SDL_sysaudio.h"
    38 #include "SDL_mixer_MMX.h"
    39 
    40 /* Function to check the CPU flags */
    41 #define MMX_CPU		0x800000
    42 #ifdef USE_ASMBLIT
    43 #define CPU_Flags()	Hermes_X86_CPU()
    44 #else
    45 #define CPU_Flags()	0L
    46 #endif
    47 
    48 #ifdef USE_ASMBLIT
    49 #define X86_ASSEMBLER
    50 #define HermesConverterInterface	void
    51 #define HermesClearInterface		void
    52 #define STACKCALL
    53 
    54 #include "HeadX86.h"
    55 #endif
    56 
    57 /* This table is used to add two sound values together and pin
    58  * the value to avoid overflow.  (used with permission from ARDI)
    59  * Changed to use 0xFE instead of 0xFF for better sound quality.
    60  */
    61 static const Uint8 mix8[] =
    62 {
    63   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    64   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    65   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    66   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    67   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    68   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    69   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    70   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    71   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    72   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    73   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    74   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03,
    75   0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E,
    76   0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19,
    77   0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20, 0x21, 0x22, 0x23, 0x24,
    78   0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F,
    79   0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A,
    80   0x3B, 0x3C, 0x3D, 0x3E, 0x3F, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45,
    81   0x46, 0x47, 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, 0x50,
    82   0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x5B,
    83   0x5C, 0x5D, 0x5E, 0x5F, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66,
    84   0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, 0x70, 0x71,
    85   0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x7B, 0x7C,
    86   0x7D, 0x7E, 0x7F, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
    87   0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F, 0x90, 0x91, 0x92,
    88   0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9A, 0x9B, 0x9C, 0x9D,
    89   0x9E, 0x9F, 0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8,
    90   0xA9, 0xAA, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF, 0xB0, 0xB1, 0xB2, 0xB3,
    91   0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xBA, 0xBB, 0xBC, 0xBD, 0xBE,
    92   0xBF, 0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9,
    93   0xCA, 0xCB, 0xCC, 0xCD, 0xCE, 0xCF, 0xD0, 0xD1, 0xD2, 0xD3, 0xD4,
    94   0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xDB, 0xDC, 0xDD, 0xDE, 0xDF,
    95   0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA,
    96   0xEB, 0xEC, 0xED, 0xEE, 0xEF, 0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5,
    97   0xF6, 0xF7, 0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xFE, 0xFE,
    98   0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE,
    99   0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE,
   100   0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE,
   101   0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE,
   102   0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE,
   103   0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE,
   104   0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE,
   105   0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE,
   106   0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE,
   107   0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE,
   108   0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE,
   109   0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE
   110 };
   111 
   112 /* The volume ranges from 0 - 128 */
   113 #define ADJUST_VOLUME(s, v)	(s = (s*v)/SDL_MIX_MAXVOLUME)
   114 #define ADJUST_VOLUME_U8(s, v)	(s = (((s-128)*v)/SDL_MIX_MAXVOLUME)+128)
   115 
   116 void SDL_MixAudio (Uint8 *dst, const Uint8 *src, Uint32 len, int volume)
   117 {
   118 	Uint16 format;
   119 
   120 	if ( volume == 0 ) {
   121 		return;
   122 	}
   123 	/* Mix the user-level audio format */
   124 	if ( current_audio ) {
   125 		if ( current_audio->convert.needed ) {
   126 			format = current_audio->convert.src_format;
   127 		} else {
   128 			format = current_audio->spec.format;
   129 		}
   130 	} else {
   131   		/* HACK HACK HACK */
   132 		format = AUDIO_S16;
   133 	}
   134 	switch (format) {
   135 
   136 		case AUDIO_U8: {
   137 			Uint8 src_sample;
   138 
   139 			while ( len-- ) {
   140 				src_sample = *src;
   141 				ADJUST_VOLUME_U8(src_sample, volume);
   142 				*dst = mix8[*dst+src_sample];
   143 				++dst;
   144 				++src;
   145 			}
   146 		}
   147 		break;
   148 
   149 		case AUDIO_S8: {
   150 
   151 #if defined(i386) && defined(__GNUC__) && defined(USE_ASMBLIT)
   152 			if (CPU_Flags() & MMX_CPU)
   153 			{
   154 				SDL_MixAudio_MMX_S8((char*)dst,(char*)src,(unsigned int)len,(int)volume);
   155 			}
   156 			else
   157 #endif
   158 			{
   159 			Sint8 *dst8, *src8;
   160 			Sint8 src_sample;
   161 			int dst_sample;
   162 			const int max_audioval = ((1<<(8-1))-1);
   163 			const int min_audioval = -(1<<(8-1));
   164 
   165 			src8 = (Sint8 *)src;
   166 			dst8 = (Sint8 *)dst;
   167 			while ( len-- ) {
   168 				src_sample = *src8;
   169 				ADJUST_VOLUME(src_sample, volume);
   170 				dst_sample = *dst8 + src_sample;
   171 				if ( dst_sample > max_audioval ) {
   172 					*dst8 = max_audioval;
   173 				} else
   174 				if ( dst_sample < min_audioval ) {
   175 					*dst8 = min_audioval;
   176 				} else {
   177 					*dst8 = dst_sample;
   178 				}
   179 				++dst8;
   180 				++src8;
   181 			}
   182 			}
   183 		}
   184 		break;
   185 
   186 		case AUDIO_S16LSB: {
   187 #if defined(i386) && defined(__GNUC__) && defined(USE_ASMBLIT)
   188 			if (CPU_Flags() & MMX_CPU)
   189 			{
   190 				SDL_MixAudio_MMX_S16((char*)dst,(char*)src,(unsigned int)len,(int)volume);
   191 			}
   192 			else
   193 #endif
   194 			{
   195 			Sint16 src1, src2;
   196 			int dst_sample;
   197 			const int max_audioval = ((1<<(16-1))-1);
   198 			const int min_audioval = -(1<<(16-1));
   199 
   200 			len /= 2;
   201 			while ( len-- ) {
   202 				src1 = ((src[1])<<8|src[0]);
   203 				ADJUST_VOLUME(src1, volume);
   204 				src2 = ((dst[1])<<8|dst[0]);
   205 				src += 2;
   206 				dst_sample = src1+src2;
   207 				if ( dst_sample > max_audioval ) {
   208 					dst_sample = max_audioval;
   209 				} else
   210 				if ( dst_sample < min_audioval ) {
   211 					dst_sample = min_audioval;
   212 				}
   213 				dst[0] = dst_sample&0xFF;
   214 				dst_sample >>= 8;
   215 				dst[1] = dst_sample&0xFF;
   216 				dst += 2;
   217 			}
   218 			}
   219 		}
   220 		break;
   221 
   222 		case AUDIO_S16MSB: {
   223 			Sint16 src1, src2;
   224 			int dst_sample;
   225 			const int max_audioval = ((1<<(16-1))-1);
   226 			const int min_audioval = -(1<<(16-1));
   227 
   228 			len /= 2;
   229 			while ( len-- ) {
   230 				src1 = ((src[0])<<8|src[1]);
   231 				ADJUST_VOLUME(src1, volume);
   232 				src2 = ((dst[0])<<8|dst[1]);
   233 				src += 2;
   234 				dst_sample = src1+src2;
   235 				if ( dst_sample > max_audioval ) {
   236 					dst_sample = max_audioval;
   237 				} else
   238 				if ( dst_sample < min_audioval ) {
   239 					dst_sample = min_audioval;
   240 				}
   241 				dst[1] = dst_sample&0xFF;
   242 				dst_sample >>= 8;
   243 				dst[0] = dst_sample&0xFF;
   244 				dst += 2;
   245 			}
   246 		}
   247 		break;
   248 
   249 		default: /* If this happens... FIXME! */
   250 			SDL_SetError("SDL_MixAudio(): unknown audio format");
   251 			return;
   252 	}
   253 }