From 2ca7c683dd095cada539874c20de57ec0f83c683 Mon Sep 17 00:00:00 2001 From: Scott Duensing Date: Wed, 14 Sep 2022 19:14:30 -0500 Subject: [PATCH] PC audio now supports volume and stereo channels. --- joeylib/src/jSDL2.c | 82 +++++++++++++++++++++++++++++++++++++++------ joeylib/src/joey.c | 18 ++++++++++ joeylib/src/joey.h | 5 +++ joeylib/src/test.c | 8 ++++- 4 files changed, 101 insertions(+), 12 deletions(-) diff --git a/joeylib/src/jSDL2.c b/joeylib/src/jSDL2.c index bb8bc3e..518bb61 100644 --- a/joeylib/src/jSDL2.c +++ b/joeylib/src/jSDL2.c @@ -32,7 +32,7 @@ #include "jPixBuf.h" -#define AUDIO_FORMAT AUDIO_F32 +#define AUDIO_FORMAT AUDIO_F32 // PocketMod returns floats. #define AUDIO_FREQUENCY 44100 #define AUDIO_CHANNELS 2 @@ -73,6 +73,7 @@ static SDL_AudioSpec _jlAudioFormat; static SDL_AudioDeviceID _jlAudioDevice; static jbool _jlModPlaying = jfalse; static jlPlatformModT *_jlModCurrent = NULL; +static jbyte _jlModVolume = 255; static jlSoundPlayingT *_jlSoundList = NULL; @@ -80,6 +81,9 @@ static Uint32 _jlUtilTimer(Uint32 interval, void *param); static void _jlAudioCallback(void *userdata, Uint8 *buffer, int bytes); +extern jbool _jlSwapChannels; + + /* #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wcast-align" @@ -154,11 +158,21 @@ void _jlPutPixel(SDL_Surface *surface, int x, int y, Uint32 pixel) { static void _jlAudioCallback(void *userdata, Uint8 *buffer, int bytes) { - int i = 0; - jlSoundPlayingT *sound = NULL; - jlSoundPlayingT *prev = NULL; - jlSoundPlayingT *temp = NULL; - uint32_t length; + int i = 0; + jlSoundPlayingT *sound = NULL; + jlSoundPlayingT *prev = NULL; + jlSoundPlayingT *temp = NULL; + uint32_t length = 0; + float *data = NULL; + float *out = NULL; + float adjust = 1; + double work = 0; + uint8_t *mix = NULL; + jbool isRight = jfalse; + static uint8_t *samples = NULL; + static uint32_t sampleBufferSize = 0; + + (void)userdata; // Silence buffer. memset(buffer, 0, bytes); @@ -166,8 +180,25 @@ static void _jlAudioCallback(void *userdata, Uint8 *buffer, int bytes) { // Load in music. if (_jlModPlaying) { while (i < bytes) { - //***TODO*** No MOD volume control. - i += pocketmod_render(userdata, buffer + i, bytes - i); + i += pocketmod_render(&_jlModContext, buffer + i, bytes - i); + } + // Adjust volume. + adjust = ((100.0 / 255.0) * (float)_jlModVolume) * 0.01; + data = (float *)buffer; + for (i=0; i<(int)(bytes / sizeof(float)); i+=2) { + data[i] *= adjust; + data[i + 1] *= adjust; + } + } + + // Create a sample buffer for manipulating sound effects. + if (sampleBufferSize < (uint32_t)bytes) { + if (samples) jlFree(samples); + samples = jlMalloc(bytes); + if (samples) { + sampleBufferSize = bytes; + } else { + sampleBufferSize = 0; } } @@ -175,8 +206,32 @@ static void _jlAudioCallback(void *userdata, Uint8 *buffer, int bytes) { if (_jlSoundList) { sound = _jlSoundList; while (sound) { - length = ((uint32_t)bytes > sound->len) ? sound->len : bytes; - SDL_MixAudioFormat(buffer, sound->buffer, AUDIO_FORMAT, length, sound->volume * 0.5); + length = ((uint32_t)bytes > sound->len) ? sound->len : (uint32_t)bytes; + // If we have a sample buffer, move sound to desired channel. + if (samples) { + mix = samples; + out = (float *)samples; + data = (float *)sound->buffer; + for (i=0; i<(int)(length / sizeof(float)); i+=2) { + // Combine channels into a mono sample. + work = (data[i] + data[i + 1]) * 0.5; + // Determine channel. + isRight = (sound->channel & 0x01); + if (_jlSwapChannels) isRight = !isRight; + if (isRight) { + // Move sound into right channel. + out[i] = 0; + out[i + 1] = work; + } else { + // Move sound into left channel. + out[i] = work; + out[i + 1] = 0; + } + } + } else { + mix = sound->buffer; + } + SDL_MixAudioFormat(buffer, mix, AUDIO_FORMAT, length, sound->volume * 0.5); sound->buffer += length; sound->len -= length; // Are we done with this sound? @@ -391,6 +446,11 @@ void jlModStop(void) { } +void jlModVolume(jbyte volume) { + _jlModVolume = volume; +} + + void jlSoundFree(jlSoundT *sound) { jlPlatformSoundT *s = (jlPlatformSoundT *)sound; @@ -662,7 +722,7 @@ int main(int argc, char *argv[]) { _jlAudioFormat.channels = 2; _jlAudioFormat.samples = AUDIO_CHANNELS; _jlAudioFormat.callback = _jlAudioCallback; - _jlAudioFormat.userdata = &_jlModContext; + _jlAudioFormat.userdata = NULL; _jlAudioDevice = SDL_OpenAudioDevice(NULL, 0, &_jlAudioFormat, &_jlAudioFormat, SDL_AUDIO_ALLOW_FREQUENCY_CHANGE); if (!_jlAudioDevice) jlUtilDie(SDL_GetError()); SDL_PauseAudioDevice(_jlAudioDevice, 0); diff --git a/joeylib/src/joey.c b/joeylib/src/joey.c index 4bc22a8..a2cbf68 100644 --- a/joeylib/src/joey.c +++ b/joeylib/src/joey.c @@ -74,6 +74,10 @@ jmp_buf _jlJumpBuffer; jbool _jlIsRunning = jtrue; #endif +#ifndef JL_HAS_SOUNDSWAPCHANNELS +jbool _jlSwapChannels = jfalse; +#endif + static jlColorT _jlDefaultPalette[16]; static jlStackT *_jlFillStackTop = NULL; @@ -797,6 +801,13 @@ void jlModStop(void) { #endif +#ifndef JL_HAS_MODVOLUME +void jlModVolume(jbyte volume) { + (void)volume; +} +#endif + + #ifndef JL_HAS_PALETTEDEFAULT void jlPaletteDefault(void) { jbyte i; @@ -882,6 +893,13 @@ void jlSoundStop(jlSoundT *sound) { #endif +#ifndef JL_HAS_SOUNDSWAPCHANNELS +void jlSoundSwapChannels(jbool swap) { + _jlSwapChannels = swap; +} +#endif + + #ifndef JL_HAS_STNFREE void jlStnFree(jlStnT *stn) { if (stn != NULL) { diff --git a/joeylib/src/joey.h b/joeylib/src/joey.h index 9c07c92..c9146c8 100644 --- a/joeylib/src/joey.h +++ b/joeylib/src/joey.h @@ -98,6 +98,7 @@ typedef unsigned int juint32; #define JL_HAS_MODPAUSE #define JL_HAS_MODPLAY #define JL_HAS_MODSTOP +#define JL_HAS_MODVOLUME #define JL_HAS_PALETTESET #define JL_HAS_PALETTESETFROMIMG #define JL_HAS_SOUNDFREE @@ -142,6 +143,7 @@ typedef unsigned int juint32; #define JL_HAS_MODPAUSE #define JL_HAS_MODPLAY #define JL_HAS_MODSTOP +#define JL_HAS_MODVOLUME #define JL_HAS_PALETTESET #define JL_HAS_PALETTESETFROMIMG #define JL_HAS_SOUNDFREE @@ -186,6 +188,7 @@ typedef unsigned int juint32; #define JL_HAS_MODPAUSE #define JL_HAS_MODPLAY #define JL_HAS_MODSTOP +#define JL_HAS_MODVOLUME #define JL_HAS_PALETTESET #define JL_HAS_PALETTESETFROMIMG #define JL_HAS_SOUNDFREE @@ -440,6 +443,7 @@ jbool _jlModLoad(jlModT **mod, char *filename); void jlModPause(void); void jlModPlay(jlModT *mod); void jlModStop(void); +void jlModVolume(jbyte volume); void jlPaletteDefault(void); //***TODO*** Treat palettes like we do "surfaces" - allow changing STAs or display void jlPaletteSet(jbyte index, jbyte r, jbyte g, jbyte b); //***TODO*** Really need a matching "get" @@ -451,6 +455,7 @@ jbool jlSoundIsPlaying(jlSoundT *sound); jbool _jlSoundLoad(jlSoundT **sound, char *filename); void jlSoundPlay(jlSoundT *sound, jlSoundChannelE channel, jbyte volume); void jlSoundStop(jlSoundT *sound); +void jlSoundSwapChannels(jbool swap); void jlStnFree(jlStnT *stn); #define jlStnLoad(stn, filename) _jlStnLoad((jlStnT **)&(stn), filename, __LINE__, (char *)__FILE__) // Syntatic Sugar diff --git a/joeylib/src/test.c b/joeylib/src/test.c index ac3d6f4..ecc46c1 100644 --- a/joeylib/src/test.c +++ b/joeylib/src/test.c @@ -290,6 +290,7 @@ void musicTest(void) { jlModT *music = NULL; jlSoundT *sound1 = NULL; jlSoundT *sound2 = NULL; + jbyte volume = 0; if (!jlImgLoad(kanga, "kanga")) jlUtilDie("Unable to load kanga.img!"); if (!jlImgLoad(font, "font")) jlUtilDie("Unable to load font.img!"); @@ -302,12 +303,17 @@ void musicTest(void) { jlModPlay(music); - jlSoundPlay(sound2, CHANNEL_FRONT_RIGHT, 255); + //jlSoundSwapChannels(jtrue); jlSoundPlay(sound1, CHANNEL_FRONT_LEFT, 255); + jlSoundPlay(sound2, CHANNEL_FRONT_RIGHT, 255); while (!jlKeyPressed()) { fontPrint(font, NULL, 1, 1, "%dx%d %d %d ", jlGameGetAxis(0), jlGameGetAxis(1), jlGameGetButton(0), jlGameGetButton(1)); jlDisplayPresent(); + if (volume < 255) { + volume++; + jlModVolume(volume); + } } jlKeyRead();