From 619df683b17c65825ca7f8c1cd9095d9cce2c2e2 Mon Sep 17 00:00:00 2001 From: Carsten Teibes Date: Wed, 1 Nov 2023 04:19:01 +0100 Subject: [PATCH] Refactor audio system a bit: - Use SDL2 api - Stronger typed sound functions - Use c++11 features - Proper locking and logging - Cleanup - Fix bugs 101 and (likely) 98 --- CMakeLists.txt | 1 + src/OpenJazz.h | 37 +- src/io/sound.cpp | 469 ++++++++++++++------------ src/io/sound.h | 133 ++++---- src/jj1/level/jj1bullet.cpp | 4 +- src/jj1/level/jj1level.cpp | 6 +- src/jj1/level/jj1level.h | 2 +- src/jj1/level/jj1levelload.cpp | 8 +- src/jj1/level/jj1levelplayer.cpp | 4 +- src/jj1/level/jj1levelplayerframe.cpp | 6 +- src/jj1/scene/jj1scene.cpp | 2 +- src/jj1/scene/jj1scene.h | 3 +- src/jj1/scene/jj1sceneload.cpp | 11 +- src/jj2/level/jj2level.cpp | 2 +- src/jj2/level/jj2levelplayer.cpp | 2 +- src/jj2/level/jj2levelplayerframe.cpp | 2 +- src/level/level.cpp | 2 +- src/menu/gamemenu.cpp | 6 +- src/menu/mainmenu.cpp | 2 +- src/menu/menu.cpp | 12 +- src/menu/setupmenu.cpp | 30 +- src/types.h | 61 ++++ 22 files changed, 431 insertions(+), 374 deletions(-) create mode 100644 src/types.h diff --git a/CMakeLists.txt b/CMakeLists.txt index bf587365..cfe07f40 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -69,6 +69,7 @@ add_executable(OpenJazz src/player/player.h src/setup.cpp src/setup.h + src/types.h src/util.cpp src/util.h src/version.cpp diff --git a/src/OpenJazz.h b/src/OpenJazz.h index 739f8f26..d8e39d6a 100644 --- a/src/OpenJazz.h +++ b/src/OpenJazz.h @@ -37,31 +37,10 @@ #define EXTERN extern #endif +#include "types.h" // Constants -// Numbers in -10 exponent fixed point -#define FE 128 -#define FQ 256 -#define FH 512 -#define F1 1024 -#define F2 2048 -#define F4 4096 -#define F8 8192 -#define F10 10240 -#define F12 12288 -#define F16 16384 -#define F20 20480 -#define F24 24576 -#define F32 32768 -#define F36 36864 -#define F40 40960 -#define F80 81920 -#define F64 65536 -#define F100 102400 -#define F160 163840 -#define F192 196608 - // Standard string length #define STRING_LENGTH 32 @@ -86,18 +65,4 @@ #define MAX_PALETTE_COLORS 256 -// Macros - -// For fixed-point operations -#define FTOI(x) ((x) >> 10) ///< Fixed to Int -#define ITOF(x) ((x) << 10) ///< Int to Fixed -#define MUL(x, y) (((x) * (y)) >> 10) ///< multiplication -#define DIV(x, y) (((x) << 10) / (y)) ///< division - - -// Datatype - -typedef int fixed; ///< Custom fixed-point data type - #endif - diff --git a/src/io/sound.cpp b/src/io/sound.cpp index 2c341cb6..9a846118 100644 --- a/src/io/sound.cpp +++ b/src/io/sound.cpp @@ -28,6 +28,13 @@ #include #include +#include + +#if SDL_VERSION_ATLEAST(2, 0, 0) + #define OJ_SDL2 1 +#else + #define OJ_SDL2 0 +#endif #if defined(__SYMBIAN32__) || defined(_3DS) || defined(PSP) || defined(__vita__) #define SOUND_FREQ 22050 @@ -52,14 +59,70 @@ #define MUSIC_FLAGS MODPLUG_ENABLE_NOISE_REDUCTION | MODPLUG_ENABLE_REVERB | MODPLUG_ENABLE_MEGABASS | MODPLUG_ENABLE_SURROUND #endif -ModPlugFile *musicFile; -SDL_AudioSpec audioSpec; -bool musicPaused = false; -int musicVolume = MAX_VOLUME >> 1; // 50% -int soundVolume = MAX_VOLUME >> 2; // 25% -char *currentMusic = NULL; -int musicTempo = MUSIC_NORMAL; +#define clamp_vol(vol, min, max) (((vol) < (min)) ? (min) : (((vol) > (max)) ? (max) : (vol))) + +// Datatype + +/// Raw sound effect data +typedef struct { + unsigned char *data; + char *name; + int length; +} RawSound; + +/// Resampled sound effect data +typedef struct { + unsigned char *data; + int length; + int position; +} Sound; + +// Variables + +static RawSound *rawSounds; +static int nRawSounds; +static Sound sounds[SE::MAX] = {}; +static bool soundsLoaded = false; +static ModPlugFile *musicFile = nullptr; +static SDL_AudioSpec audioSpec = {}; +static bool musicPaused = false; +static int musicVolume = MAX_VOLUME >> 1; // 50% +static int soundVolume = MAX_VOLUME >> 2; // 25% +static char *currentMusic = nullptr; +static MusicTempo musicTempo = MusicTempo::NORMAL; + +#if OJ_SDL2 +static SDL_AudioDeviceID audioDevice = 0; +#endif +// Helpers + +static void LockAudio() { +#if OJ_SDL2 + SDL_LockAudioDevice(audioDevice); +#else + SDL_LockAudio(); +#endif +} +static void UnlockAudio() { +#if OJ_SDL2 + SDL_UnlockAudioDevice(audioDevice); +#else + SDL_UnlockAudio(); +#endif +} +#if !OJ_SDL2 +int SDL_AUDIO_BITSIZE(int format) { + if (format == AUDIO_U8 || audioSpec.format == AUDIO_S8) + return 8; + else if (format == AUDIO_S16MSB || audioSpec.format == AUDIO_S16LSB || + format == AUDIO_U16MSB || audioSpec.format == AUDIO_U16LSB) + return 16; + + LOG_ERROR("Unsupported Audio format."); + return 0; +} +#endif /** * Callback used to provide data to the audio subsystem. @@ -68,55 +131,43 @@ int musicTempo = MUSIC_NORMAL; * @param stream Output stream * @param len Length of data to be placed in the output stream */ -void audioCallback (void * userdata, unsigned char * stream, int len) { - - (void)userdata; - - int count; - - if (!musicPaused) { - - // Read the next portion of music into the audio stream - - if (musicFile) ModPlug_Read(musicFile, stream, len); +void audioCallback (void * /*userdata*/, unsigned char * stream, int len) { + // Clear audio buffer + memset(stream, '\0', len * sizeof(unsigned char)); + if (musicFile && !musicPaused) { + // Read the next portion of music into the stream + ModPlug_Read(musicFile, stream, len); } - if (!sounds) return; - - for (count = 0; count < 32; count++) { - - if (sounds[count].data && (sounds[count].position >= 0)) { - - // Add the next portion of the sound clip to the audio stream + if (!soundsLoaded) return; - if (len < sounds[count].length - sounds[count].position) { + for (int i = SE::NONE; i < SE::MAX; i++) { + if (!sounds[i].data || sounds[i].position < 0) continue; - // Play as much of the clip as possible - - SDL_MixAudio(stream, - sounds[count].data + sounds[count].position, len, - soundVolume * SDL_MIX_MAXVOLUME / MAX_VOLUME); - - sounds[count].position += len; - - } else { - - // Play the remainder of the clip - - SDL_MixAudio(stream, - sounds[count].data + sounds[count].position, - sounds[count].length - sounds[count].position, - soundVolume * SDL_MIX_MAXVOLUME / MAX_VOLUME); - - sounds[count].position = -1; - - } + int rest = sounds[i].length - sounds[i].position; + int length = 0; + int position = sounds[i].position; + if (len < rest) { + // Play as much of the clip as possible + length = len; + sounds[i].position += len; + } else { + // Play the remainder of the clip + length = rest; + sounds[i].position = -1; } + // Add the next portion of the sound clip to the audio stream +#if OJ_SDL2 + SDL_MixAudioFormat(stream, sounds[i].data + position, audioSpec.format, + length, soundVolume * SDL_MIX_MAXVOLUME / MAX_VOLUME); +#else + SDL_MixAudio(stream, sounds[i].data + position, length, + soundVolume * SDL_MIX_MAXVOLUME / MAX_VOLUME); +#endif } - } @@ -124,31 +175,49 @@ void audioCallback (void * userdata, unsigned char * stream, int len) { * Initialise audio. */ void openAudio () { - - SDL_AudioSpec asDesired; - musicFile = NULL; - // Set up SDL audio - + SDL_AudioSpec asDesired = {}; asDesired.freq = SOUND_FREQ; asDesired.format = AUDIO_S16; asDesired.channels = 2; asDesired.samples = SOUND_SAMPLES; asDesired.callback = audioCallback; - asDesired.userdata = NULL; + asDesired.userdata = nullptr; + + bool audioOk = false; + +#if OJ_SDL2 + audioDevice = SDL_OpenAudioDevice(nullptr, 0, &asDesired, &audioSpec, + SDL_AUDIO_ALLOW_ANY_CHANGE); + + if(!audioDevice || SDL_AUDIO_ISFLOAT(audioSpec.format) || + (SDL_AUDIO_BITSIZE(audioSpec.format) != 8 && SDL_AUDIO_BITSIZE(audioSpec.format) != 16)) { + LOG_DEBUG("SDL audio format unsupported, letting SDL convert it."); + + audioDevice = SDL_OpenAudioDevice(nullptr, 0, &asDesired, &audioSpec, 0); + } + audioOk = (audioDevice != 0); +#else + audioOk = (SDL_OpenAudio(&asDesired, &audioSpec) == 0); +#endif - if (SDL_OpenAudio(&asDesired, &audioSpec) < 0) + if(!audioOk) { LOG_ERROR("Unable to open audio: %s", SDL_GetError()); + return; + } + LOG_DEBUG("Opened %dHz Audio at %d bit, %d channels with %d samples", + audioSpec.freq, SDL_AUDIO_BITSIZE(audioSpec.format), audioSpec.channels, audioSpec.samples); // Load sounds - - if (loadSounds("SOUNDS.000") != E_NONE) sounds = NULL; + soundsLoaded = loadSounds("SOUNDS.000"); // Start audio for sfx to work - +#if OJ_SDL2 + SDL_PauseAudioDevice(audioDevice, 0); +#else SDL_PauseAudio(0); - +#endif } @@ -156,30 +225,25 @@ void openAudio () { * Stop audio. */ void closeAudio () { - stopMusic(); + +#if OJ_SDL2 + SDL_CloseAudioDevice(audioDevice); + audioDevice = 0; +#else SDL_CloseAudio(); +#endif if (rawSounds) { - for (int i = 0; i < nRawSounds; i++) { - delete[] rawSounds[i].data; delete[] rawSounds[i].name; - } delete[] rawSounds; - - } - - if (sounds) { - - freeSounds(); - delete[] sounds; - } + if (soundsLoaded) freeSounds(); } @@ -190,13 +254,6 @@ void closeAudio () { * @param restart Restart music when same file is played. */ void playMusic (const char * fileName, bool restart) { - - File *file; - unsigned char *psmData; - int size; - bool loadOk = false; - ModPlug_Settings settings; - /* Only stop any existing music playing, if a different file should be played or a restart has been requested. */ if ((currentMusic && (strcmp(fileName, currentMusic) == 0)) && !restart) @@ -204,41 +261,35 @@ void playMusic (const char * fileName, bool restart) { stopMusic(); - // Load the music file + LockAudio(); + // Load the music file + File *file; try { - file = new File(fileName, PATH_TYPE_GAME); - } catch (int e) { - + UnlockAudio(); return; - } // Save current music filename - if (currentMusic) delete[] currentMusic; currentMusic = createString(fileName); // Find the size of the file - size = file->getSize(); + int size = file->getSize(); // Read the entire file into memory file->seek(0, true); - psmData = file->loadBlock(size); + unsigned char *psmData = file->loadBlock(size); delete file; // Set up libpsmplug - + ModPlug_Settings settings = {}; settings.mFlags = MUSIC_FLAGS; settings.mChannels = audioSpec.channels; - - if ((audioSpec.format == AUDIO_U8) || (audioSpec.format == AUDIO_S8)) - settings.mBits = 8; - else settings.mBits = 16; - + settings.mBits = SDL_AUDIO_BITSIZE(audioSpec.format); settings.mFrequency = audioSpec.freq; settings.mResamplingMode = MUSIC_RESAMPLEMODE; settings.mReverbDepth = 25; @@ -255,25 +306,21 @@ void playMusic (const char * fileName, bool restart) { // Load the file into libmodplug musicFile = ModPlug_Load(psmData, size); - loadOk = (musicFile != NULL); - delete[] psmData; - if (!loadOk) { - + if (!musicFile) { LOG_ERROR("Could not play music file: %s", fileName); - - return; - + delete[] currentMusic; + currentMusic = nullptr; } // Re-apply volume setting setMusicVolume(musicVolume); // Start the audio playing - SDL_PauseAudio(0); musicPaused = false; + UnlockAudio(); } @@ -291,29 +338,21 @@ void pauseMusic (bool pause) { * Stop the current music. */ void stopMusic () { - // Stop the music playing + LockAudio(); - SDL_PauseAudio(~0); + if (musicFile) { + ModPlug_Unload(musicFile); + musicFile = nullptr; + } // Cleanup - if (currentMusic) { - delete[] currentMusic; - currentMusic = NULL; - - } - - if (musicFile) { - - ModPlug_Unload(musicFile); - musicFile = NULL; - + currentMusic = nullptr; } - SDL_PauseAudio(0); - + UnlockAudio(); } @@ -323,9 +362,7 @@ void stopMusic () { * @return music volume (0-100) */ int getMusicVolume () { - return musicVolume; - } @@ -335,15 +372,12 @@ int getMusicVolume () { * @param volume new volume (0-100) */ void setMusicVolume (int volume) { - - musicVolume = volume; - if (volume < 1) musicVolume = 0; - if (volume > MAX_VOLUME) musicVolume = MAX_VOLUME; + musicVolume = clamp_vol(volume, 0, MAX_VOLUME); // do not access music player settings when not playing + if (!musicFile) return; - if (musicFile) ModPlug_SetMasterVolume(musicFile, musicVolume * 2.56); - + ModPlug_SetMasterVolume(musicFile, musicVolume * 2.56); } @@ -352,10 +386,8 @@ void setMusicVolume (int volume) { * * @return music tempo (MUSIC_NORMAL, MUSIC_FAST) */ -int getMusicTempo () { - +MusicTempo getMusicTempo () { return musicTempo; - } @@ -364,24 +396,13 @@ int getMusicTempo () { * * @param tempo new tempo (MUSIC_NORMAL, MUSIC_FAST) */ -void setMusicTempo (int tempo) { - - if ((tempo != MUSIC_FAST) && (tempo != MUSIC_NORMAL)) - musicTempo = MUSIC_NORMAL; - else - musicTempo = tempo; +void setMusicTempo (MusicTempo tempo) { + musicTempo = tempo; // do not access music player settings when not playing + if (!musicFile) return; - if (musicFile) { - - if (musicTempo == MUSIC_FAST) - ModPlug_SetMusicTempoFactor(musicFile, 80); - else - ModPlug_SetMusicTempoFactor(musicFile, 128); - - } - + ModPlug_SetMusicTempoFactor(musicFile, static_cast(tempo)); } @@ -390,26 +411,13 @@ void setMusicTempo (int tempo) { * * @param fileName Name of a file containing sound clips */ -int loadSounds (const char *fileName) { - +bool loadSounds (const char *fileName) { File *file; try { - file = new File(fileName, PATH_TYPE_GAME); - } catch (int e) { - - return e; - - } - - sounds = new Sound[32]; - - for (int i = 0; i < 32; i++) { - - sounds[i].data = NULL; - + return false; } // Locate the header data @@ -418,9 +426,11 @@ int loadSounds (const char *fileName) { // Calculate number of sounds nRawSounds = (file->getSize() - headerOffset) / 18; + LOG_TRACE("Loading %d sounds...", nRawSounds); - // Load sound clips + assert(nRawSounds < SE::MAX); + // Load sound clips rawSounds = new RawSound[nRawSounds]; for (int i = 0; i < nRawSounds; i++) { @@ -441,13 +451,11 @@ int loadSounds (const char *fileName) { rawSounds[i].data = file->loadBlock(rawSounds[i].length); } - delete file; resampleSounds(); - return E_NONE; - + return true; } @@ -455,105 +463,138 @@ int loadSounds (const char *fileName) { * Resample sound clip data. */ void resampleSound (int index, const char* name, int rate) { + // Skip SE::NONE + int se = index + 1; - int rsFactor; - - if (sounds[index].data) { + if(!isValidSoundIndex(static_cast(se))) { + LOG_ERROR("Cannot resample Sound Index %d", se); + return; + } - delete[] sounds[index].data; - sounds[index].data = NULL; + if (sounds[se].data) { + LOG_TRACE("Overwriting Sound index %d: %s", se, name); + delete[] sounds[se].data; + sounds[se].data = nullptr; } // Search for matching sound - for (int i = 0; i < nRawSounds; i++) { + if (strcmp(name, rawSounds[i].name) != 0) continue; + +#if OJ_SDL2 + // We let SDL2 resample as needed + SDL_AudioCVT cvt; + int res = SDL_BuildAudioCVT(&cvt, AUDIO_S8, 1, rate, audioSpec.format, + audioSpec.channels, audioSpec.freq); + if (res >= 0) { + cvt.len = rawSounds[i].length; + cvt.buf = new unsigned char[cvt.len * cvt.len_mult]; + if(!cvt.buf) { + LOG_ERROR("Cannot create conversion buffer."); + return; + } + memcpy(cvt.buf, rawSounds[i].data, cvt.len); + sounds[se].length = cvt.len; + // only convert, if needed + if (res > 0) { + if((res = SDL_ConvertAudio(&cvt)) == 0) { + // successful + sounds[se].length = cvt.len_cvt; + } + } + } + if(res < 0) { + LOG_WARN("Cannot resample sound effect: %s", SDL_GetError()); + return; + } + // From here it does not matter, if converted or already right samplerate + sounds[se].data = new unsigned char[sounds[se].length]; + if(!sounds[se].data) { + LOG_ERROR("Cannot create buffer for resampled sound effect."); + return; + } + // Copy data over + memcpy(sounds[se].data, cvt.buf, sounds[se].length * sizeof(unsigned char)); + delete[](cvt.buf); +#else + // Calculate the resampling factor + int rsFactor; + if (SDL_AUDIO_BITSIZE(audioSpec.format) == 8) + rsFactor = (F2 * audioSpec.freq) / rate; + else + rsFactor = (F4 * audioSpec.freq) / rate; - if (!strcmp(name, rawSounds[i].name)) { - - // Calculate the resampling factor - if ((audioSpec.format == AUDIO_U8) || (audioSpec.format == AUDIO_S8)) - rsFactor = (F2 * audioSpec.freq) / rate; - else rsFactor = (F4 * audioSpec.freq) / rate; - - sounds[index].length = MUL(rawSounds[i].length, rsFactor); - - // Allocate the buffer for the resampled clip - sounds[index].data = new unsigned char[sounds[index].length]; - - // Resample the clip - for (int sample = 0; sample < sounds[index].length; sample++) - sounds[index].data[sample] = rawSounds[i].data[DIV(sample, rsFactor)]; - - sounds[index].position = -1; + sounds[se].length = MUL(rawSounds[i].length, rsFactor); + // Allocate the buffer for the resampled clip + sounds[se].data = new unsigned char[sounds[se].length]; + if(!sounds[se].data) { + LOG_ERROR("Cannot create buffer for resampled sound effect."); return; - } - } + // Resample the clip + for (int sample = 0; sample < sounds[se].length; sample++) + sounds[se].data[sample] = rawSounds[i].data[DIV(sample, rsFactor)]; +#endif + sounds[se].position = -1; + return; + } } /** * Resample all sound clips to matching indices. */ -void resampleSounds () { - - for (int i = 0; (i < 32) && (i < nRawSounds); i++) { - +void resampleSounds() { + for (int i = 0; i < nRawSounds; i++) { resampleSound(i, rawSounds[i].name, 11025); - } - } /** * Delete resampled sound clip data. */ -void freeSounds () { - - if (sounds) { - - for (int i = 0; i < 32; i++) { - - if (sounds[i].data) delete[] sounds[i].data; - - } +void freeSounds() { + if (!soundsLoaded) return; + for (int i = SE::NONE; i < SE::MAX; i++) { + if (sounds[i].data) delete[] sounds[i].data; } - } /** * Set the sound clip to be played. * - * @param index Number of the sound to play plus one (0 to play no sound) + * @param index Number of the sound to play */ -void playSound (char index) { +void playSound(SE::Type index) { + if (!soundsLoaded) return; - if (sounds && (index > 0) && (index <= 32)) sounds[index - 1].position = 0; + if (!isValidSoundIndex(index)) { + LOG_WARN("Cannot play invalid sound %d", index); + return; + } + sounds[index].position = 0; } /** * Check if a sound clip is playing. * - * @param index Number of the sound to check plus one + * @param index Number of the sound to check * * @return Whether the sound is playing */ -bool isSoundPlaying (char index) { - - if (!sounds || (index <= 0) || (index > 32)) - return false; - - return (sounds[index - 1].position > 0); +bool isSoundPlaying (SE::Type index) { + if (!soundsLoaded || !isValidSoundIndex(index)) return false; + return (sounds[index].position > 0); } @@ -563,9 +604,7 @@ bool isSoundPlaying (char index) { * @return sound volume (0-100) */ int getSoundVolume () { - return soundVolume; - } @@ -575,9 +614,5 @@ int getSoundVolume () { * @param volume new volume (0-100) */ void setSoundVolume (int volume) { - - soundVolume = volume; - if (volume < 1) soundVolume = 0; - if (volume > MAX_VOLUME) soundVolume = MAX_VOLUME; - + soundVolume = clamp_vol(volume, 0, MAX_VOLUME); } diff --git a/src/io/sound.h b/src/io/sound.h index 97e202f9..28753df9 100644 --- a/src/io/sound.h +++ b/src/io/sound.h @@ -17,71 +17,60 @@ * */ -#ifndef _SOUND_H -#define _SOUND_H - +#ifndef OJ_SOUND_H +#define OJ_SOUND_H #include "OpenJazz.h" - // Constants // Sound effects -#define S_INVULN 1 -#define S_MACHGUN 2 -#define S_BOOM 3 -#define S_OW 4 -#define S_YUM 5 -#define S_FIRE 6 -#define S_UPLOOP 7 -#define S_1UP 8 -#define S_PHOTON 9 -#define S_WAIT 10 -#define S_ORB 11 -#define S_JUMPA 12 -#define S_GODLIKE 13 -#define S_YEAHOO 14 -#define S_BIRDY 15 -#define S_FLAMER 16 -#define S_ELECTR 17 -#define S_SPRING 18 -#define S_ROCKET 19 -#define S_STOP 20 -#define S_BLOCK 21 - -#define MAX_VOLUME 100 -#define MUSIC_NORMAL 0 -#define MUSIC_FAST 1 - - -// Datatype - -/// Raw sound effect data -typedef struct { - unsigned char *data; - char *name; - int length; +namespace SE { + enum Type : int { + NONE = 0, + INVULN, + MACHGUN, + BOOM, + OW, + YUM, + FIRE, + UPLOOP, + ONEUP, + PHOTON, + WAIT, + ORB, + JUMPA, + GODLIKE, + YEAHOO, + BIRDY, + FLAMER, + ELECTR, + SPRING, + ROCKET, + STOP, + BLOCK, + CUSTOM_22, + CUSTOM_23, + CUSTOM_24, + CUSTOM_25, + CUSTOM_26, + CUSTOM_27, + CUSTOM_28, + CUSTOM_29, + CUSTOM_30, + CUSTOM_31, + CUSTOM_32, + MAX + }; +}; -} RawSound; - - -/// Resampled sound effect data -typedef struct { - - unsigned char *data; - int length; - int position; - -} Sound; +#define MAX_VOLUME 100 +enum class MusicTempo { NORMAL = 128, FAST = 80 }; // Variables -EXTERN RawSound *rawSounds; -EXTERN int nRawSounds; -EXTERN Sound *sounds; - #if defined(WIZ) || defined(GP2X) EXTERN int volume; EXTERN int volume_direction; @@ -90,23 +79,25 @@ EXTERN int volume_direction; // Functions -EXTERN void openAudio (); -EXTERN void closeAudio (); -EXTERN void playMusic (const char *fileName, bool restart = false); -EXTERN void pauseMusic (bool pause); -EXTERN void stopMusic (); -EXTERN int getMusicVolume (); -EXTERN void setMusicVolume (int volume); -EXTERN int getMusicTempo (); -EXTERN void setMusicTempo (int tempo); -EXTERN int loadSounds (const char *fileName); -EXTERN void resampleSound (int index, const char* name, int rate); -EXTERN void resampleSounds (); -EXTERN void freeSounds (); -EXTERN void playSound (char index); -EXTERN bool isSoundPlaying (char index); -EXTERN int getSoundVolume (); -EXTERN void setSoundVolume (int volume); +void openAudio(); +void closeAudio(); +void playMusic(const char *fileName, bool restart = false); +void pauseMusic(bool pause); +void stopMusic(); +int getMusicVolume(); +void setMusicVolume(int volume); +MusicTempo getMusicTempo(); +void setMusicTempo(MusicTempo tempo); +bool loadSounds(const char *fileName); +void resampleSound(int index, const char* name, int rate); +void resampleSounds(); +void freeSounds(); +void playSound(SE::Type se); +bool isSoundPlaying(SE::Type se); +int getSoundVolume(); +void setSoundVolume(int volume); + +inline void playConfirmSound() { playSound(SE::ORB); } +inline bool isValidSoundIndex(SE::Type index) { return (index >= SE::NONE && index < SE::MAX); } #endif - diff --git a/src/jj1/level/jj1bullet.cpp b/src/jj1/level/jj1bullet.cpp index 05349f49..becd0457 100644 --- a/src/jj1/level/jj1bullet.cpp +++ b/src/jj1/level/jj1bullet.cpp @@ -184,7 +184,9 @@ JJ1Bullet* JJ1Bullet::step (unsigned int ticks) { // If the scenery has been hit and this is not a bouncer, destroy the bullet if (level->checkMaskUp(x, y) && (set[B_BEHAVIOUR] != 4)) { - playSound(set[B_FINISHSOUND]); + // FIXME: rewrite "set" + auto se = static_cast(set[B_FINISHSOUND]); + playSound(se); return remove(); diff --git a/src/jj1/level/jj1level.cpp b/src/jj1/level/jj1level.cpp index fa364596..516b81d5 100644 --- a/src/jj1/level/jj1level.cpp +++ b/src/jj1/level/jj1level.cpp @@ -600,7 +600,9 @@ void JJ1Level::createBullet (JJ1LevelPlayer* sourcePlayer, unsigned char gridX, } - playSound(set[B_STARTSOUND]); + // FIXME: rewrite "set" + auto se = static_cast(set[B_STARTSOUND]); + playSound(se); } @@ -831,7 +833,7 @@ int JJ1Level::play () { returnTime = ticks + T_END; paletteEffects = new WhiteOutPaletteEffect(T_END, paletteEffects); - playSound(S_ORB); + playSound(SE::ORB); } diff --git a/src/jj1/level/jj1level.h b/src/jj1/level/jj1level.h index 5a2534a0..f803fb66 100644 --- a/src/jj1/level/jj1level.h +++ b/src/jj1/level/jj1level.h @@ -142,7 +142,7 @@ typedef struct { unsigned char bulletPeriod; ///< The time between successive bullet shots unsigned char speed; ///< The speed at which the event moves unsigned char animSpeed; ///< The speed of the event's animation - unsigned char sound; ///< The sound played on the appropriate trigger + SE::Type sound; ///< The sound played on the appropriate trigger signed char multiA; ///< Usage depends on event type signed char multiB; ///< Usage depends on event type signed char pieceSize; ///< Size of pieces in bridges, swinging balls chains, etc. diff --git a/src/jj1/level/jj1levelload.cpp b/src/jj1/level/jj1levelload.cpp index d3724f06..6db6cc4e 100644 --- a/src/jj1/level/jj1levelload.cpp +++ b/src/jj1/level/jj1levelload.cpp @@ -732,7 +732,13 @@ int JJ1Level::load (char* fileName, bool checkpoint) { eventSet[count].bulletPeriod = buffer[(count * ELENGTH) + 13]; eventSet[count].speed = buffer[(count * ELENGTH) + 15] + 1; eventSet[count].animSpeed = buffer[(count * ELENGTH) + 17] + 1; - eventSet[count].sound = buffer[(count * ELENGTH) + 21]; + auto se = static_cast(buffer[(count * ELENGTH) + 21]); + if (!isValidSoundIndex(se)) { + eventSet[count].sound = SE::NONE; + LOG_WARN("Event %d has invalid sound effect %d.", count, se); + } else { + eventSet[count].sound = se; + } eventSet[count].multiA = buffer[(count * ELENGTH) + 22]; eventSet[count].multiB = buffer[(count * ELENGTH) + 23]; eventSet[count].pieceSize = buffer[(count * ELENGTH) + 24]; diff --git a/src/jj1/level/jj1levelplayer.cpp b/src/jj1/level/jj1levelplayer.cpp index 65b65ca2..d44cd76d 100644 --- a/src/jj1/level/jj1levelplayer.cpp +++ b/src/jj1/level/jj1levelplayer.cpp @@ -319,7 +319,7 @@ bool JJ1LevelPlayer::hit (Player *source, unsigned int ticks) { } - playSound(S_UPLOOP); + playSound(SE::UPLOOP); if (energy) { @@ -568,7 +568,7 @@ bool JJ1LevelPlayer::takeEvent (JJ1EventType* event, unsigned char gridX, unsign // Speed up music - setMusicTempo(MUSIC_FAST); + setMusicTempo(MusicTempo::FAST); break; diff --git a/src/jj1/level/jj1levelplayerframe.cpp b/src/jj1/level/jj1levelplayerframe.cpp index 0edc2df0..c96b7381 100644 --- a/src/jj1/level/jj1levelplayerframe.cpp +++ b/src/jj1/level/jj1levelplayerframe.cpp @@ -338,7 +338,7 @@ void JJ1LevelPlayer::control (unsigned int ticks) { eventType = JJ1PE_NONE; - playSound(S_PHOTON); + playSound(SE::PHOTON); } else if (((eventType == JJ1PE_NONE) || (eventType == JJ1PE_PLATFORM)) && !player->pcontrols[C_JUMP]) { @@ -517,7 +517,7 @@ void JJ1LevelPlayer::control (unsigned int ticks) { // Replay sound effect before invincibility wears off if ((reaction == PR_INVINCIBLE) && (reactionTime < ticks + 2200)) - if (!isSoundPlaying(S_INVULN)) playSound(S_INVULN); + if (!isSoundPlaying(SE::INVULN)) playSound(SE::INVULN); } @@ -552,7 +552,7 @@ void JJ1LevelPlayer::move (unsigned int ticks) { // reset music speed before running shoes wear off if (fastFeetTime - ticks < T_FASTFEET >> 2) - setMusicTempo(MUSIC_NORMAL); + setMusicTempo(MusicTempo::NORMAL); } else { diff --git a/src/jj1/scene/jj1scene.cpp b/src/jj1/scene/jj1scene.cpp index 1f642202..59a1254d 100644 --- a/src/jj1/scene/jj1scene.cpp +++ b/src/jj1/scene/jj1scene.cpp @@ -47,7 +47,7 @@ */ JJ1SceneFrame::JJ1SceneFrame(int newFrameType, unsigned char* newFrameData, int newFrameSize) { - soundId = 0; + soundId = SE::NONE; frameData = newFrameData; frameType = newFrameType; frameSize = newFrameSize; diff --git a/src/jj1/scene/jj1scene.h b/src/jj1/scene/jj1scene.h index 98181b3c..637af0a8 100644 --- a/src/jj1/scene/jj1scene.h +++ b/src/jj1/scene/jj1scene.h @@ -23,6 +23,7 @@ #define _SCENE_H #include "io/file.h" +#include "types.h" // Enums @@ -189,7 +190,7 @@ class JJ1SceneFrame { unsigned char* frameData; int frameSize; unsigned int frameType; - unsigned char soundId; + SE::Type soundId; JJ1SceneFrame (int frameType, unsigned char* frameData, int frameSize); ~JJ1SceneFrame (); diff --git a/src/jj1/scene/jj1sceneload.cpp b/src/jj1/scene/jj1sceneload.cpp index abfc8cb4..f03888ce 100644 --- a/src/jj1/scene/jj1sceneload.cpp +++ b/src/jj1/scene/jj1sceneload.cpp @@ -377,9 +377,14 @@ void JJ1Scene::loadAni (File *f, int dataIndex) { case ESTAniHeader: // Sound item { - unsigned char soundIndex = f->loadChar(); - animations->lastFrame->soundId = soundIndex; - LOG_MAX("PL Audio tag with index: %d", soundIndex); + auto se = static_cast(f->loadChar()); + if (!isValidSoundIndex(se)) { + LOG_WARN("PL Audio tag with invalid index: %d", se); + animations->lastFrame->soundId = SE::NONE; + } else { + LOG_MAX("PL Audio tag with index: %d", se); + animations->lastFrame->soundId = se; + } LOG_MAX("PL Audio tag play at: %x", f->loadChar()); LOG_MAX("PL Audio tag play offset: %x", f->loadChar()); } diff --git a/src/jj2/level/jj2level.cpp b/src/jj2/level/jj2level.cpp index d3141791..11376c78 100644 --- a/src/jj2/level/jj2level.cpp +++ b/src/jj2/level/jj2level.cpp @@ -491,7 +491,7 @@ int JJ2Level::play () { if (!returnTime) { returnTime = ticks + 3000; - playSound(S_UPLOOP); + playSound(SE::UPLOOP); } diff --git a/src/jj2/level/jj2levelplayer.cpp b/src/jj2/level/jj2levelplayer.cpp index f5509c78..13ab85d5 100644 --- a/src/jj2/level/jj2levelplayer.cpp +++ b/src/jj2/level/jj2levelplayer.cpp @@ -278,7 +278,7 @@ bool JJ2LevelPlayer::hit (Player *source, unsigned int ticks) { //if (bird) bird->hit(); - playSound(S_OW); + playSound(SE::OW); } diff --git a/src/jj2/level/jj2levelplayerframe.cpp b/src/jj2/level/jj2levelplayerframe.cpp index 65f44fcb..e525894a 100644 --- a/src/jj2/level/jj2levelplayerframe.cpp +++ b/src/jj2/level/jj2levelplayerframe.cpp @@ -417,7 +417,7 @@ void JJ2LevelPlayer::control (unsigned int ticks, int msps) { event = JJ2PE_NONE; - playSound(S_JUMPA); + playSound(SE::JUMPA); } diff --git a/src/level/level.cpp b/src/level/level.cpp index 26b3990d..847508cc 100644 --- a/src/level/level.cpp +++ b/src/level/level.cpp @@ -330,7 +330,7 @@ int Level::select (bool& menu, int option) { case 3: // Load - playSound(S_WAIT); + playSound(SE::WAIT); break; diff --git a/src/menu/gamemenu.cpp b/src/menu/gamemenu.cpp index 709cc15f..1d013323 100644 --- a/src/menu/gamemenu.cpp +++ b/src/menu/gamemenu.cpp @@ -114,7 +114,7 @@ int GameMenu::playNewGame (GameModeType mode, char* firstLevel) { Game* game; int ret; - playSound(S_ORB); + playConfirmSound(); if (mode == M_SINGLE) { @@ -316,7 +316,7 @@ int GameMenu::loadGame () { if (controls.release(C_ENTER)) { - playSound(S_ORB); + playConfirmSound(); if (newGameDifficulty(M_SINGLE, levelNum, worldNum) == E_QUIT) return E_QUIT; @@ -419,7 +419,7 @@ int GameMenu::newGameLevel (GameModeType mode) { */ int GameMenu::selectEpisode (GameModeType mode, int episode) { - playSound(S_ORB); + playConfirmSound(); if (episode < 10) { diff --git a/src/menu/mainmenu.cpp b/src/menu/mainmenu.cpp index 7fc23bd1..ccee0209 100644 --- a/src/menu/mainmenu.cpp +++ b/src/menu/mainmenu.cpp @@ -155,7 +155,7 @@ int MainMenu::select (int option) { JJ1Scene *scene; SetupMenu setupMenu; - playSound(S_ORB); + playConfirmSound(); switch (option) { diff --git a/src/menu/menu.cpp b/src/menu/menu.cpp index 3d597801..5e1e6251 100644 --- a/src/menu/menu.cpp +++ b/src/menu/menu.cpp @@ -113,8 +113,7 @@ int Menu::generic (const char** optionNames, int options, int& chosen) { if (controls.release(C_ENTER)) { - playSound(S_ORB); - + playConfirmSound(); return E_NONE; } @@ -132,8 +131,7 @@ int Menu::generic (const char** optionNames, int options, int& chosen) { if (controls.wasCursorReleased()) { - playSound(S_ORB); - + playConfirmSound(); return E_NONE; } @@ -189,7 +187,7 @@ int Menu::textInput (const char* request, char*& text, bool ip) { if (res) { - playSound(S_ORB); + playConfirmSound(); delete[] text; text = input; @@ -202,7 +200,7 @@ int Menu::textInput (const char* request, char*& text, bool ip) { if (PSVITA_InputString(request, text, input)) { - playSound(S_ORB); + playConfirmSound(); delete[] text; text = input; @@ -334,7 +332,7 @@ int Menu::textInput (const char* request, char*& text, bool ip) { if (controls.release(C_ENTER)) { - playSound(S_ORB); + playConfirmSound(); // Replace the original string with the input string delete[] text; diff --git a/src/menu/setupmenu.cpp b/src/menu/setupmenu.cpp index 85c538a6..f30de8a3 100644 --- a/src/menu/setupmenu.cpp +++ b/src/menu/setupmenu.cpp @@ -79,9 +79,7 @@ int SetupMenu::setupKeyboard () { if (progress == PCONTROLS) { // If all controls have been assigned, return - - playSound(S_ORB); - + playConfirmSound(); return E_NONE; } @@ -165,9 +163,7 @@ int SetupMenu::setupJoystick () { if (progress == PCONTROLS) { // If all controls have been assigned, return - - playSound(S_ORB); - + playConfirmSound(); return E_NONE; } @@ -195,9 +191,7 @@ int SetupMenu::setupJoystick () { if (progress == PCONTROLS) { // If all controls have been assigned, return - - playSound(S_ORB); - + playConfirmSound(); return E_NONE; } @@ -225,9 +219,7 @@ int SetupMenu::setupJoystick () { if (progress == PCONTROLS) { // If all controls have been assigned, return - - playSound(S_ORB); - + playConfirmSound(); return E_NONE; } @@ -266,9 +258,7 @@ int SetupMenu::setupJoystick () { if (progress == PCONTROLS) { // If all controls have been assigned, return - - playSound(S_ORB); - + playConfirmSound(); return E_NONE; } @@ -447,7 +437,7 @@ int SetupMenu::setupResolution () { // Apply resolution change if (controls.release(C_ENTER)) { - playSound(S_ORB); + playConfirmSound(); if (video.reset(screenW, screenH)) { @@ -533,7 +523,7 @@ int SetupMenu::setupScaling () { // Check for a scaling change if (scaleFactor != video.getScaleFactor()) { - playSound(S_ORB); + playConfirmSound(); scaleFactor = video.setScaleFactor(scaleFactor); } @@ -578,7 +568,7 @@ int SetupMenu::setupSound () { if ((x >= 0) && (x < (MAX_VOLUME >> 1)) && (y >= 0) && (y < 11)) setMusicVolume(x << 1); if ((x >= 0) && (x < (MAX_VOLUME >> 1)) && (y >= 16) && (y < 27)) setSoundVolume(x << 1); - if (controls.wasCursorReleased()) playSound(S_ORB); + if (controls.wasCursorReleased()) playConfirmSound(); } @@ -609,7 +599,7 @@ int SetupMenu::setupSound () { if (soundActive) setSoundVolume(getSoundVolume() - 4); else setMusicVolume(getMusicVolume() - 4); - playSound(S_ORB); + playConfirmSound(); } @@ -618,7 +608,7 @@ int SetupMenu::setupSound () { if (soundActive) setSoundVolume(getSoundVolume() + 4); else setMusicVolume(getMusicVolume() + 4); - playSound(S_ORB); + playConfirmSound(); } diff --git a/src/types.h b/src/types.h new file mode 100644 index 00000000..f69550b7 --- /dev/null +++ b/src/types.h @@ -0,0 +1,61 @@ + +/** + * + * @file types.h + * + * Part of the OpenJazz project + * + * @par Licence: + * Copyright (c) 2005-2017 Alister Thomson + * Copyright (c) 2015-2023 Carsten Teibes + * + * OpenJazz is distributed under the terms of + * the GNU General Public License, version 2.0 + * + */ + + +#ifndef OJ_TYPES_H +#define OJ_TYPES_H + +// Constants + +// Numbers in -10 exponent fixed point +#define FE 128 +#define FQ 256 +#define FH 512 +#define F1 1024 +#define F2 2048 +#define F4 4096 +#define F8 8192 +#define F10 10240 +#define F12 12288 +#define F16 16384 +#define F20 20480 +#define F24 24576 +#define F32 32768 +#define F36 36864 +#define F40 40960 +#define F80 81920 +#define F64 65536 +#define F100 102400 +#define F160 163840 +#define F192 196608 + +// Macros + +// For fixed-point operations +#define FTOI(x) ((x) >> 10) ///< Fixed to Int +#define ITOF(x) ((x) << 10) ///< Int to Fixed +#define MUL(x, y) (((x) * (y)) >> 10) ///< multiplication +#define DIV(x, y) (((x) << 10) / (y)) ///< division + +// Datatypes + +typedef int fixed; ///< Custom fixed-point data type + +namespace SE { + enum Type : int; ///< Sound Index type +} + +#endif