Hey
I’ve modified the typical “play” function for SDL to support directional or
projected sound and volume changes. In other words, the function can change
the balance between left and right speaker and adjust the overall volume.
This is done independantly of each channel, so that each channel is
unaffected by the balance and volume of other sound channels
This function will load wave files to the memory and then scale the right or
left channel in memory before playing. It’s a terrible design, but I
understand SDL doesn’t support this feature on it’s own. This is presently
the best I’ve managed to come up with.
The soundfiles must be in stereo for the balance to work. I could make the
function duplicate the mono channel for mono files. This will be on the TODO
list.
This function works for smaller wave files without any noticable realtime
lag, but only because I have a dual core x gazillion Hz processor and a few
gig ram.
There are no handling of oposite endianesses. It works on my system with my
wave files, but that may be a coincidence. The function needs a lot more
error and situation handling.
I’d love to have comments on the code, and if anyone knows a better way to
solve the issue, please let me know.
I chose to let balance range from -1 to 1 so that a simple sine function of
the angle of an object in 2D/3D can be used as input.
With a linear attenuation of volume reaching 0 at 500, for example:
volume = (500.0-distance) / 500.0;
if (volume < 0.0) volume = 0.0;
PlaySoundStereo(“mysound.wav”, cos(angle), volume)
void PlaySoundStereo(char *file, float balance, float volume)
{
//float position; //-1 to 1 -1 = left, 0 = center, 1 = right
//float volume //0 to 1
float leftvolume;
float rightvolume;
int index;
SDL_AudioSpec wave;
Uint8 *data;
Uint32 dlen;
SDL_AudioCVT cvt;
Uint32 i;
short *int16;
char *int8;
int leftchannel = 1;
leftvolume = volume * ((1.0f - balance ) / 2.0f);
rightvolume = volume * ((balance + 1.0f) / 2.0f);
/* Look for an empty (or finished) sound slot */
for ( index=0; index<NUM_SOUNDS; ++index ) {
if ( sounds[index].dpos == sounds[index].dlen ) {
break;
}
}
if ( index == NUM_SOUNDS )
return;
/* Load the sound file and convert it to 16-bit stereo at 22kHz */
if ( SDL_LoadWAV(file, &wave, &data, &dlen) == NULL ) {
fprintf(stderr, “Couldn’t load %s: %s\n”, file, SDL_GetError());
return;
}
//// START - Ingvar’s code for resampling sound file in memory ("data")
according to balance and volume. /////////////
if (wave.channels == 1) {
//No balance adjustments for mono, yet.
if (wave.format == AUDIO_S8) {
int8 = (char) (data);
for (i=0; i<dlen/4; i++) {
(*int8) = (char)((float)(int8) * volume);
int8++;
}
} else {
int16 = (short) (data);
for (i=0; i<dlen/2; i++) {
(*int16) = (short)((float)(int16) * volume);
int16++;
}
}
} else if (wave.channels == 2) {
/
wave.format:
AUDIO_S8 - Signed 8-bit samples
AUDIO_U16 or AUDIO_U16LSB - Unsigned 16-bit little-endian samples
AUDIO_S16 or AUDIO_S16LSB - Signed 16-bit little-endian samples
AUDIO_U16MSB - Unsigned 16-bit big-endian samples
AUDIO_S16MSB - Signed 16-bit big-endian samples
AUDIO_U16SYS - Either AUDIO_U16LSB or AUDIO_U16MSB depending on you
systems endianness
AUDIO_S16SYS - Either AUDIO_S16LSB or AUDIO_S16MSB depending on you
*/
if (wave.format == AUDIO_S8) {
int8 = (char*) (data);
for (i=0; i<dlen/4; i++) {
if (leftchannel) {
(*int8) = (char)((float)(*int8) * leftvolume);
} else {
(*int8) = (char)((float)(*int8) * rightvolume);
}
int8++;
leftchannel = !leftchannel;
}
} else {
int16 = (short*) (data);
for (i=0; i<dlen/2; i++) {
if (leftchannel) {
(*int16) = (short)((float)(*int16) * leftvolume);
} else {
(*int16) = (short)((float)(*int16) * rightvolume);
}
int16++;
leftchannel = !leftchannel;
}
}
}
//// END - Ingvar’s code for resampling sound file in memory ("*data")
according to balance and volume. /////////////
SDL_BuildAudioCVT(&cvt, wave.format, wave.channels, wave.freq, AUDIO_S16,
2, 22050);
cvt.buf = malloc(dlen*cvt.len_mult);
memcpy(cvt.buf, data, dlen);
cvt.len = dlen;
SDL_ConvertAudio(&cvt);
SDL_FreeWAV(data);
/* Put the sound data in the slot (it starts playing immediately) */
if ( sounds[index].data ) {
free(sounds[index].data);
}
SDL_LockAudio();
sounds[index].data = cvt.buf;
sounds[index].dlen = cvt.len_cvt;
sounds[index].dpos = 0;
SDL_UnlockAudio();
}